Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
/***
|Name|CalendarPlugin|
|Source|http://www.TiddlyTools.com/#CalendarPlugin|
|Version|1.5.1|
|Author|Eric Shulman|
|Original Author|SteveRumsby|
|License|unknown|
|~CoreVersion|2.1|
|Type|plugin|
|Description|display monthly and yearly calendars|
NOTE: For //enhanced// date popup display, optionally install [[DatePlugin]] and [[ReminderMacros]]
!!!Usage:
<<<
|{{{<<calendar>>}}}|full-year calendar for the current year|
|{{{<<calendar year>>}}}|full-year calendar for the specified year|
|{{{<<calendar year month>>}}}|one month calendar for the specified month and year|
|{{{<<calendar thismonth>>}}}|one month calendar for the current month|
|{{{<<calendar lastmonth>>}}}|one month calendar for last month|
|{{{<<calendar nextmonth>>}}}|one month calendar for next month|
|{{{<<calendar +n>>}}}<br>{{{<<calendar -n>>}}}|one month calendar for a month +/- 'n' months from now|
<<<
!!!Configuration:
<<<
|''First day of week:''<br>{{{config.options.txtCalFirstDay}}}|<<option txtCalFirstDay>>|(Monday = 0, Sunday = 6)|
|''First day of weekend:''<br>{{{config.options.txtCalStartOfWeekend}}}|<<option txtCalStartOfWeekend>>|(Monday = 0, Sunday = 6)|
<<option chkDisplayWeekNumbers>> Display week numbers //(note: Monday will be used as the start of the week)//
|''Week number display format:''<br>{{{config.options.txtWeekNumberDisplayFormat }}}|<<option txtWeekNumberDisplayFormat >>|
|''Week number link format:''<br>{{{config.options.txtWeekNumberLinkFormat }}}|<<option txtWeekNumberLinkFormat >>|
<<<
!!!Revisions
<<<
2011.01.04 1.5.1 corrected parameter handling for {{{<<calendar year>>}}} to show entire year instead of just first month. In createCalendarMonthHeader(), fixed next/previous month year calculation (use parseInt() to convert to numeric value). Code reduction (setting options).
2009.04.31 1.5.0 rewrote onClickCalendarDate() (popup handler) and added config.options.txtCalendarReminderTags. Partial code reduction/cleanup. Assigned true version number (1.5.0)
2008.09.10 added '+n' (and '-n') param to permit display of relative months (e.g., '+6' means 'six months from now', '-3' means 'three months ago'. Based on suggestion from Jean.
2008.06.17 added support for config.macros.calendar.todaybg
2008.02.27 in handler(), DON'T set hard-coded default date format, so that *customized* value (pre-defined in config.macros.calendar.journalDateFmt is used.
2008.02.17 in createCalendarYear(), fix next/previous year calculation (use parseInt() to convert to numeric value). Also, use journalDateFmt for date linking when NOT using [[DatePlugin]].
2008.02.16 in createCalendarDay(), week numbers now created as TiddlyLinks, allowing quick creation/navigation to 'weekly' journals (based on request from Kashgarinn)
2008.01.08 in createCalendarMonthHeader(), 'month year' heading is now created as TiddlyLink, allowing quick creation/navigation to 'month-at-a-time' journals
2007.11.30 added 'return false' to onclick handlers (prevent IE from opening blank pages)
2006.08.23 added handling for weeknumbers (code supplied by Martin Budden (see 'wn**' comment marks). Also, incorporated updated by Jeremy Sheeley to add caching for reminders (see [[ReminderMacros]], if installed)
2005.10.30 in config.macros.calendar.handler(), use 'tbody' element for IE compatibility. Also, fix year calculation for IE's getYear() function (which returns '2005' instead of '105'). Also, in createCalendarDays(), use showDate() function (see [[DatePlugin]], if installed) to render autostyled date with linked popup. Updated calendar stylesheet definition: use .calendar class-specific selectors, add text centering and margin settings
2006.05.29 added journalDateFmt handling
<<<
!!!Code
***/
//{{{
version.extensions.CalendarPlugin= { major: 1, minor: 5, revision: 1, date: new Date(2011,1,4)};
// COOKIE OPTIONS
var opts={
txtCalFirstDay: 0,
txtCalStartOfWeekend: 5,
chkDisplayWeekNumbers: false,
txtCalFirstDay: 0,
txtWeekNumberDisplayFormat: 'w0WW',
txtWeekNumberLinkFormat: 'YYYY-w0WW',
txtCalendarReminderTags: 'reminder'
};
for (var id in opts) if (config.options[id]===undefined) config.options[id]=opts[id];
// INTERNAL CONFIGURATION
config.macros.calendar = {
monthnames:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
daynames:['M','T','W','T','F','S','S'],
todaybg:'#ccccff',
weekendbg:'#c0c0c0',
monthbg:'#e0e0e0',
holidaybg:'#ffc0c0',
journalDateFmt:'DD MMM YYYY',
monthdays:[31,28,31,30,31,30,31,31,30,31,30,31],
holidays:[ ] // for customization see [[CalendarPluginConfig]]
};
//}}}
//{{{
function calendarIsHoliday(date)
{
var longHoliday = date.formatString('0DD/0MM/YYYY');
var shortHoliday = date.formatString('0DD/0MM');
for(var i = 0; i < config.macros.calendar.holidays.length; i++) {
if( config.macros.calendar.holidays[i]==longHoliday
|| config.macros.calendar.holidays[i]==shortHoliday)
return true;
}
return false;
}
//}}}
//{{{
config.macros.calendar.handler = function(place,macroName,params) {
var calendar = createTiddlyElement(place, 'table', null, 'calendar', null);
var tbody = createTiddlyElement(calendar, 'tbody');
var today = new Date();
var year = today.getYear();
if (year<1900) year+=1900;
// get journal format from SideBarOptions (ELS 5/29/06 - suggested by MartinBudden)
var text = store.getTiddlerText('SideBarOptions');
var re = new RegExp('<<(?:newJournal)([^>]*)>>','mg'); var fm = re.exec(text);
if (fm && fm[1]!=null) { var pa=fm[1].readMacroParams(); if (pa[0]) this.journalDateFmt = pa[0]; }
var month=-1;
if (params[0] == 'thismonth') {
var month=today.getMonth();
} else if (params[0] == 'lastmonth') {
var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
} else if (params[0] == 'nextmonth') {
var month = today.getMonth()+1; if (month>11) { month=0; year++; }
} else if (params[0]&&'+-'.indexOf(params[0].substr(0,1))!=-1) {
var month = today.getMonth()+parseInt(params[0]);
if (month>11) { year+=Math.floor(month/12); month%=12; };
if (month<0) { year+=Math.floor(month/12); month=12+month%12; }
} else if (params[0]) {
year = params[0];
if(params[1]) {
month=parseInt(params[1])-1;
if (month>11) month=11; if (month<0) month=0;
}
}
if (month!=-1) {
cacheReminders(new Date(year, month, 1, 0, 0), 31);
createCalendarOneMonth(tbody, year, month);
} else {
cacheReminders(new Date(year, 0, 1, 0, 0), 366);
createCalendarYear(tbody, year);
}
window.reminderCacheForCalendar = null;
}
//}}}
//{{{
// cache used to store reminders while the calendar is being rendered
// it will be renulled after the calendar is fully rendered.
window.reminderCacheForCalendar = null;
//}}}
//{{{
function cacheReminders(date, leadtime)
{
if (window.findTiddlersWithReminders == null) return;
window.reminderCacheForCalendar = {};
var leadtimeHash = [];
leadtimeHash [0] = 0;
leadtimeHash [1] = leadtime;
var t = findTiddlersWithReminders(date, leadtimeHash, null, 1);
for(var i = 0; i < t.length; i++) {
//just tag it in the cache, so that when we're drawing days, we can bold this one.
window.reminderCacheForCalendar[t[i]['matchedDate']] = 'reminder:' + t[i]['params']['title'];
}
}
//}}}
//{{{
function createCalendarOneMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, 'tr');
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon]+' '+year, true, year, mon);
row = createTiddlyElement(calendar, 'tr');
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, 'tr');
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon]+' '+ year, false, year, mon);
row = createTiddlyElement(calendar, 'tr');
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarYear(calendar, year)
{
var row;
row = createTiddlyElement(calendar, 'tr');
var back = createTiddlyElement(row, 'td');
var backHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, parseInt(year)-1);
return false; // consume click
};
createTiddlyButton(back, '<', 'Previous year', backHandler);
back.align = 'center';
var yearHeader = createTiddlyElement(row, 'td', null, 'calendarYear', year);
yearHeader.align = 'center';
yearHeader.setAttribute('colSpan',config.options.chkDisplayWeekNumbers?22:19);//wn**
var fwd = createTiddlyElement(row, 'td');
var fwdHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, parseInt(year)+1);
return false; // consume click
};
createTiddlyButton(fwd, '>', 'Next year', fwdHandler);
fwd.align = 'center';
createCalendarMonthRow(calendar, year, 0);
createCalendarMonthRow(calendar, year, 3);
createCalendarMonthRow(calendar, year, 6);
createCalendarMonthRow(calendar, year, 9);
}
//}}}
//{{{
function createCalendarMonthRow(cal, year, mon)
{
var row = createTiddlyElement(cal, 'tr');
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);
row = createTiddlyElement(cal, 'tr');
createCalendarDayHeader(row, 3);
createCalendarDayRows(cal, year, mon);
}
//}}}
//{{{
function createCalendarMonthHeader(cal, row, name, nav, year, mon)
{
var month;
if (nav) {
var back = createTiddlyElement(row, 'td');
back.align = 'center';
back.style.background = config.macros.calendar.monthbg;
var backMonHandler = function() {
var newyear = year;
var newmon = mon-1;
if(newmon == -1) { newmon = 11; newyear = parseInt(newyear)-1;}
removeChildren(cal);
cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
createCalendarOneMonth(cal, newyear, newmon);
return false; // consume click
};
createTiddlyButton(back, '<', 'Previous month', backMonHandler);
month = createTiddlyElement(row, 'td', null, 'calendarMonthname')
createTiddlyLink(month,name,true);
month.setAttribute('colSpan', config.options.chkDisplayWeekNumbers?6:5);//wn**
var fwd = createTiddlyElement(row, 'td');
fwd.align = 'center';
fwd.style.background = config.macros.calendar.monthbg;
var fwdMonHandler = function() {
var newyear = year;
var newmon = mon+1;
if(newmon == 12) { newmon = 0; newyear = parseInt(newyear)+1;}
removeChildren(cal);
cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
createCalendarOneMonth(cal, newyear, newmon);
return false; // consume click
};
createTiddlyButton(fwd, '>', 'Next month', fwdMonHandler);
} else {
month = createTiddlyElement(row, 'td', null, 'calendarMonthname', name)
month.setAttribute('colSpan',config.options.chkDisplayWeekNumbers?8:7);//wn**
}
month.align = 'center';
month.style.background = config.macros.calendar.monthbg;
}
//}}}
//{{{
function createCalendarDayHeader(row, num)
{
var cell;
for(var i = 0; i < num; i++) {
if (config.options.chkDisplayWeekNumbers) createTiddlyElement(row, 'td');//wn**
for(var j = 0; j < 7; j++) {
var d = j + (config.options.txtCalFirstDay - 0);
if(d > 6) d = d - 7;
cell = createTiddlyElement(row, 'td', null, null, config.macros.calendar.daynames[d]);
if(d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))
cell.style.background = config.macros.calendar.weekendbg;
}
}
}
//}}}
//{{{
function createCalendarDays(row, col, first, max, year, mon) {
var i;
if (config.options.chkDisplayWeekNumbers){
if (first<=max) {
var ww = new Date(year,mon,first);
var td=createTiddlyElement(row, 'td');//wn**
var link=createTiddlyLink(td,ww.formatString(config.options.txtWeekNumberLinkFormat),false);
link.appendChild(document.createTextNode(
ww.formatString(config.options.txtWeekNumberDisplayFormat)));
}
else createTiddlyElement(row, 'td');//wn**
}
for(i = 0; i < col; i++)
createTiddlyElement(row, 'td');
var day = first;
for(i = col; i < 7; i++) {
var d = i + (config.options.txtCalFirstDay - 0);
if(d > 6) d = d - 7;
var daycell = createTiddlyElement(row, 'td');
var isaWeekend=((d==(config.options.txtCalStartOfWeekend-0)
|| d==(config.options.txtCalStartOfWeekend-0+1))?true:false);
if(day > 0 && day <= max) {
var celldate = new Date(year, mon, day);
// ELS 10/30/05 - use <<date>> macro's showDate() function to create popup
// ELS 05/29/06 - use journalDateFmt
if (window.showDate) showDate(daycell,celldate,'popup','DD',
config.macros.calendar.journalDateFmt,true, isaWeekend);
else {
if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;
var title = celldate.formatString(config.macros.calendar.journalDateFmt);
if(calendarIsHoliday(celldate))
daycell.style.background = config.macros.calendar.holidaybg;
var now=new Date();
if ((now-celldate>=0) && (now-celldate<86400000)) // is today?
daycell.style.background = config.macros.calendar.todaybg;
if(window.findTiddlersWithReminders == null) {
var link = createTiddlyLink(daycell, title, false);
link.appendChild(document.createTextNode(day));
} else
var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);
}
}
day++;
}
}
//}}}
//{{{
// Create a pop-up containing:
// * a link to a tiddler for this date
// * a 'new tiddler' link to add a reminder for this date
// * links to current reminders for this date
// NOTE: this code is only used if [[ReminderMacros]] is installed AND [[DatePlugin]] is //not// installed.
function onClickCalendarDate(ev) { ev=ev||window.event;
var d=new Date(this.getAttribute('title')); var date=d.formatString(config.macros.calendar.journalDateFmt);
var p=Popup.create(this); if (!p) return;
createTiddlyLink(createTiddlyElement(p,'li'),date,true);
var rem='\\n\\<\\<reminder day:%0 month:%1 year:%2 title: \\>\\>';
rem=rem.format([d.getDate(),d.getMonth()+1,d.getYear()+1900]);
var cmd="<<newTiddler label:[[new reminder...]] prompt:[[add a new reminder to '%0']]"
+" title:[[%0]] text:{{store.getTiddlerText('%0','')+'%1'}} tag:%2>>";
wikify(cmd.format([date,rem,config.options.txtCalendarReminderTags]),p);
createTiddlyElement(p,'hr');
var t=findTiddlersWithReminders(d,[0,31],null,1);
for(var i=0; i<t.length; i++) {
var link=createTiddlyLink(createTiddlyElement(p,'li'), t[i].tiddler, false);
link.appendChild(document.createTextNode(t[i]['params']['title']));
}
Popup.show(); ev.cancelBubble=true; if (ev.stopPropagation) ev.stopPropagation(); return false;
}
//}}}
//{{{
function calendarMaxDays(year, mon)
{
var max = config.macros.calendar.monthdays[mon];
if(mon == 1 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) max++;
return max;
}
//}}}
//{{{
function createCalendarDayRows(cal, year, mon)
{
var row = createTiddlyElement(cal, 'tr');
var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first1 < 0) first1 = first1 + 7;
var day1 = -first1 + 1;
var first2 = (new Date(year, mon+1, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first2 < 0) first2 = first2 + 7;
var day2 = -first2 + 1;
var first3 = (new Date(year, mon+2, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first3 < 0) first3 = first3 + 7;
var day3 = -first3 + 1;
var max1 = calendarMaxDays(year, mon);
var max2 = calendarMaxDays(year, mon+1);
var max3 = calendarMaxDays(year, mon+2);
while(day1 <= max1 || day2 <= max2 || day3 <= max3) {
row = createTiddlyElement(cal, 'tr');
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;
createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;
}
}
//}}}
//{{{
function createCalendarDayRowsSingle(cal, year, mon)
{
var row = createTiddlyElement(cal, 'tr');
var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first1 < 0) first1 = first1+ 7;
var day1 = -first1 + 1;
var max1 = calendarMaxDays(year, mon);
while(day1 <= max1) {
row = createTiddlyElement(cal, 'tr');
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
}
}
//}}}
//{{{
setStylesheet('.calendar, .calendar table, .calendar th, .calendar tr, .calendar td { text-align:center; } .calendar, .calendar a { margin:0px !important; padding:0px !important; }', 'calendarStyles');
//}}}
/***
|Name|CheckboxPlugin|
|Source|http://www.TiddlyTools.com/#CheckboxPlugin|
|Documentation|http://www.TiddlyTools.com/#CheckboxPluginInfo|
|Version|2.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Add checkboxes to your tiddler content|
This plugin extends the TiddlyWiki syntax to allow definition of checkboxes that can be embedded directly in tiddler content. Checkbox states are preserved by:
* by setting/removing tags on specified tiddlers,
* or, by setting custom field values on specified tiddlers,
* or, by saving to a locally-stored cookie ID,
* or, automatically modifying the tiddler content (deprecated)
When an ID is assigned to the checkbox, it enables direct programmatic access to the checkbox DOM element, as well as creating an entry in TiddlyWiki's config.options[ID] internal data. In addition to tracking the checkbox state, you can also specify custom javascript for programmatic initialization and onClick event handling for any checkbox, so you can provide specialized side-effects in response to state changes.
!!!!!Documentation
>see [[CheckboxPluginInfo]]
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to [[CheckboxPluginInfo]]
2008.01.05 [2.4.0] set global "window.place" to current checkbox element when processing checkbox clicks. This allows init/beforeClick/afterClick handlers to reference RELATIVE elements, including using "story.findContainingTiddler(place)". Also, wrap handlers in "function()" so "return" can be used within handler code.
|please see [[CheckboxPluginInfo]] for additional revision details|
2005.12.07 [0.9.0] initial BETA release
<<<
!!!!!Code
***/
//{{{
version.extensions.CheckboxPlugin = {major: 2, minor: 4, revision:0 , date: new Date(2008,1,5)};
//}}}
//{{{
config.checkbox = { refresh: { tagged:true, tagging:true, container:true } };
config.formatters.push( {
name: "checkbox",
match: "\\[[xX_ ][\\]\\=\\(\\{]",
lookahead: "\\[([xX_ ])(=[^\\s\\(\\]{]+)?(\\([^\\)]*\\))?({[^}]*})?({[^}]*})?({[^}]*})?\\]",
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
// get params
var checked=(lookaheadMatch[1].toUpperCase()=="X");
var id=lookaheadMatch[2];
var target=lookaheadMatch[3];
if (target) target=target.substr(1,target.length-2).trim(); // trim off parentheses
var fn_init=lookaheadMatch[4];
var fn_clickBefore=lookaheadMatch[5];
var fn_clickAfter=lookaheadMatch[6];
var tid=story.findContainingTiddler(w.output); if (tid) tid=tid.getAttribute("tiddler");
var srctid=w.tiddler?w.tiddler.title:null;
config.macros.checkbox.create(w.output,tid,srctid,w.matchStart+1,checked,id,target,config.checkbox.refresh,fn_init,fn_clickBefore,fn_clickAfter);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} );
config.macros.checkbox = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if(!(tiddler instanceof Tiddler)) { // if no tiddler passed in try to find one
var here=story.findContainingTiddler(place);
if (here) tiddler=store.getTiddler(here.getAttribute("tiddler"))
}
var srcpos=0; // "inline X" not applicable to macro syntax
var target=params.shift(); if (!target) target="";
var defaultState=params[0]=="checked"; if (defaultState) params.shift();
var id=params.shift(); if (id && !id.length) id=null;
var fn_init=params.shift(); if (fn_init && !fn_init.length) fn_init=null;
var fn_clickBefore=params.shift();
if (fn_clickBefore && !fn_clickBefore.length) fn_clickBefore=null;
var fn_clickAfter=params.shift();
if (fn_clickAfter && !fn_clickAfter.length) fn_clickAfter=null;
var refresh={ tagged:true, tagging:true, container:false };
this.create(place,tiddler.title,tiddler.title,0,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter);
},
create: function(place,tid,srctid,srcpos,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter) {
// create checkbox element
var c = document.createElement("input");
c.setAttribute("type","checkbox");
c.onclick=this.onClickCheckbox;
c.srctid=srctid; // remember source tiddler
c.srcpos=srcpos; // remember location of "X"
c.container=tid; // containing tiddler (may be null if not in a tiddler)
c.tiddler=tid; // default target tiddler
c.refresh = {};
c.refresh.container = refresh.container;
c.refresh.tagged = refresh.tagged;
c.refresh.tagging = refresh.tagging;
place.appendChild(c);
// set default state
c.checked=defaultState;
// track state in config.options.ID
if (id) {
c.id=id.substr(1); // trim off leading "="
if (config.options[c.id]!=undefined)
c.checked=config.options[c.id];
else
config.options[c.id]=c.checked;
}
// track state in (tiddlername|tagname) or (fieldname@tiddlername)
if (target) {
var pos=target.indexOf("@");
if (pos!=-1) {
c.field=pos?target.substr(0,pos):"checked"; // get fieldname (or use default "checked")
c.tiddler=target.substr(pos+1); // get specified tiddler name (if any)
if (!c.tiddler || !c.tiddler.length) c.tiddler=tid; // if tiddler not specified, default == container
if (store.getValue(c.tiddler,c.field)!=undefined)
c.checked=(store.getValue(c.tiddler,c.field)=="true"); // set checkbox from saved state
} else {
var pos=target.indexOf("|"); if (pos==-1) var pos=target.indexOf(":");
c.tag=target;
if (pos==0) c.tag=target.substr(1); // trim leading "|" or ":"
if (pos>0) { c.tiddler=target.substr(0,pos); c.tag=target.substr(pos+1); }
if (!c.tag.length) c.tag="checked";
var t=store.getTiddler(c.tiddler);
if (t && t.tags)
c.checked=t.isTagged(c.tag); // set checkbox from saved state
}
}
// trim off surrounding { and } delimiters from init/click handlers
if (fn_init) c.fn_init="(function(){"+fn_init.trim().substr(1,fn_init.length-2)+"})()";
if (fn_clickBefore) c.fn_clickBefore="(function(){"+fn_clickBefore.trim().substr(1,fn_clickBefore.length-2)+"})()";
if (fn_clickAfter) c.fn_clickAfter="(function(){"+fn_clickAfter.trim().substr(1,fn_clickAfter.length-2)+"})()";
c.init=true; c.onclick(); c.init=false; // compute initial state and save in tiddler/config/cookie
},
onClickCheckbox: function(event) {
window.place=this;
if (this.init && this.fn_init) // custom function hook to set initial state (run only once)
{ try { eval(this.fn_init); } catch(e) { displayMessage("Checkbox init error: "+e.toString()); } }
if (!this.init && this.fn_clickBefore) // custom function hook to override changes in checkbox state
{ try { eval(this.fn_clickBefore) } catch(e) { displayMessage("Checkbox onClickBefore error: "+e.toString()); } }
if (this.id)
// save state in config AND cookie (only when ID starts with 'chk')
{ config.options[this.id]=this.checked; if (this.id.substr(0,3)=="chk") saveOptionCookie(this.id); }
if (this.srctid && this.srcpos>0 && (!this.id || this.id.substr(0,3)!="chk") && !this.tag && !this.field) {
// save state in tiddler content only if not using cookie, tag or field tracking
var t=store.getTiddler(this.srctid); // put X in original source tiddler (if any)
if (t && this.checked!=(t.text.substr(this.srcpos,1).toUpperCase()=="X")) { // if changed
t.set(null,t.text.substr(0,this.srcpos)+(this.checked?"X":"_")+t.text.substr(this.srcpos+1),null,null,t.tags);
if (!story.isDirty(t.title)) story.refreshTiddler(t.title,null,true);
store.setDirty(true);
}
}
if (this.field) {
if (this.checked && !store.tiddlerExists(this.tiddler))
store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
// set the field value in the target tiddler
store.setValue(this.tiddler,this.field,this.checked?"true":"false");
// DEBUG: displayMessage(this.field+"@"+this.tiddler+" is "+this.checked);
}
if (this.tag) {
if (this.checked && !store.tiddlerExists(this.tiddler))
store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
var t=store.getTiddler(this.tiddler);
if (t) {
var tagged=(t.tags && t.tags.indexOf(this.tag)!=-1);
if (this.checked && !tagged) { t.tags.push(this.tag); store.setDirty(true); }
if (!this.checked && tagged) { t.tags.splice(t.tags.indexOf(this.tag),1); store.setDirty(true); }
}
// if tag state has been changed, update display of corresponding tiddlers (unless they are in edit mode...)
if (this.checked!=tagged) {
if (this.refresh.tagged) {
if (!story.isDirty(this.tiddler)) // the TAGGED tiddler in view mode
story.refreshTiddler(this.tiddler,null,true);
else // the TAGGED tiddler in edit mode (with tags field)
config.macros.checkbox.refreshEditorTagField(this.tiddler,this.tag,this.checked);
}
if (this.refresh.tagging)
if (!story.isDirty(this.tag)) story.refreshTiddler(this.tag,null,true); // the TAGGING tiddler
}
}
if (!this.init && this.fn_clickAfter) // custom function hook to react to changes in checkbox state
{ try { eval(this.fn_clickAfter) } catch(e) { displayMessage("Checkbox onClickAfter error: "+e.toString()); } }
// refresh containing tiddler (but not during initial rendering, or we get an infinite loop!) (and not when editing container)
if (!this.init && this.refresh.container && this.container!=this.tiddler)
if (!story.isDirty(this.container)) story.refreshTiddler(this.container,null,true); // the tiddler CONTAINING the checkbox
return true;
},
refreshEditorTagField: function(title,tag,set) {
var tagfield=story.getTiddlerField(title,"tags");
if (!tagfield||tagfield.getAttribute("edit")!="tags") return; // if no tags field in editor (i.e., custom template)
var tags=tagfield.value.readBracketedList();
if (tags.contains(tag)==set) return; // if no change needed
if (set) tags.push(tag); // add tag
else tags.splice(tags.indexOf(tag),1); // remove tag
for (var t=0;t<tags.length;t++) tags[t]=String.encodeTiddlyLink(tags[t]);
tagfield.value=tags.join(" "); // reassemble tag string (with brackets as needed)
return;
}
};
//}}}
/***
|Name|[[DatePlugin]]|
|Source|http://www.TiddlyTools.com/#DatePlugin|
|Documentation|http://www.TiddlyTools.com/#DatePluginInfo|
|Version|2.7.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|formatted dates plus popup menu with 'journal' link, changes and (optional) reminders|
This plugin provides a general approach to displaying formatted dates and/or links and popups that permit easy navigation and management of tiddlers based on their creation/modification dates.
!!!!!Documentation
>see [[DatePluginInfo]]
!!!!!Configuration
<<<
<<option chkDatePopupHideCreated>> omit 'created' section from date popups
<<option chkDatePopupHideChanged>> omit 'changed' section from date popups
<<option chkDatePopupHideTagged>> omit 'tagged' section from date popups
<<option chkDatePopupHideReminders>> omit 'reminders' section from date popups
<<option chkShowJulianDate>> display Julian day number (1-365) below current date
see [[DatePluginConfig]] for additional configuration settings, for use in calendar displays, including:
*date formats
*color-coded backgrounds
*annual fixed-date holidays
*weekends
<<<
!!!!!Revisions
<<<
2010.12.15 2.7.2 omit date highlighting when hiding popup items (created/changed/tagged/reminders)
|please see [[DatePluginInfo]] for additional revision details|
2005.10.30 0.9.0 pre-release
<<<
!!!!!Code
***/
//{{{
version.extensions.DatePlugin= {major: 2, minor: 7, revision: 2, date: new Date(2010,12,15)};
config.macros.date = {
format: 'YYYY.0MM.0DD', // default date display format
linkformat: 'YYYY.0MM.0DD', // 'dated tiddler' link format
linkedbg: '#babb1e',
todaybg: '#ffab1e',
weekendbg: '#c0c0c0',
holidaybg: '#ffaace',
createdbg: '#bbeeff',
modifiedsbg: '#bbeeff',
remindersbg: '#c0ffee',
weekend: [ 1,0,0,0,0,0,1 ], // [ day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 ],
holidays: [ '01/01', '07/04', '07/24', '11/24' ]
// NewYearsDay, IndependenceDay(US), Eric's Birthday (hooray!), Thanksgiving(US)
};
config.macros.date.handler = function(place,macroName,params)
{
// default: display current date
var now =new Date();
var date=now;
var mode='display';
if (params[0]&&['display','popup','link'].contains(params[0].toLowerCase()))
{ mode=params[0]; params.shift(); }
if (!params[0] || params[0]=='today')
{ params.shift(); }
else if (params[0]=='filedate')
{ date=new Date(document.lastModified); params.shift(); }
else if (params[0]=='tiddler')
{ date=store.getTiddler(story.findContainingTiddler(place).id.substr(7)).modified; params.shift(); }
else if (params[0].substr(0,8)=='tiddler:')
{ var t; if ((t=store.getTiddler(params[0].substr(8)))) date=t.modified; params.shift(); }
else {
var y = eval(params.shift().replace(/Y/ig,(now.getYear()<1900)?now.getYear()+1900:now.getYear()));
var m = eval(params.shift().replace(/M/ig,now.getMonth()+1));
var d = eval(params.shift().replace(/D/ig,now.getDate()+0));
date = new Date(y,m-1,d);
}
// date format with optional custom override
var format=this.format; if (params[0]) format=params.shift();
var linkformat=this.linkformat; if (params[0]) linkformat=params.shift();
showDate(place,date,mode,format,linkformat);
}
window.showDate=showDate;
function showDate(place,date,mode,format,linkformat,autostyle,weekend)
{
mode =mode||'display';
format =format||config.macros.date.format;
linkformat=linkformat||config.macros.date.linkformat;
// format the date output
var title=date.formatString(format);
var linkto=date.formatString(linkformat);
// just show the formatted output
if (mode=='display') { place.appendChild(document.createTextNode(title)); return; }
// link to a 'dated tiddler'
var link = createTiddlyLink(place, linkto, false);
link.appendChild(document.createTextNode(title));
link.title = linkto;
link.date = date;
link.format = format;
link.linkformat = linkformat;
// if using a popup menu, replace click handler for dated tiddler link
// with handler for popup and make link text non-italic (i.e., an 'existing link' look)
if (mode=='popup') {
link.onclick = onClickDatePopup;
link.style.fontStyle='normal';
}
// format the popup link to show what kind of info it contains (for use with calendar generators)
if (autostyle) setDateStyle(place,link,weekend);
}
//}}}
//{{{
// NOTE: This function provides default logic for setting the date style when displayed in a calendar
// To customize the date style logic, please see[[DatePluginConfig]]
function setDateStyle(place,link,weekend) {
// alias variable names for code readability
var date=link.date;
var fmt=link.linkformat;
var linkto=date.formatString(fmt);
var cmd=config.macros.date;
var co=config.options; // abbrev
if ((weekend!==undefined?weekend:isWeekend(date))&&(cmd.weekendbg!=''))
{ place.style.background = cmd.weekendbg; }
if (hasModifieds(date)||hasCreateds(date)||hasTagged(date,fmt))
{ link.style.fontStyle='normal'; link.style.fontWeight='bold'; }
if (hasReminders(date))
{ link.style.textDecoration='underline'; }
if (isToday(date))
{ link.style.border='1px solid black'; }
if (isHoliday(date)&&(cmd.holidaybg!=''))
{ place.style.background = cmd.holidaybg; }
if (hasCreateds(date)&&(cmd.createdbg!=''))
{ place.style.background = cmd.createdbg; }
if (hasModifieds(date)&&(cmd.modifiedsbg!=''))
{ place.style.background = cmd.modifiedsbg; }
if ((hasTagged(date,fmt)||store.tiddlerExists(linkto))&&(cmd.linkedbg!=''))
{ place.style.background = cmd.linkedbg; }
if (hasReminders(date)&&(cmd.remindersbg!=''))
{ place.style.background = cmd.remindersbg; }
if (isToday(date)&&(cmd.todaybg!=''))
{ place.style.background = cmd.todaybg; }
if (config.options.chkShowJulianDate) { // optional display of Julian date numbers
var m=[0,31,59,90,120,151,181,212,243,273,304,334];
var d=date.getDate()+m[date.getMonth()];
var y=date.getFullYear();
if (date.getMonth()>1 && (y%4==0 && y%100!=0) || y%400==0)
d++; // after February in a leap year
wikify('@@font-size:80%;<br>'+d+'@@',place);
}
}
//}}}
//{{{
function isToday(date) // returns true if date is today
{ var now=new Date(); return ((now-date>=0) && (now-date<86400000)); }
function isWeekend(date) // returns true if date is a weekend
{ return (config.macros.date.weekend[date.getDay()]); }
function isHoliday(date) // returns true if date is a holiday
{
var longHoliday = date.formatString('0MM/0DD/YYYY');
var shortHoliday = date.formatString('0MM/0DD');
for(var i = 0; i < config.macros.date.holidays.length; i++) {
var holiday=config.macros.date.holidays[i];
if (holiday==longHoliday||holiday==shortHoliday) return true;
}
return false;
}
//}}}
//{{{
// Event handler for clicking on a day popup
function onClickDatePopup(e) { e=e||window.event;
var p=Popup.create(this); if (!p) return false;
// always show dated tiddler link (or just date, if readOnly) at the top...
if (!readOnly || store.tiddlerExists(this.date.formatString(this.linkformat)))
createTiddlyLink(createTiddlyElement(p,'li'),this.date.formatString(this.linkformat),true);
else
createTiddlyText(createTiddlyElement(p,'li'),this.date.formatString(this.linkformat));
addCreatedsToPopup(p,this.date,this.format);
addModifiedsToPopup(p,this.date,this.format);
addTaggedToPopup(p,this.date,this.linkformat);
addRemindersToPopup(p,this.date,this.linkformat);
Popup.show(); e.cancelBubble=true; if(e.stopPropagation)e.stopPropagation(); return false;
}
//}}}
//{{{
function indexCreateds() // build list of tiddlers, hash indexed by creation date
{
var createds= { };
var tiddlers = store.getTiddlers('title','excludeLists');
for (var t = 0; t < tiddlers.length; t++) {
var date = tiddlers[t].created.formatString('YYYY0MM0DD')
if (!createds[date])
createds[date]=new Array();
createds[date].push(tiddlers[t].title);
}
return createds;
}
function hasCreateds(date) // returns true if date has created tiddlers
{
if (config.options.chkDatePopupHideCreated) return false;
if (!config.macros.date.createds) config.macros.date.createds=indexCreateds();
return (config.macros.date.createds[date.formatString('YYYY0MM0DD')]!=undefined);
}
function addCreatedsToPopup(p,when,format)
{
if (config.options.chkDatePopupHideCreated) return false;
var force=(store.isDirty() && when.formatString('YYYY0MM0DD')==new Date().formatString('YYYY0MM0DD'));
if (force || !config.macros.date.createds) config.macros.date.createds=indexCreateds();
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var createds = config.macros.date.createds[when.formatString('YYYY0MM0DD')];
if (createds) {
createds.sort();
var e=createTiddlyElement(p,'div',null,null,'created ('+createds.length+')');
for(var t=0; t<createds.length; t++) {
var link=createTiddlyLink(createTiddlyElement(p,'li'),createds[t],false);
link.appendChild(document.createTextNode(indent+createds[t]));
}
}
}
//}}}
//{{{
function indexModifieds() // build list of tiddlers, hash indexed by modification date
{
var modifieds= { };
var tiddlers = store.getTiddlers('title','excludeLists');
for (var t = 0; t < tiddlers.length; t++) {
var date = tiddlers[t].modified.formatString('YYYY0MM0DD')
if (!modifieds[date])
modifieds[date]=new Array();
modifieds[date].push(tiddlers[t].title);
}
return modifieds;
}
function hasModifieds(date) // returns true if date has modified tiddlers
{
if (config.options.chkDatePopupHideChanged) return false;
if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();
return (config.macros.date.modifieds[date.formatString('YYYY0MM0DD')]!=undefined);
}
function addModifiedsToPopup(p,when,format)
{
if (config.options.chkDatePopupHideChanged) return false;
var date=when.formatString('YYYY0MM0DD');
var force=(store.isDirty() && date==new Date().formatString('YYYY0MM0DD'));
if (force || !config.macros.date.modifieds) config.macros.date.modifieds=indexModifieds();
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var mods = config.macros.date.modifieds[date];
if (mods) {
// if a tiddler was created on this date, don't list it in the 'changed' section
if (config.macros.date.createds && config.macros.date.createds[date]) {
var temp=[];
for(var t=0; t<mods.length; t++)
if (!config.macros.date.createds[date].contains(mods[t]))
temp.push(mods[t]);
mods=temp;
}
mods.sort();
var e=createTiddlyElement(p,'div',null,null,'changed ('+mods.length+')');
for(var t=0; t<mods.length; t++) {
var link=createTiddlyLink(createTiddlyElement(p,'li'),mods[t],false);
link.appendChild(document.createTextNode(indent+mods[t]));
}
}
}
//}}}
//{{{
function hasTagged(date,format) // returns true if date is tagging other tiddlers
{
if (config.options.chkDatePopupHideTagged) return false;
return store.getTaggedTiddlers(date.formatString(format)).length>0;
}
function addTaggedToPopup(p,when,format)
{
if (config.options.chkDatePopupHideTagged) return false;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var tagged=store.getTaggedTiddlers(when.formatString(format));
if (tagged.length) var e=createTiddlyElement(p,'div',null,null,'tagged ('+tagged.length+')');
for(var t=0; t<tagged.length; t++) {
var link=createTiddlyLink(createTiddlyElement(p,'li'),tagged[t].title,false);
link.appendChild(document.createTextNode(indent+tagged[t].title));
}
}
//}}}
//{{{
function indexReminders(date,leadtime) // build list of tiddlers with reminders, hash indexed by reminder date
{
var reminders = { };
if(window.findTiddlersWithReminders!=undefined) { // reminder plugin is installed
var t = findTiddlersWithReminders(date, [0,leadtime], null, null, 1);
for(var i=0; i<t.length; i++) reminders[t[i].matchedDate]=true;
}
return reminders;
}
function hasReminders(date) // returns true if date has reminders
{
if (config.options.chkDatePopupHideReminders) return false;
if (window.reminderCacheForCalendar)
return window.reminderCacheForCalendar[date]; // use calendar cache
if (!config.macros.date.reminders)
config.macros.date.reminders = indexReminders(date,90); // create a 90-day leadtime reminder cache
return (config.macros.date.reminders[date]);
}
function addRemindersToPopup(p,when,format)
{
if (config.options.chkDatePopupHideReminders) return false;
if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed
var indent = String.fromCharCode(160)+String.fromCharCode(160);
var reminders=findTiddlersWithReminders(when, [0,31],null,null,1);
createTiddlyElement(p,'div',null,null,'reminders ('+(reminders.length||'none')+')');
for(var t=0; t<reminders.length; t++) {
link = createTiddlyLink(createTiddlyElement(p,'li'),reminders[t].tiddler,false);
var diff=reminders[t].diff;
diff=(diff<1)?'Today':((diff==1)?'Tomorrow':diff+' days');
var txt=(reminders[t].params['title'])?reminders[t].params['title']:reminders[t].tiddler;
link.appendChild(document.createTextNode(indent+diff+' - '+txt));
}
if (readOnly) return; // readonly... omit 'new reminder...' command
var rem='\\<\\<reminder day:%0 month:%1 year:%2 title:"Enter a reminder title here"\\>\\>';
rem=rem.format([when.getDate(),when.getMonth()+1,when.getYear()+1900]);
var cmd="<<newTiddler label:[["+indent+"new reminder...]] prompt:[[add a reminder to '%0']]"
+" title:[[%0]] text:{{var t=store.getTiddlerText('%0','');t+(t.length?'\\n':'')+'%1'}} tag:%2>>";
wikify(cmd.format([when.formatString(format),rem,config.options.txtCalendarReminderTags||'']),
createTiddlyElement(p,'li'));
}
//}}}
/***
|Name|DatePluginConfig|
|Source|http://www.TiddlyTools.com/#DatePluginConfig|
|Documentation|http://www.TiddlyTools.com/#DatePluginInfo|
|Version|2.6.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|formats, background colors and other optional settings for DatePlugin|
***/
// // Default popup content display options (can be overridden by cookies)
//{{{
if (config.options.chkDatePopupHideCreated===undefined)
config.options.chkDatePopupHideCreated=false;
if (config.options.chkDatePopupHideChanged===undefined)
config.options.chkDatePopupHideChanged=false;
if (config.options.chkDatePopupHideTagged===undefined)
config.options.chkDatePopupHideTagged=false;
if (config.options.chkDatePopupHideReminders===undefined)
config.options.chkDatePopupHideReminders=false;
//}}}
// // show Julian date number below regular date
//{{{
if (config.options.chkShowJulianDate===undefined)
config.options.chkShowJulianDate=false;
//}}}
// // fixed-date annual holidays
//{{{
config.macros.date.holidays=[
"01/01", // NewYearsDay,
"07/04", // US Independence Day
"07/24" // Eric's Birthday (hooray!)
];
//}}}
// // weekend map (1=weekend, 0=weekday)
//{{{
config.macros.date.weekend=[ 1,0,0,0,0,0,1 ]; // day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6
//}}}
// // date display/link formats
//{{{
config.macros.date.format="YYYY.0MM.0DD"; // default date display format
config.macros.date.linkformat="YYYY.0MM.0DD"; // 'dated tiddler' link format
//}}}
// // When displaying a calendar (see [[CalendarPlugin]]), you can customize the colors/styles that are applied to the calendar dates by modifying the values and/or functions below:
//{{{
// default calendar colors
config.macros.date.weekendbg="#c0c0c0";
config.macros.date.holidaybg="#ffaace";
config.macros.date.createdbg="#bbeeff";
config.macros.date.modifiedsbg="#bbeeff";
config.macros.date.linkedbg="#babb1e";
config.macros.date.remindersbg="#c0ffee";
// apply calendar styles
function setDateStyle(place,link,weekend) {
// alias variable names for code readability
var date=link.date;
var fmt=link.linkformat;
var linkto=date.formatString(fmt);
var cmd=config.macros.date;
if ((weekend!==undefined?weekend:isWeekend(date))&&(cmd.weekendbg!=""))
{ place.style.background = cmd.weekendbg; }
if (hasModifieds(date)||hasCreateds(date)||hasTagged(date,fmt))
{ link.style.fontStyle="normal"; link.style.fontWeight="bold"; }
if (hasReminders(date))
{ link.style.textDecoration="underline"; }
if (isToday(date))
{ link.style.border="1px solid black"; }
if (isHoliday(date)&&(cmd.holidaybg!=""))
{ place.style.background = cmd.holidaybg; }
if (hasCreateds(date)&&(cmd.createdbg!=""))
{ place.style.background = cmd.createdbg; }
if (hasModifieds(date)&&(cmd.modifiedsbg!=""))
{ place.style.background = cmd.modifiedsbg; }
if ((hasTagged(date,fmt)||store.tiddlerExists(linkto))&&(cmd.linkedbg!=""))
{ place.style.background = cmd.linkedbg; }
if (hasReminders(date)&&(cmd.remindersbg!=""))
{ place.style.background = cmd.remindersbg; }
if (isToday(date)&&(cmd.todaybg!=""))
{ place.style.background = cmd.todaybg; }
if (config.options.chkShowJulianDate) {
var m=[0,31,59,90,120,151,181,212,243,273,304,334];
var d=date.getDate()+m[date.getMonth()];
var y=date.getFullYear();
if (date.getMonth()>1 && (y%4==0 && y%100!=0) || y%400==0) d++; // after February in a leap year
wikify("@@font-size:80%;<br>"+d+"@@",place);
}
var t=store.getTiddlerText(linkto,'')
if (config.options.chkInlineCalendarJournals && t.length) wikify('<br>'+t,place);
}
//}}}
[[Edit the wiki text in the view mode]]
/***
!! Deprecated [[twve.tcalc]] functions
!!! twtable.calculate
!!!! 2015/06/16 [3.2.4]
***/
//{{{
twtable.calculate = function (text,changed) {
// Calculates a table represented by twtable.
// Arguments:
// text -- (Optional) The tiddler text (containing
// that table).
// Normally twtable already contains the
// tiddler text so you are fine to leave
// this argument undefined. However, in
// cases where the modified text hasn't been
// saved back to twtable.tiddler.text, the
// modified text can be passed in here.
// changed -- (Optional) An object containing the
// (row, col) indexes of a changed cell.
// It is usually fine to leave this
// argument undefined, the twve.tcalc will
// simply re-calculate the whole table. If,
// however, this is given instead, then
// twve.tcalc does partial recalculation
// by re-calculating only 1) the changed
// cell itself, 2) cells referring to
// the changed cell, 3) cells referring to
// one of those in 2), ... etc.
if ( ! (config.options.chktwveTcalcEnabled
|| config.options.chktwveTcalcAllTables
|| twtable.hasClass('spreadsheet')) )
return false;
if ( twve.MathJax ) twve.MathJax.enabled = false;
var t0 = new Date();
if ( ! text ) text = twtable.tiddler.text;
var cpos = twve.table.allCells(
text,twtable.start,twtable.end
);
twve.tcalc.cur_twtable = twtable;
var tr = twtable.dom.querySelectorAll('tr');
var trc = [];
for (var r=0,rlen=tr.length; r<rlen; r++){
trc[r] = tr[r].querySelectorAll('th,td');
}
var recalc_now = null;
var peq, ctxt;
if ( changed ) {
// Changed cells are specified. Do partial re-calculation.
// The array recalc_now stores the cells that need to re-
// calculate immediately. In the 1st run these are the
// changed cells themselves. In the later runs they are
// the cells referring to one of those re-calculated in
// the previous run.
recalc_now = [];
// Convert the (row, col) indexes to reference strings.
//for ( var i = 0; i < changed.length; i++ ) {
recalc_now[0] = twtable.referenceToTest(
changed.row,changed.col
);
//}
// The partial recalculation is currently disabled for it
// could, in some extreme cases, be 100 times slower than a
// whole table recalculation. Vincent 2015/02/11
// We need to clear all the calculated results for a whole
// table recalculation.
var t1 = new Date();
for (var r=0,rlen=tr.length; r<rlen; r++)
for (var c=0,clen=trc[r].length; c<clen; c++) {
if ( r==changed.row && c==changed.col )
continue;
var p = twve.tcalc.cellTitleExpression(trc[r][c]);
if ( p > 0 ) {
// There is an expression in this cell,
// restore it.
twve.tcalc.nodeText(
trc[r][c],trc[r][c].title.substring(p)
);
}
}
if ( config.options.chktwveTcalcDebugMode )
displayMessage(
'table cleared in '+(new Date()-t1)+' ms'
);
changed = null;
}
// If we are doing partial re-calculation, then cexp is an
// array storing formulas in the cells. Otherwise we don't
// need it.
var cexp = null;
if ( changed ) {
cexp = [];
for (var r=0,rlen=tr.length; r<rlen; r++){
cexp[r] = [];
}
}
// The variable recalc_next is used for partial
// re-calculation. It is an array containing the cells to
// re-calculate in the next run. If re-calculating the whole
// table then recalc_next shall remain null.
var recalc_next = null;
// Temporarily disable the IncludeCSS option
var includeCSS = config.options.chktwveTableIncludeCSS;
config.options.chktwveTableIncludeCSS = false;
// Start the calculation
do {
if ( cexp ) recalc_next = [];
for (var r=0,rlen=tr.length; r<rlen; r++){
for (var c=0,clen=trc[r].length; c<clen; c++){
var cell = trc[r][c];
//var rndx = twve.table.cellRowIndex(cell);
var cndx = twve.table.cellColIndex(cell);
if ( cexp && cexp[r].length > cndx ) {
ctxt = cexp[r][cndx];
} else {
ctxt = cpos[r][cndx]
? twtable.getCellText(cpos[r][cndx],text)
: '';
if ( cexp ) cexp[r][cndx] = ctxt;
}
twtable.cellReferenceTitle(cell,r,cndx);
// Leave for empty cells
if ( ! ctxt ) continue;
// Check for formulas to re-calculate.
if((peq=twve.tcalc.indexOfExpression(ctxt))<0){
if ( cexp )
// This cell contains no formulas. Skip
// it in the later runs of partial
// re-calculation.
cexp[r][cndx] = '';
continue;
}
// If the cell text contains a formula
if ( ! cexp ) {
// We are re-calculating the whole table.
twtable.calculateCell(cell,ctxt,peq);
continue;
}
// we are doing partial re-calculation.
//ctxt = ctxt.toUpperCase();
for(var i=0,ilen=recalc_now.length;i<ilen;i++){
// If this cell is one of the cells
// that needs re-calculation in this
// run, do it.
if (recalc_now[i].ndx.row==r
&& recalc_now[i].ndx.col==c) {
// Re-calculate the cell
twtable.calculateCell(cell,ctxt,peq);
// Clear this cell so it won't get
// re-calculated again.
cexp[r][cndx] = '';
break;
}
// If this cell contains a formula
// referring to a changed cell,
// calculate it in the next run.
if(twtable.isReferring(ctxt,recalc_now[i])){
recalc_next.push(
twtable.referenceToTest(r,c)
);
break;
}
// Check for consecutive references
var args = twve.tcalc.allArguments(ctxt);
if ( args &&
twtable.includeCell(
args,
recalc_now[i].ndx.row,
recalc_now[i].ndx.col
)
) {
recalc_next.push(
twtable.referenceToTest(r,c)
);
break;
}
}
}
}
// If we are re-calculating the whole table, or just
// part of the table and all affected cells are
// re-calculated, then this re-calculation is finished.
if ( ! cexp || recalc_next.length == 0 ) break;
// Otherwise do the next run of partial re-calculation.
recalc_now = recalc_next;
} while ( true );
// End of calculation, mark the table as calculated.
//twtable.dom.classList.add('calculated')
// Restore the IncludeCSS option.
config.options.chktwveTableIncludeCSS = includeCSS;
// Report calculation time if desired.
if ( config.options.chktwveTcalcDebugMode )
displayMessage(
'table re-calculated in '+(new Date()-t0)+' ms'
);
twve.tcalc.cur_twtable = null;
if ( twve.MathJax ) twve.MathJax.enabled = true;
return true;
};
//}}}
/***
!! Deprecated [[twve.extra]] functions
!!! twelem.box
!!!! 2014/06/15 [3.0.4]
***/
//{{{
var preBox = twelem.box;
twelem.box = function(){
var eb = preBox.apply(this,arguments);
if ( twelem.multiLine() ||
twelem.start.matched.indexOf('\n') > -1 ) return eb;
// In-line elements
var lh = twelem.lineHeight();
var lines = Math.floor(eb[0].height/lh);
if ( lines > 1 ) {
if ( config.browser.isAndroid && lines > 4 ) {
lines = Math.floor(eb[0].height/(lh * 1.15));
}
// box is higher than one line
eb.lines = lines;
var $domparent = twelem.$dom.parent();
var $siblings = $domparent.contents();
var ndx = $siblings.index(twelem.$dom);
var $prev = ndx > 0
? $siblings.eq(ndx-1) : null;
var $next = ndx < $siblings.size()-1
? $siblings.eq(ndx+1) : null;
var $space = null;
if ( $prev && ! $prev.is('br') ) {
if ( (config.browser.isIE || config.browser.isIE11)
&& ndx == 1 && $domparent.is('li') ) {
// IE gets wrong box in such cases.
$space = jQuery(document.createTextNode('O'));
$space.insertBefore($prev);
}
var $c = $prev.contents();
if ( $c.size() > 0 ) $prev = $c.eq(-1);
// If there are $contents() in $prev, get the box of
// the last one in $contents().
eb.prev = twve.node.box($prev,null,null,-1);
if ( $space ) {
$space.remove().insertAfter($prev);
eb.prev[0].right = twve.node.box($space)[0].left;
$space.remove();
}
if ( eb.prev[0].bottom <= eb[0].top ) {
// The previous element is not in the first line.
eb.prev = null;
}
if ( ! $next ) {
if ( ! $space )
$space = jQuery(document.createTextNode('O'));
$space.insertAfter(twelem.$dom);
$next = $space;
}
}
if ( $next ) {
if ( $next.is('br') ) {
if ( ! $space )
$space = jQuery(document.createTextNode('O'));
$space.insertAfter(twelem.$dom);
$next = $space;
}
var $c = $next.contents();
if ( $c.size() > 0 ) $next = $c.eq(0);
eb.next = twve.node.box($next);
// In TiddlySpace the bounding box of $next is
// always lower, I have no idea why.
if (
window.location.hostname.indexOf(
'tiddlyspace'
)==-1 && eb.next[0].top >= eb[0].bottom
){
// The next element is not in the last line.
eb.next = null;
}
if ( $space ) $space.remove();
}
if ( eb.prev || eb.next ) {
eb.lineheight = eb[0].height/lines;
} else {
eb.lines = 0;
}
}
return eb;
};
//}}}
/***
!!! twwrap.box
!!!! 2014/06/15 [3.0.4]
***/
//{{{
twwrap.box = function(action,ev){
// Find previous element
// Find next element
var eb = twve.node.box(twwrap.$dom);
var lh = twwrap.lineHeight();
var mpos = null;
if ( ev ) {
lh /= 2;
twwrap.prev = twwrap.closestElement(eb,ev,-lh);
twwrap.next = twwrap.closestElement(eb,ev,lh);
mpos = twve.tiddler.mousePosition(ev);
lh *= 2;
}
var ebnext = null;
var ebprev = null;
// If either the previous or the next element is a
// <<tiddler>> macro created span, and the mouse pointer is
// very close to it (within m pixels), let the user edit
// the <<tiddler>> macro itself.
var m = 5;
if(twwrap.next && twwrap.next.$dom.is(
twve.tiddlerSpan.getSelector()
)) {
ebnext = twwrap.next.box();
if ( twwrap.transcludedLocked() ) {
twwrap.tiddlerSpanBox(eb,ebnext,m);
return eb;
} else if ( ev && (ebnext.top-mpos.y <= m) ) {
twwrap.tiddlerSpanBox(eb,ebnext,m);
twwrap.prev = twwrap.next;
twwrap.lockTranscluded();
return eb;
}
}
if(twwrap.prev && twwrap.prev.$dom.is(
twve.tiddlerSpan.getSelector()
)) {
ebprev = twwrap.prev == twwrap.next
? (ebnext ? ebnext : twwrap.next.box())
: twwrap.prev.box();
if ( ev && (mpos.y-ebprev.bottom <= m) ) {
twwrap.tiddlerSpanBox(eb,ebprev,m);
twwrap.next = twwrap.prev;
twwrap.lockTranscluded();
return eb;
}
}
// Otherwise check for slider panel or plain text.
if ( twwrap.next ) {
ebnext = twwrap.next.box();
// There is a next element
if(twwrap.next.$dom.is(
twve.sliderPanel.getSelector()
)) {
// If the next element is a sliderPanel, get the
// offset of its button.
var offset = twve.node.offset(
twwrap.next.$dom.prev()
);
// Compare the mouse position with the button offset.
if ( twwrap.transcludedLocked() ) {
// See above
twwrap.sliderPanelBox(eb,ebnext,offset);
} else if (ev && ebnext.top-mpos.y<=lh) {
// If the y-component of mouse position is within
// the button, let the user edit the <<slider>>
// macro itself.
twwrap.sliderPanelBox(eb,ebnext,offset);
twwrap.prev = twwrap.next;
twwrap.lockTranscluded();
} else {
// If, however, mouse position is above the
// button, let the user edit the plain text
// before the <<slider>> macro.
if ( twwrap.prev ) {
ebprev = twwrap.prev == twwrap.next
? ebnext : twwrap.prev.box();
eb.top = ebprev.bottom;
}
eb.bottom = offset.top;
}
eb.height = eb.bottom - eb.top;
} else {
var last = twwrap.next.$dom[0].previousSibling;
var eblast = last
? twve.node.box(jQuery(last)) : null;
// The next element is NOT a sliderPanel.
if ( twwrap.prev ) {
// There is also a previous element
if(! twwrap.prev.isVisible() &&
/^[hH]/.test(twwrap.next.$dom[0].nodeName)
//&& twwrap.prev.$dom.is(
// twve.foldedSection.getSelector()
//)
)
lh *= 1.3;
var first = twwrap.prev.$dom[0].nextSibling;
if ( ! first && ! last ) {
ebprev = twwrap.prev == twwrap.next
? ebnext : twwrap.prev.box();
if ( ebnext.top - ebprev.bottom < lh ) {
twwrap.prev = twwrap.next = null;
} else {
eb.top = ebprev.bottom;
eb.bottom = ebnext.top;
eb.height = eb.bottom - eb.top;
}
} else {
if ( first ) {
var ebfirst = twve.node.box(
jQuery(first)
);
eb.left = ebfirst.left;
eb.top = ebfirst.top;
}
if ( last ) {
//eb.right = eblast.right;
eb.bottom = eblast.bottom;
}
eb.width = eb.right - eb.left;
eb.height = eb.bottom - eb.top;
}
} else {
if ( last ) {
//eb.right = eblast.right;
eb.bottom = eblast.bottom;
//eb.width = eb.right - eb.left;
eb.height = eb.bottom - eb.top;
} else if ( ebnext.top - eb.top >= lh ) {
// There is next element but no previous
// element
eb.bottom = ebnext.top;
eb.height = eb.bottom - eb.top;
} else
twwrap.next = null;
}
}
} else if ( twwrap.prev ) {
// There is no next but previous element
var first = twwrap.prev.$dom[0].nextSibling;
if ( first ) {
var ebfirst = twve.node.box(jQuery(first));
eb.left = ebfirst.left;
eb.top = ebfirst.top;
} else {
ebprev = twwrap.prev.box();
if ( eb.bottom - ebprev.bottom >= lh ) {
eb.top = ebprev.bottom;
eb.height = eb.bottom - eb.top;
} else
twwrap.prev = null;
}
}
return eb;
};
//}}}
/***
!! Deprecated [[twve.core]] functions
!!! twve.node.box
!!!! 2014/06/15 [3.0.4]
***/
//{{{
box : function($node,outerw,outerh,ndx) {
var nb = twve.box.create();
if ( ! $node || $node.size() == 0 ) return nb;
if ( $node[0].nodeType == 3 ) {
// text node
var r = document.createRange();
// The following two functions basically return the same
// results with most of the browser/OS combinations, such
// as FF/Win8, FF/Linux, IE/Win8, etc. with Opera/Linux,
// however, only the first one returns the (almost) correct
// results (the right side is too far).
r.selectNodeContents($node[0]);
//r.selectNode($node[0]);
// In addition, different browsers handle BR in different
// ways. In cases without BR, the browsers return visually
// consistent results (tested with FireFox 29.0.1, IE
// 11.0.9600.17107, Chrome 35.0.1916.114 m and Opera
// 21.0.1432.67). In cases with BR, however, Chrome and
// Opera do not return the expected bounding rectangle as
// FireFox and IE do.
//
// Vincent 2014/05/28
// The following function returns an array of rectangles.
r = r.getClientRects();
if ( typeof ndx != 'number' ) ndx = 0;
else if ( ndx < 0 ) ndx += r.length;
r = r[ndx];
var $win = jQuery(window);
var scrL = $win.scrollLeft();
var scrT = $win.scrollTop();
nb.left = r.left+scrL;
nb.top = r.top+scrT;
nb.right = r.right+scrL;
nb.bottom = r.bottom+scrT;
nb.width = (r.right-r.left);
nb.height = (r.bottom-r.top);
} else {
// element
var pos = twve.node.offset($node);
var w = outerw
? $node.outerWidth(true)
: ($node[0].clientHeight &&
$node[0].scrollHeight > $node[0].clientHeight
? $node[0].clientWidth : $node.innerWidth());
var h = outerh
? $node.outerHeight(true)
: ($node[0].clientWidth &&
$node[0].scrollWidth > $node[0].clientWidth
? $node[0].clientHeight : $node.innerHeight());
nb.left = pos.left;
nb.top = pos.top;
nb.right = (pos.left+w);
nb.bottom = (pos.top+h);
nb.width = w;
nb.height = h;
}
return nb;
},
//}}}
/***
!! Deprecated [[TWtid|TWtid.2.0.10]] core functions
!!!Revision history
* 2014/03/29
** Removed from {{{TWtable}}} 1.5.0
* 2013/12/15
** Removed from the {{{TWtid}}} 2.0.9
!!! start_edit_mode
***/
//{{{
pre_start_edit_mode : null,
start_edit_mode : function ( twtable ) {
var vh=twve.table.large.pre_start_edit_mode.apply(this,arguments);
if ( ! vh ) return null;
var $table = twve.table.table_element(twtable.$dom);
if ( ! $table ) return null;
// We needed to locate the correct scroll position of a table
// if the uer scrolled the table before pressing the 'E' button.
if ( $table.attr('oversize') ) {
var $div_tb = $table.parent();
twve.table.large.table_scrolled(
$div_tb.parent(),
$div_tb.position(),
$div_tb.scrollLeft(),
$div_tb.scrollTop()
);
}
return vh;
},
//}}}
/***
!!!! twve.tiddler.savedElement
***/
//{{{
savedElement : function (twelem,which) {
// Check if the argument twelem (twve.element object) is the
// same as the saved one, if there is.
// This function is mostly needed when the tiddler gets
// refreshed while the editing is not finished. For
// example, if the user has just inserted/deleted one
// table row/column, which caused a refresh of the whole
// tiddler, then the DOM element (table) that the user was
// working on has gone flushed.
// Logically the user is still working on the same table
// and would not expect a refocusing or reactivation of the
// table. Technically, however, the newly created table is
// a different DOM element, the original table was
// destroyed upon refresh. The plugin needs to
// refocus/reactivate the new table for further editing.
// For that purpose, the plugin saves the information of
// the table before refreshing, and uses that to find the
// corresponding new table afterwards.
// To compare two twve.element, this function does the
// followings:
// 1. Compare their wrapper_title to make sure they are
// defined in the same tiddler.
// 2. Compare their
// a. rendering index, if the second argument is
// given a string starting with 'r'(case
// insensitive), or
// b. defining index, if the second argument is not
// specified,
// to make sure they are the same element.
if ( ! twve.tiddler.cur_twelem ) {
// There is no element saved. Nothing to compare.
return false;
}
if ( twve.tiddler.cur_twelem == twelem ) return true;
if ( twelem.wrapper_title
!= twve.tiddler.cur_twelem.wrapper_title )
return false;
if ( /^[rR]/.test(which) )
// testing the rendering index
return twelem.rIndex >= 0
? twve.tiddler.cur_twelem.rIndex == twelem.rIndex
: false;
else
// testing the defining index
return twelem.dIndex >= 0
? twve.tiddler.cur_twelem.dIndex == twelem.dIndex
: false;
},
//}}}
/***
!!!! twve.tiddler.inactiveElem
***/
//{{{
inactiveElem : function($elem) {
var $preview = twve.tiddler.getPreviewer();
var result = $preview
&& ($elem[0] == $preview[0]
|| $elem.closest('.preview').size()>0);
if ( ! result ) {
var $ed = twve.tiddler.getEditBox();
result = $ed && $ed.is($elem);
}
if ( ! result )
result = twve.menu.isButton($elem);
return result;
},
//}}}
/***
!!! save_info methods
***/
//{{{
tiddlers_recorded : null,
push_tiddler : function ( tiddler ) {
if ( ! this.tiddlers_recorded ) {
this.tiddlers_recorded = new Array();
this.tiddlers_recorded.push(tiddler);
} else if ( this.tiddlers_recorded.indexOf(tiddler)==-1 )
this.tiddlers_recorded.push(tiddler);
},
save_table_info : function (tiddler,wndx,info_key,value ) {
if ( ! tiddler.table_info )
tiddler.table_info = new Array();
var saved_info = tiddler.table_info[wndx],
info = (value===undefined?info_key:{info_key:value});
if ( ! saved_info ) saved_info = {};
tiddler.table_info[wndx] = merge(saved_info, info);
this.push_tiddler(tiddler);
},
get_table_info : function (tiddler, wndx, key) {
if ( ! tiddler || ! tiddler.table_info ) return null;
if ( wndx === undefined ) return tiddler.table_info;
var info = tiddler.table_info[wndx];
return (info ? (key ? info[key] : info) : null);
},
//}}}
/***
!!! restore_scroll_pos
***/
//{{{
restore_scroll_pos : function (twtable) {
var pos = twve.table.get_table_info(
twtable.tiddler,twtable.dIndex,'ScrollPos'
);
if ( pos ) {
twtable
.$dom.parent()
.scrollLeft(pos.left)
.scrollTop(pos.top);
}
},
//}}}
/***
!!! wrapper_tiddler
***/
//{{{
wrapper_tiddler : function (twelem,type) {
// Find the direct wrapper and corresponding tiddler for
// an element, as well as all transcluded copies.
// Wrappers can be any of the follows:
// div.viewer -- normally loaded
// div.sliderPanel -- <<slider>> transcluded
// div.tabContents -- <<tabs>> transcluded
// span[tiddler] -- <<tiddler>> transcluded
// span.sliderPanel -- <<foldHeadings>> folded
// First we look for the direct wrapper, find its tiddler
// title, then we use that title to find all other
// copies, including partially and fully transcluded
// wrappers.
// Note that except for div.viewer, the normally loaded
// wrapper, all kinds of wrappers can be contained in other
// kinds.
var $elem = twelem.$dom;
// First we check if the wrapper title is already given.
// If so, we find the tiddler and wrappers with this title.
// Otherwise we find the title of the direct wrapper, then
// the tiddler and wrappers.
if ( ! twelem.wrapper_title ) {
if ( TWtid.is_wrapper($elem) ) {
// If the element is a wrapper
if ( $elem.is('.viewer,div[id=tiddlerDisplay]') ) {
twelem.$direct_wrapper = $elem;
} else {
twelem.$direct_wrapper =
/^elem/.test(type)
? TWtid.direct_wrapper($elem.parent())
: $elem;
}
} else {
// It's a regular element
twelem.$direct_wrapper = TWtid.direct_wrapper($elem);
}
twelem.wrapper_title =
TWtid.title_from_wrapper(twelem.$direct_wrapper);
}
if ( store.isShadowTiddler(twelem.wrapper_title) ) {
twelem.tiddler = null;
twelem.$wrapper = null;
} else {
twelem.tiddler = this.get_tiddler(twelem.wrapper_title);
twelem.$wrapper=this.wrapper_from_title(
twelem.wrapper_title
);
}
return twelem;
},
//}}}
/***
!! Deprecated line-based [[TWtid|TWtid.2.0.10]] core functions.
These functions handle wiki text in a line-based manner (use a line break to indicate the end of something), which is good for line-base block elements such as tables (line break is the end of a table row), blockquotes (line break is the end of the blockquote), etc. This, however, is not good for other kinds of elements that could be defined in a character-based manner (in-line elements that do not use a line break to indicate an end), such as an //itallic text// ({{{//italic//}}}) or @@color:red;red-colored text@@ ({{{@@color:red;red-colored text@@}}}).
To extend the capabilities of the [[TWtid|TWtid.2.0.10]]+[[TWted|TWted.2.0.10]] plugins to handle the character-based (in-line) elements, the core functions were adapted to the character-based behavior so these are no longer needed.
!!!Revision history
* 2013/05/24
** Removed from the {{{TWtid}}} 1.6.4.
!!!{{{TWtid}}}.xxx functions
***/
/***
!!! elem_first_line
***/
//{{{
elem_first_line : function (text,prefix,ndx,title
,subprefix,start) {
// Find the first line of text that defines a block element,
// such as
// paragraphs, tables, lists, headers,
// blockquotes,
// definition lists, block examples, preformat,
// each of which has its own defining prefix.
// a paragraph starts with a regular word;
// a table starts with a (|);
// a list starts with either (*) or (#);
// a header starts with (!), level 1 to 6;
// a blockquote starts with (>), level 1 to 3;
// a definition lists starts with a (;) and
// ends with a (:)-started line;
// a block example starts with (<<<) and
// ends with a (>>>)-started line;
// a preformat starts with ({{{) and
// ends with a (}}})-started line.
// Before calling this function, either the rendering index
// or the defining index of the element must be already known.
// Normally we obtaine the rendering index first, using
// TWtid.element_index() function, then obtain the defining
// index through this function. However, upon synchronizing
// transcluded copies of the same tiddler, we first obtain the
// rendering index of the element in one of the transcluded copies,
// then the defining index in the wikitext, and use that defining
// index to find the rendering index of the corresponding element
// in other copies.
// Parameters:
// text: Line-feed splitted wikitext.
// prefix: Prefix that defines a specific block element.
// For example, a table should start with a '|',
// while a list would start with a '*' or '#'.
// ndx: Index of the element. This ndx should be an object
// containing the rendering index (rIndex) and
// defining index (dIndex).
// For example, ndx = {
// 'rIndex':rndx
// , 'dIndex':dndx
// }
// title: Title of the wrapper. Would be the tiddler title
// for non-transcluded or fully transcluded tiddlers,
// or the tiddler title followed by '##' and a
// section title for partially transcluded tiddlers.
// subprefix: Prefix that defines the next level element.
// For example, a list defined by prefix '*' may
// contain sublists defined by subprefix '**'
// or '##'.
// start: Line number to start searching.
var i = (start?start:0)
, s = this.tiddler_slice(title);
if ( s ) {
// Slice, not yet decided what to do
//while ( ++i < text.length )
// if ( text[i].indexOf(s) > -1 )
// return text[i].charAt(0) == '|' ? i : -1;
// Slice not found
return -1;
}
s = this.tiddler_section(title);
var to_skip = 0;
if ( s ) {
to_skip = TWtid.header_to_skip(s);
if ( to_skip > 0 )
s = TWtid.remove_header_tail(s);
}
var rndx = -1, dndx = -1;
var started = (!s);
//displayMessage('looking for elem'+TWtid.object_string(ndx)+' in '+title);
do {
if ( s && !started && text[i].charAt(0) == '!' ) {
// There is section name in the title, meaning
// that section is partially transcluded.
// If counting of order of appearance has already
// started and we are seeing another section here,
// meaning the element is not found within this
// section. Stop and return -1 (not found);
//if ( started ) return -1;
// Start counting the order of appearance
// of elements if the transcluded section is found.
if ( text[i].indexOf(s) > -1 ) {
//displayMessage('found '+s+' in '+text[i]);
if ( to_skip > 0 ) {
to_skip--;
} else started = true;
}
}
if ( prefix.test(text[i]) ) {
// Element defining text, returns it if index matches.
if ( started ) {
++dndx;
++rndx;
if (ndx.dIndex > -1 ) {
// Defining index is given
if ( dndx == ndx.dIndex) {
// Matched. Assign its rendering index and return
// the line number.
ndx.rIndex = rndx;
//displayMessage('elem'+TWtid.object_string(ndx)+' in '+title+' found at line number '+i);
return i;
}
} else if ( ndx.rIndex > -1 ) {
// Rendering index is given
if ( rndx == ndx.rIndex ) {
// Matched. Assign its defining index and return
// the line number.
ndx.dIndex = dndx;
return i;
}
} else {
// Neither of the indices are given.
// Assume matched. Assign both indices and return
// the line number.
ndx.dIndex = dndx;
ndx.rIndex = rndx;
return i;
}
}
// Otherwise skip to the end of this element
// For headers and blockquotes that only occupy one line,
// there is no need to skip to the end. For other kind
// of block element we skip to the end of it.
if ( prefix.source.indexOf('\\{\\{\\{')>-1
|| TWtid.preformat_first_line(text[i]) ) {
i = this.preformat_last_line(text,i+1);
} else if ( /\{\d\}\[/.test(prefix.source) ) {
// If this is a list, there can be two types,
// skip only the current type.
i = this.list_last_line(
text,i+1,this.list_prefix(text[i])
);
} else if ( prefix.source == '^[\\|]' ) {
// This is a table
i = this.table_last_line(text,i+1);
} else if ( /\>\{\d,\d\}/.test(prefix.source) ) {
// Blockquotes
if ( this.blockexample_first_line(text[i]) )
i = this.blockexample_last_line(text,i+1);
else {
if ( (/^\>{2}/.test(text[i])
&& ! /^\>{1}/.test(text[i-1]))
|| (/^\>{3}/.test(text[i])
&& ! /^\>{2}/.test(text[i-1])) )
// Blockquotes that do not start with the
// first level.
++rndx;
i = this.blockquote_last_line(text,i);
}
}
} else if ( /\{\d\}\[/.test(prefix.source) ) {
// For lists not starting from level 1.
if ( subprefix.test(text[i]) ) {
++dndx;
if ( started ) {
++rndx;
if (ndx.dIndex > -1 ) {
if ( dndx == ndx.dIndex) {
ndx.rIndex = rndx;
return i;
}
} else if ( rndx == ndx.rIndex ) {
ndx.dIndex = dndx;
return i;
}
}
}
}
} while ( ++i < text.length );
return -1;
},
//}}}
/***
!!! lines_of_text
***/
//{{{
lines_of_text : function(wikitext, r0, rl, newtxt) {
// Gets/Sets lines of text, from r0 to rl, inclusive, of
// the wikitext. If the last argument newtxt is undefined,
// this function returns the lines of text from r0 to rl.
// Otherwise it replaces the lines of text with the newtxt.
if ( newtxt === undefined ) {
// Gets the lines of text.
var txt = rl>=r0 ? wikitext[r0] : '';
for ( var r = r0+1; r <= rl; r++ )
txt += '\n'+wikitext[r];
return txt;
} else {
// Replaces the lines of text with newtxt.
if ( newtxt )
wikitext.splice(r0,rl-r0+1,newtxt);
else
wikitext.splice(r0,rl-r0+1);
}
},
//}}}
/***
!!! list_prefix related
***/
//{{{
escape_string : function(s) {
return s.replace(/[-/$*#]/g, '\\$&');
},
list_prefix : function ( level, ch ) {
if ( typeof level == 'string' ) {
var lev_ch = TWtid.element_level(level,0,/(\*|\#)/);
if ( lev_ch.level == 0 ) return null;
level = lev_ch.level;
ch = lev_ch.ch;
}
//displayMessage('looking for the last line of list '+ch+' level='+level);
var prefix = ch
? '^'+ch+'{'+level+'}[^'+ch+']'
: '^(*{'+level+'}[^*]|#{'+level+'}[^#])';
//: '^(*{'+level+'}|#{'+level+'})';
var subprefix = '^(*{'+(level+1)+'}|#{'+(level+1)+'})';
//function escapeRegExp ( s ) {
//return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
// return s.replace(/[-/$*#]/g, '\\$&');
//}
return {
'prefix':new RegExp(TWtid.escape_string(prefix))
, 'subprefix':new RegExp(TWtid.escape_string(subprefix))
};
},
list_last_line : function (text, r0, ps) {
// Find the closing line number that defines the list starting
// at line number r0.
var i = r0;
do {
if ( ! ps.subprefix.test(text[i]) )
break;
} while ( ++i < text.length );
return i-1;
},
line_number_of_list_item : function (text,r0,lindx) {
// Find the list item, indexd as lindx, within the list that
// starts at r0.
// A list can contain another list within itself, we need to
// skip those sub-lists.
var lev_ch = TWtid.element_level(text[r0]);
var ps = this.list_prefix(lev_ch.level, lev_ch.ch);
var i = r0, li = 0;
do {
// Go through the list until the desired item found or
// the end of list reached.
if ( ps.prefix.test(text[i]) ) {
if ( li == lindx )
// Found the list item
return i;
else li++;
} else if ( ! ps.subprefix.test(text[i]) )
break;
} while (++i < text.length);
return -1;
},
//}}}
/***
!!! xxx_first_line / xxx_last_line
***/
//{{{
header_first_line : function (text,ndx,title,start) {
// Find the line number that defines the header with index
// ndx. In the wiki style text a header is defined by a
// line of text that starts with '!'. Level 1 heading starts with
// one '!', while level 2 starts with two, etc. There could be
// up (down) to 6 heading levels.
return this.elem_first_line(text,/^\!{1,6}/,ndx,title,null,start);
},
blockquote_first_line : function (text,ndx,title,start) {
// Find the line number that defines the blockquote with index
// ndx. In the wiki style text a blockquote is defined by a
// line of text that starts with '>'. Level 1 blockquote starts
// with one '>', while level 2 starts with two, etc. There could
// be up (down) to 3 levels of blockquotes.
return this.elem_first_line(
text,/^(\>{1,3}|\<\<\<)/,ndx,title,null,start
);
},
blockquote_last_line : function (text, r0) {
// Find the closing line number that defines the blockquote starting
// at line number r0.
var r = r0;
var lev_ch = TWtid.element_level(text[r0]);
var prefix = text[r].substring(0,lev_ch.level);
while ( ++r < text.length )
if ( ! (text[r].startsWith(prefix)
&& text[r].charAt(lev_ch.level) != lev_ch.ch) )
break;
return r-1;
},
blockexample_first_line : function ( text ) {
return /^\<\<\</.test(text);
},
blockexample_last_line : function (text, r0) {
var r = r0;
while ( r < text.length )
if ( /^\<\<\</.test(text[r]) ) return r;
else r++;
return r-1;
},
preformat_first_line : function (text,ndx,title) {
// Find the line number that defines a block preformat with index
// ndx. In the wiki style text a block preformat is defined by an
// opening line of text that starts with '{{{', followed by the
// text of the example, then a closing line of text that starts
// with '}}}'.
var prefix = /^(\{\{\{|\/\/\{\{\{|\/\*\{\{\{|\<\!--\{\{\{)/;
return typeof text == 'string'
? prefix.test(text)
: this.elem_first_line(text,prefix,ndx,title);
},
preformat_last_line : function ( text, r0 ) {
var r = r0;
while ( r < text.length )
if ( /^(\}\}\}|\/\/\}\}\}|\/\*\}\}\}|\<\!--\}\}\})/
.test(text[r]) ) return r;
else r++;
return r-1;
},
//}}}
/***
!!! table_first_line / table_last_line
***/
//{{{
table_first_line : function (text,ndx,title) {
// Find the beginning line number that defines the table with
// index ndx. In the wiki style text a table is defined by
// (non-blank) lines of text that start and end with '|'. Therefore
// we only focus on those lines that start with a '|'.
return this.elem_first_line(text,/^[\|]/,ndx,title);
},
table_last_line : function ( text, r0 ) {
// Find the closing line number that defines the table starting
// at line number r0.
var r = r0;
while ( r < text.length )
if ( /^[\|]/.test(text[r] ) ) r++;
else break;
return r-1;
},
//}}}
/***
!!! split_line_text
@@This function is actually not depricated but moved to TWtcalc@@, a simple calculator for TiddlyWiki.
***/
//{{{
split_line_text : function ( txt, sep, brackets ) {
// Split one line of wikitext that contains several items, a table
// row or an array, for example.
//
// Arguments: txt The txt to split.
// sep The separator of items
// brackets An array of pairs of opening/closing
// brackets that should not split.
var result = txt.split (sep), r0 = 0
, rl = result.length-1, popen, mclose, pclose;
while ( ! result[r0] && r0 < rl ) r0++;
while ( ! result[rl] && r0 < rl ) rl--;
for ( var b = 0; b < brackets.length; b++ ) {
for ( var i = r0; i < rl; i++ ) {
if ( ! result[i] ) continue;
if ((popen=result[i].lastIndexOf(brackets[b].open))==-1)
continue;
if ( typeof brackets[b].close == 'string' )
pclose = result[i].lastIndexOf(brackets[b].close);
else {
if ( (mclose=result[i].match(brackets[b].close)) ) {
pclose = result[i]
.lastIndexOf(mclose[mclose.length-1]);
} else pclose = -1;
}
if ( popen > pclose ) {
// Found one part of the text that contains an opening
// bracket(s), but not its closing counterpart. This is
// possibly one of the cases mentioned above. If so then
// the next part may contain the corresponding closing
// bracket(s).
if ( typeof brackets[b].close == 'string' )
pclose = result[i+1].indexOf(brackets[b].close);
else
pclose = result[i+1].search(brackets[b].close);
popen = result[i+1].indexOf(brackets[b].open);
if ( pclose > -1 ) {
if ( popen==-1 || popen>pclose ) {
// Fond the corresponding closing bracket(s).
// Combine the two parts to restore it.
result[i] += sep + result[i+1];
// Remove the second part.
result.splice ( i+1, 1 );
if ( popen > pclose )
// There is another opening bracket(s) after
// the desired closing bracket(s), check this
// cell again.
--i;
}
}
}
}
}
return result;
},
//}}}
/***
!!! split_table_row
***/
//{{{
split_table_row : function ( linetxt ) {
// Split one line of wikitext that defines a table row.
// A table row consists of one or more table cells, each separated
// by '|' in the wikitext that defines the table row. For example,
// | 1 | 2 | 3 |
// defines a table row with three table cells.
// Naively a simple linetxt.split ('|') should do the work. Yes it
// does, in cases where the cell contents are just regular text.
// If some of the cells contain a link to a tiddler
// [[text|tiddler.name]], or to an external URL [[text|http://xxx]],
// then there will be an extra '|' that should not be considered as
// cell separation delimiter. If we still want to use
// linetxt.split ('|'), then such links should be recoverd before use.
// 2012/08/19. Another case is the preformatted text enclosed within
// {{{ and }}}. There could be one or more instances of '|' contained
// in the preformatted text.
// 2012/10/31. Even another case is the local file URL in Windows
// system: the semicolon ':' used to specify hard disk drive
// is replaced with the vertical bar '|'.
// For example, the URL for a local file stored in
// C:\Document\something.txt would be
// {{{file:///C|/Document/something.txt}}}.
// The vertical bar here should also be restored before use.
return this.split_line_text (linetxt, '|', [
{open:'[[', close:']]'}
,{open:'{{{', close:'}}}'}
,{open:'\(', close:'\)'}
,{open:'$', close:'$'}
,{open:'\[', close:'\]'}
,{open:'$$', close:'$$'}
//,{'open':'<<<', 'close':'>>>'}
,{open:'file:///', close:/^[\/][\w]+[\w\.\/\#]/}
]);
},
//}}}
/***
!!! cell_index_in_line
***/
//{{{
cell_index_in_line : function ( linetxt, c ) {
// Find the index number of the text that defines the cth cell in
// linetxt.
// Arguments:
// linetxt: The text that defines the table row. The linetxt
// MUST already be '|' splitted using
// this.split_table_row(linetxt).
// c: The colum index of the cell within the table row
// defined by linetxt.
// Returns:
// If found, returns the index number of the text that defines
// the cth cell. Otherwise returns -1.
if ( c < 0 || c >= linetxt.length ) return -1;
var col = -1;
for ( var dc = 1; dc < linetxt.length-1; dc++ ) {
if ( linetxt[dc] != '>' && linetxt[dc] != '~' ) col++;
if ( col == c ) return dc;
}
return -1;
},
//}}}
/***
!!! cell_text
***/
//{{{
cell_text : function ( wikitxt,start,end,row,col,span ) {
// Retrieve the wikitext associated with a table cell located at
// (row, col), together with the corresponding row/col indices of
// the cell if it were not row/column spanned. These indices are
// convenient for handling the underlying wikitxt of a spanned
// cell.
//displayMessage('search for table cell ('+row+','+col+') spanned ('+span.row+','+span.col+') between '+TWtid.object_string(start)+' nearby '+wikitxt.substring(start.ndx,start.ndx+10)+' and '+TWtid.object_string(end)+' nearby '+wikitxt.substring(end.ndx-10,end.ndx));
var rstart, rend, c, txt = new Array(), rowtxt;
// Find the right cell (skip the spanned columns).
for ( var r = 0; r < span.row; r++ ) {
rstart = this.table_row_starts(wikitxt,start,end,row+r);
//displayMessage('table row '+(row+r)+' starts at '+rstart+' nearby '+wikitxt.substring(rstart,rstart+10));
rend = TWtid.table_row_ends(wikitxt,rstart+1);
//displayMessage(' ends at '+rend+' nearby '+wikitxt.substring(rend-10,rend));
rowtxt = this.split_table_row(wikitxt.substring(rstart,rend));
if(r==0)
if ( (c=this.cell_index_in_line(rowtxt,col)) == -1 ) {
// Retrieving a non-existing cell. Append it to this row.
c = rowtxt.length-1;
rowtxt.push('');
}
txt.push ( new Array() );
for ( col = span.col-1; col >= 0; col-- )
txt[r].push ( rowtxt[c-col] );
//displayMessage(' txt['+r+']='+txt[r]);
}
return txt;
},
//}}}
/***
!!! apparent_dim
***/
//{{{
apparent_dim : function ($elem) {
switch ( $elem[0].nodeName ) {
case 'TABLE' :
if ( config.options.chkTWtidEnabled ) {
$elem = $elem.parent();
}
case 'PRE' :
case 'BLOCKQUOTE' :
return {'W':$elem.width(), 'H':$elem.height()};
case 'SPAN' :
case 'DIV' :
if ( $elem.is('.sliderPanel,.tabContents,[tiddler]') )
return {'W':$elem.width(), 'H':$elem.height()};
}
var $place = $elem.find('span').size()>0
? $elem.parent()
: $elem;
var $span = jQuery('<span></span>');
$span.appendTo($place);
if ( config.browser.isIE )
$span.html($elem.html());
else
TWtid.copy_html($span,$elem);
//if ( typeof MathJax !== 'undefined' )
// MathJax.Hub.Queue(['Typeset',MathJax.Hub,$span[0]]);
var dim = {
'W':$span.outerWidth()
,'H':$span.outerHeight()
}
$span.remove();
return dim;
},
//}}}
/***
!!! apparent_dim
***/
//{{{
text_nodes : function ($elem) {
return $elem.contents().filter(function() {
return this.nodeType == 3;
})
},
//}}}
/***
! {{{TWted}}}.xxx functions
***/
//{{{
locatable_elem : function($elem) {
// This function was written for leveled elements
// that do not start from the top level. For example,
// a blockquote that starts from the 2nd level.
var $parent, $children,
name = $elem[0].nodeName;
while ( true ) {
$parent = $elem.parent(name);
if ( $parent.size() == 0 ) return $elem;
$children = $parent.children().not(name);
if ( $children.size() > 0 ) return $elem;
$elem = $parent;
}
},
//}}}
在 {{{TableEditor}}} 裡面我使用 {{{TextArea}}} 來編輯表格欄位的內容,通常沒有什麼問題,但有時候當欄位內容稍長一些,我總覺得如果 {{{TextArea}}} 可以大一點讓我看到多一點內容的話會比較好一點。@@color:blue;網路上並不難找到可用的範例將 {{{TextArea}}} 的高度動態調整到符合文字內容的長度,@@ 然而,讓我很驚訝的是,許多人的方式實在過於複雜而遠遠超出我這個功力還不到家的 Javascript 非菜鳥的理解程度。我相信他們有很好的理由把程式寫得那麼複雜,但就我的簡單目的來說我寧可不要借用一個永遠無法理解的方式去做,所以我決定自己來。
In {{{TableEditor}}} a {{{TextArea}}} is used to edit cell content. Usually it works fine but sometimes when I typed a bit more text into the {{{TextArea}}}, I would feel better to have a larger {{{TextArea}}} so I can see more of the text I just typed in. @@color:blue;It is not difficult to find working examples that dynamically set the {{{TextArea}}} height to fit its content.@@ It is, however, to my surprise that many of them are way complicated and far beyond this not-that-experienced-Javascriptor's understanding. I am sure they have a good reason to make it so complicated, but for my simple purpose I don't want to borrow something that I can never understand. So I decided to do it myself.
經過一些嘗試我發現增加高度的方法其實很直接:
>把高度設定成 {{{scrollHeight}}} 就可以了。
After some trying I figured that it is quite straightforward to extend the height:
>simply set it to the value of {{{scrollHeight}}} property.
//{{{
// $ta is the jQuery object representing my TextArea.
$ta.height($ta.prop('scrollHeight'));
//}}}
這樣做 {{{TextArea}}} 的高度會自動增加,當 {{{scrollHeight}}} 比 {{{height()}}} 要更大的時候。__不過呢,使用 {{{Chrome, FireFox and Safari}}} 的時候在相反的情況下它卻不會自動縮小。''只有 {{{IE9}}} 的動作是和預期的一致''。__
This way {{{TextArea}}} extends vertically when {{{scrollHeight}}} is larger than {{{height()}}}. __However, it doesn't shrink in opposite cases with {{{Chrome, FireFox and Safari}}}. ''Only {{{IE9}}} works the expected way''.__
>瀏覽器之間的不相容行為一直讓我抓狂!過去的經驗裡比較多次是 {{{IE}}} 讓我吃苦,不過這回 {{{IE9}}} 卻絕對是個甜頭。
>The inconsistent behaviors between different browsers always drive me crazy! In my experiences most of the times {{{IE}}} was the bitter one, but in this case {{{IE9}}} is definitely the sweet candy.
很快我了解到為何它不會自動縮小:{{{scrollHeight}}} 的值沒有跟著文字縮短而變小。我試著找出一個方法來得到正確的 {{{scrollHeight}}},數小時間用盡我能想到的辦法,包括自己計算所需要的高度,仍然沒有收穫,幾乎要放棄。在最後幾分鐘裡我發現 [[這個網頁|http://code.google.com/p/chromium/issues/detail?id=141977]],它啟發了我一個想法:__先改變高度再來獲得 {{{scrollHeight}}} 的值__。
Pretty soon I realized why it doesn't shrink: the {{{scrollHeight}}} property does not reduce as the text shortens. I tried to find a way to get the correct {{{scrollHeight}}}, in several hours I tried all I could think of, including to calculate the required height myself, all to no avail. I almost gave up. In the last few minutes I found [[this page|http://code.google.com/p/chromium/issues/detail?id=141977]] which inspired me one idea: __change the height before getting the {{{scrollHeight}}} property__.
成了。
It works.
//{{{
// $ta is the jQuery object representing my TextArea.
$ta.height(0);
$ta.height($ta.prop('scrollHeight'));
//}}}
要實用的話應該有一個最小高度以免 {{{TextArea}}} 變得太小無法使用。下面列出 {{{TableEditor}}} 裡面實際使用的程式碼。
To be practical there should be a minimum height to keep the {{{TextArea}}} from going too small to use. The actual codes used in {{{TableEditor}}} are listed below.
//{{{
// This is the keydown handler of a TextArea.
keydown : function (ev) {
var $ta = jQuery(this);
switch ( ev.which ) {
// ...
// ...
default :
// Adjust the edit box height to fit its content.
// The following codes are pushed to the next event
// loop in order to obtain the updated scaleHeight
// of $ta after receiving the current character. We
// need to do so because the keydown event is invoked
// BEFORE the element receives the input character.
setTimeout ( function(){
// It is interesting that with Chrome, FireFox,
// and Safari we need to change the height of $ta
// to get the correct scrollHeight, while with IE9
// we get it either way. The scrH0 is just the minimum
// height that we stored upon initialization.
$ta.height(0);
var scrH0 = $ta.attr('scrH0')*1,
scrH = $ta.prop('scrollHeight');
$ta.height(scrH > scrH0 ? scrH : scrH0);
}, 0 );
break;
}
},
//}}}
>This tiddler, together with [[Edit Your Wrappers]] and [[Edit Your Elements]], describes how to bring view mode editing and/or transclusion synchronization features into your plugins.
To make the visual outputs of your plugin editable in the view mode, you will need to adapt your codes a little bit to work with ''twve''. As stated in the //Introduction// of the [[Editable Objects]], there is some distinction between editable objects, namely the //wrappers// and the //elements//. Codes for //wrappers// shall inherit from [[twve.wrapper]], while that for elements shall do from [[twve.element]]. Because of this, the first thing you need to do is to decide whether your codes are for wrappers or for elements. This is fortunately easy to do: your codes are for wrappers if they are to transclude other tiddler's content, and for elements otherwise.
Once you have decided which type of codes you have, you then adapt the codes according to the descriptions in [[Edit Your Wrappers]] and [[Edit Your Elements]], respectively.
>This tiddler describes how to equip your custom elements (DOM elements with definite wiki text signatures) with view mode editing and/or transclusion synchronization features.
To give your custom elements capabilities of view mode editing and/or transclusion synchronization, you simply do the followings:
!!! __Add__
* __Add__ a property called ''enableEdit'' to the code object representing your wrappers.
* Your wrapper is editable when this property is set to //true//.
* The representing code object can be one in JASON format or a function.
!!! __Implement__
__Implement__ the following __REQUIRED__ methods:
# ''twveSelector''(selector), which
## optionally accepts a twve.selector object selector
## calls selector.includeSelector('incclude selector') to add the include selector, and,
## if desired, calls selector.excludeSelector('excclude selector') to add the exclude selector,
## returns the twve.selector object selector.
## See <<slider '' 'twve.core##twve.code.twveSelector' 'twve.code.twveSelector([selector])'>> for a simple example, or
## <<slider '' 'twve.extra##twve.listitem.twveSelector' 'twve.listitem.twveSelector([selector])'>> for a more advanced one.
## For more information about ''twve.selector'', see [[twve.selector]].
# ''wikiTags''(), which returns the wiki tags of its kind of elements.
## See <<slider '' 'twve.core##twve.code.wikiTags' 'twve.code.wikiTags()'>> for a simple example, or
## <<slider '' 'twve.extra##twve.listitem.wikiTags' 'twve.listitem.wikiTags()'>> for a slightly advanced one.
## For an even more advanced example, see <<slider '' 'twve.core##twve.pre.wikiTags' 'twve.pre.wikiTags()'>> for the preformatted blocks.
## For more information about wiki tags, see [[twve.tags]].
# ''create''(src), which creates elements of its kind.
## Your elements should inherit from either [[twve.element]] (as the first statement in <<slider '' 'twve.core##twve.code.create' 'twve.code.create([src])'>>) or [[twve.leveledElement]] (as the first statement in <<slider '' 'twve.extra##twve.listitem.create' 'twve.listitem.create([src])'>>).
## In this method you can optionally override the default methods defined in [[twve.editable]], [[twve.element]] or [[twve.leveledElement]].
*** See <<slider '' 'twve.extra##twve.listitem.create' 'twve.listitem.create([src])'>> for example.
*** [[twve.editable]] is the base for all editable objects in ~TiddlyWiki.
!!! __Register__
__Register__ your element by calling ''twve.tiddler.registerElement''(//yourObject//) in the initialization procedure of your plugin. Your elements are then editable in the view mode!
<<foldHeadings>>
>This tiddler describes how to equip your custom wrappers (the containers that holds transcluded content most likely) with view mode editing and/or transclusion synchronization features.
To give your custom wrappers capabilities of view mode editing and/or transclusion synchronization, you simply do the followings:
!!! __Add__
* __Add__ a property called ''enableEdit'' to the code object representing your wrappers.
* Your wrapper is editable when this property is set to //true//.
* The representing code object can be one in JASON format or a function.
!!! __Implement__
__Implement__ the following methods:
* __REQUIRED__
## ''twveSelector''(selector), which
### optionally accepts a twve.selector object selector
### calls selector.includeSelector('incclude selector') to add the include selector, and,
### if desired, calls selector.excludeSelector('excclude selector') to add the exclude selector,
### returns the twve.selector object selector.
### See <<slider '' 'twve.core##twve.viewer.twveSelector' 'twve.viewer.twveSelector([selector])'>> for an example.
### For more information about ''twve.selector'', see [[twve.selector]].
## ''titleOfWrapper''($w), which returns the tiddler title that's been transcluded into $w (jQuery object representing a wrapper of its kind).
### See <<slider '' 'twve.core##twve.tiddlerSpan.titleOfWrapper' 'twve.tiddlerSpan.titleOfWrapper($w)'>> (wrappers holding {{{<<tiddler>>}}} transcluded content) or <<slider '' 'twve.core##twve.viewer.titleOfWrapper' 'twve.viewer.titleOfWrapper($w)'>> (wrappers holding normally loaded content) for examples.
## ''wrapperFromTitle''(title, $parent), which finds all the wrappers with the given title (tiddler title) in $parent (jQuery object).
### See <<slider '' 'twve.core##twve.tiddlerSpan.wrapperFromTitle' 'twve.tiddlerSpan.wrapperFromTitle(title,$parent)'>> (wrappers holding {{{<<tiddler>>}}} transcluded content) or <<slider '' 'twve.core##twve.viewer.wrapperFromTitle' 'twve.viewer.wrapperFromTitle(title,$parent)'>> (wrappers holding normally loaded content) for examples.
## ''create''(src), which creates wrappers of its kind.
### Your wrappers should inherit from [[twve.wrapper]].
**** See the 1^^st^^ line in <<slider '' 'twve.core##twve.viewer.create' 'twve.viewer.create([src])'>> for a simple example.
**** There are cases where a //transclusion wrapper// is treated as an //element//, such as to locate the plain text between two block elements, with one of which being a transclusion wrapper. To support such cases, modify the inheritance statement as the first statement in <<slider '' 'twve.core##twve.tiddlerSpan.create' 'twve.tiddlerSpan.create([src])'>> (for {{{<<tiddler>>}}} transcluded wrappers).
### In this method you can optionally override the default methods defined in [[twve.editable]] or [[twve.wrapper]].
**** For an example see <<slider '' 'twve.extra##twve.foldedSection.create' 'twve.foldedSection.create([src])'>> ({{{<<foldHeadings>>}}} created wrappers) that overrides the {{{twwrap.setElement($elem)}}} method.
**** [[twve.editable]] is the base for all editable objects in ~TiddlyWiki.
* __OPTIONAL__
## ''wikiTags''(), which returns the wiki tags of its kind of wrappers.
### See <<slider '' 'twve.core##twve.tiddlerSpan.wikiTags' 'twve.tiddlerSpan.wikiTags()'>> for an example.
### For more information about wiki tags, see [[twve.tags]].
!!! __Register__
__Register__ your wrapper by calling ''twve.tiddler.registerWrapper''(//yourObject//) in the initialization procedure of your plugin. Your wrappers are then editable in the view mode!
<<foldHeadings>>
The ''T''iddly''W''iki ''V''iew mode ''E''ditor, briefly ''twve'', is a collection of plugins that gives you the possibilities to
# edit the wiki text in the view mode, transcluded as well as normally loaded;
# keep all transcluded copies updated.
<<<
# For __users__ who wants to play with it right away, go straight to __[[Examples]]__ for the fun of it. For those who wants some basic information about the editable objects, please read __[[Editable Objects]]__.
# For __''plugin authors''__ interested in having //view mode editing// and/or //transclusion synchronization// features in your plugins, please see __[[Edit Your ...]]__ and __[[Event Handler -- .changed()]]__ for more information.
# For people who are interested in problems/issues encountered in the development, see [[Issues and Problems]] for a list of them and the corresponding solutions/workarounds.
<<<
! Why?
>''Can't find the specific text!''
Wiki text is easy, compared to HTML, to create a web page using a __simple text editor__. For example, to create the following table
|H1|H2|H3|h
|C11|C12|C13|
|C21|C22|C23|
with HTML markups, one would say
{{{
<table>
<tr><th>H1</th><th>H2</th><th>H3</th></tr>
<tr><td>C11</td><td>C12</td><td>C13</td></tr>
<tr><td>C21</td><td>C22</td><td>C23</td></tr>
</table>
}}}
With wiki markups (or markdowns if you like), however, it becomes much simpler (~TiddlyWiki markup),
{{{
|H1|H2|H3|h
|C11|C12|C13|
|C21|C22|C23|
}}}
Personally I think many wiki lovers are attracted to it for this simplicity. At least I am.
Simple is usualy charming, yet sometimes annoying. Take ~TiddlyWiki for example, it is so easy to get started and I found myself stuck on it within a short time. I kept creating more and more complicated pages and everything was fine until one day I had to change one table in a not-so-short tiddler: __I couldn't find the specific text to edit!__
I needed an easier way to edit tables in a large tiddler!
! What?
> ''Edit where I can see it.''
My idea was to edit the wiki text in the view mode, hence no need to find the text in the poor editor with my poor eyes and easily tired brain. I searched a while over the net for a solution and couldn't find one suitable for ~TiddlyWiki. Then I gave up searching and started to create one of my own. It started with the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] which supported simple editing of table cell contents, then evolved into [[TWtable|http://twtable.tiddlyspace.com/#TWtable.1.5.0]]+[[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] that supported more advanced features such as multi-lined cell content, lists in a cell, etc., and later to [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]]+[[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] combination that supports view mode editing on most of the block elements in addition to tables. The combination kept evolving and later became the collection ''twve'', which contains the following components:
<<<
!!!! ''twve.core''
<<tiddler "Editable Objects##''twve.core''">>
!!!! ''twve.table''
<<tiddler "Editable Objects##''twve.table''">>
!!!! ''twve.tablelarge''
<<tiddler "Editable Objects##''twve.tablelarge''">>
!!!! ''twve.extra''
<<tiddler "Editable Objects##''twve.extra''">>
!!!! ''twve.tcalc''
<<tiddler "Editable Objects##''twve.tcalc''">>
<<<
Some people might say:
>"//Tiddlers are not meant to be large. Cut them small!//"
I agree. They were meant to be small, in times when we had no computers to easily collect and organize information. Now that we do, think about this: @@color:blue;If you have a car that can run 300 km/hr, would you be happy to drive it at 50 in the city, or to create a place that you can enjoy the speed?@@
! How?
> ''Find the missing half!''
The heart of editing wiki text in the view mode is to find the correspondence between a DOM element and its defining wiki text.
When the user tries to change the content of a DOM element, the plugins find its wiki text and put it in an editbox for the user to edit. When done, the user tells the plugins by clicking away or pressing {{{Ctrl-Enter/Enter}}}, depending on the options settings or the multi-line nature of the element. The plugins then save the changed text and update the element, and update all transcluded copies, if any.
>The ideas behind //finding wiki text// and //transclusion synchronization// are given in other tiddlers.
>[[Matching the two halves]]
>[[TransclusionSupport--How To]]
>[[TransclusionSupport--PartialTransclusion]]
[[Editable Objects]], together with [[Editable Wrappers]] and [[Editable Elements]], describe more details about what is editable and what can be done in the view mode.
/***
|Name|EditFieldPlugin|
|Source|http://www.TiddlyTools.com/#EditFieldPlugin|
|Version|1.6.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|extend core edit macro for use in ViewTemplates or direct embedding in tiddler content|
!!!!!Usage
<<<
Normally, when a tiddler is edited, a set of input fields is displayed (as defined by the [[EditTemplate]]), and any changes you make are only saved when you press the "done" (or "cancel") command in the tiddler editor's toolbar. However, you can also ''embed an input field directly in //viewed// tiddler content'' by writing:
{{{
<<edit fieldname numberOfLines defaultValue>>
<<edit fieldname@TiddlerName numberOfLines defaultValue>>
}}}
or in the [[ViewTemplate]] (to add it to every tiddler):
{{{
<span macro='edit fieldname numberOfLines defaultValue'></span>
<span macro='edit fieldname@TiddlerName numberOfLines defaultValue'></span>
}}}
Unfortunately, while the input field will be displayed, the "done" command item is not available when //viewing// a tiddler, so the 'save/discard' handling cannot be invoked once you have decided that your input activities are complete, and any changes you make will not stored anywhere. To address this, the plugin extends the input field handler so that when a field is embedded in normal tiddler content and you make changes, it can automatically save/discard your changes as soon as you press ENTER or move away ('onBlur' handling) from that input field. You can also abandon your changes to input field content by pressing ESCAPE.
The plugin also adds support for an optional 'remote field reference' syntax: "{{{fieldname@TiddlerName}}}" so you can display and edit fields stored in other tiddlers. This allows you to create, for example, 'summary' tiddlers for reviewing/editing field values from several tiddlers at the same time.
Note: a message is displayed before saving/discarding any field changes. To suppress either (or both) of these confirmation messages, you can add the following configuration settings to [[EditFieldPluginConfig]] (or any other tiddler you are using to store and apply 'persistent settings'):
{{{
config.macros.edit.cancelMsg = "";
config.macros.edit.saveMsg = "";
}}}
<<<
!!!!!Examples
<<<
*"""<<edit foobar>>"""<br><<edit foobar>>
*"""<<edit foobar@SomeTiddler>>"""<br><<edit foobar@SomeTiddler>>
*"""<<edit tags>>"""<br><<edit tags>>
*"""<<edit text 15>>"""<br>{{editor{<<edit text 15>>}}}
<<<
!!!!!Revisions
<<<
2010.11.15 [1.6.1] In handler(), corrected display field values for 'non-stored' tiddler content (e.g., shadows, tags, default new tiddler)
2010.10.31 [1.6.0] In handler(), fixed display of remote field values. In onblur(), refactored save/cancel message text for easier customization and bypass confirmation if text is blank.
2009.09.16 [1.5.1] fixed 'onblur' handling for local fields (fieldname@here). Added support for '@here' syntax
2009.09.05 [1.5.0] code refactored. added handling for fieldname@tiddlername
2007.08.22 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.EditFieldPlugin= {major: 1, minor: 6, revision: 1, date: new Date(2010,11,15)};
config.macros.edit.editFieldPlugin_savedHandler=config.macros.edit.handler;
config.macros.edit.cancelMsg = "Abandon changes to %0@%1?";
config.macros.edit.saveMsg = "Save changes to %0@%1?";
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// let core render input/textarea, then get resulting element
config.macros.edit.editFieldPlugin_savedHandler.apply(this,arguments);
var fieldType=params[0]=="text"||params[1]?'textarea':'input';
var elems=place.getElementsByTagName(fieldType); var e=elems[elems.length-1];
// extended fieldname@tiddlername handling
var parts=e.getAttribute("edit").split('@');
var field=parts[0];
var title=parts[1]||tiddler.title;
if (title=='here') title=tiddler.title;
// stop field from being saved with 'done' button
if (parts[1]) { e.setAttribute("edit",null); e.setAttribute("field",field); }
// save starting value and target tiddler
e.value=store.getValue(title,field)||e.value; // get field value
e.setAttribute("currval",e.value); // save starting value
e.setAttribute("tiddler",title);
// force height for textarea field
if (fieldType=="textarea" && params[1]) e.style.height=params[1]+"em";
// if viewing tiddler, add autosave handlers
var here=story.findContainingTiddler(place);
var isViewed=here&&here.getAttribute("template").indexOf("ViewTemplate")!=-1;
if (parts[1]||isViewed) { // remote reference or view mode editing...
story.setDirty(tiddler.title,false); // clear tiddler ("dirty") flag (set by core)
e.onkeydown=function(ev) { // ENTER key=save (for single-line input)
var event=ev?ev:window.event;
this.setAttribute("keyCode",event.keyCode); // save last keyCode (for blur)
if (event.keyCode==13 && this.nodeName.toUpperCase()!="TEXTAREA")
this.saveField(); // save input to tiddler field
}
e.onblur=function(ev) { // confirm input when focus moves away
var event=ev?ev:window.event;
var tid=this.getAttribute("tiddler"); if (!tid || !tid.length) return;
var field=this.getAttribute("edit")||this.getAttribute("field");
if (this.value==this.getAttribute("currval")) return; // no change
if (this.getAttribute("keyCode")=="27") { // if user pressed ESC
var msg=config.macros.edit.cancelMsg.format([field,tid]);
if (!msg.length || confirm(msg)) this.value=this.getAttribute("currval"); // reset value
this.id=new Date().getTime(); // set unique ID for delayed re-focus after blur
setTimeout("document.getElementById('"+this.id+"').focus()",1);
} else { // other focus change events
var msg=config.macros.edit.saveMsg.format([field,tid]);
if (!msg.length || confirm(msg)) this.saveField(); // save value
else this.value=this.getAttribute("currval");
}
};
e.saveField=function() { // unpack/validate attribs and then save the field
var tid=this.getAttribute("tiddler"); if (!tid || !tid.length) return;
var field=this.getAttribute("edit")||this.getAttribute("field");
var title=(field=="title")?this.value:tid;
if (!title.length) { // error: blank tiddler title
this.value=this.getAttribute("currval"); // reset value
this.id=new Date().getTime(); // set unique ID for delayed messages/refocus
setTimeout("displayMessage('Please enter a non-blank value')",1);
setTimeout("document.getElementById('"+this.id+"').focus()",2);
return;
}
config.macros.edit.saveField(tid,title,field,this.value);
this.setAttribute("currval",this.value); // remember new starting value
};
}
}
//}}}
//{{{
// save input value to tiddler field (create/touch/rename tiddler as needed)
config.macros.edit.saveField = function(tid,title,field,val) {
var t=store.getTiddler(tid);
store.suspendNotifications();
var anim=config.options.chkAnimate; config.options.chkAnimate=false; // suspend animation
var who=t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
store.saveTiddler(t?tid:title,title,t?t.text:"",who,when,t?t.tags:[],t?t.fields:null);
store.setValue(title,field,val); // save field
if (tid!=title) // new title... show renamed tiddler in place of current one
{ story.displayTiddler(story.getTiddler(tid),title); story.closeTiddler(tid); }
if (field=="text") // content changed, refresh tiddler display
{ story.refreshTiddler(title,null,true); }
config.options.chkAnimate=anim; // resume animation
store.resumeNotifications();
store.notify(title,true);
}
//}}}
>This tiddler describes which //elements// are editable and what can be done in the view mode. For the same information about //wrappers//, see [[Editable Wrappers]].
! Introduction
In ''twve'' the //elements// are DOM elements with definite wiki text signatures that can be easily identified in the wiki text. For example, in ~TiddlyWiki a table is defined by one or more consecutive lines of text that begin with one vertical bar (|), and contains one or more following ones. Going through all the supported elements in ~TiddlyWiki we can find that DOM elements can be classified into two types: //twve.element// and //twve.leveledElement//. DOM elements like TABLE, CODE, and PREFORMATTED BLOCK, are examples of //twve.element//, while those like LISTITEM, HEADING, and BLOCKQUOTE, are //twve.leveledElements//. The main differences between them are
# a //twve.leveledElement// has //levels// associated with it, and
# a //twve.leveledElement// may contain sub-elements of its own type.
A //twve.element//, however, does not have the above properties: it is not leveled and does not contain children of its own type.
For each of these elements the ''twve'' has one particular code object to represent it. For example, [[twve.table]] represents //tables//, while [[twve.listitem]] represents //listitems// in ~TiddlyWiki. All these code objects inherit properties and methods from either the [[twve.element]] or the [[twve.leveledElement]] (which also inherits properties and methods from ''twve.element''). The ''twve.element'' implements the common features that all these elements share, while the ''twve.leveledElement'' implements the particular features to take care of levels and sub-elements of its own type.
! Code Objects
The ''twve'' has the following built-in code objects:
<<<
!! ''twve.element''
!!!! twve.code
!!!! twve.pre
!!!! twve.table
!!!! twve.tabset
<<<
<<<
!! twve.leveledElement
!!!! twve.heading
!!!! twve.listitem
!!!! twve.blockquotes
<<<
<<<
''The easiest way to learn -- Just use it!''
Start using the plugins, you will notice the focusing borders flying around with your mouse cursor. __These focusing borders are basically telling you //where you can edit now//__.
* For most of the elements, just click within the focusing borders and edit.
* For transcluded content and their macro commands, see [[twve--Example--Transclusion--Content]] and [[twve--Example--Transclusion--Macros]].
See [[Editable Elements]] for an example, or @@color:blue;open up any of the tiddlers tagged with 'Examples' and play with it.@@ You will see how easy it is.
><<slider "" "Examples" "tiddlers tagged with 'Examples'">>
Have Fun!
<<<
! Want to know about them?
>This tiddler, together with [[Editable Wrappers]] and [[Editable Elements]], describes which objects are editable and what can be done in the view mode. If you are interested in bringing view mode editing and/or transclusion synchronization capabilities into your plugins, see [[Edit Your ...]] for more information.
!! Introduction
In ''twve'' there are two kinds of editable objects, namely ''wrappers'' and ''elements''. The //wrappers// are containers holding all or part of a tiddler's content, while //elements// are DOM elements that have definite wiki text signatures. For example, the {{{div.viewer}}} that holds the content of a normally loaded tiddler is one kind of //wrappers//, the {{{div.tabContents}}} that holds all or part of a tiddler's content transcluded through the {{{<<tabs>>}}} macro, is another. The //list item//, however, is an example of //elements// with clear wiki text signatures that it always occupies one line of text, and that line of text always starts with one or more pound signs (#) or asterisks (*).
These two types of objects have some things in common and many things different. In ''twve'' their common parts are implemented in [[twve.editable]] for them to share, while the differences go into [[twve.wrapper]] and [[twve.element]] accordingly. Editable wrappers inherit properties and methods from the [[twve.wrapper]], while editable elements inherit those from the [[twve.element]].
!!! Code structrure
The codes of the ''twve'' are divided into the following parts
!!!! ''twve.core''
The core of the collection, mainly providing
# easy access to the defining wiki text corresponding to any DOM element,
** modify the content of an element
** change the type of the element
** keyboard navigation within the same type of elements
# immediate update for all transcluded copies of a DOM element after changes made,
# post-change processing through the <<slider '' 'twve.core##editable.changed' '.changed(param)'>> event handler (first defined in [[twve.editable]]).
** Such as //testing the input value//, //resizing the table// or //recalculating the table//.
** See [[Event Handler -- .changed()]] for more information.
There are quite a few objects defined in the core.
* The very base
** [[twve.object]] -- The very base of all ''twve'' objects
* Tiddler and wiki text related:
## [[twve.tiddler]] -- representing the tiddlers;
## [[twve.text]] -- wiki text handling;
## [[twve.tags]] -- representing the opening and closing tags of elements;
### [[twve.tags.nested]] -- tags that could contain other tags (of the same type) between its opening and closing tags.
## [[twve.position]] -- for the searching purposes
* DOM related components:
## [[twve.selector]] -- for CSS selectors
## [[twve.node]] -- base for all DOM related objects
## [[twve.button]] -- buttons in ''twve''
## [[twve.menu]] -- menus in ''twve''
## [[twve.editable]] -- the common parts of [[twve.element]] and [[twve.wrapper]];
### [[twve.element]] -- editable elements
#### [[twve.heading]] -- headings
#### [[twve.pre]] -- preformatted block
#### [[twve.code]] -- code block
### [[twve.wrapper]]: base for editable wrappers;
#### [[twve.viewer]]: for normally loaded tiddlers;
#### [[twve.tabContent]]: for {{{<<tabs>>}}} transcluded tiddlers;
#### [[twve.tiddlerSpan]]: for {{{<<tiddler>>}}} transcluded tiddlers;
#### [[twve.sliderPanel]]: for {{{<<slider>>}}} transcluded tiddlers;
The following lines of codes show the definition of each part.
!!!! ''twve.table''
Table editor with the following features:
# Editing the content of a table cell.
# Rendering multi-lined content in a table cell:
** Block elements such as list items and blockquotes can be put in a table cell without transclusion.
** To break a text line in a table cell,
*** use {{{<br>}}} or {{{<br />}}} in the ~TiddlyWiki's built-in editor, any other text editor, or the edit box of ''twve'' with the option {{{chktwveTableMultiLine}}} set to //false//;
*** simply press {{{Enter}}} in the edit box of the ''twve'', if the option {{{chktwveTableMultiLine}}} is set to //true//.
# Insertion/deletion of a table row/column.
# Switching (rotating) the content of adjacent table rows/columns.
# Copy/cut/paste a table row/column/cell.
# Transposition of a table (with or without spanned cells).
!!!! ''twve.tablelarge''
Large table supports, including
# scrolling of table body,
# fixed rows/columns (//unstable//).
!!!! ''twve.extra''
Editor for non-table components, such as
# list items:
** modify the content
** change the level
** change the type (ordered to/from unordered)
** //move// the item up/down (//incomplete, currently only works within the same level//)
# headings:
** modify the content
** change the level (1 to 6)
# blockquotes
** modify the content
** change the level (1 to 3)
# blockexamples:
** modify the content
# preformatted blocks:
** modify the content
** change the creation wrappers
# plain text:
** modify the content
The objects defined in [[twve.extra]] are
* DOM related components:
## [[twve.foldedSection]]: for {{{<<foldHeadings>>}}} folded sections (defined in twve.extra);
## [[twve.tiddlerTitle]]: for tiddler title;
!!!! ''twve.tcalc''
A table calculator offering simple spreadsheet functionalities in ~TiddlyWiki.
# Calculates numerical values in table cells.
# Cell references syntax follows the ~LibreOffice.calc/Microsoft Excel style.
** Column index starts from {{{A}}} and ends at {{{ZZ}}}.
** Row index starts from 0 and has virtually no upper limit.
*** A0 refers to the first cell (A) in the first row (0), while C3 refers to the 3^^rd^^ cell (C) in the 4^^th^^ row (3). These are called //single cell references//.
*** A0:C3 refers to the collection of all the following cells: A0, A1, A2, A3, B0, B1, B2, B3, C0, C1, C2, C3. This is called //block reference//.
** Supports relative and absolute referencing the same way as the two major spreadsheet applications.
*** By default all references are relative. For example, if we put formula {{{=A0+1}}} into cell A1, it means //at cell A1, put the result of one plus the numerical value in the cell above.//
**** __Relative references are automatically adjusted when copy-and-pasted to another cell__. For example, if we copy the content of cell A1, which is {{{=A0+1}}} if we stick to the example above, and then paste it into cell A2, it would be adjusted to {{{=A1+1}}} to keep the meaning of //one plus the numerical value in the cell above//.
*** To indicate absolute references, //add a dollar sign ($) immediately before the row/column reference symbols//. For example, $A0 means the column reference (A) is absolute while the row reference (0) is relative, A$0 means the opposite, and $A$0 means both column and row references are absolute.
**** __Absolute references are NOT adjusted upon copy-and-pasting__.
# ''Auto adjustment of cell references upon''
** changes made to a table cell;
** __insertion/deletion of rows/columns__;
** __exchange with adjacent rows/columns__;
** __sorting with SortableGridPlugin or TableSortingPlugin__.
*** Block references such as {{{C4:H10}}} are auto-adjusted only upon insertion/deletion of rows/columns, NOT upon sorting.
# ''Auto recalculation upon''
** changes made to a table cell;
** __insertion/deletion of rows/columns__;
** __exchange with adjacent rows/columns__;
** __sorting with SortableGridPlugin or TableSortingPlugin__.
# Supports several forms of decimal mark and thousands separation.
# Supports user-defined functions written in Javascript.
** To define your own functions, see [[twve.tcalc--Defining Your Own Functions]] for details.
** See the full version of [[twve.tcalc]] for pre-defined functions.
!!!!!!
<<foldHeadings>>
>This tiddler describes which //wrappers// are editable and what can be done in the view mode. For the same information about //elements//, see [[Editable Elements]].
In ''twve'' the //wrappers// are containers that hold all or part of a tiddler's content, such as //div.viewwer// (normally loaded tiddlers), //div.tabContents// ({{{<<tabs>>}}} transcluded tiddlers), //div.sliderPanel// ({{{<<slider>>}}} transcluded tiddlers), etc.
The ''twve'' supports view mode editing on most of the elements, block or inline. When some change is made to an element, an event handler <<slider '' 'twve.core##editable.changed' '.changed(param)'>> is called for further process.
<<<
For example, the [[twve.table]] adjusts table dimensions when something is changed (<<slider '' 'twve.table##twtable.changed' 'twtable.changed(param)'>>).
Another example, the [[twve.tcalc]] recalculates a table in this handler when 1) a table cell is changed, 2) a row/column is pasted/deleted. Furthermore, it adjusts cell references when 1) a row/column is inserted/deleted, 2) adjacent rows/columns are switched (rotated), and 3) sorted. See <<slider '' 'twve.tcalc##twtable.changed' 'twtable.changed(param) in twve.tcalc'>> for more information.
<<<
The parameters passed to this event handler are ''twelem'' and ''param''.
# ''twelem'': A ''twve.editable'' object representing the element just changed.
# ''param'': An object containing detailed information associated with the event.
** param.''what'': A string defining the event. Currently supported events are
*** ''MODIFIED'' for general cases,
*** ''ROW INSERTED'', ''ROW DELETED'', ''ROW PASTED'' for table row operations,
*** ''COL INSERTED'', ''COL DELETED'', ''COL PASTED'' for table column operations,
*** ''ROW MOVED'', ''SORTED'' for table sorting operations.
*** ''TRANSPOSED'', for table transposition.
** General event
*** param.''what'' == ''MODIFIED'': called when the content of a table cell is changed
**** param.''row'': row index of the cell just changed
**** param.''col'': column index of the cell just changed
**** param.''text'': text containing modified content of the table
** Table row events
*** param.''what'' == ''ROW INSERTED''
**** param.''where'': index of the row just inserted
**** param.''text'': text containing modified content of the table
*** param.''what'' == ''ROW DELETED''
**** param.''where'': index of the row just deleted
**** param.''text'': text containing modified content of the table
*** param.''what'' == ''ROW PASTED'': called when the user had just pasted a whole table row
**** param.''where'': index of the row just pasted
**** param.''from'': index of the source row
**** param.''text'': text containing modified content of the table
** Table column events
*** param.''what'' == ''COL INSERTED''
**** param.''where'': index of the column just inserted
**** param.''text'': text containing modified content of the table
*** param.''what'' == ''COL DELETED''
**** param.''where'': index of the column just deleted
**** param.''text'': text containing modified content of the table
*** param.''what'' == ''COL PASTED''
**** param.''where'': index of the column just pasted
**** param.''from'': index of the source column
**** param.''text'': text containing modified content of the table
** Sorting events
*** param.''what'' == ''ROW MOVED'': called during sorting each time a table row is moved
**** param.''from'': original index of the row just moved
**** param.''to'': new index of the row just moved
**** param.''text'': text containing modified content of the table
*** param.''what'' == ''SORTED'': called after sorting is done
**** param.''text'': text containing modified content of the table
** Transposition event
*** param.''what'' == ''TRANSPOSED'': called when the table is transposed
**** param.''text'': text containing modified content of the table
<<<
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
This tiddler collects some examples to show what ''twve'' can do in the view mode.
<<<
!!! ''twve.core'' Examples
<<tiddler "twve.core Examples">>
!!! ''twve.extra'' Examples
<<tiddler "twve.extra Examples">>
!!! ''twve.table'' examples
<<tiddler "twve.table Examples">>
!!! ''twve.tcalc'' examples
<<tiddler "twve.tcalc Examples">>
<<foldHeadings>>
/***
|Name|ExternalTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ExternalTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ExternalTiddlersPluginInfo|
|Version|1.3.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|TemporaryTiddlersPlugin (optional, recommended)|
|Description|retrieve and wikify content from external files or remote URLs|
This plugin extends the {{{<<tiddler>>}}} macro syntax so you can retrieve and wikify content directly from external files or remote URLs. You can also define alternative "fallback" sources to provide basic "import on demand" handling by automatically creating/importing tiddler content from external sources when the specified ~TiddlerName does not already exist in your document.
!!!!!Documentation
>see [[ExternalTiddlersPluginInfo]]
!!!!!Configuration
<<<
<<option chkExternalTiddlersImport>> automatically create/import tiddlers when using external fallback references
{{{usage: <<option chkExternalTiddlersImport>>}}}
<<option chkExternalTiddlersQuiet>> don't display messages when adding tiddlers ("quiet mode")
{{{usage: <<option chkExternalTiddlersQuiet>>}}}
<<option chkExternalTiddlersTemporary>> tag retrieved tiddlers as 'temporary'(requires [[TemporaryTiddlersPlugin]])
{{{usage: <<option chkExternalTiddlersTemporary>>}}}
tag retrieved tiddlers with: <<option txtExternalTiddlersTags>>
{{{usage: <<option txtExternalTiddlersTags>>}}}
__password-protected server settings //(optional, if needed)//:__
>username: <<option txtRemoteUsername>> password: <<option txtRemotePassword>>
>{{{usage: <<option txtRemoteUsername>> <<option txtRemotePassword>>}}}
>''note: these settings are also used by [[LoadTiddlersPlugin]] and [[ImportTiddlersPlugin]]''
<<<
!!!!!Revisions
<<<
2008.10.27 [1.3.1] in insertTiddler(), fixed Safari bug by replacing static Array.concat(...) with new Array().concat(...)
|please see [[ExternalTiddlersPluginInfo]] for additional revision details|
2007.11.25 [1.0.0] initial release - moved from CoreTweaks
<<<
!!!!!Code
***/
//{{{
version.extensions.ExternalTiddlersPlugin= {major: 1, minor: 3, revision: 1, date: new Date(2008,10,27)};
// optional automatic import/create for missing tiddlers
if (config.options.chkExternalTiddlersImport==undefined) config.options.chkExternalTiddlersImport=true;
if (config.options.chkExternalTiddlersTemporary==undefined) config.options.chkExternalTiddlersTemporary=true;
if (config.options.chkExternalTiddlersQuiet==undefined) config.options.chkExternalTiddlersQuiet=false;
if (config.options.txtExternalTiddlersTags==undefined) config.options.txtExternalTiddlersTags="external";
if (config.options.txtRemoteUsername==undefined) config.options.txtRemoteUsername="";
if (config.options.txtRemotePassword==undefined) config.options.txtRemotePassword="";
config.macros.tiddler.externalTiddlers_handler = config.macros.tiddler.handler;
config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("name",null,true,false,true);
var names = params[0]["name"];
var list = names[0];
var items = list.split("|");
var className = names[1] ? names[1] : null;
var args = params[0]["with"];
// UTILITY FUNCTIONS
function extract(text,tids) { // get tiddler source content from plain text or TW doc
if (!text || !tids || !tids.length) return text; // no text or no tiddler list... return text as-is
var remoteStore=new TiddlyWiki();
if (!remoteStore.importTiddlyWiki(text)) return text; // not a TW document... return text as-is
var out=[]; for (var t=0;t<tids.length;t++)
{ var txt=remoteStore.getTiddlerText(tids[t]); if (txt) out.push(txt); }
return out.join("\n");
}
function substitute(text,args) { // replace "substitution markers" ($1-$9) with macro param values (if any)
if (!text || !args || !args.length) return text;
var n=args.length; if (n>9) n=9;
for(var i=0; i<n; i++) { var re=new RegExp("\\$" + (i + 1),"mg"); text=text.replace(re,args[i]); }
return text;
}
function addTiddler(src,text,tids) { // extract tiddler(s) from text and create local copy
if (!config.options.chkExternalTiddlersImport) return; // not enabled... do nothing
if (!text || !tids || !tids.length) return; // no text or no tiddler list... do nothing
var remoteStore=new TiddlyWiki();
if (!remoteStore.importTiddlyWiki(text)) // not a TW document... create a single tiddler from text
makeTiddler(src,text,tids[0]);
else // TW document with "permaview-like" suffix... copy tiddler(s) from remote store
for (var t=0;t<tids.length;t++)
insertTiddler(src,remoteStore.getTiddler(tids[t]));
return;
}
function makeTiddler(src,text,title) { // create a new tiddler object from text
var who=config.options.txtUserName; var when=new Date();
var msg="/%\n\nThis tiddler was automatically created using ExternalTiddlersPlugin\n";
msg+="by %0 on %1\nsource: %2\n\n%/";
var tags=config.options.txtExternalTiddlersTags.readBracketedList();
if (config.options.chkExternalTiddlersTemporary) tags.pushUnique(config.options.txtTemporaryTag);
store.saveTiddler(null,title,msg.format([who,when,src])+text,who,when,tags,{});
if (!config.options.chkExternalTiddlersQuiet) displayMessage("Created new tiddler '"+title+"' from text file "+src);
}
function insertTiddler(src,t) { // import a single tiddler object into the current document store
if (!t) return;
var who=config.options.txtUserName; var when=new Date();
var msg="/%\n\nThis tiddler was automatically imported using ExternalTiddlersPlugin\n";
msg+="by %0 on %1\nsource: %2\n\n%/";
var newtags=new Array().concat(t.tags,config.options.txtExternalTiddlersTags.readBracketedList());
if (config.options.chkExternalTiddlersTemporary) newtags.push(config.options.txtTemporaryTag);
store.saveTiddler(null,t.title,msg.format([who,when,src])+t.text,t.modifier,t.modified,newtags,t.fields);
if (!config.options.chkExternalTiddlersQuiet) displayMessage("Imported tiddler '"+t.title+"' from "+src);
}
function getGUID() // create a Globally Unique ID (for async reference to DOM elements)
{ return new Date().getTime()+Math.random().toString(); }
// loop through "|"-separated list of alternative tiddler/file/URL references until successful
var fallback="";
for (var i=0; i<items.length; i++) { var src=items[i];
// if tiddler (or shadow) exists, replace reference list with current source name and apply core handler
if (store.getTiddlerText(src)) {
arguments[2][0]=src; // params[] array
var p=arguments[4].split(list); arguments[4]=p[0]+src+p[1]; // paramString
this.externalTiddlers_handler.apply(this,arguments);
break; // stop processing alternatives
}
// tiddler doesn't exist, and not an external file/URL reference... skip it
if (!config.formatterHelpers.isExternalLink(src)) {
if (!fallback.length) fallback=src; // title to use when importing external tiddler
continue;
}
// separate 'permaview' list of tiddlers (if any) from file/URL (i.e., '#name name name..." suffix)
var p=src.split("#"); src=p[0]; var tids=p[1]?p[1].readBracketedList(false):[];
// if reference is to a remotely hosted document or the current document is remotely hosted...
if (src.substr(0,4)=="http" || document.location.protocol.substr(0,4)=="http") {
if (src.substr(0,4)!="http") // fixup URL for relative remote references
{ var h=document.location.href; src=h.substr(0,h.lastIndexOf("/")+1)+src; }
var wrapper = createTiddlyElement(place,"span",getGUID(),className); // create placeholder for async rendering
var callback=function(success,params,text,src,xhr) { // ASYNC CALLBACK
if (!success) { displayMessage(xhr.status); return; } // couldn't read remote file... report the error
if (params.fallback.length)
addTiddler(params.url,text,params.tids.length?params.tids:[params.fallback]); // import tiddler
var wrapper=document.getElementById(params.id); if (!wrapper) return;
wikify(substitute(extract(text,params.tids),params.args),wrapper); // ASYNC RENDER
};
var callbackparams={ url:src, id:wrapper.id, args:args, tids:tids, fallback:fallback } // ASYNC PARAMS
var name=config.options.txtRemoteUsername; // optional value
var pass=config.options.txtRemotePassword; // optional value
var x=doHttp("GET",src,null,null,name,pass,callback,callbackparams,null)
if (typeof(x)=="string") // couldn't start XMLHttpRequest... report error
{ displayMessage("error: cannot access "+src); displayMessage(x); }
break; // can't tell if async read will succeed.... stop processing alternatives anyway.
}
else { // read file from local filesystem
var text=loadFile(getLocalPath(src));
if (!text) { // couldn't load file... fixup path for relative reference and retry...
var h=document.location.href;
var text=loadFile(getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf("/")+1)))+src);
}
if (text) { // test it again... if file was loaded OK, render it in a class wrapper
if (fallback.length) // create new tiddler using primary source name (if any)
addTiddler(src,text,tids.length?tids:[fallback]);
var wrapper=createTiddlyElement(place,"span",null,className);
wikify(substitute(extract(text,tids),args),wrapper); // render
break; // stop processing alternatives
}
}
}
};
//}}}
/***
|Name|FoldHeadingsPlugin|
|Source|http://www.TiddlyTools.com/#FoldHeadingsPlugin|
|Version|1.1.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|automatically turn headings into slider-like panels that can be folded/unfolded with a single click|
This plugin defines a macro that automatically converts heading-formatted content into sliders that let you expand/collapse their content by clicking on individual headings.
!!!!!Usage
<<<
{{{
<<foldHeadings opened|closed tag tag tag...>>
}}}
where: ''opened'' or ''closed'' is a keyword indicating the initial state of the sections (default: opened), and ''tag tag tag...'' is an optional list of tags to match, so that the foldable effect is only applied to tiddlers that contain one (or more) of the indicated tags.
When you place the macro in a tiddler, any heading-formatted content (i.e, "!" through "!!!!!") in that tiddler will automatically become //'fold-able'//, allowing you to expand/collapse the content that follows each heading simply by clicking on that heading. Each content section begins with the first element following a heading, and continues until either another heading is found or the end of the tiddler is reached. For example:
{{{
<<foldHeadings closed>>
}}}
is embedded in ''this'' tiddler in order to make all the headings it contains 'fold-able'. Note that the macro has been placed at the //end// of the tiddler because it only operates on *rendered* content. Thus, only headings that //precede// it in the same tiddler will become fold-able, as any headings that //follow// it are not actually rendered until //after// the macro has been processed.
You can further limit the effect of the macro within the tiddler by surrounding several headings in a "CSS class wrapper" ("""{{classname{...}}}""") or other containing DOM element (e.g., """@@display:inline;...@@""") and then embedding the {{{<<foldHeadings>>}}} macro inside that container (at the end)... only those headings that are also within that container will be made fold-able, instead of converting ''all'' the headings in that tiddler.
Conversely, if you want the fold-able ability to apply to the headings in //all// tiddlers, ''without having to alter //any// of those individual tiddlers'', you can add the macro to the end of your [[ViewTemplate]], so that it will be invoked after the content in each tiddler has been rendered, causing all headings they contain to automatically become fold-able. For example:
{{{
<span macro="foldHeadings closed"></span>
}}}
You can also limit this effect to selected tiddlers by specifying one or more tags as additional macro parameters. For example:
{{{
<span macro="foldHeadings closed systemConfig"></span>
}}}
is only applied to headings contained in //plugin tiddlers// (i.e., tiddlers tagged with <<tag systemConfig>>), while headings in other tiddlers remain unaffected by the macro, even though it is embedded in the common [[ViewTemplate]] definition.
<<<
!!!!!Revisions
<<<
2009.11.30 [1.1.2] corrected CSS 'text-weight' to 'font-weight'
2009.01.06 [1.1.1] removed hijack of scrollToSection() (see [[SectionLinksPlugin]] for equivalent code)
2008.11.17 [1.1.0] added hijack of 'scrollToSection()' function (see [[CoreTweaks]] and http://trac.tiddlywiki.org/ticket/784)
2007.12.06 [1.0.2] fix handling for empty sections when checking for sliderPanel/floatingPanel
2007.12.02 [1.0.1] fix handling when content following a heading is already a sliderPanel/floatingPanel
2007.12.01 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.FoldHeadingsPlugin= {major: 1, minor: 1, revision: 2, date: new Date(2009,11,30)};
config.macros.foldHeadings = {
guideText: "opened|closed className",
showtip: "click to show '%0'",
hidetip: "click to hide '%0'",
showlabel: "...",
hidelabel: "[x]",
html: "<span style='float:right;font-weight:normal;font-size:80%;' class='TiddlyLinkExisting'>%0 </span>",
handler: function(place,macroName,params) {
var show=params[0] && params.shift().toLowerCase()!="closed";
if (params.length) { // if filtering by tag(s)
var here=story.findContainingTiddler(place);
if (here) var tid=store.getTiddler(here.getAttribute("tiddler"));
if (!tid || !tid.tags.containsAny(params)) return; // in a tiddler and not tagged... do nothing...
}
var elems=place.parentNode.getElementsByTagName("*");
var heads=[]; for (var i=0; i<elems.length; i++) { // get non-foldable heading elements
var n=elems[i].nodeName; var foldable=hasClass(elems[i],"foldable");
if ((n=="H1"||n=="H2"||n=="H3"||n=="H4"||n=="H5")&&!foldable)
heads.push(elems[i]);
}
for (var i=0; i<heads.length; i++) { var h=heads[i]; // for each heading element...
// find start/end of section content (up to next heading or end of content)
var start=end=h.nextSibling; while (end && end.nextSibling) {
var n=end.nextSibling.nodeName.toUpperCase();
if (n=="H1"||n=="H2"||n=="H3"||n=="H4"||n=="H5") break;
end=end.nextSibling;
}
if (start && hasClass(start,"sliderPanel")||hasClass(start,"floatingPanel")) continue; // heading is already a slider!
var span=createTiddlyElement(null,"span",null,"sliderPanel"); // create container
span.style.display=show?"inline":"none"; // set initial display state
h.parentNode.insertBefore(span,start); // and insert it following the heading element
// move section elements into container...
var e=start; while (e) { var next=e.nextSibling; span.insertBefore(e,null); if (e==end) break; e=next; }
// set heading label/tip/cursor...
h.title=(show?this.hidetip:this.showtip).format([h.textContent])
h.innerHTML=this.html.format([show?this.hidelabel:this.showlabel])+h.innerHTML;
h.style.cursor='pointer';
addClass(h,"foldable"); // so we know it been done (and to add extra styles)
h.onclick=function() {
var panel=this.nextSibling; var show=panel.style.display=="none";
// update panel display state
if (config.options.chkAnimate) anim.startAnimating(new Slider(panel,show));
else panel.style.display = show?"inline":"none";
// update heading label/tip
this.removeChild(this.firstChild); // remove existing label
var fh=config.macros.foldHeadings; // abbreviation for readability...
this.title=(show?fh.hidetip:fh.showtip).format([this.textContent])
this.innerHTML=fh.html.format([show?fh.hidelabel:fh.showlabel])+this.innerHTML;
}
}
}
}
if (story.scrollToSection) {
Story.prototype.foldheadings_scrollToSection=Story.prototype.scrollToSection;
Story.prototype.scrollToSection=function(title,section) {
var e=this.foldheadings_scrollToSection.apply(this,arguments);
// if scrolling to a folded section heading, click to expand it
if (e && hasClass(e,'foldable') && e.nextSibling.style.display=='none') e.onclick();
}
}
//}}}
// //<<foldHeadings closed>>
底下兩種做法
{{{
$ta = jQuery('<textarea>'+txt+'</textarea>');
}}}
以及
{{{
$ta = jQuery('<textarea></textarea>');
$ta.val(txt);
}}}
在 txt 只包含 wikitext 時候似乎沒什麼問題,但是在 txt 包含 HTML entities 時所產生的結果是不一樣的。第一種會產生 HTML entity 的【符號】,而第二種則是將產生 HTML entity 的文字原原本本呈現出來。
/***
|Name|HTMLFormattingPlugin|
|Source|http://www.TiddlyTools.com/#HTMLFormattingPlugin|
|Documentation|http://www.TiddlyTools.com/#HTMLFormattingPluginInfo|
|Version|2.4.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|embed wiki syntax formatting inside of HTML content|
The ~HTMLFormatting plugin allows you to ''mix wiki-style formatting syntax within HTML formatted content'' by extending the action of the standard TiddlyWiki formatting handler.
!!!!!Documentation
>see [[HTMLFormattingPluginInfo]]
!!!!!Configuration
<<<
Use {{{<hide linebreaks>}}} within HTML content to wiki-style rendering of line breaks. To //always// omit all line breaks from the rendered output, you can set this option:
><<option chkHTMLHideLinebreaks>> ignore all line breaks
which can also be 'hard coded' into your document by adding the following to a tiddler, tagged with <<tag systemConfig>>
>{{{config.options.chkHTMLHideLinebreaks=true;}}}
<<<
!!!!!Revisions
<<<
2010.05.07 2.4.1 added chkHTMLHideLinebreaks option
| see [[HTMLFormattingPluginInfo]] for additional revision details |
2005.06.26 1.0.0 Initial Release (as code adaptation - pre-dates TiddlyWiki plugin architecture!!)
<<<
!!!!!Code
***/
//{{{
version.extensions.HTMLFormattingPlugin= {major: 2, minor: 4, revision: 1, date: new Date(2010,5,7)};
// find the formatter for HTML and replace the handler
initHTMLFormatter();
function initHTMLFormatter()
{
for (var i=0; i<config.formatters.length && config.formatters[i].name!="html"; i++) {}
if (i<config.formatters.length) config.formatters[i].handler=function(w) {
if (!this.lookaheadRegExp) // fixup for TW2.0.x
this.lookaheadRegExp = new RegExp(this.lookahead,"mg");
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var html=lookaheadMatch[1];
// if <nowiki> is present, just let browser handle it!
if (html.indexOf('<nowiki>')!=-1)
createTiddlyElement(w.output,"span").innerHTML=html;
else {
// if <hide linebreaks> is present, or chkHTMLHideLinebreaks is set
// suppress wiki-style literal handling of newlines
if (config.options.chkHTMLHideLinebreaks||(html.indexOf('<hide linebreaks>')!=-1))
html=html.replace(/\n/g,' ');
// remove all \r's added by IE textarea and mask newlines and macro brackets
html=html.replace(/\r/g,'').replace(/\n/g,'\\n').replace(/<</g,'%%(').replace(/>>/g,')%%');
// create span, let browser parse HTML
var e=createTiddlyElement(w.output,"span"); e.innerHTML=html;
// then re-render text nodes as wiki-formatted content
wikifyTextNodes(e,w);
}
w.nextMatch = this.lookaheadRegExp.lastIndex; // continue parsing
}
}
}
// wikify #text nodes that remain after HTML content is processed (pre-order recursion)
function wikifyTextNodes(theNode,w)
{
function unmask(s) { return s.replace(/\%%\(/g,'<<').replace(/\)\%%/g,'>>').replace(/\\n/g,'\n'); }
switch (theNode.nodeName.toLowerCase()) {
case 'style': case 'option': case 'select':
theNode.innerHTML=unmask(theNode.innerHTML);
break;
case 'textarea':
theNode.value=unmask(theNode.value);
break;
case '#text':
var txt=unmask(theNode.nodeValue);
var newNode=createTiddlyElement(null,"span");
theNode.parentNode.replaceChild(newNode,theNode);
wikify(txt,newNode,highlightHack,w.tiddler);
break;
default:
for (var i=0;i<theNode.childNodes.length;i++)
wikifyTextNodes(theNode.childNodes.item(i),w); // recursion
break;
}
}
//}}}
<html><div id="table1"></div>
<div id="table2"></div>
<div id="table3"></div></html><script>
var data1 = [
["xx", "Ford", "Volvo", "Toyota", "Honda"],
["2014", 10, 11, 12, 13],
["2015", 20, 11, 14, 13],
["2016", 30, 15, 12, 13]
];
var hot = new Handsontable(document.getElementById('table1'), {
data: data1,
minSpareRows: 1,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
afterChange: config.extensions.Handsontable.afterChange
});
var data2 = [
["yy", "Ford", "Volvo", "Toyota", "Honda"],
["2014", 10, 11, 12, 13],
["2015", 20, 11, 14, 13],
["2016", 30, 15, 12, 13]
];
hot = new Handsontable(document.getElementById('table2'), {
data: data2,
minSpareRows: 1,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
afterChange: config.extensions.Handsontable.afterChange
});
var data3 = [
["zz", "Ford", "Volvo", "Toyota", "Honda"],
["2014", 10, 11, 12, 13],
["2015", 20, 11, 14, 13],
["2016", 30, 15, 12, 13]
];
hot = new Handsontable(document.getElementById('table3'), {
data: data3,
minSpareRows: 1,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
afterChange: config.extensions.Handsontable.afterChange
});
</script>
/***
|''Name:''|HandsontablePlugin|
|''Description:''|Brings Handsontable into the TiddlyWiki classic world.|
|''Author:''|Vincent Yeh|
|''Source:''|http://twve.tiddlyspot.com/#HandsontablePlugin|
|''Type:''|plugin|
|''Version:''|0.0.1|
|''Status:''|This plugin is a new born baby.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2015/06/22 Born to earth.|
|''License:''|MIT|
|''~CoreVersion:''|2.5.0|
!! Changelog
!!! 0.0.1 2015/06/22
* Born this day.
* Works __with or without a locally installed version__ of Handsontable.
** If a local version is not installed, this plugin will load its on-line version (http://handsontable.com/dist/handsontable.full.min.js) and its stylesheet (http://handsontable.com/dist/handsontable.full.min.css).
* Updates wiki text upon content change using Handsontable.
* __Works with multiple tables__ in one tiddler if each table data is given a __unique variable name__.
* Tested only in normally loaded tiddlers.
!! How to Use
# (Optional) Install Handsontable from http://handsontable.com/
** If a local version is not installed, this plugin will load its on-line version (http://handsontable.com/dist/handsontable.full.min.js) and its stylesheet (http://handsontable.com/dist/handsontable.full.min.css).
# Install InlineJavascriptPlugin from http://www.tiddlytools.com/#InlineJavascriptPlugin
# Install this plugin.
# Follow the examples in http://handsontable.com/, and assign """config.extensions.Handsontable.afterChange""" to the """afterChange""" property in the options upon calling """new Handsontable()""".
!! Demo
To use the Handsontable in a tiddler, one needs to
# create an element, such as a <div>, as the table container,
# use inline Javascript to prepare the data and create the table.
See the following codes for an example, or [[Handsontable Demo]] for another.
<<<
//{{{
<html><div id="table1"></div></html><script>
var data1 = [
["xx", "Ford", "Volvo", "Toyota", "Honda"],
["2014", 10, 11, 12, 13],
["2015", 20, 11, 14, 13],
["2016", 30, 15, 12, 13]
];
var hot = new Handsontable(document.getElementById('table1'), {
data: data1,
minSpareRows: 1,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
afterChange: config.extensions.Handsontable.afterChange
});
//}}}
<<<
!! Code
***/
/***
!!! config.extensions.Handsontable
***/
//{{{
config.extensions.Handsontable = {
major: 0, minor: 0, revision: 1,
date: new Date('2015/06/22'),
script : "http://handsontable.com/dist/handsontable.full.min.js",
style : "http://handsontable.com/dist/handsontable.full.min.css",
afterChange: function(change,source){
if ( source == 'loadData' ) return;
// Find the tiddler containing this table.
var container = this.container;
var parent = story.findContainingTiddler(container);
var tiddler = store.getTiddler(
parent.getAttribute("tiddler")
);
// Get the tiddler text.
var text = tiddler.text;
// Find the start and end of table data.
// First we find the index number of this Handsontable.
var hots = parent.querySelectorAll('div.ht_master');
var ndx = -1;
for(var t=0,tlen=hots.length; t<tlen; t++)
if(hots[t]==container){
ndx = t;
break;
}
// Then we find the variable name for the table data,
// assuming it exists. The variable name is assigned to the
// data property of the option in calling the new Handsontable()
// constructor. Hence we look for the right new Handsontable()
// statement and the data property in its option.
var p1 = -1;
var regexp = /new\s+Handsontable/gm;
while(ndx>=0){
if (regexp.test(text)) p1 = regexp.lastIndex;
ndx--;
}
regexp = /data\s*:/gm;
regexp.lastIndex = p1;
if ( regexp.test(text) ) p1 = regexp.lastIndex;
var p2 = text.indexOf(',',p1+1);
var dataname = text.substring(p1,p2).trim();
// Now we look back for the beginning of data definition. If
// there are multiple tables created using the Handsontable,
// each of their data sets must have a unique variable name
// or the update may go to the wrong place.
p1 = text.lastIndexOf(dataname,p1)+dataname.length;
// and the end of data definition.
p2 = text.indexOf('];',p1)+2;
// Convert the modified data into the defining string.
var data = this.getData();
var txtdata = ' = [\n';
for(var r=0,rlen=data.length-this.getSettings().minSpareRows;
r<rlen; r++){
txtdata += '[';
for ( var c=0,clen=data[r].length; c<clen; c++ ){
var wrap = typeof data[r][c] == 'string' ? '"' : '';
txtdata += wrap+(data[r][c]||'')+wrap+(c<clen-1?', ':'');
}
txtdata +=']' + (r<rlen-1?',':'') + '\n';
}
txtdata += '];'
// Replace the original definition string with the modified one.
text = text.substring(0,p1)+txtdata+text.substring(p2);
// Set it back to the tiddler,
tiddler.set(
tiddler.title,text,tiddler.modifier,new Date()
);
// and mark it as modified.
store.setDirty(true);
}
};
//}}}
/***
!!! loadScript
***/
//{{{
function loadScript(source,callback){
// This function combines the codes from two places:
// http://stackoverflow.com/questions/5791279/jquery-getscript-vs-document-createelementscript
// and
// http://stackoverflow.com/questions/16839698/jquery-getscript-alternative-in-native-javascript
var script = document.createElement('script');
script.onload = script.onreadystatechange = function(_,isAbort){
if ( isAbort ||
!script.readyState ||
/loaded|complete/.test(script.readyState) ) {
script.onload = script.onreadystatechange = null;
script = undefined;
if (!isAbort && callback) callback();
}
};
script.type= 'text/javascript';
script.src= source;
script.async = true;
(document.head||document.documentElement).appendChild(script);
};
//}}}
/***
!!! loadStyle
***/
//{{{
function loadStyle(source,media){
// This function follows the codes found in
// http://stackoverflow.com/questions/574944/how-to-load-up-css-files-using-javascript
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
if ( media ) style.media = media;
style.href = source;
(document.head||document.documentElement).appendChild(style);
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.HOTOptions = {
init : function () {
if(typeof Handsontable == 'undefined'){
loadScript(
config.extensions.Handsontable.script,
function(){
loadStyle(
config.extensions.Handsontable.style,
'screen'
);
//config.extensions.Handsontable.init();
}
);
}else{
//config.extensions.Handsontable.init();
}
if (config.options.chkHOTEnabled===undefined)
config.options.chkHOTEnabled = true;
}
};
//}}}
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.2.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|adds support for resizing images|
This plugin adds optional syntax to scale an image to a specified width and height and/or interactively resize the image with the mouse.
!!!!!Usage
<<<
The extended image syntax is:
{{{
[img(w+,h+)[...][...]]
}}}
where ''(w,h)'' indicates the desired width and height (in CSS units, e.g., px, em, cm, in, or %). Use ''auto'' (or a blank value) for either dimension to scale that dimension proportionally (i.e., maintain the aspect ratio). You can also calculate a CSS value 'on-the-fly' by using a //javascript expression// enclosed between """{{""" and """}}""". Appending a plus sign (+) to a dimension enables interactive resizing in that dimension (by dragging the mouse inside the image). Use ~SHIFT-click to show the full-sized (un-scaled) image. Use ~CTRL-click to restore the starting size (either scaled or full-sized).
<<<
!!!!!Examples
<<<
{{{
[img(100px+,75px+)[images/meow2.jpg]]
}}}
[img(100px+,75px+)[images/meow2.jpg]]
{{{
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img( 1%+,+)[images/meow.gif]]
}}}
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img( 1%+,+)[images/meow.gif]]
{{tagClear{
}}}
<<<
!!!!!Revisions
<<<
2010.07.24 [1.2.2] moved tip/dragtip text to config.formatterHelpers.imageSize object to enable customization
2009.02.24 [1.2.1] cleanup width/height regexp, use '+' suffix for resizing
2009.02.22 [1.2.0] added stretchable images
2008.01.19 [1.1.0] added evaluated width/height values
2008.01.18 [1.0.1] regexp for "(width,height)" now passes all CSS values to browser for validation
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImageSizePlugin= {major: 1, minor: 2, revision: 2, date: new Date(2010,7,24)};
//}}}
//{{{
var f=config.formatters[config.formatters.findByField("name","image")];
f.match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
f.lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](?:\(([^,]*),([^\)]*)\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
f.handler=function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var floatLeft=lookaheadMatch[1];
var floatRight=lookaheadMatch[2];
var width=lookaheadMatch[3];
var height=lookaheadMatch[4];
var tooltip=lookaheadMatch[5];
var src=lookaheadMatch[6];
var link=lookaheadMatch[7];
// Simple bracketted link
var e = w.output;
if(link) { // LINKED IMAGE
if (config.formatterHelpers.isExternalLink(link)) {
if (config.macros.attach && config.macros.attach.isAttachment(link)) {
// see [[AttachFilePluginFormatters]]
e = createExternalLink(w.output,link);
e.href=config.macros.attach.getAttachment(link);
e.title = config.macros.attach.linkTooltip + link;
} else
e = createExternalLink(w.output,link);
} else
e = createTiddlyLink(w.output,link,false,null,w.isStatic);
addClass(e,"imageLink");
}
var img = createTiddlyElement(e,"img");
if(floatLeft) img.align="left"; else if(floatRight) img.align="right";
if(width||height) {
var x=width.trim(); var y=height.trim();
var stretchW=(x.substr(x.length-1,1)=='+'); if (stretchW) x=x.substr(0,x.length-1);
var stretchH=(y.substr(y.length-1,1)=='+'); if (stretchH) y=y.substr(0,y.length-1);
if (x.substr(0,2)=="{{")
{ try{x=eval(x.substr(2,x.length-4))} catch(e){displayMessage(e.description||e.toString())} }
if (y.substr(0,2)=="{{")
{ try{y=eval(y.substr(2,y.length-4))} catch(e){displayMessage(e.description||e.toString())} }
img.style.width=x.trim(); img.style.height=y.trim();
config.formatterHelpers.addStretchHandlers(img,stretchW,stretchH);
}
if(tooltip) img.title = tooltip;
// GET IMAGE SOURCE
if (config.macros.attach && config.macros.attach.isAttachment(src))
src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
if (config.browser.isIE || config.browser.isSafari) {
img.onerror=(function(){
this.src=config.formatterHelpers.resolvePath(this.src,false);
return false;
});
} else
src=config.formatterHelpers.resolvePath(src,true);
}
img.src=src;
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
config.formatterHelpers.imageSize={
tip: 'SHIFT-CLICK=show full size, CTRL-CLICK=restore initial size',
dragtip: 'DRAG=stretch/shrink, '
}
config.formatterHelpers.addStretchHandlers=function(e,stretchW,stretchH) {
e.title=((stretchW||stretchH)?this.imageSize.dragtip:'')+this.imageSize.tip;
e.statusMsg='width=%0, height=%1';
e.style.cursor='move';
e.originalW=e.style.width;
e.originalH=e.style.height;
e.minW=Math.max(e.offsetWidth/20,10);
e.minH=Math.max(e.offsetHeight/20,10);
e.stretchW=stretchW;
e.stretchH=stretchH;
e.onmousedown=function(ev) { var ev=ev||window.event;
this.sizing=true;
this.startX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
this.startY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
this.startW=this.offsetWidth;
this.startH=this.offsetHeight;
return false;
};
e.onmousemove=function(ev) { var ev=ev||window.event;
if (this.sizing) {
var s=this.style;
var currX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
var currY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
var newW=(currX-this.offsetLeft)/(this.startX-this.offsetLeft)*this.startW;
var newH=(currY-this.offsetTop )/(this.startY-this.offsetTop )*this.startH;
if (this.stretchW) s.width =Math.floor(Math.max(newW,this.minW))+'px';
if (this.stretchH) s.height=Math.floor(Math.max(newH,this.minH))+'px';
clearMessage(); displayMessage(this.statusMsg.format([s.width,s.height]));
}
return false;
};
e.onmouseup=function(ev) { var ev=ev||window.event;
if (ev.shiftKey) { this.style.width=this.style.height=''; }
if (ev.ctrlKey) { this.style.width=this.originalW; this.style.height=this.originalH; }
this.sizing=false;
clearMessage();
return false;
};
e.onmouseout=function(ev) { var ev=ev||window.event;
this.sizing=false;
clearMessage();
return false;
};
}
//}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Revisions
<<<
2010.12.15 1.9.6 allow (but ignore) type="..." syntax
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.InlineJavascriptPlugin= {major: 1, minor: 9, revision: 6, date: new Date(2010,12,15)};
config.formatters.push( {
name: "inlineJavascript",
match: "\\<script",
lookahead: "\\<script(?: type=\\\"[^\\\"]*\\\")?(?: src=\\\"([^\\\"]*)\\\")?(?: label=\\\"([^\\\"]*)\\\")?(?: title=\\\"([^\\\"]*)\\\")?(?: key=\\\"([^\\\"]*)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var src=lookaheadMatch[1];
var label=lookaheadMatch[2];
var tip=lookaheadMatch[3];
var key=lookaheadMatch[4];
var show=lookaheadMatch[5];
var code=lookaheadMatch[6];
if (src) { // external script library
var script = document.createElement("script"); script.src = src;
document.body.appendChild(script); document.body.removeChild(script);
}
if (code) { // inline code
if (show) // display source in tiddler
wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
if (label) { // create 'onclick' command link
var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
link.code="function _out(place,tiddler){"+fixup+"\n};_out(this,this.tiddler);"
link.tiddler=w.tiddler;
link.onclick=function(){
this.bufferedHTML="";
try{ var r=eval(this.code);
if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
if(this.bufferedHTML.length)
s.innerHTML=this.bufferedHTML;
if((typeof(r)==="string")&&r.length) {
wikify(r,s,null,this.tiddler);
return false;
} else return r!==undefined?r:false;
} catch(e){alert(e.description||e.toString());return false;}
};
link.setAttribute("title",tip||"");
var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
link.setAttribute("href",URIcode);
link.style.cursor="pointer";
if (key) link.accessKey=key.substr(0,1); // single character only
}
else { // run script immediately
var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
var c="function _out(place,tiddler){"+fixup+"\n};_out(w.output,w.tiddler);";
try { var out=eval(c); }
catch(e) { out=e.description?e.description:e.toString(); }
if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
}
}
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} )
//}}}
// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") window.wikifyPlainText=function(text,limit,tiddler) {
if(limit > 0) text = text.substr(0,limit);
var wikifier = new Wikifier(text,formatter,null,tiddler);
return wikifier.wikifyPlain();
}
//}}}
// // GLOBAL FUNCTION: $(...) -- 'shorthand' convenience syntax for document.getElementById()
//{{{
if (typeof($)=='undefined') { function $(id) { return document.getElementById(id.replace(/^#/,'')); } }
//}}}
[[Dynamic TextArea Height]]
[[Positioning Issue: Where is the mighty mouse?]]
[[Problem with SyntaxHighlighterPlugin3]]
[[Table Cell Alignment]]
[[The focus issue]]
[[The-tweaky-TiddlyWiki-options]]
[[Transposing a table -- backward thinking]]
[[Working-with-MathJaxPlugin]]
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date("mar 17, 2007"),
source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){
url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
}
return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
To locate the character in the mouseup event, the plugins need
# the selection information, and
# capabilities to distinguish different nodes in the wiki text.
>To obtain the selection information, the plugins make use of the Javascript's {{{window.getSelection()}}} method.
>
>To distinguish different nodes in the wiki text, the plugins use the {{{twve.tiddler.wikiTags($elem)}}} method to obtain the signature (opening and closing tags) of different kinds of DOM elements, together with other methods (see below) to identify the character or selected text.
!!! The getSelection() method
The getSelection() method returns the current selection information on the HTML page, including
* anchorNode -- where the mouse button was pressed,
* focusNode -- where the mouse button was released,
* anchorOffset -- index of the character in anchorNode at which the mouse button was pressed,^^#^^
* focusOffset -- index of the character in focusNode at which the mouse button was released.^^#^^
----
^^# If the mouseup event happens on a <BR> node, then both anchorNode and focusNode are the element containing that <BR> node, while the anchorOffset and focusOffset are the index number of that <BR> node within the element.^^
----
>If there was nothing selected in the mouseup event, the anchorOffset would be the same as the focusOffset (and the anchorNode the same as the focusNode for sure). The character upon mouseup is just the one at the focusOffset in the focusNode.
----
>If, however, some text was selected instead, the selected text would start from the character at the anchorOffset in the anchorNode, include all the characters contained in the nodes between anchorNode and focusNode, if any, and end at the character at the focusOffset in the focusNode.
!!! The twve.tiddler.wikiTags($elem) method
The <<slider '' 'twve.core##twve.tiddler.wikiTags' 'twve.tiddler.wikiTags($elem)'>> method obtains the wiki text signature (the opening and closing tags) of //registered// DOM elements (see [[Editable Elements]] for more information). These signatures can be used to distinguish different nodes in the wiki text.
! Locating the character
To locate the character (in mouseup event) in the wiki text of the tiddler, the plugins do
# locate the wiki text of the element,
# obtain all the nodes in the element (using [[jQuery's .contents()|http://api.jquery.com/contents/]] method),
# find the index of focusNode within the element (using the the <<slider '' 'twve.extra##twve.element.nodeIndex' 'twve.element.nodeIndex($nodes,node)'>> method),
# use the index to search for the focusNode in the wiki text,
# calculate the character index (corresponding to the focusOffset) in the wiki text (using the <<slider '' 'twve.extra##twve.tiddler.selectionInfo' 'twve.tiddler.selectionInfo($elem,elem_txt)'>>, which further uses <<slider '' 'twve.extra##twve.tiddler.selectionOffset' 'twve.tiddler.selectionOffset(...)'>> and <<slider '' 'twve.extra##twve.tiddler.tagsInfo' 'twve.tiddler.tagsInfo($node,txt,start)'>> methods to accomplish its job).
! Retrieving the selected text
To obtain the selected text, the plugins do
# locate the wiki text of the element,
# obtain all the nodes in the element (using [[jQuery's .contents()|http://api.jquery.com/contents/]] method),
# find the indexes of anchorNode and focusNode within the element (using the the <<slider '' 'twve.extra##twve.element.nodeIndex' 'twve.element.nodeIndex($nodes,node)'>> method),
# use the indexes to search for the anchorNode and focusNode in the wiki text,
# calculate the starting index (anchorOffset) and the ending index (focusOffset) in the wiki text (using the <<slider '' 'twve.extra##twve.tiddler.selectionInfo' 'twve.tiddler.selectionInfo($elem,elem_txt)'>>, which further uses <<slider '' 'twve.extra##twve.tiddler.selectionOffset' 'twve.tiddler.selectionOffset(...)'>> and <<slider '' 'twve.extra##twve.tiddler.tagsInfo' 'twve.tiddler.tagsInfo($node,txt,start)'>> methods to accomplish its job),
# retrieve the characters between the starting and ending indexes.
<<calendar thismonth>>
[[Examples]]
[[twve.core Examples]]
[[twve.extra Examples]]
[[twve.table Examples]]
[[twve.tcalc Examples]]
*[[TiddlyWiki | http://www.tiddlywiki.com/]]
*[[TiddlyWiki.org | http://tiddlywiki.org/wiki/Main_Page]]
*[[PluginTools | http://www.tiddlytools.com]]
*[[MathJax \(\TeX\) support | http://www.mathjax.org/docs/supported-tex-commands/]]
----
The ''twve'' plugins identify a DOM element and its defining wiki text by __matching their order of appearance__. The order of appearance of a DOM element is the index number, within its own type and within the same tiddler, that it is rendered in the resulting HTML page. Similarly, the order of appearance of its defining wiki text is the index number, again within its own type and within the same tiddler, that it is defined in the original tiddler text. In these plugins the two indexes are called //rendering index// and //defining index//, respectively.
> At first thought, these two indexes should be the same and no need to distinguish them. Indeed they are the same in most of the cases: in normally loaded and fully transcluded tiddlers. The earlier versions of the plugins did use only one of them without much trouble. They are, however, different from each other when
>> a tiddler is transcluded partially: the 2^^nd^^ table in a tiddler can be rendered as the 1^^st^^ in a partially transcluded copy;
> or
>> when a //list item// / //blockquote// does not start from top level: there will be additional empty //list item(s)// / //blockquote(s)// to contain that one.
> The plugins started to distinguish them when partial transclusion support was included, and later realized it's also needed for the non-top-level started //list items// / //blockquotes//.
When the user clicks on a DOM element to edit its content, the plugins find its type and rendering index, look for the wiki text signature of that type of elements, and count the defining index until it //matches// the rendering index. The plugins then brings up the wiki text and put it in an edit box for the user to edit. When the user finishes editing by {{{hitting Enter/Ctrl-Enter}}} or {{{clicking away}}}, the plugins update the changes, refresh the DOM element, and synchronize all its transcluded copies. To refresh the element, the plugins simply update its content or create a new one to replace it. To synchronize the changes, however, they rely on the defining index to find the rendering index in each copy, then refresh it to reflect the changes accordingly.
> The //wiki text signatures// of supported elements are specified in the {{{.markupTags()}}} method of their corresponding code objects. For example, the signatures of a table are specified in <<slider '' 'twve.table##twve.table.markupTags' 'twve.table.markupTags()'>>.
> For more information about editable elements and their wiki text signatures, see [[Editable Objects]].
!!! The rendering index
To find the rendering index of an element, the plugins use the <<slider '' 'twve.core##twelem.renderingIndex' '.renderingIndex()'>> method of a [[twve.element]] object. This method
# collects in an array all elements of the same type in its direct wrapper, //excluding// those transcluded into the direct wrapper, using <<slider '' 'twve.core##twve.wrapper.findElements' 'twve.wrapper.findElements($w,selector)'>>, and then
# uses the {{{.index()}}} method of the array object to find the rendering index of the element.
!!! The defining index
The defining index of an element is obtained along the way to find the wiki text, within [[twve.element]]'s <<slider '' 'twve.core##twelem.starts' '.starts([start])'>> method, which locates the first character of the defining wiki text.
After finding the beginning (and the defining index) of an element, the plugins find the end of the element's wiki text using the <<slider '' 'twve.core##twelem.ends' '.ends([start,txt])'>> method, then take out the wiki text (using [[twve.editable]]'s <<slider '' 'twve.core##editable.getText' '.getText([open,close])'>> method) for the user to edit.
> @@Plugin authors interested in having access to the wiki text, please see [[Edit Your ...]] for more information.@@
/***
|''Name:''|MathJaxPlugin|
|''Description:''|Enable LaTeX formulas for TiddlyWiki|
|''Version:''|1.0.1|
|''Date:''|Feb 11, 2012|
|''Source:''|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin|
|''Author:''|Guy Rutenberg|
|''License:''|[[BSD open source license]]|
|''~CoreVersion:''|2.5.0|
!! Changelog
!!! 1.0.1 Feb 11, 2012
* Fixed interoperability with TiddlerBarPlugin
!! How to Use
Currently the plugin supports the following delemiters:
* """\(""".."""\)""" - Inline equations
* """$$""".."""$$""" - Displayed equations
* """\[""".."""\]""" - Displayed equations
!! Demo
This is an inline equation \(P(E) = {n \choose k} p^k (1-p)^{ n-k}\) and this is a displayed equation:
\[J_\alpha(x) = \sum_{m=0}^\infty \frac{(-1)^m}{m! \, \Gamma(m + \alpha + 1)}{\left({\frac{x}{2}}\right)}^{2 m + \alpha}\]
This is another displayed equation $$e=mc^2$$
!! Code
***/
//{{{
config.extensions.MathJax = {
// mathJaxScript : "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",
// uncomment the following line if you want to access MathJax using SSL
mathJaxScript : "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",
displayTiddler: function(TiddlerName) {
var elem = config.extensions.MathJax.displayTiddler_old.apply(this, arguments);
if ( typeof MathJax !== 'undefined' ) MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
return elem;
}
};
config.extensions.MathJax.displayTiddler_old = Story.prototype.displayTiddler;
Story.prototype.displayTiddler = config.extensions.MathJax.displayTiddler;
jQuery.getScript(config.extensions.MathJax.mathJaxScript, function(){
MathJax.Hub.Config({
extensions: ["tex2jax.js"],
"HTML-CSS": { scale: 100 }
});
MathJax.Hub.Startup.onload();
});
config.formatters.push({
name: "mathJaxFormula",
match: "\\\\\\[|\\$\\$|\\\\\\(",
//lookaheadRegExp: /(?:\\\[|\$\$)((?:.|\n)*?)(?:\\\]|$$)/mg,
handler: function(w)
{
switch(w.matchText) {
case "\\[": // displayed equations
this.lookaheadRegExp = /\\\[((?:.|\n)*?)(\\\])/mg;
break;
case "$$": // inline equations
this.lookaheadRegExp = /\$\$((?:.|\n)*?)(\$\$)/mg;
break;
case "\\(": // inline equations
this.lookaheadRegExp = /\\\(((?:.|\n)*?)(\\\))/mg;
break;
default:
break;
}
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"span",null,null,lookaheadMatch[0]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
});
//}}}
/***
|Name|NestedSlidersPlugin|
|Source|http://www.TiddlyTools.com/#NestedSlidersPlugin|
|Documentation|http://www.TiddlyTools.com/#NestedSlidersPluginInfo|
|Version|2.4.9|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|show content in nest-able sliding/floating panels, without creating separate tiddlers for each panel's content|
!!!!!Documentation
>see [[NestedSlidersPluginInfo]]
!!!!!Configuration
<<<
<<option chkFloatingSlidersAnimate>> allow floating sliders to animate when opening/closing
>Note: This setting can cause 'clipping' problems in some versions of InternetExplorer.
>In addition, for floating slider animation to occur you must also allow animation in general (see [[AdvancedOptions]]).
<<<
!!!!!Revisions
<<<
2008.11.15 - 2.4.9 in adjustNestedSlider(), don't make adjustments if panel is marked as 'undocked' (CSS class). In onClickNestedSlider(), SHIFT-CLICK docks panel (see [[MoveablePanelPlugin]])
|please see [[NestedSlidersPluginInfo]] for additional revision details|
2005.11.03 - 1.0.0 initial public release. Thanks to RodneyGomes, GeoffSlocock, and PaulPetterson for suggestions and experiments.
<<<
!!!!!Code
***/
//{{{
version.extensions.NestedSlidersPlugin= {major: 2, minor: 4, revision: 9, date: new Date(2008,11,15)};
// options for deferred rendering of sliders that are not initially displayed
if (config.options.chkFloatingSlidersAnimate===undefined)
config.options.chkFloatingSlidersAnimate=false; // avoid clipping problems in IE
// default styles for 'floating' class
setStylesheet(".floatingPanel { position:absolute; z-index:10; padding:0.5em; margin:0em; \
background-color:#eee; color:#000; border:1px solid #000; text-align:left; }","floatingPanelStylesheet");
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
config.formatters.push( {
name: "nestedSliders",
match: "\\n?\\+{3}",
terminator: "\\s*\\={3}\\n?",
lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\^(?:[^\\^\\*\\@\\[\\>]*\\^)?)?(\\*)?(\\@)?(?:\\{\\{([\\w]+[\\s\\w]*)\\{)?(\\[[^\\]]*\\])?(\\[[^\\]]*\\])?(?:\\}{3})?(\\#[^:]*\\:)?(\\>)?(\\.\\.\\.)?\\s*",
handler: function(w)
{
lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
{
var defopen=lookaheadMatch[1];
var cookiename=lookaheadMatch[2];
var header=lookaheadMatch[3];
var panelwidth=lookaheadMatch[4];
var transient=lookaheadMatch[5];
var hover=lookaheadMatch[6];
var buttonClass=lookaheadMatch[7];
var label=lookaheadMatch[8];
var openlabel=lookaheadMatch[9];
var panelID=lookaheadMatch[10];
var blockquote=lookaheadMatch[11];
var deferred=lookaheadMatch[12];
// location for rendering button and panel
var place=w.output;
// default to closed, no cookie, no accesskey, no alternate text/tip
var show="none"; var cookie=""; var key="";
var closedtext=">"; var closedtip="";
var openedtext="<"; var openedtip="";
// extra "+", default to open
if (defopen) show="block";
// cookie, use saved open/closed state
if (cookiename) {
cookie=cookiename.trim().slice(1,-1);
cookie="chkSlider"+cookie;
if (config.options[cookie]==undefined)
{ config.options[cookie] = (show=="block") }
show=config.options[cookie]?"block":"none";
}
// parse label/tooltip/accesskey: [label=X|tooltip]
if (label) {
var parts=label.trim().slice(1,-1).split("|");
closedtext=parts.shift();
if (closedtext.substr(closedtext.length-2,1)=="=")
{ key=closedtext.substr(closedtext.length-1,1); closedtext=closedtext.slice(0,-2); }
openedtext=closedtext;
if (parts.length) closedtip=openedtip=parts.join("|");
else { closedtip="show "+closedtext; openedtip="hide "+closedtext; }
}
// parse alternate label/tooltip: [label|tooltip]
if (openlabel) {
var parts=openlabel.trim().slice(1,-1).split("|");
openedtext=parts.shift();
if (parts.length) openedtip=parts.join("|");
else openedtip="hide "+openedtext;
}
var title=show=='block'?openedtext:closedtext;
var tooltip=show=='block'?openedtip:closedtip;
// create the button
if (header) { // use "Hn" header format instead of button/link
var lvl=(header.length>5)?5:header.length;
var btn = createTiddlyElement(createTiddlyElement(place,"h"+lvl,null,null,null),"a",null,buttonClass,title);
btn.onclick=onClickNestedSlider;
btn.setAttribute("href","javascript:;");
btn.setAttribute("title",tooltip);
}
else
var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider,buttonClass);
btn.innerHTML=title; // enables use of HTML entities in label
// set extra button attributes
btn.setAttribute("closedtext",closedtext);
btn.setAttribute("closedtip",closedtip);
btn.setAttribute("openedtext",openedtext);
btn.setAttribute("openedtip",openedtip);
btn.sliderCookie = cookie; // save the cookiename (if any) in the button object
btn.defOpen=defopen!=null; // save default open/closed state (boolean)
btn.keyparam=key; // save the access key letter ("" if none)
if (key.length) {
btn.setAttribute("accessKey",key); // init access key
btn.onfocus=function(){this.setAttribute("accessKey",this.keyparam);}; // **reclaim** access key on focus
}
btn.setAttribute("hover",hover?"true":"false");
btn.onmouseover=function(ev) {
// optional 'open on hover' handling
if (this.getAttribute("hover")=="true" && this.sliderPanel.style.display=='none') {
document.onclick.call(document,ev); // close transients
onClickNestedSlider(ev); // open this slider
}
// mouseover on button aligns floater position with button
if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this,this.sliderPanel);
}
// create slider panel
var panelClass=panelwidth?"floatingPanel":"sliderPanel";
if (panelID) panelID=panelID.slice(1,-1); // trim off delimiters
var panel=createTiddlyElement(place,"div",panelID,panelClass,null);
panel.button = btn; // so the slider panel know which button it belongs to
btn.sliderPanel=panel; // so the button knows which slider panel it belongs to
panel.defaultPanelWidth=(panelwidth && panelwidth.length>2)?panelwidth.slice(1,-1):"";
panel.setAttribute("transient",transient=="*"?"true":"false");
panel.style.display = show;
panel.style.width=panel.defaultPanelWidth;
panel.onmouseover=function(event) // mouseover on panel aligns floater position with button
{ if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this.button,this); }
// render slider (or defer until shown)
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
if ((show=="block")||!deferred) {
// render now if panel is supposed to be shown or NOT deferred rendering
w.subWikify(blockquote?createTiddlyElement(panel,"blockquote"):panel,this.terminator);
// align floater position with button
if (window.adjustSliderPos) window.adjustSliderPos(place,btn,panel);
}
else {
var src = w.source.substr(w.nextMatch);
var endpos=findMatchingDelimiter(src,"+++","===");
panel.setAttribute("raw",src.substr(0,endpos));
panel.setAttribute("blockquote",blockquote?"true":"false");
panel.setAttribute("rendered","false");
w.nextMatch += endpos+3;
if (w.source.substr(w.nextMatch,1)=="\n") w.nextMatch++;
}
}
}
}
)
function findMatchingDelimiter(src,starttext,endtext) {
var startpos = 0;
var endpos = src.indexOf(endtext);
// check for nested delimiters
while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {
// count number of nested 'starts'
var startcount=0;
var temp = src.substring(startpos,endpos-1);
var pos=temp.indexOf(starttext);
while (pos!=-1) { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }
// set up to check for additional 'starts' after adjusting endpos
startpos=endpos+endtext.length;
// find endpos for corresponding number of matching 'ends'
while (startcount && endpos!=-1) {
endpos = src.indexOf(endtext,endpos+endtext.length);
startcount--;
}
}
return (endpos==-1)?src.length:endpos;
}
//}}}
//{{{
window.onClickNestedSlider=function(e)
{
if (!e) var e = window.event;
var theTarget = resolveTarget(e);
while (theTarget && theTarget.sliderPanel==undefined) theTarget=theTarget.parentNode;
if (!theTarget) return false;
var theSlider = theTarget.sliderPanel;
var isOpen = theSlider.style.display!="none";
// if SHIFT-CLICK, dock panel first (see [[MoveablePanelPlugin]])
if (e.shiftKey && config.macros.moveablePanel) config.macros.moveablePanel.dock(theSlider,e);
// toggle label
theTarget.innerHTML=isOpen?theTarget.getAttribute("closedText"):theTarget.getAttribute("openedText");
// toggle tooltip
theTarget.setAttribute("title",isOpen?theTarget.getAttribute("closedTip"):theTarget.getAttribute("openedTip"));
// deferred rendering (if needed)
if (theSlider.getAttribute("rendered")=="false") {
var place=theSlider;
if (theSlider.getAttribute("blockquote")=="true")
place=createTiddlyElement(place,"blockquote");
wikify(theSlider.getAttribute("raw"),place);
theSlider.setAttribute("rendered","true");
}
// show/hide the slider
if(config.options.chkAnimate && (!hasClass(theSlider,'floatingPanel') || config.options.chkFloatingSlidersAnimate))
anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
else
theSlider.style.display = isOpen ? "none" : "block";
// reset to default width (might have been changed via plugin code)
theSlider.style.width=theSlider.defaultPanelWidth;
// align floater panel position with target button
if (!isOpen && window.adjustSliderPos) window.adjustSliderPos(theSlider.parentNode,theTarget,theSlider);
// if showing panel, set focus to first 'focus-able' element in panel
if (theSlider.style.display!="none") {
var ctrls=theSlider.getElementsByTagName("*");
for (var c=0; c<ctrls.length; c++) {
var t=ctrls[c].tagName.toLowerCase();
if ((t=="input" && ctrls[c].type!="hidden") || t=="textarea" || t=="select")
{ try{ ctrls[c].focus(); } catch(err){;} break; }
}
}
var cookie=theTarget.sliderCookie;
if (cookie && cookie.length) {
config.options[cookie]=!isOpen;
if (config.options[cookie]!=theTarget.defOpen) window.saveOptionCookie(cookie);
else window.removeCookie(cookie); // remove cookie if slider is in default display state
}
// prevent SHIFT-CLICK from being processed by browser (opens blank window... yuck!)
// prevent clicks *within* a slider button from being processed by browser
// but allow plain click to bubble up to page background (to close transients, if any)
if (e.shiftKey || theTarget!=resolveTarget(e))
{ e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); }
Popup.remove(); // close open popup (if any)
return false;
}
//}}}
//{{{
// click in document background closes transient panels
document.nestedSliders_savedOnClick=document.onclick;
document.onclick=function(ev) { if (!ev) var ev=window.event; var target=resolveTarget(ev);
if (document.nestedSliders_savedOnClick)
var retval=document.nestedSliders_savedOnClick.apply(this,arguments);
// if click was inside a popup... leave transient panels alone
var p=target; while (p) if (hasClass(p,"popup")) break; else p=p.parentNode;
if (p) return retval;
// if click was inside transient panel (or something contained by a transient panel), leave it alone
var p=target; while (p) {
if ((hasClass(p,"floatingPanel")||hasClass(p,"sliderPanel"))&&p.getAttribute("transient")=="true") break;
p=p.parentNode;
}
if (p) return retval;
// otherwise, find and close all transient panels...
var all=document.all?document.all:document.getElementsByTagName("DIV");
for (var i=0; i<all.length; i++) {
// if it is not a transient panel, or the click was on the button that opened this panel, don't close it.
if (all[i].getAttribute("transient")!="true" || all[i].button==target) continue;
// otherwise, if the panel is currently visible, close it by clicking it's button
if (all[i].style.display!="none") window.onClickNestedSlider({target:all[i].button})
if (!hasClass(all[i],"floatingPanel")&&!hasClass(all[i],"sliderPanel")) all[i].style.display="none";
}
return retval;
};
//}}}
//{{{
// adjust floating panel position based on button position
if (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel) {
if (hasClass(panel,"floatingPanel") && !hasClass(panel,"undocked")) {
// see [[MoveablePanelPlugin]] for use of 'undocked'
var rightEdge=document.body.offsetWidth-1;
var panelWidth=panel.offsetWidth;
var left=0;
var top=btn.offsetHeight;
if (place.style.position=="relative" && findPosX(btn)+panelWidth>rightEdge) {
left-=findPosX(btn)+panelWidth-rightEdge; // shift panel relative to button
if (findPosX(btn)+left<0) left=-findPosX(btn); // stay within left edge
}
if (place.style.position!="relative") {
var left=findPosX(btn);
var top=findPosY(btn)+btn.offsetHeight;
var p=place; while (p && !hasClass(p,'floatingPanel')) p=p.parentNode;
if (p) { left-=findPosX(p); top-=findPosY(p); }
if (left+panelWidth>rightEdge) left=rightEdge-panelWidth;
if (left<0) left=0;
}
panel.style.left=left+"px"; panel.style.top=top+"px";
}
}
//}}}
//{{{
// TW2.1 and earlier:
// hijack Slider stop handler so overflow is visible after animation has completed
Slider.prototype.coreStop = Slider.prototype.stop;
Slider.prototype.stop = function()
{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }
// TW2.2+
// hijack Morpher stop handler so sliderPanel/floatingPanel overflow is visible after animation has completed
if (version.major+.1*version.minor+.01*version.revision>=2.2) {
Morpher.prototype.coreStop = Morpher.prototype.stop;
Morpher.prototype.stop = function() {
this.coreStop.apply(this,arguments);
var e=this.element;
if (hasClass(e,"sliderPanel")||hasClass(e,"floatingPanel")) {
// adjust panel overflow and position after animation
e.style.overflow = "visible";
if (window.adjustSliderPos) window.adjustSliderPos(e.parentNode,e.button,e);
}
};
}
//}}}
!! What
The plugin [[twve.tcalc]] (requiring [[twve.core]] and [[twve.table]]) works as a table calculator providing simple spreadsheet functionalities to the ~TiddlyWiki. It takes a table as a spreadsheet and addresses the table cells the same way as the popular spreadsheet applications (~LibreOffice.calc, ~MicroSoft Excel, etc.). It offers features like relative and absolute references, copy-and-pasting, auto recalculation, and auto reference adjustment. This tiddler addresses the issues about auto recalculation.
When the user made changes to one or more of the table cells, [[twve.tcalc]] automatically recalculates the table. In earlier versions prior to v1.0.5 the recalculation was performed on the whole table every time a table cell was changed, regardless of the number of cells being affected. For small tables this is not a problem at all. For large tables, however, we can expect
# a long recalculation time (if the table contains a significant amount of calculations), and/or
# a possible waste of time (if only a small fraction of the cells is affected by the change).
It then makes sense to recalculate only the affected cells to save time. In order to do so, I started (from v1.0.5) to rewrite the function <<slider '' 'TWtcalc.1.0.10##re_calculate_table' 'TWtcalc.re_calculate_table(twtable,text[,affected])'>> to do partial recalculation. In 1.0.5 only //directly affected// cells (those containing //explicit// references to one of the changed cells) were recalculated. In 1.0.7 //indirectly affected// cells (those containing //implicit// references) were also recalculated. Later in [[twve.tcalc]] the re-calculation function and the original calculation function were merged into one, the <<slider '' 'twve.tcalc##twtable.calculate' 'twtable.calculate'>>.
!! How
The idea of partial recalculation is to recalculate not all the cells but only
# the changed cells themselves,
# the cells referring to one of the changed cells (explicit reference, directly affected),
# the cells referring to one of the cells in 2. (implicit reference, indirectly affected),
# the cells referring to one of the cells in 3. (further implicit reference, indirectly affected),
# ......
The process goes on and on until no more cells need recalculation. This way shall leave the unaffected cells alone, and recalculate only the affected ones.
For small tables with multiple levels of implicit references (meaning the above process has to go beyond step 3), one might expect partial recalculation would take a longer time than a whole table recalculation, because there would be more searching and looping in the process. This should be generally true but we can expect a non-significant increase of time because 1) searching and looping don't take much time, as compared to calculations, and 2) total time of calculation is short for small tables, and a small increase to a short time can still be short.
For large tables, however, this is expected to save time, especially for tables that contain a significant amount of calculations in the cell content.
!! Results
The codes were tested with a small table having 24 cells in total, 5 of which contain formulas (one in each) to calculate. Averaging over 10 times of recalculations triggered by the same change that includes 3 levels of implicit referencing (so the above process has to go to step 5), partial recalculation took about 8 ms, while whole table recalculation took 7 ms to finish the job. The partial recalculation indeed takes a longer time and the difference is also indeed not significant.
<<foldHeadings>>
//Note: The codes are not tested with large tables yet.//
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 19, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.2.0 (Beta 5)'
};
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");
merge(config.macros.option.types, {
'pas': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "pasOptionInput",
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
config.macros.option.genericCreate(place,'pas',opt,className,desc);
// checkbox linked with this password "save this password on this computer"
config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
// text savePasswordCheckboxLabel
place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
},
onChange: config.macros.option.genericOnChange
}
});
merge(config.optionHandlers['chk'], {
//get: function(name) {
// is there an option linked with this chk ?
// var opt = name.substr(3);
// if (config.options[opt])
// saveOptionCookie(opt);
// return config.options[name] ? "true" : "false";
//}
set: function(name,value) {
config.options[name] = value == 'true';
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
saveOptionCookie(opt);
}
});
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options["chk"+name]) {
return encodeCookie(config.options[name].toString());
} else {
return "";
}
},
set: function(name,value) {config.options[name] = decodeCookie(value);}
}
});
// need to reload options to load passwordOptions
loadOptionsCookie();
/*
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
merge(config.optionsDesc,{
pasPassword: "Test password"
});
*/
//}}}
/***
|Name|PlayerPlugin|
|Source|http://www.TiddlyTools.com/#PlayerPlugin|
|Version|1.1.4|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Embed a media player in a tiddler|
!!!!!Usage
<<<
{{{<<player [id=xxx] [type] [URL] [width] [height] [autoplay|true|false] [showcontrols|true|false] [extras]>>}}}
''id=xxx'' is optional, and specifies a unique identifier for each embedded player. note: this is required if you intend to display more than one player at the same time.
''type'' is optional, and is one of the following: ''windows'', ''realone'', ''quicktime'', ''flash'', ''image'' or ''iframe''. If the media type is not specified, the plugin automatically detects Windows, Real, QuickTime, Flash video or JPG/GIF images by matching known file extensions and/or specialized streaming-media transfer protocols (such as RTSP:). For unrecognized media types, the plugin displays an error message.
''URL'' is the location of the media content
''width'' and ''height'' are the dimensions of the video display area (in pixels)
''autoplay'' or ''true'' or ''false'' is optional, and specifies whether the media content should begin playing as soon as it is loaded, or wait for the user to press the "play" button. Default is //not// to autoplay.
''showcontrols'' or ''true'' or ''false'' is optional, and specifies whether the embedded media player should display its built-in control panel (e.g., play, pause, stop, rewind, etc), if any. Default is to display the player controls.
''extras'' are optional //pairs// of parameters that can be passed to the embedded player, using the {{{<param name=xxx value=yyy>}}} HTML syntax.
''If you use [[AttachFilePlugin]] to encode and store a media file within your document, you can play embedded media content by using the title of the //attachment tiddler//'' as a parameter in place of the usual reference to an external URL. When playing an attached media content, you should always explicitly specify the media type parameter, because the name used for the attachment tiddler may not contain a known file extension from which a default media type can be readily determined.
<<<
!!!!!Configuration
<<<
Default player size:
width: <<option txtPlayerDefaultWidth>> height: <<option txtPlayerDefaultHeight>>
<<<
!!!!!Examples
<<<
+++[Windows Media]...
Times Square Live Webcam
{{{<<player id=1 http://www.earthcam.com/usa/newyork/timessquare/asx/tsq_stream.asx>>}}}
<<player id=1 http://www.earthcam.com/usa/newyork/timessquare/asx/tsq_stream.asx>>
===
+++[RealOne]...
BBC London: Live and Recorded news
{{{<<player id=2 http://www.bbc.co.uk/london/realmedia/news/tvnews.ram>>}}}
<<player id=2 http://www.bbc.co.uk/london/realmedia/news/tvnews.ram>>
===
+++[Quicktime]...
America Free TV: Classic Comedy
{{{<<player id=3 http://www.americafree.tv/unicast_mov/AmericaFreeTVComedy.mov>>}}}
<<player id=3 http://www.americafree.tv/unicast_mov/AmericaFreeTVComedy.mov>>
===
+++[Flash]...
Asteroids arcade game
{{{<<player id=4 http://www.80smusiclyrics.com/games/asteroids/asteroids.swf 400 300>>}}}
<<player id=4 http://www.80smusiclyrics.com/games/asteroids/asteroids.swf 400 300>>
Google Video
{{{<<player id=5 flash http://video.google.com/googleplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DoQAAAIVnUNP6GYRY8YnIRNPe4Uk5-j1q1MVpJIW4uyEFpq5Si0hcSDuig_JZcB9nNpAhbScm9W_8y_vDJQBw1DRdCVbXl-wwm5dyUiiStl_rXt0ATlstVzrUNC4fkgK_j7nmse7kxojRj1M3eo3jXKm2V8pQjWk97GcksMFFwg7BRAXmRSERexR210Amar5LYzlo9_k2AGUWPLyRhMJS4v5KtDSvNK0neL83ZjlHlSECYXyk%26sigh%3Dmpt2EOr86OAUNnPQ3b9Tr0wnDms%26begin%3D0%26len%3D429700%26docid%3D-914679554478687740&thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer%3Fcontentid%3De7e77162deb04c42%26second%3D5%26itag%3Dw320%26urlcreated%3D1144620753%26sigh%3DC3fqXPPS1tFiUqLzmkX3pdgYc2Y&playerId=-91467955447868774 400 326>>}}}
<<player id=5 flash http://video.google.com/googleplayer.swf?videoUrl=http%3A%2F%2Fvp.video.google.com%2Fvideodownload%3Fversion%3D0%26secureurl%3DoQAAAIVnUNP6GYRY8YnIRNPe4Uk5-j1q1MVpJIW4uyEFpq5Si0hcSDuig_JZcB9nNpAhbScm9W_8y_vDJQBw1DRdCVbXl-wwm5dyUiiStl_rXt0ATlstVzrUNC4fkgK_j7nmse7kxojRj1M3eo3jXKm2V8pQjWk97GcksMFFwg7BRAXmRSERexR210Amar5LYzlo9_k2AGUWPLyRhMJS4v5KtDSvNK0neL83ZjlHlSECYXyk%26sigh%3Dmpt2EOr86OAUNnPQ3b9Tr0wnDms%26begin%3D0%26len%3D429700%26docid%3D-914679554478687740&thumbnailUrl=http%3A%2F%2Fvideo.google.com%2FThumbnailServer%3Fcontentid%3De7e77162deb04c42%26second%3D5%26itag%3Dw320%26urlcreated%3D1144620753%26sigh%3DC3fqXPPS1tFiUqLzmkX3pdgYc2Y&playerId=-91467955447868774 400 326>>
YouTube Video
{{{<<player id=6 flash http://www.youtube.com/v/OdT9z-JjtJk 400 300>>}}}
<<player id=6 flash http://www.youtube.com/v/OdT9z-JjtJk 400 300>>
===
+++[Still Images]...
GIF (best for illustrations, animations, diagrams, etc.)
{{{<<player id=7 image images/meow.gif auto auto>>}}}
<<player id=7 image images/meow.gif auto auto>>
JPG (best for photographs, scanned images, etc.)
{{{<<player id=8 image images/meow2.jpg 200 150>>}}}
<<player id=8 image images/meow2.jpg 200 150>>
===
<<<
!!!!!Revisions
<<<
2008.05.10 [1.1.4] in handlers(), immediately return if no params (prevents error in macro). Also, refactored auto-detect code to make type mapping configurable.
2007.10.15 [1.1.3] in loadURL(), add recognition for .PNG (still image), fallback to iframe for unrecognized media types
2007.08.31 [1.1.2] added 'click-through' link for JPG/GIF images
2007.06.21 [1.1.1] changed "hidecontrols" param to "showcontrols" and recognize true/false values in addition to 'showcontrols', added "autoplay" param (also recognize true/false values), allow "auto" as value for type param
2007.05.22 [1.1.0] added support for type=="iframe" (displays src URL in an IFRAME)
2006.12.06 [1.0.1] in handler(), corrected check for config.macros.attach (instead of config.macros.attach.getAttachment) so that player plugin will work when AttachFilePlugin is NOT installed. (Thanks to Phillip Ehses for bug report)
2006.11.30 [1.0.0] support embedded media content using getAttachment() API defined by AttachFilePlugin or AttachFilePluginFormatters. Also added support for 'image' type to render JPG/GIF still images
2006.02.26 [0.7.0] major re-write. handles default params better. create/recreate player objects via loadURL() API for use with interactive forms and scripts.
2006.01.27 [0.6.0] added support for 'extra' macro params to pass through to object parameters
2006.01.19 [0.5.0] Initial ALPHA release
2005.12.23 [0.0.0] Started
<<<
!!!!!Code
***/
//{{{
version.extensions.PlayerPlugin= {major: 1, minor: 1, revision: 4, date: new Date(2008,5,10)};
config.macros.player = {};
config.macros.player.html = {};
config.macros.player.handler= function(place,macroName,params) {
if (!params.length) return; // missing parameters - do nothing
var id=null;
if (params[0].substr(0,3)=="id=") id=params.shift().substr(3);
var type="";
if (!params.length) return; // missing parameters - do nothing
var p=params[0].toLowerCase();
if (p=="auto" || p=="windows" || p=="realone" || p=="quicktime" || p=="flash" || p=="image" || p=="iframe")
type=params.shift().toLowerCase();
var url=params.shift(); if (!url || !url.trim().length) url="";
if (url.length && config.macros.attach!=undefined) // if AttachFilePlugin is installed
if ((tid=store.getTiddler(url))!=null && tid.isTagged("attachment")) // if URL is attachment
url=config.macros.attach.getAttachment(url); // replace TiddlerTitle with URL
var width=params.shift();
var height=params.shift();
var autoplay=false;
if (params[0]=='autoplay'||params[0]=='true'||params[0]=='false')
autoplay=(params.shift()!='false');
var show=true;
if (params[0]=='showcontrols'||params[0]=='true'||params[0]=='false')
show=(params.shift()!='false');
var extras="";
while (params[0]!=undefined)
extras+="<param name='"+params.shift()+"' value='"+params.shift()+"'> ";
this.loadURL(place,id,type,url,width,height,autoplay,show,extras);
}
if (config.options.txtPlayerDefaultWidth==undefined) config.options.txtPlayerDefaultWidth="100%";
if (config.options.txtPlayerDefaultHeight==undefined) config.options.txtPlayerDefaultHeight="480"; // can't use "100%"... player height doesn't stretch right :-(
config.macros.player.typeMap={
windows: ['mms', '.asx', '.wvx', '.wmv', '.mp3'],
realone: ['rtsp', '.ram', '.rpm', '.rm', '.ra'],
quicktime: ['.mov', '.qt'],
flash: ['.swf', '.flv'],
image: ['.jpg', '.gif', '.png'],
iframe: ['.htm', '.html', '.shtml', '.php']
};
config.macros.player.loadURL=function(place,id,type,url,width,height,autoplay,show,extras) {
if (id==undefined) id="tiddlyPlayer";
if (!width) var width=config.options.txtPlayerDefaultWidth;
if (!height) var height=config.options.txtPlayerDefaultHeight;
if (url && (!type || !type.length || type=="auto")) { // determine type from URL
u=url.toLowerCase();
var map=config.macros.player.typeMap;
for (var t in map) for (var i=0; i<map[t].length; i++)
if (u.indexOf(map[t][i])!=-1) var type=t;
}
if (!type || !config.macros.player.html[type]) var type="none";
if (!url) var url="";
if (show===undefined) var show=true;
if (!extras) var extras="";
if (type=="none" && url.trim().length) type="iframe"; // fallback to iframe for unrecognized media types
// adjust parameter values for player-specific embedded HTML
switch (type) {
case "windows":
autoplay=autoplay?"1":"0"; // player-specific param value
show=show?"1":"0"; // player-specific param value
break;
case "realone":
autoplay=autoplay?"true":"false";
show=show?"block":"none";
height-=show?60:0; // leave room for controls
break;
case "quicktime":
autoplay=autoplay?"true":"false";
show=show?"true":"false";
break;
case "image":
show=show?"block":"none";
break;
case "iframe":
show=show?"block":"none";
break;
}
// create containing div for player HTML
// and add or replace player in TW DOM structure
var newplayer = document.createElement("div");
newplayer.playerType=type;
newplayer.setAttribute("id",id+"_div");
var existing = document.getElementById(id+"_div");
if (existing && !place) place=existing.parentNode;
if (!existing)
place.appendChild(newplayer);
else {
if (place==existing.parentNode) place.replaceChild(newplayer,existing)
else { existing.parentNode.removeChild(existing); place.appendChild(newplayer); }
}
var html=config.macros.player.html[type];
html=html.replace(/%i%/mg,id);
html=html.replace(/%w%/mg,width);
html=html.replace(/%h%/mg,height);
html=html.replace(/%u%/mg,url);
html=html.replace(/%a%/mg,autoplay);
html=html.replace(/%s%/mg,show);
html=html.replace(/%x%/mg,extras);
newplayer.innerHTML=html;
}
//}}}
// // Player-specific API functions: isReady(id), isPlaying(id), toggleControls(id), showControls(id,flag)
//{{{
// status values:
// Windows: 0=Undefined, 1=Stopped, 2=Paused, 3=Playing, 4=ScanForward, 5=ScanReverse
// 6=Buffering, 7=Waiting, 8=MediaEnded, 9=Transitioning, 10=Ready, 11=Reconnecting
// RealOne: 0=Stopped, 1=Contacting, 2=Buffering, 3=Playing, 4=Paused, 5=Seeking
// QuickTime: 'Waiting', 'Loading', 'Playable', 'Complete', 'Error:###'
// Flash: 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete
config.macros.player.isReady=function(id)
{
var d=document.getElementById(id+"_div"); if (!d) return false;
var p=document.getElementById(id); if (!p) return false;
if (d.playerType=='windows') return !((p.playState==0)||(p.playState==7)||(p.playState==9)||(p.playState==11));
if (d.playerType=='realone') return (p.GetPlayState()>1);
if (d.playerType=='quicktime') return !((p.getPluginStatus()=='Waiting')||(p.getPluginStatus()=='Loading'));
if (d.playerType=='flash') return (p.ReadyState>2);
return true;
}
config.macros.player.isPlaying=function(id)
{
var d=document.getElementById(id+"_div"); if (!d) return false;
var p=document.getElementById(id); if (!p) return false;
if (d.playerType=='windows') return (p.playState==3);
if (d.playerType=='realone') return (p.GetPlayState()==3);
if (d.playerType=='quicktime') return (p.getPluginStatus()=='Complete');
if (d.playerType=='flash') return (p.ReadyState<4);
return false;
}
config.macros.player.showControls=function(id,flag) {
var d=document.getElementById(id+"_div"); if (!d) return false;
var p=document.getElementById(id); if (!p) return false;
if (d.playerType=='windows') { p.ShowControls=flag; p.ShowStatusBar=flag; }
if (d.playerType=='realone') { alert('show/hide controls not available'); }
if (d.playerType=='quicktime') // if player not ready, retry in one second
{ if (this.isReady(id)) p.setControllerVisible(flag); else setTimeout('config.macros.player.showControls("'+id+'",'+flag+')',1000); }
if (d.playerType=='flash') { alert('show/hide controls not available'); }
}
config.macros.player.toggleControls=function(id) {
var d=document.getElementById(id+"_div"); if (!d) return false;
var p=document.getElementById(id); if (!p) return false;
if (d.playerType=='windows') var flag=!p.ShowControls;
if (d.playerType=='realone') var flag=true; // TBD
if (d.playerType=='quicktime') var flag=!p.getControllerVisible();
if (d.playerType=='flash') var flag=true; // TBD
this.showControls(id,flag);
}
config.macros.player.fullScreen=function(id) {
var d=document.getElementById(id+"_div"); if (!d) return false;
var p=document.getElementById(id); if (!p) return false;
if (d.playerType=='windows') p.DisplaySize=3;
if (d.playerType=='realone') p.SetFullScreen();
if (d.playerType=='quicktime') { alert('full screen not available'); }
if (d.playerType=='flash') { alert('full screen not available'); }
}
//}}}
// // Player HTML
//{{{
// placeholder (no player)
config.macros.player.html.none=' \
<table id="%i%" width="%w%" height="%h%" style="background-color:#111;border:0;margin:0;padding:0;"> \
<tr style="background-color:#111;border:0;margin:0;padding:0;"> \
<td width="%w%" height="%h%" style="background-color:#111;color:#ccc;border:0;margin:0;padding:0;text-align:center;"> \
\
%u% \
\
</td></tr></table>';
//}}}
//{{{
// JPG/GIF/PNG still images
config.macros.player.html.image='\
<a href="%u%" target="_blank"><img width="%w%" height="%h%" style="display:%s%;" src="%u%"></a>';
//}}}
//{{{
// IFRAME web page viewer
config.macros.player.html.iframe='\
<iframe id="%i%" width="%w%" height="%h%" style="display:%s%;background:#fff;" src="%u%"></iframe>';
//}}}
//{{{
// Windows Media Player
// v7.1 ID: classid=CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6
// v9 ID: classid=CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95
config.macros.player.html.windows=' \
<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;width:%w%;height:%h%px;" \
classid="CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95" \
codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715" \
align="baseline" border="0" \
standby="Loading Microsoft Windows Media Player components..." \
type="application/x-oleobject"> \
<param name="FileName" value="%u%"> <param name="ShowControls" value="%s%"> \
<param name="ShowPositionControls" value="1"> <param name="ShowAudioControls" value="1"> \
<param name="ShowTracker" value="1"> <param name="ShowDisplay" value="0"> \
<param name="ShowStatusBar" value="1"> <param name="AutoSize" value="1"> \
<param name="ShowGotoBar" value="0"> <param name="ShowCaptioning" value="0"> \
<param name="AutoStart" value="%a%"> <param name="AnimationAtStart" value="1"> \
<param name="TransparentAtStart" value="0"> <param name="AllowScan" value="1"> \
<param name="EnableContextMenu" value="1"> <param name="ClickToPlay" value="1"> \
<param name="InvokeURLs" value="1"> <param name="DefaultFrame" value="datawindow"> \
%x% \
<embed src="%u%" style="margin:0;padding:0;width:%w%;height:%h%px;" \
align="baseline" border="0" width="%w%" height="%h%" \
type="application/x-mplayer2" \
pluginspage="http://www.microsoft.com/windows/windowsmedia/download/default.asp" \
name="%i%" showcontrols="%s%" showpositioncontrols="1" \
showaudiocontrols="1" showtracker="1" showdisplay="0" \
showstatusbar="%s%" autosize="1" showgotobar="0" showcaptioning="0" \
autostart="%a%" autorewind="0" animationatstart="1" transparentatstart="0" \
allowscan="1" enablecontextmenu="1" clicktoplay="0" invokeurls="1" \
defaultframe="datawindow"> \
</embed> \
</object>';
//}}}
//{{{
// RealNetworks' RealOne Player
config.macros.player.html.realone=' \
<table width="%w%" style="border:0;margin:0;padding:0;"><tr style="border:0;margin:0;padding:0;"><td style="border:0;margin:0;padding:0;"> \
<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;" \
CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"> \
<PARAM NAME="CONSOLE" VALUE="player"> \
<PARAM NAME="CONTROLS" VALUE="ImageWindow"> \
<PARAM NAME="AUTOSTART" Value="%a%"> \
<PARAM NAME="MAINTAINASPECT" Value="true"> \
<PARAM NAME="NOLOGO" Value="true"> \
<PARAM name="BACKGROUNDCOLOR" VALUE="#333333"> \
<PARAM NAME="SRC" VALUE="%u%"> \
%x% \
<EMBED width="%w%" height="%h%" controls="ImageWindow" type="audio/x-pn-realaudio-plugin" style="margin:0;padding:0;" \
name="%i%" \
src="%u%" \
console=player \
maintainaspect=true \
nologo=true \
backgroundcolor=#333333 \
autostart=%a%> \
</OBJECT> \
</td></tr><tr style="border:0;margin:0;padding:0;"><td style="border:0;margin:0;padding:0;"> \
<object id="%i%_controls" width="%w%" height="60" style="margin:0;padding:0;display:%s%" \
CLASSID="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"> \
<PARAM NAME="CONSOLE" VALUE="player"> \
<PARAM NAME="CONTROLS" VALUE="All"> \
<PARAM NAME="NOJAVA" Value="true"> \
<PARAM NAME="MAINTAINASPECT" Value="true"> \
<PARAM NAME="NOLOGO" Value="true"> \
<PARAM name="BACKGROUNDCOLOR" VALUE="#333333"> \
<PARAM NAME="SRC" VALUE="%u%"> \
%x% \
<EMBED WIDTH="%w%" HEIGHT="60" NOJAVA="true" type="audio/x-pn-realaudio-plugin" style="margin:0;padding:0;display:%s%" \
controls="All" \
name="%i%_controls" \
src="%u%" \
console=player \
maintainaspect=true \
nologo=true \
backgroundcolor=#333333> \
</OBJECT> \
</td></tr></table>';
//}}}
//{{{
// QuickTime Player
config.macros.player.html.quicktime=' \
<OBJECT ID="%i%" WIDTH="%w%" HEIGHT="%h%" style="margin:0;padding:0;" \
CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" \
CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab"> \
<PARAM name="SRC" VALUE="%u%"> \
<PARAM name="AUTOPLAY" VALUE="%a%"> \
<PARAM name="CONTROLLER" VALUE="%s%"> \
<PARAM name="BGCOLOR" VALUE="#333333"> \
<PARAM name="SCALE" VALUE="aspect"> \
<PARAM name="SAVEEMBEDTAGS" VALUE="true"> \
%x% \
<EMBED name="%i%" WIDTH="%w%" HEIGHT="%h%" style="margin:0;padding:0;" \
SRC="%u%" \
AUTOPLAY="%a%" \
SCALE="aspect" \
CONTROLLER="%s%" \
BGCOLOR="#333333" \
EnableJavaSript="true" \
PLUGINSPAGE="http://www.apple.com/quicktime/download/"> \
</EMBED> \
</OBJECT>';
//}}}
//{{{
// Flash Player
config.macros.player.html.flash='\
<object id="%i%" width="%w%" height="%h%" style="margin:0;padding:0;" \
classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" \
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"> \
<param name="movie" value="%u%"> \
<param name="quality" value="high"> \
<param name="SCALE" value="exactfit"> \
<param name="bgcolor" value="333333"> \
%x% \
<embed name="%i%" src="%u%" style="margin:0;padding:0;" \
height="%h%" width="%w%" quality="high" \
pluginspage="http://www.macromedia.com/go/getflashplayer" \
type="application/x-shockwave-flash" scale="exactfit"> \
</embed> \
</object>';
//}}}
>2013/05/01
>@@color:blue;Thanks to ~TonG who shared his CSS style settings for the {{{#displayArea}}} that also suffers from the same problem. He fixed it by removing some of the settings, including the@@
>>{{{position: relative;}}},
>@@color:blue;to the {{{.tiddler}}} div. That gave me a hint that it could be the@@
>>{{{position: relative;}}}
>@@color:blue;that was causing the problem. I did some experiments and learned that@@
>>{{{position: static;}}}
>>or simply {{{NO positioning setting}}}
>@@color:blue;for the {{{#displayArea}}} would give the correct offset. Other settings would give the shifted result. Therefore the codes are now changed to@@
{{{
var $display = $elem.closest('#displayArea');
if ( $display.css('position') != 'static' ) {
var dpos = $display.offset();
pos.left -= dpos.left;
pos.top -= dpos.top;
}
}}}
>@@color:blue;It seems to work correctly now.@@
!! What?
The {{{TWtid}}}+{{{TWted}}} plugins rely heavily on the comparison between the mouse position and the returned value of {{{jQuery}}}'s {{{.offset()}}} method, to locate a piece of plain text, and to show a border around the area to edit. It works fine except that the method gives different positions with ~TiddlySpace: the returned offset is //shifted// from the expected position.
!! What then?
Subtract the left/top of {{{#displayArea}}} from the left/top of the returned position.
{{{
var pos = $elem.offset();
var dpos = $elem.closest('#displayArea').offset();
pos.left -= dpos.left;
pos.top -= dpos.top;
}}}
This positioning issue does not happen with local ~TiddlyWiki files, so the adjustment is needed only for ~TiddlySpace (and others maybe?) At first I thought it was an on-line off-line issue, and did the adjustment when the tiddler was viewed on-line. However, soon after the release of v1.6.0 Arc Acorn found the positions are still wrong with a local copy of ~TiddlySpace, meaning there is something different between ~TiddlySpace and ~TiddlyWiki. I dig into a saved copy of ~TiddlySpace and found an easy way to recognize the platform, and modified the codes accordingly. It seems to work.
{{{
var pos = $elem.offset();
// TiddlySpace gives a position different from TiddlyWiki in jQuery's .offset()
// method: it is shifted by the left and top offset of #displayArea. Adjust it here.
if ( config.extensions.tiddlyspace ) {
var dpos = $elem.closest('#displayArea').offset();
pos.left -= dpos.left;
pos.top -= dpos.top;
}
}}}
>Thanks to Craig in Calgary for bringing up this issue.
>Later in 2012/10/18 I realized the parent of a SyntaxHighlighterPlugin3 created table has a class {{{syntaxhighlighter}}}, which can be used to easily distinguish the table from others.
The TableCalculator was messing up the output of SyntaxHighlighterPlugin3, mainly on the line numbers - they are supposed to go vertical but then horizontal, pushing the highlighted text to the far right if the number of lines is large enough. The reason was that SyntaxHighlighterPlugin3 uses __multiple text nodes in a table cell__ to show the line numbers (so they go vertical), while the TableCalculator assumes only __one text node in one cell__.
>Well, actually I didn't even know there are text nodes in a cell before using jQuery.
>And I didn't realize that jQuery(cell).text() pulls out all the text nodes in a cell and joins them into one single string...
>>Yes it is said in the [[.text() page|http://api.jquery.com/text/]] that "__{{{including their descendants.}}}__", but I did not realize that...
When the TableCalculator sees a table in a tiddler, it takes the text out of each cell to see if some things need to be done, does them if so, then __puts it back, even when nothing was done__. But since it assumes only one text node in one cell, all the other text nodes that the SyntaxHighlighterPlugin3 put there were gone in the last step, resulting in a horizontal series of line numbers and a messed up output.
The workaround I put there is
# skip the cells with more than one text nodes, and
# leave the cell untouched if nothing was done to its content.
Hopefully the plugins would go along with each other form now.
!!! @@color:blue;Installation@@
# Install [[twve.core.min]]. This is the very base for all other ''twve'' components.
# To edit tables only, add [[twve.table.min]].
** Optionally add [[twve.tablelarge.min]] for large table support.
# To edit elements other than tables, add [[twve.extra.min]].
# To do simple calculations in tables, add [[twve.tcalc.min]] in addition to [[twve.table.min]].
!!! @@color:green;Examples@@
><<tiddler "Examples">>
@@color:red;Note: The ''twve'' v3.0.0 is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# Easy incorporation into other plugins.
** Redesigned code structure and largely rewritten codes.
# Visually consistent keyboard navigation in tables with spanned cells and missing cells.
# Better performance by partial refreshing.
# Tons of bug fixes.
!!! twve.core v3.0.0 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/05/13 [3.0.0]'>>
!!! twve.extra v3.0.0 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/05/13 [3.0.0]'>>
!!! twve.table v3.0.0 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/05/13 [3.0.0]'>>
!!! twve.tablelarge v3.0.0 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2014/05/13 [3.0.0]'>>
!!! twve.tcalc v3.0.0 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/05/13 [3.0.0]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tcalc]]
!! @@Major Changes@@
Bug fixes.
!!! twve.core v3.0.1 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/05/14 [3.0.1]'>>
!!! twve.extra v3.0.1 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/05/14 [3.0.1]'>>
!!! twve.table v3.0.1 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/05/14 [3.0.1]'>>
!!! twve.tcalc v3.0.1 -- @@color:green;Recommended.@@
<<<
//This was already released earlier today.//
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/05/14 [3.0.1]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# @@color:red;Quite some bug fixes@@ and @@color:blue;some improvements@@.
# Improved __partial refreshing__.
# Improved __keyboard navigation__ (requiring ''twve.extra'').
!!! twve.core v3.0.2 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/05/23 [3.0.2]'>>
!!! twve.extra v3.0.2 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/05/23 [3.0.2]'>>
!!! twve.table v3.0.2 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/05/23 [3.0.2]'>>
!!! twve.tablelarge v3.0.2 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2014/05/23 [3.0.2]'>>
!!! twve.tcalc v3.0.2 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/05/23 [3.0.2]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]]
!! @@Major Changes@@
# Some bug fixes
!!! twve.core v3.0.3 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/06/02 [3.0.3]'>>
!!! twve.extra v3.0.3 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/06/02 [3.0.3]'>>
!!! twve.table v3.0.3 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/06/02 [3.0.3]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# Started removing jQuery dependencies to migrate to ~TW5.
** I don't seem to find a version of jQuery embeded in ~TW5 as there is in TWC, that will keep ''twve'' from going to ~TW5 because ''twve'' relies heavily on jQuery. Therefore I started to remove jQuery-dependencies from ''twve'', which turned out to be quite some work that shall take a long while. I am wondering: @@color:red;Is there a way to carry jQuery in ~TW5?@@
# Supports view mode editing of ''transcluded slices''.
!!! twve.core v3.1.0 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/08/03 [3.1.0]'>>
!!! twve.extra v3.1.0 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/08/03 [3.1.0]'>>
!!! twve.table v3.1.0 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/08/03 [3.1.0]'>>
!!! twve.tablelarge v3.1.0 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2014/08/03 [3.1.0]'>>
!!! twve.tcalc v3.1.0 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/08/03 [3.1.0]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# ''Removed jQuery dependencies, ready to migrate to ~TW5''.
# Supports view mode editing of ''empty transcluded slices''.
# Supports mixing of ''twve.tcalc'' functions and Javascript Math functions.
!!! twve.core v3.2.0 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/11/21 [3.2.0]'>>
!!! twve.extra v3.2.0 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/11/21 [3.2.0]'>>
!!! twve.table v3.2.0 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/11/21 [3.2.0]'>>
!!! twve.tablelarge v3.2.0 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2014/11/21 [3.2.0]'>>
!!! twve.tcalc v3.2.0 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/11/21 [3.2.0]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.2.x is NOT compatible with earlier versions due to the removal of jQuery dependency.@@
@@color:darkgreen;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# Improvements in //editing a paragraph of plain text//.
# Bug fixes in various situations.
# A few newly defined functions in ''twve.tcalc''.
!!! twve.core v3.2.1 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2014/12/22 [3.2.1]'>>
!!! twve.extra v3.2.1 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2014/12/22 [3.2.1]'>>
!!! twve.table v3.2.1 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2014/12/22 [3.2.1]'>>
!!! twve.tablelarge v3.2.1 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2014/12/22 [3.2.1]'>>
!!! twve.tcalc v3.2.1 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2014/12/22 [3.2.1]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.2.x is NOT compatible with earlier versions due to the removal of jQuery dependency.@@
@@color:darkgreen;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# Improvements in //editing a paragraph of plain text//.
# Bug fixes in various situations.
# A few newly defined functions in ''twve.tcalc''.
!!! twve.core v3.2.2 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2015/02/13 [3.2.2]'>>
!!! twve.extra v3.2.2 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2015/02/13 [3.2.2]'>>
!!! twve.table v3.2.2 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2015/02/13 [3.2.2]'>>
!!! twve.tablelarge v3.2.2 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2015/02/13 [3.2.2]'>>
!!! twve.tcalc v3.2.2 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2015/02/13 [3.2.2]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.2.x is NOT compatible with earlier versions due to the removal of jQuery dependency.@@
@@color:darkgreen;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tablelarge]], [[twve.tcalc]]
!! @@Major Changes@@
# Added supports for CSS wrappers created using """{{Class name{ .... }}}""".
# Bug fixes in various situations.
# A few newly defined functions and some bug fixes in ''twve.tcalc''.
!!! twve.core v3.2.3 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2015/03/23 [3.2.3]'>>
!!! twve.extra v3.2.3 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2015/03/23 [3.2.3]'>>
!!! twve.table v3.2.3 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2015/03/23 [3.2.3]'>>
!!! twve.tablelarge v3.2.3 -- @@color:green;Recommended.@@
<<<
Large table supports. See one of the examples in [[twve.table--Example--Large Table]].
<<<
<<tiddler 'twve.tablelarge##2015/03/23 [3.2.3]'>>
!!! twve.tcalc v3.2.3 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2015/03/23 [3.2.3]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.2.x is NOT compatible with earlier versions due to the removal of jQuery dependency.@@
@@color:darkgreen;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tcalc]]
!! @@Major Changes@@
# Added support
** for regular expression in the open/close tags of elements;
** for MathJax auto numbering;
** ''cross-table calculations''.
** ''parts-per notations in calculations'';
** ''time addition/subtraction in the format of hh:mm:ss or hh:mm or mm:ss'';
** vector calculations.
# Added functions
** LENGTH/LEN(cell)
** VALUE/VAL(cell[,n])
** VALUEWITH/VALWITH(cell,preceding[,following])
** SUMIN(cell[,n1,n2,...])
** SUMWITH(cell,preceding[,following])
!!! twve.core v3.2.4 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2015/06/15 [3.2.4]'>>
!!! twve.extra v3.2.4 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2015/06/15 [3.2.4]'>>
!!! twve.table v3.2.4 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2015/06/15 [3.2.4]'>>
!!! twve.tcalc v3.2.4 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2015/06/15 [3.2.4]'>>
<<foldHeadings>>
@@color:red;Note: The ''twve'' v3.2.x is NOT compatible with earlier versions due to the removal of jQuery dependency.@@
@@color:darkgreen;Note: The ''twve'' v3.x.x is NOT compatible with earlier versions due to its different design.@@
<<tiddler "Release Note -- Head">>
!! Affected in this release
[[twve.core]], [[twve.extra]], [[twve.table]], [[twve.tcalc]]
!! @@Major Changes@@
* Added functions
** CELL(row,col) -- retrieves the content of a table cell at (row, col), where row is the 0-based index of a table row, col is either the 0-based column index or its letter representation.
** LEFT(source,len) -- extracts the left part of a string.
** MID(source,start,len) -- extracts the middle part of a string.
** RIGHT(source,len) -- extracts the right part of a string.
** SUMCOL([col[,how]]) -- sums up numerical values in the column with index col (this column if omitted), excluding the cell containing this formula. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** SUMROW([row[,how]]) -- sums up numerical values in the row with index row (this row if omitted), excluding the cell containing this formula. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** SUMCOLWITH([COL[,preceding[,following[,how]]]]) -- sums up numerical values in the column with index col (this column if omitted), excluding the cell containing this formula, with signature text immediately preceding and/or following the value. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** SUMROWWITH([ROW[,preceding[,following[,how]]]]) -- sums up numerical values in the row with index row (this row if omitted), excluding the cell containing this formula, with optional signature text immediately preceding and/or following the value. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTCOL([col[,how]]) -- count the number of numerical values in the column with index col (this column if omitted), excluding this cell. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTROW([row[,how]]) -- count the number of numerical values in the row with index row (this row if omitted), excluding this cell. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTCOLA([col[,how]) -- count the number of non-empty values in the column with index col (this column if omitted), excluding this cell. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTROWA([row[,how]]) -- count the number of non-empty values in the row with index row (this row if omitted), excluding this cell. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** DATEADD(date,dy,dm,dd[,format])
*** Calculate a new date shifted by dy/dm/dd with respect to date.
*** date must be given.
*** At least one of the (dy, dm, dd) must be specified.
*** Optional format can be given in the usual way, such as mm/dd, yy/mm/dd, yyyy/mm/dd, etc.
** DATE(y,m,d[,format])
*** Specify a date of y/m/d.
*** At least one of the (y, m, d) must be specified, missing one(s) are taken as the corresponding part(s) of today.
**** For example, if year is missing while month and date are given, the year part will be taken as this year.
*** Optional format can be given in the usual way, such as mm/dd, yy/mm/dd, yyyy/mm/dd, etc.
!!! twve.core v3.2.5 -- @@color:red;Required.@@
<<<
The very base of the view mode tiddler editor ''twve'' (including ''twve.core'', ''twve.extra'', and ''twve.table''), and the simple calculator ''twve.tcalc''.
# Offers view mode editing and transclusion synchronization capabilities for
** [[wrappers|twve--Example--Wrappers]],
** [[preformatted blocks|twve--Example--Preformatted]],
** [[code pieces|twve--Example--Code]].
** See [[twve.core Examples]] for more.
# Easy incorporation into other plugins. See [[Edit Your ...]] for more information.
<<<
<<tiddler 'twve.core##2015/11/06 [3.2.5]'>>
!!! twve.extra v3.2.5 -- @@color:blue;Highly recommended.@@
<<<
# Extends view mode editing and transclusion synchronization capabilities to cover
** [[list items|twve--Example--List Items]].
** [[headings|twve--Example--Headings]].
** [[blockquotes|twve--Example--Blockquotes]].
** and more... See [[twve.extra Examples]].
<<<
<<tiddler 'twve.extra##2015/11/06 [3.2.5]'>>
!!! twve.table v3.2.5 -- @@color:blue;Highly recommended.@@
<<<
Extends view mode editing and transclusion synchronization capabilities to cover tables. See one of the examples in [[twve.table Examples]].
<<<
<<tiddler 'twve.table##2015/11/06 [3.2.5]'>>
!!! twve.tcalc v3.2.5 -- @@color:green;Recommended.@@
<<<
Simple calculator for ~TiddlyWiki. See one of the examples in [[twve.tcalc Examples]].
<<<
<<tiddler 'twve.tcalc##2015/11/06 [3.2.5]'>>
<<foldHeadings>>
/***
|Requires|ShCore.js|
***/
//{{{
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
function getKeywordsCSS(str)
{
return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b';
};
function getValuesCSS(str)
{
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
};
var keywords = 'ascent azimuth background-attachment background-color background-image background-position ' +
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
var values = 'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+
'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+
'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+
'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
var fonts = '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
this.regexList = [
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings
{ regex: /\#[a-fA-F0-9]{3,6}/g, css: 'value' }, // html colors
{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g, css: 'value' }, // sizes
{ regex: /!important/g, css: 'color3' }, // !important
{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'), css: 'keyword' }, // keywords
{ regex: new RegExp(getValuesCSS(values), 'g'), css: 'value' }, // values
{ regex: new RegExp(this.getKeywords(fonts), 'g'), css: 'color1' } // fonts
];
this.forHtmlScript({
left: /(<|<)\s*style.*?(>|>)/gi,
right: /(<|<)\/\s*style\s*(>|>)/gi
});
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['css'];
SyntaxHighlighter.brushes.CSS = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();
//}}}
/***
***/
/*{{{*/
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
var keywords = 'break case catch continue ' +
'default delete do else false ' +
'for function if in instanceof ' +
'new null return super switch ' +
'this throw true try typeof var while with'
;
var r = SyntaxHighlighter.regexLib;
this.regexList = [
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
];
this.forHtmlScript(r.scriptScriptTags);
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['js', 'jscript', 'javascript'];
SyntaxHighlighter.brushes.JScript = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();
/*}}}*/
//{{{
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['text', 'plain'];
SyntaxHighlighter.brushes.Plain = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();
//}}}
//{{{
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
// Contributed by Gheorghe Milas and Ahmad Sherif
var keywords = 'and assert break class continue def del elif else ' +
'except exec finally for from global if import in is ' +
'lambda not or pass print raise return try yield while';
var funcs = '__import__ abs all any apply basestring bin bool buffer callable ' +
'chr classmethod cmp coerce compile complex delattr dict dir ' +
'divmod enumerate eval execfile file filter float format frozenset ' +
'getattr globals hasattr hash help hex id input int intern ' +
'isinstance issubclass iter len list locals long map max min next ' +
'object oct open ord pow print property range raw_input reduce ' +
'reload repr reversed round set setattr slice sorted staticmethod ' +
'str sum super tuple type type unichr unicode vars xrange zip';
var special = 'None True False self cls class_';
this.regexList = [
{ regex: SyntaxHighlighter.regexLib.singleLinePerlComments, css: 'comments' },
{ regex: /^\s*@\w+/gm, css: 'decorator' },
{ regex: /(['\"]{3})([^\1])*?\1/gm, css: 'comments' },
{ regex: /"(?!")(?:\.|\\\"|[^\""\n])*"/gm, css: 'string' },
{ regex: /'(?!')(?:\.|(\\\')|[^\''\n])*'/gm, css: 'string' },
{ regex: /\+|\-|\*|\/|\%|=|==/gm, css: 'keyword' },
{ regex: /\b\d+\.?\w*/g, css: 'value' },
{ regex: new RegExp(this.getKeywords(funcs), 'gmi'), css: 'functions' },
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' },
{ regex: new RegExp(this.getKeywords(special), 'gm'), css: 'color1' }
];
this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['py', 'python'];
SyntaxHighlighter.brushes.Python = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();
//}}}
//{{{
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
;(function()
{
// CommonJS
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
function Brush()
{
function process(match, regexInfo)
{
var constructor = SyntaxHighlighter.Match,
code = match[0],
tag = new XRegExp('(<|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
result = []
;
if (match.attributes != null)
{
var attributes,
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
'\\s*=\\s*' +
'(?<value> ".*?"|\'.*?\'|\\w+)',
'xg');
while ((attributes = regex.exec(code)) != null)
{
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
}
}
if (tag != null)
result.push(
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
);
return result;
}
this.regexList = [
{ regex: new XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
{ regex: new XRegExp('(<|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
];
};
Brush.prototype = new SyntaxHighlighter.Highlighter();
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
SyntaxHighlighter.brushes.Xml = Brush;
// CommonJS
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();
//}}}
/*{{{*/
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter a,
.syntaxhighlighter div,
.syntaxhighlighter code,
.syntaxhighlighter table,
.syntaxhighlighter table td,
.syntaxhighlighter table tr,
.syntaxhighlighter table tbody,
.syntaxhighlighter table thead,
.syntaxhighlighter table caption,
.syntaxhighlighter textarea {
-moz-border-radius: 0 0 0 0 !important;
-webkit-border-radius: 0 0 0 0 !important;
background: none !important;
border: 0 !important;
bottom: auto !important;
float: none !important;
height: auto !important;
left: auto !important;
line-height: 1.1em !important;
margin: 0 !important;
outline: 0 !important;
overflow: visible !important;
padding: 0 !important;
position: static !important;
right: auto !important;
text-align: left !important;
top: auto !important;
vertical-align: baseline !important;
width: auto !important;
box-sizing: content-box !important;
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
font-weight: normal !important;
font-style: normal !important;
font-size: 1em !important;
min-height: inherit !important;
min-height: auto !important;
}
.syntaxhighlighter {
width: 100% !important;
margin: 1em 0 1em 0 !important;
position: relative !important;
overflow: auto !important;
font-size: .9em !important;
}
.syntaxhighlighter.source {
overflow: hidden !important;
}
.syntaxhighlighter .bold {
font-weight: bold !important;
}
.syntaxhighlighter .italic {
font-style: italic !important;
}
.syntaxhighlighter .line {
white-space: pre !important;
}
.syntaxhighlighter table {
width: 100% !important;
}
.syntaxhighlighter table caption {
text-align: left !important;
padding: .5em 0 0.5em 1em !important;
}
.syntaxhighlighter table td.code {
width: 100% !important;
}
.syntaxhighlighter table td.code .container {
position: relative !important;
}
.syntaxhighlighter table td.code .container textarea {
box-sizing: border-box !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
width: 100% !important;
height: 100% !important;
border: none !important;
background: white !important;
padding-left: 1em !important;
overflow: hidden !important;
white-space: pre !important;
}
.syntaxhighlighter table td.gutter .line {
text-align: right !important;
padding: 0 0.5em 0 1em !important;
}
.syntaxhighlighter table td.code .line {
padding: 0 1em !important;
}
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
padding-left: 0em !important;
}
.syntaxhighlighter.show {
display: block !important;
}
.syntaxhighlighter.collapsed table {
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar {
padding: 0.1em 0.8em 0em 0.8em !important;
font-size: 1em !important;
position: static !important;
width: auto !important;
height: auto !important;
}
.syntaxhighlighter.collapsed .toolbar span {
display: inline !important;
margin-right: 1em !important;
}
.syntaxhighlighter.collapsed .toolbar span a {
padding: 0 !important;
display: none !important;
}
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
display: inline !important;
}
.syntaxhighlighter .toolbar {
position: absolute !important;
right: 1px !important;
top: 1px !important;
width: 11px !important;
height: 11px !important;
font-size: 10px !important;
z-index: 10 !important;
}
.syntaxhighlighter .toolbar span.title {
display: inline !important;
}
.syntaxhighlighter .toolbar a {
display: block !important;
text-align: center !important;
text-decoration: none !important;
padding-top: 1px !important;
}
.syntaxhighlighter .toolbar a.expandSource {
display: none !important;
}
.syntaxhighlighter.ie {
font-size: .9em !important;
padding: 1px 0 1px 0 !important;
}
.syntaxhighlighter.ie .toolbar {
line-height: 8px !important;
}
.syntaxhighlighter.ie .toolbar a {
padding-top: 0px !important;
}
.syntaxhighlighter.printing .line.alt1 .content,
.syntaxhighlighter.printing .line.alt2 .content,
.syntaxhighlighter.printing .line.highlighted .number,
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
background: none !important;
}
.syntaxhighlighter.printing .line .number {
color: #bbbbbb !important;
}
.syntaxhighlighter.printing .line .content {
color: black !important;
}
.syntaxhighlighter.printing .toolbar {
display: none !important;
}
.syntaxhighlighter.printing a {
text-decoration: none !important;
}
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
color: black !important;
}
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
color: #008200 !important;
}
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
color: blue !important;
}
.syntaxhighlighter.printing .keyword {
color: #006699 !important;
font-weight: bold !important;
}
.syntaxhighlighter.printing .preprocessor {
color: gray !important;
}
.syntaxhighlighter.printing .variable {
color: #aa7700 !important;
}
.syntaxhighlighter.printing .value {
color: #009900 !important;
}
.syntaxhighlighter.printing .functions {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .constants {
color: #0066cc !important;
}
.syntaxhighlighter.printing .script {
font-weight: bold !important;
}
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
color: gray !important;
}
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
color: red !important;
}
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
color: black !important;
}
/*}}}*/
/***
|Name|ShCore.js|
***/
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
//{{{
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a<f.L;a++)I(f[a]===e)H a;H-1}M=6(f,e){K a=[],b=M.1B,c=0,d,h;I(M.1R(f)){I(e!==1d)1S 3m("2a\'t 5r 5I 5F 5B 5C 15 5E 5p");H r(f)}I(v)1S 2U("2a\'t W 3l M 59 5m 5g 5x 5i");e=e||"";O(d={2N:11,19:[],2K:6(g){H e.1i(g)>-1},3d:6(g){e+=g}};c<f.L;)I(h=B(f,c,b,d)){a.U(h.3k);c+=h.1C[0].L||1}Y I(h=n.X.W(z[b],f.1a(c))){a.U(h[0]);c+=h[0].L}Y{h=f.3a(c);I(h==="[")b=M.2I;Y I(h==="]")b=M.1B;a.U(h);c++}a=15(a.1K(""),n.Q.W(e,w,""));a.1w={1m:f,19:d.2N?d.19:N};H a};M.3v="1.5.0";M.2I=1;M.1B=2;K C=/\\$(?:(\\d\\d?|[$&`\'])|{([$\\w]+)})/g,w=/[^5h]+|([\\s\\S])(?=[\\s\\S]*\\1)/g,A=/^(?:[?*+]|{\\d+(?:,\\d*)?})\\??/,v=11,u=[],n={X:15.Z.X,1A:15.Z.1A,1C:1r.Z.1C,Q:1r.Z.Q,1e:1r.Z.1e},x=n.X.W(/()??/,"")[1]===1d,D=6(){K f=/^/g;n.1A.W(f,"");H!f.12}(),y=6(){K f=/x/g;n.Q.W("x",f,"");H!f.12}(),E=15.Z.3n!==1d,z={};z[M.2I]=/^(?:\\\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S]))/;z[M.1B]=/^(?:\\\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\\d*|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S])|\\(\\?[:=!]|[?*+]\\?|{\\d+(?:,\\d*)?}\\??)/;M.1h=6(f,e,a,b){u.U({2q:r(f,"g"+(E?"y":"")),2b:e,3r:a||M.1B,2p:b||N})};M.2n=6(f,e){K a=f+"/"+(e||"");H M.2n[a]||(M.2n[a]=M(f,e))};M.3c=6(f){H r(f,"g")};M.5l=6(f){H f.Q(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,"\\\\$&")};M.5e=6(f,e,a,b){e=r(e,"g"+(b&&E?"y":""));e.12=a=a||0;f=e.X(f);H b?f&&f.P===a?f:N:f};M.3q=6(){M.1h=6(){1S 2U("2a\'t 55 1h 54 3q")}};M.1R=6(f){H 53.Z.1q.W(f)==="[2m 15]"};M.3p=6(f,e,a,b){O(K c=r(e,"g"),d=-1,h;h=c.X(f);){a.W(b,h,++d,f,c);c.12===h.P&&c.12++}I(e.1J)e.12=0};M.57=6(f,e){H 6 a(b,c){K d=e[c].1I?e[c]:{1I:e[c]},h=r(d.1I,"g"),g=[],i;O(i=0;i<b.L;i++)M.3p(b[i],h,6(k){g.U(d.3j?k[d.3j]||"":k[0])});H c===e.L-1||!g.L?g:a(g,c+1)}([f],0)};15.Z.1p=6(f,e){H J.X(e[0])};15.Z.W=6(f,e){H J.X(e)};15.Z.X=6(f){K e=n.X.1p(J,14),a;I(e){I(!x&&e.L>1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;b<e.L;b++)I(a=J.1w.19[b-1])e[a]=e[b];!D&&J.1J&&!e[0].L&&J.12>e.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;d<b.L;d++)I(b[d])14[0][b[d]]=14[d+1]}I(a&&f.1J)f.12=14[14.L-2]+14[0].L;H e.1p(N,14)});Y{c=J+"";c=n.Q.W(c,f,6(){K d=14;H n.Q.W(e,C,6(h,g,i){I(g)5b(g){24"$":H"$";24"&":H d[0];24"`":H d[d.L-1].1a(0,d[d.L-2]);24"\'":H d[d.L-1].1a(d[d.L-2]+d[0].L);5a:i="";g=+g;I(!g)H h;O(;g>d.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P<a.L&&3b.Z.U.1p(b,d.1a(1));h=d[0].L;c=f.12;I(b.L>=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a<d.L&&i==N;a++)i=p(d[a],b,c);H i}6 C(a,b){K c={},d;O(d 2g a)c[d]=a[d];O(d 2g b)c[d]=b[d];H c}6 w(a,b,c,d){6 h(g){g=g||1P.5y;I(!g.1F){g.1F=g.52;g.3N=6(){J.5w=11}}c.W(d||1P,g)}a.3g?a.3g("4U"+b,h):a.4y(b,h,11)}6 A(a,b){K c=e.1Y.2j,d=N;I(c==N){c={};O(K h 2g e.1U){K g=e.1U[h];d=g.4x;I(d!=N){g.1V=h.4w();O(g=0;g<d.L;g++)c[d[g]]=h}}e.1Y.2j=c}d=e.1U[c[a]];d==N&&b!=11&&1P.1X(e.13.1x.1X+(e.13.1x.3E+a));H d}6 v(a,b){O(K c=a.1e("\\n"),d=0;d<c.L;d++)c[d]=b(c[d],d);H c.1K("\\n")}6 u(a,b){I(a==N||a.L==0||a=="\\n")H a;a=a.Q(/</g,"&1y;");a=a.Q(/ {2,}/g,6(c){O(K d="",h=0;h<c.L-1;h++)d+=e.13.1W;H d+" "});I(b!=N)a=v(a,6(c){I(c.L==0)H"";K d="";c=c.Q(/^(&2s;| )+/,6(h){d=h;H""});I(c.L==0)H d;H d+\'<17 1g="\'+b+\'">\'+c+"</17>"});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+=" ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.P<b.P)H-1;Y I(a.P>b.P)H 1;Y I(a.L<b.L)H-1;Y I(a.L>b.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'<a 2h="\'+c+\'">\'+c+"</a>"+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<a.L;c++)a[c].3s=="20"&&b.U(a[c]);H b}6 f(a){a=a.1F;K b=p(a,".20",R);a=p(a,".3O",R);K c=1E.4i("3t");I(!(!a||!b||p(a,"3t"))){B(b.1c);r(b,"1m");O(K d=a.3G,h=[],g=0;g<d.L;g++)h.U(d[g].4z||d[g].4A);h=h.1K("\\r");c.39(1E.4D(h));a.39(c);c.2C();c.4C();w(c,"4u",6(){c.2G.4E(c);b.1l=b.1l.Q("1m","")})}}I(1j 3F!="1d"&&1j M=="1d")M=3F("M").M;K e={2v:{"1g-27":"","2i-1s":1,"2z-1s-2t":11,1M:N,1t:N,"42-45":R,"43-22":4,1u:R,16:R,"3V-17":R,2l:11,"41-40":R,2k:11,"1z-1k":11},13:{1W:"&2s;",2M:R,46:11,44:11,34:"4n",1x:{21:"4o 1m",2P:"?",1X:"1v\\n\\n",3E:"4r\'t 4t 1D O: ",4g:"4m 4B\'t 51 O 1z-1k 4F: ",37:\'<!4T 1z 4S "-//4V//3H 4W 1.0 4Z//4Y" "1Z://2y.3L.3K/4X/3I/3H/3I-4P.4J"><1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v</1t></3J><3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;"><T 1L="2O-3D:3C;3w-32:1.6z;"><T 1L="25-22:6A-6E;">1v</T><T 1L="25-22:.6C;3w-6B:6R;"><T>3v 3.0.76 (72 73 3x)</T><T><a 2h="1Z://3u.2w/1v" 1F="38" 1L="2f:#3y">1Z://3u.2w/1v</a></T><T>70 17 6U 71.</T><T>6T 6X-3x 6Y 6D.</T></T><T>6t 61 60 J 1k, 5Z <a 2h="6u://2y.62.2w/63-66/65?64=5X-5W&5P=5O" 1L="2f:#3y">5R</a> 5V <2R/>5U 5T 5S!</T></T></3B></1z>\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'<T 1g="16">\',d=e.16.2x,h=d.2X,g=0;g<h.L;g++)c+=(d[h[g]].1H||b)(a,h[g]);c+="</T>";H c},2o:6(a,b,c){H\'<2W><a 2h="#" 1g="6e 6h\'+b+" "+b+\'">\'+c+"</a></2W>"},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h<c.L;h++)d.U(c[h]);c=d}c=c;d=[];I(e.13.2M)c=c.1O(z());I(c.L===0)H d;O(h=0;h<c.L;h++){O(K g=c[h],i=a,k=c[h].1l,j=3W 0,l={},m=1f M("^\\\\[(?<2V>(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g<c.L;g++){b=c[g];K i=b.1F,k=b.1n,j=k.1D,l;I(j!=N){I(k["1z-1k"]=="R"||e.2v["1z-1k"]==R){d=1f e.4l(j);j="4O"}Y I(d=A(j))d=1f d;Y 6H;l=i.3X;I(h.2M){l=l;K m=x(l),s=11;I(m.1i("<![6G[")==0){m=m.4h(9);s=R}K o=m.L;I(m.1i("]]\\>")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;m<j.L;m++)j[m].P+=l}K c=A(a),d,h=1f e.1U.5Y,g=J,i="2F 1H 2Q".1e(" ");I(c!=N){d=1f c;O(K k=0;k<i.L;k++)(6(){K j=i[k];g[j]=6(){H h[j].1p(h,14)}})();d.28==N?1P.1X(e.13.1x.1X+(e.13.1x.4g+a)):h.2J.U({1I:d.28.17,2D:6(j){O(K l=j.17,m=[],s=d.2J,o=j.P+j.18.L,F=d.28,q,G=0;G<s.L;G++){q=y(l,s[G]);b(q,o);m=m.1O(q)}I(F.18!=N&&j.18!=N){q=y(j.18,F.18);b(q,j.P);m=m.1O(q)}I(F.1b!=N&&j.1b!=N){q=y(j.1b,F.1b);b(q,j.P+j[0].5Q(j.1b));m=m.1O(q)}O(j=0;j<m.L;j++)m[j].1V=c.1V;H m}})}};e.4j=6(){};e.4j.Z={V:6(a,b){K c=J.1n[a];c=c==N?b:c;K d={"R":R,"11":11}[c];H d==N?c:d},3Y:6(a){H 1E.4i(a)},4c:6(a,b){K c=[];I(a!=N)O(K d=0;d<a.L;d++)I(1j a[d]=="2m")c=c.1O(y(b,a[d]));H J.4e(c.6b(D))},4e:6(a){O(K b=0;b<a.L;b++)I(a[b]!==N)O(K c=a[b],d=c.P+c.L,h=b+1;h<a.L&&a[b]!==N;h++){K g=a[h];I(g!==N)I(g.P>d)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P<d)a[h]=N}H a},4d:6(a){K b=[],c=2u(J.V("2i-1s"));v(a,6(d,h){b.U(h+c)});H b},3U:6(a){K b=J.V("1M",[]);I(1j b!="2m"&&b.U==N)b=[b];a:{a=a.1q();K c=3W 0;O(c=c=1Q.6c(c||0,0);c<b.L;c++)I(b[c]==a){b=c;1N a}b=-1}H b!=-1},2r:6(a,b,c){a=["1s","6i"+b,"P"+a,"6r"+(b%2==0?1:2).1q()];J.3U(b)&&a.U("67");b==0&&a.U("1N");H\'<T 1g="\'+a.1K(" ")+\'">\'+c+"</T>"},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i<d;i++){K k=b?b[i]:h+i,j;I(k==0)j=e.13.1W;Y{j=g;O(K l=k.1q();l.L<j;)l="0"+l;j=l}a=j;c+=J.2r(i,k,a)}H c},49:6(a,b){a=x(a);K c=a.1e("\\n");J.V("2z-1s-2t");K d=2u(J.V("2i-1s"));a="";O(K h=J.V("1D"),g=0;g<c.L;g++){K i=c[g],k=/^(&2s;|\\s)+/.X(i),j=N,l=b?b[g]:d+g;I(k!=N){j=k[0].1q();i=i.1o(j.L);j=j.Q(" ",e.13.1W)}i=x(i);I(i.L==0)i=e.13.1W;a+=J.2r(g,l,(j!=N?\'<17 1g="\'+h+\' 5N">\'+j+"</17>":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"</4a>":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i<b.L;i++){K k=b[i],j;I(!(k===N||k.L===0)){j=c(k);h+=u(a.1o(d,k.P-d),j+"48")+u(k.1T,j+k.23);d=k.P+k.L+(k.75||0)}}h+=u(a.1o(d),c()+"48");H h},1H:6(a){K b="",c=["20"],d;I(J.V("2k")==R)J.1n.16=J.1n.1u=11;1l="20";J.V("2l")==R&&c.U("47");I((1u=J.V("1u"))==11)c.U("6S");c.U(J.V("1g-27"));c.U(J.V("1D"));a=a.Q(/^[ ]*[\\n]+|[\\n]*[ ]*$/g,"").Q(/\\r/g," ");b=J.V("43-22");I(J.V("42-45")==R)a=n(a,b);Y{O(K h="",g=0;g<b;g++)h+=" ";a=a.Q(/\\t/g,h)}a=a;a:{b=a=a;h=/<2R\\s*\\/?>|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i<b.L&&g>0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i<b.L;i++)b[i]=b[i].1o(g);a=b.1K("\\n")}I(1u)d=J.4d(a);b=J.4c(J.2J,a);b=J.4b(a,b);b=J.49(b,d);I(J.V("41-40"))b=E(b);1j 2H!="1d"&&2H.3S&&2H.3S.1C(/5s/)&&c.U("5t");H b=\'<T 1c="\'+t(J.1c)+\'" 1g="\'+c.1K(" ")+\'">\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"</2d>":"")+\'<2d 1g="17"><T 1g="3O">\'+b+"</T></2d></3P></3T></3Z></T>"},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{}))
//}}}
/*{{{*/
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter {
background-color: white !important;
}
.syntaxhighlighter .line.alt1 {
background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
background-color: white !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
background-color: #e0e0e0 !important;
}
.syntaxhighlighter .line.highlighted.number {
color: black !important;
}
.syntaxhighlighter table caption {
color: black !important;
}
.syntaxhighlighter .gutter {
color: #afafaf !important;
}
.syntaxhighlighter .gutter .line {
border-right: 3px solid #6ce26c !important;
}
.syntaxhighlighter .gutter .line.highlighted {
background-color: #6ce26c !important;
color: white !important;
}
.syntaxhighlighter.printing .line .content {
border: none !important;
}
.syntaxhighlighter.collapsed {
overflow: visible !important;
}
.syntaxhighlighter.collapsed .toolbar {
color: blue !important;
background: white !important;
border: 1px solid #6ce26c !important;
}
.syntaxhighlighter.collapsed .toolbar a {
color: blue !important;
}
.syntaxhighlighter.collapsed .toolbar a:hover {
color: red !important;
}
.syntaxhighlighter .toolbar {
color: white !important;
background: #6ce26c !important;
border: none !important;
}
.syntaxhighlighter .toolbar a {
color: white !important;
}
.syntaxhighlighter .toolbar a:hover {
color: black !important;
}
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
color: black !important;
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
color: #008200 !important;
}
.syntaxhighlighter .string, .syntaxhighlighter .string a {
color: blue !important;
}
.syntaxhighlighter .keyword {
color: #006699 !important;
}
.syntaxhighlighter .preprocessor {
color: gray !important;
}
.syntaxhighlighter .variable {
color: #aa7700 !important;
}
.syntaxhighlighter .value {
color: #009900 !important;
}
.syntaxhighlighter .functions {
color: #ff1493 !important;
}
.syntaxhighlighter .constants {
color: #0066cc !important;
}
.syntaxhighlighter .script {
font-weight: bold !important;
color: #006699 !important;
background-color: none !important;
}
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
color: gray !important;
}
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
color: red !important;
}
.syntaxhighlighter .keyword {
font-weight: bold !important;
}
/*}}}*/
/***
<<highlightSyntax>>
***/
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.7|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)
Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2010.11.30 2.9.7 use story.getTiddler()
2008.10.17 2.9.6 changed chkSinglePageAutoScroll default to false
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 1.0.0 Initial Release. Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 7, date: new Date(2010,11,30)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
config.options.chkSinglePageMode=eval(v);
if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
config.lastURL = window.location.hash;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
config.options.chkSinglePageAutoScroll=false;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
if (!config.options.chkSinglePageMode)
{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
if (config.lastURL == window.location.hash) return; // no change in hash
var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
if (tids.length==1) // permalink (single tiddler in URL)
story.displayTiddler(null,tids[0]);
else { // restore permaview or default view
config.lastURL = window.location.hash;
if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
story.closeAllTiddlers();
story.displayTiddlers(null,tids);
}
}
if (Story.prototype.SPM_coreDisplayTiddler==undefined)
Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
var tiddlerElem=story.getTiddler(title); // ==null unless tiddler is already displayed
var opt=config.options;
var single=opt.chkSinglePageMode && !startingUp;
var top=opt.chkTopOfPageMode && !startingUp;
var bottom=opt.chkBottomOfPageMode && !startingUp;
if (single) {
story.forEachTiddler(function(tid,elem) {
// skip current tiddler and, optionally, tiddlers that are folded.
if ( tid==title
|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
return;
// if a tiddler is being edited, ask before closing
if (elem.getAttribute("dirty")=="true") {
if (opt.chkSinglePageKeepEditedTiddlers) return;
// if tiddler to be displayed is already shown, then leave active tiddler editor as is
// (occurs when switching between view and edit modes)
if (tiddlerElem) return;
// otherwise, ask for permission
var msg="'"+tid+"' is currently being edited.\n\n";
msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
if (!confirm(msg)) return; else story.saveTiddler(tid);
}
story.closeTiddler(tid);
});
}
else if (top)
arguments[0]=null;
else if (bottom)
arguments[0]="bottom";
if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
config.lastURL = window.location.hash;
document.title = wikifyPlain("SiteTitle") + " - " + title;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
var isTopTiddler=(tiddlerElem.previousSibling==null);
if (!isTopTiddler && (single || top))
tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
else if (bottom)
tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
} else
this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
var tiddlerElem=story.getTiddler(title);
if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
// scroll to top of page or top of tiddler
var isTopTiddler=(tiddlerElem.previousSibling==null);
var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
// if animating, defer scroll until after animation completes
var delay=opt.chkAnimate?config.animDuration+10:0;
setTimeout("window.scrollTo(0,"+yPos+")",delay);
}
}
if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
// suspend single/top/bottom modes when showing multiple tiddlers
var opt=config.options;
var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
this.SPM_coreDisplayTiddlers.apply(this,arguments);
opt.chkBottomOfPageMode=saveBPM;
opt.chkTopOfPageMode=saveTPM;
opt.chkSinglePageMode=saveSPM;
}
//}}}
a ''T''iddlyWiki ''V''iew mode ''E''ditor
//{{{
body {-webkit-text-size-adjust:95%;}
#mainMenu {
width: 10%;
text-align:left;
padding:0.5em;
margin:0.5em;
font-size:1em;
}
#displayArea{
margin-left: 13%;
margin-right: 16%;
margin-top: 0;
}
#messageArea{
}
#tiddlerDisplay{
}
#sidebar {
width: 16%;
left:84%;
}
#sidebarOptions {
}
#sidebarTabs {
}
.editor textarea {
font-family: 'times';
}
.viewer {
font-family: 'times';
text-align:left;
}
.viewer h1, h2, h3, h4, h5 {
color: #014;
}
.viewer div.sliderPanel {
background-color: rgb(250,250,250);
}
.viewer .smaller {
font-size: 0.75em;
line-height: 1em;
}
.viewer .larger {
font-size: 1.25em;
line-height: 1.5em;
}
.viewer .Title {
text-align: center;
font-size: 1.5em;
font-weight: bold;
padding-left: 5%;
padding-right: 5%;
}
.viewer .SubTitle {
text-align: center;
font-size: 1.2em;
padding-left: 8%;
padding-right: 8%;
}
.viewer .Author {
font-size: 1.1em;
text-align: center;
padding-left: 5%;
padding-right: 5%;
}
.viewer .Affiliation, .viewer .Address {
list-style-position:inside;
font-size: 1.1em;
font-style: italic;
text-align: center;
padding-left: 5%;
padding-right: 5%;
}
.viewer .Abstract {
font-size: 1em;
padding-left: 10%;
padding-right: 10%;
text-align: justify;
text-justify: newspaper;
}
.viewer .Section {
font-size: 1.2em;
font-weight: bold;
text-transform: uppercase;
text-align: justify;
text-justify: newspaper;
}
.viewer .Paragraph {
text-indent: 1em;
text-align: justify;
text-justify: newspaper;
}
.viewer .divIndent {
margin-left: 4em;
}
.viewer .divNarrow {
margin-left: 4em;
margin-right: 4em;
}
.viewer .divIndentFirst {
text-indent: 2em;
}
.viewer .divCenter{
text-align: center;
}
.viewer .divRight {
text-align: right;
}
.oddRow {
background-color: #eee;
}
.evenRow {
background-color: #fff;
}
.FrameHalfLeft {
padding: 2px;
margin: 0px 10px 10px 0px;
width: 45.5%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: left;
text-align: left;
}
.FrameHalfRight {
padding: 2px;
margin: 0px 0px 10px 10px;
width: 45.5%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: right;
text-align: right;
}
.frame-half-left {
padding: 2px;
margin: 0px 10px 10px 0px;
width: 45.5%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: left;
text-align: left;
}
.frame-half-right {
padding: 2px;
margin: 0px 0px 10px 10px;
width: 45.5%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: right;
text-align: right;
}
.frame-small-left {
padding: 2px;
margin: 0px 10px 10px 0px;
width: 29.5%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: left;
text-align: left;
}
.frame-small-right {
padding: 2px;
margin: 0px 0px 10px 10px;
width: 29%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: right;
text-align: right;
}
.frame-major {
padding: 2px;
margin: 10px;
width: 90%;
background-color: rgb(200, 200, 200);
border-style: groove;
font-size: 70%;
color: blue;
float: none;
text-align: center;
}
.imageSpace {
margin: 0 0 2px 0;
background-color:rgb(240, 240, 240);
text-align: center;
border-style: groove;
}
.image-space {
margin: 0 0 2px 0;
background-color:rgb(240, 240, 240);
text-align: center;
border-style: groove;
}
.framed {
width: 86%;
margin: 0 0 0 0;
}
.cut-in {
padding: 20px;
border-width: 1px;
border-style: solid none solid none;"
}
.narrow {
padding: 0 15% 0 15%;
*padding: 0 9% 0 9%;
}
.narrower {
padding: 0 20% 0 20%;
*padding: 0 12% 0 12%;
}
.example{
background-color:black;
color:white;
font-size:0.7em;
}
.expression{
background-color:#555;
color:white;
font-size:0.7em;
}
.phonetic {
font-size:0.55em;
}
.noun {
background-color:black;
color:white;
}
//}}}
/***
|''Name''|SyntaxHighlighterPlugin3|
|''Description''|Enables syntax highlighting|
|''Author''|PMario|
|''Version''|0.2.1|
|''Status''|''beta''|
|''Source''|http://syntaxhighlighter.tiddlyspace.com/#SyntaxHighlighterPlugin3|
|''License''|[[BSD|http://www.opensource.org/licenses/bsd-license.php]]|
|''CoreVersion''|2.5.0|
|''Requires''|ShCore.js|
|''Keywords''|syntax highlighting color code|
!Documentation
*see: [[SyntaxHighlighterPlugin3Info]]
!Description
Enables syntax highlighting for <pre> and <code> blocks. Adds a new formatter for {{{<code class='brush:???'>}}}
!Usage
!!!!StyleSheet
<<<
*add this to your StyleSheet
{{{
[[ShCore.css]]
[[ShThemeDefault.css]]
}}}
<<<
!!!!Macro
<<<
*The macro is only needed if you have inline html blocks. see: [[SyntaxHighlighterPlugin3Info]]
<<<
!!!!ViewTemplate
<<<
*Same as macro, but will be executed automatically for every tiddler. see: [[SyntaxHighlighterPlugin3Info]]
<<<
!!!!Parameters
<<<
{{{<<highlightSyntax [tagName]>> }}}
*will render all blocks, with any defined tag name. eg: tagName = code.
*[tagName] is optional. Default is "pre".
<<<
!!!!Configuration options
<<<
Guess syntax: <<option chkGuessSyntax>> .. If activated, ~TiddlyWiky <pre> blocks will be rendered according to there block braces. see [[SyntaxHighlighterPlugin3Info]]
Expert mode: <<option chkExpertSyntax>> .. If activated, additional values below will be used. see [[SyntaxHighlighterPlugin3Info]]
{{{ {{{ }}} txtShText: <<option txtShText>> eg: 'brush:text tab-size:4 + options'
{{{ /*{{{* / }}} txtShCss: <<option txtShCss>> eg: 'brush:css + options'
{{{ //{{{ }}} txtShPlugin: <<option txtShPlugin>> 'brush:js + options'
{{{ <!--{{{-->> }}} txtShXml: <<option txtShXml>> 'brush:xml + options'
Additional options can be found at: [[SyntaxHighlighter homepage|http://alexgorbatchev.com/SyntaxHighlighter/manual/configuration/]]
<<<
!!!!Revision History
<<<
*V 0.2.1 2011-08-01
**fix wrong error text
*V 0.2.0 2010-08-22
**New formatter for {{{<code class='brush:???'>}}} is available now
**expert mode uses config options now
<<<
!!!!ToDo
<<<
*
<<<
!!!Code
***/
//{{{
version.extensions.SyntaxHighlighterPlugin3 = {major: 0, minor: 2, revision: 1, date: new Date(2011,8,1)};
(function($) {
if(!window.SyntaxHighlighter) {
throw "Missing dependency: ShCore.js (check brushes too)";
}
config.macros.highlightSyntax = {
getElementsByClass: function (searchClass,node,tag) {
var classElements = [];
if ( node == null ) node = document;
if ( tag == null ) tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp("(^|\\s)"+searchClass+"(:|\\s|$)");
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
},
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
// the configured tagName can be temporarily overwritten by the macro.
var tagName = params[0] || SyntaxHighlighter.config.tagName;
var arr = this.getElementsByClass('brush', story.findContainingTiddler(place), tagName);
for (i=0; i<arr.length; i++) {
SyntaxHighlighter.highlight(null, arr[i]);
}
} // handler
};
})(jQuery);
//}}}
/***
!!!!!New formatter for {{{<code class='brush:??'>}}}
***/
//{{{
config.formatters.push({
name: "highlightSyntax",
match: "^<code[\\s]+[^>]+>\\n",
element: "pre",
handler: function(w)
{
this.lookaheadRegExp = /<code[\s]+class.*=.*["'](.*)["'].*>\n((?:^[^\n]*\n)+?)(^<\/code>$\n?)/img;
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var options = lookaheadMatch[1];
var text = lookaheadMatch[2];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
var element = createTiddlyElement(w.output,this.element,null,options,text);
SyntaxHighlighter.highlight(null, element);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
});
//}}}
/***
!!!!!Add class attribute to pre, if defined
***/
//{{{
(function(formatters) { //# set up alias
var helper = {};
helper.enclosedTextHelper = function(w){
var attr;
var co = config.options;
var expert = (co.chkExpertSyntax != undefined)? co.chkExpertSyntax : false;
var guess = (co.chkGuessSyntax != undefined)? co.chkGuessSyntax : true;
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
switch(w.matchText) {
case "{{{\n": // text
attr = (expert) ? (co.txtShText) ? (co.txtShText) : 'brush:text' : 'brush:text' ;
break;
case "/*{{{*/\n": // CSS
attr = (expert) ? (co.txtShCss) ? (co.txtShCss) : 'brush:css' : 'brush:css';
break;
case "//{{{\n": // plugin
attr = (expert) ? (co.txtShPlugin) ? (co.txtShPlugin) : 'brush:js' : 'brush:js';
break;
case "<!--{{{-->\n": //template
attr = (expert) ? (co.txtShXml) ? (co.txtShXml) : 'brush:xml' : 'brush:xml';
break;
}
var element = createTiddlyElement(w.output,this.element,null,attr,text);
if (guess || expert) SyntaxHighlighter.highlight(null, element);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
};
// merge the new helper function into formatterHelpers.
merge(config.formatterHelpers, helper);
})(config.formatters); //# end of alias
//}}}
chktwveCoreClickAway: true
chktwveCoreConfirmToDelete: true
chktwveCoreEditWrappers: false
chktwveCoreEnabled: true
chktwveCoreManualSave: true
chktwveCoreManualUpload: true
chktwveCorePreview: true
chktwveCoreShowFocus: true
chktwveExtraCyclickNavi: false
chktwveExtraIncludeSubs: true
chktwveExtraInline: true
chktwveExtraLocateChar: false
chktwveExtraMathAutoNumber: true
chktwveExtraNoClick: false
chktwveTableEditAll: true
chktwveTableEnabled: true
chktwveTableIncludeCSS: true
chktwveTableMultiLine: true
chktwveTableTranspose: true
chktwveTcalcAllTables: true
chktwveTcalcDebugMode: false
chktwveTcalcEnabled: true
chktwveTcalcInTextCalc: true
chktwveTcalcThousandSeparated: false
txttwveCoreMinEditWidth: 6
txttwveCorePreviewCaret: %7C
txttwveCorePreviewHeight: 15
txttwveExtraPreviewOpacity: 1.00
txttwveTableFixCols: 0
txttwveTableFixRows: 0
txttwveTableMaxHeight: 700px
txttwveTableMaxWidth: 100%25
txttwveTableMinCellWidth: 2
txttwveTcalcDecimalMark: .
txttwveTcalcThousandSeparator: %2C
/***
!! Note
* @@color:red;This is a development snapshot which may or may not function properly.@@
|editable|k
|''Name:''|~TWtcalc|
|''Description:''|A simple spreadsheet for ~TiddlyWiki|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twtable.tiddlyspace.com/#TWtcalc.min <br>* (regular) http://twtable.tiddlyspace.com/#TWtcalc |
|''Type:''|plugin|
|''Version:''|1.0.10|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2013/06/02 released 1.0.0<br>2012/10/19 released 0.7.0<br>2012/09/04 started from ~TableCalculator 0.6.14|
|''License:''|Same as ~TiddlyWiki|
|''Core Version:''|2.6.5|
|''Needs to have:''|~TWtid|
!!Features
* Calculate numeric values in table cells.
* Cell references follow the OpenOffice.calc/Excel syntax.
** Column index starts from {{{A}}} and ends at {{{ZZ}}}.
** Row index starts from 0 and has virtually no upper limit.
* ''Auto adjustment of cell references upon''
** __insertion/deletion of rows/columns__;
** __sorting with SortableGridPlugin or TableSortingPlugin__.
*** Block references such as {{{C4:H10}}} are auto-adjusted only upon insertion/deletion of rows/columns, NOT upon sorting.
* Support several forms of decimal mark and thousands separation.
* Support user-defined functions written in Javascript.
** To define your own functions, see [[TableCalculator--Example--Defining your own functions]] or [[External link of that tiddler|http://twtable.tiddlyspace.com/#%5B%5BTableCalculator--Example--Defining%20your%20own%20functions%5D%5D]] for details.
** Several pre-defined functions are listed in section {{{Usage}}} below.
!!Usage
* Add class {{{spreadsheet}}} or check option {{{chkTWtcalcAllTables}}} to allow calculations in table content.
* At the cell to store calculation results, starts with an equal sign {{{=}}}, as in OpenOffice.calc or Excel, followed by cell references/Javascript expressions/user-defined functions for calculation. For example,
*** {{{=Math.sqrt(3)}}} and {{{=Math.cos(Math.PI)}}} are valid Javascript statements;
*** {{{=A3+B4}}} and {{{=sum(A6:D10)}}} are OpenOffice.calc/Excel-like expressions.
*** See below for a brief list of user-defined functions.
* @@color:blue;Cells for calculation can be specified in one of the following formats:@@
** {{{func(A3:H6)}}}: calculate over a block of consecutive cells
** {{{func(B3, H5, AC9,...)}}}: calculate over separated individual cells
** {{{func(AA3:CD5, B5, Z7...)}}}: a mixture of the two cases
* Use the following options to specify the decimal mark and thousands separation.
** Option txtTWtcalcDecimalMark specifies the default decimal mark.
** Option txtTWtcalcThousandSeparator specifies the default thousands separator.
** Option chkTWtcalcThousandSeparated toggles application of thousands separation.
* Pre-defined functions now include:
** sum
*** calculates the sum of cells
** product
*** calculates the product of cells
** average/mean
*** calculates the average value of cells
** count
*** counts the number of numeric cells
*** supports conditional counting, see revision
**** Syntax: {{{=COUNT([condition], cell references)}}}
***** condition is optional and if given must be quoted with single or double quotes
***** use the percentage symbol {{{%}}} as a placeholder for the values to test
**** Example, {{{=COUNT('%>60 && %<70', A1:A90)}}} returns the number of numerical values greater than 60 and less than 70 among cells between A1 and A90, inclusively.
** counta
*** counts the number of non-empty cells
** concat
*** joins the contents of cells
** isnumeric
*** tests whether a cell contains a numerical expression
** stderr/stdev
*** calculates the standard deviation of a set of numbers
** today()/now()
*** Current date and time. See Javascript for references.
** days(date1, date2, num_dec)
*** Number of days from date1 to date2. The 3rd argement num_dec specifies the number of digits after decimal point.
** round(value, num_dec)
*** Returns the rounded result of value, up to num_dec digits after decimal point.
** random(factor)
*** Returns a random number between 0 and factor, if factor is given. Otherwise returns a random number between 0 and 1.
** column/col
*** Returns the column index (number). Without an argument this function returns the index of the current column, with a cell reference as its argument, such as COLUMN(AX3)/COL(AX3), it returns the column index of the cell.
** columna/cola
*** Returns the column reference (letter). Without an argument this function returns the reference of the current column, with a cell reference as its argument, such as COLUMN(AX3)/COL(AX3), it returns the column reference of the cell.
** stderr/stdev
*** Calculates the standard deviation of a set of numbers. @@color:red;This function is not yet fully tested.@@
** if(condition, statement_if_true, statement_if_false)
*** Works the same way as the IF() function in MS Excel, except that the statements can include wiki style text.
**** For example, IF ($G1>=F1, color:blue;$~G1-F1, color:red;$~G1-F1) results in a @@color:blue;positive@@ or a @@color:red;negative@@ value of {{{|$G1-F1|}}}.
!!Options
Look for [[TWtcalcOptions]] in the system Options panel.
!!Example
''See [[TWtcalc.example]] tiddler or [[its external link|http://twtable.tiddlyspace.com/#TWtcalc.example]] for example(s).''
!!ToDo
!!Revision history
!!!! 2014/01/05 [1.0.10]
* Bug fixes
** for table calculations in the previewer
!!!! 2013/11/26 [1.0.9]
* Bug fixes
** for partial re-calculation upon cell changes.
*** In 1.0.7 {{{TWtcalc.re_calculate_table()}}} function was modified to only re-calculate affected cells (partial recalculation), that is, cells referring (directly and indirectly) to the changed cells, but accidentally ignored the changed cells themselves.
** to prevent calculating a non-existing table.
!!!! 2013/11/17 [1.0.8]
* Changed the mechanism of handling the floating menu and made it scrollable.
!!!! 2013/11/08 [1.0.7]
* Added a system menu to the right of tiddler title, providing quick access to the plugin options.
* Bug fix for partial re-calculation after changes are made.
** Partial re-calculation, introduced in v1.0.5, is to re-calculate not the whole table but only the cells affected by the last change. It increases performance notably for large tables.
** Previous version re-calculated only cells __directly__ affected by the last change, i.e., cells that explicitly refer to the changed cells, and ignored those indirectly affected cells (cells that implicitly refer to the changed cells). This version fixed this bug.
!!!! 2013/09/29 [1.0.6]
* Added codes to handle {{{ROW EXCHANGED}}} and {{{COL EXCHANGED}}} events in the event handler, in response to the new feature -- moving rows up/down or columns to the left/right -- in TWted.
** ''Note:'' //Beware when moving rows/columns containing TWtcalc block formulas such as {{{SUM(A2:B4)}}} or {{{AVG(C5:H7)}}}, which are ''not'' fully taken care of yet.//
!!!! 2013/07/30 [1.0.5]
* Re-calculates only the affected cells when one is changed.
** Previously the TWtcalc re-calculated the whole table each time a cell was changed.
* Bug fixes for table resizing and cell width adjustment (to ensure a minimum width) upon recalculation
* Bug fix for calculations over consecutive cells.
!!!! 2013/07/05 [1.0.4]
* Bug fixes for table sorting with TableSortingPlugin and SortableGridPlugin
!!!! 2013/06/28 [1.0.3]
* Bug fixes.
!!!! 2013/06/10 [1.0.2]
* Some more bug fixes.
!!!! 2013/06/03 [1.0.1]
* Some bug fixes.
!!!! 2013/06/02 [1.0.0]
* Adapted to use the newly introduced class TWElement.
* Adapted to the changes in core behavior (from line-based to character-based manner).
!!!! 2013/04/15 [0.8.1]
* Bug fixes for options panel.
!!!! 2013/04/14 [0.8.0]
* Auto-adjusts cell references upon pasting rows/columns.
** When a row/column is pasted as a whole, all formulas in the cells of the pasted row/column are adjusted accordingly.
* Auto-adjusts cell references upon cut-and-pasting a single cell.
** When a single cell is cut and pasted into another, not only its formula gets adjusted, but also others referring to the cut cell get adjusted accordingly.
* Options tiddler now available in the system Options panel.
* Supports calculations in preview.
* Bug fixes for auto-adjustment in cell references upon copy-and-pasting a single cell.
* Bug fixes for auto-recalculation.
!!!! 2013/02/09 [0.7.7]
* Added a very primitive expression checker to check simple arithmetic expressions.
* Added option chkTWtcalcEnabled to enable/disable TWtcalc. Default to true.
* Changed default behavior of calculation from all tables to only tables with class {{{spreadsheet}}}.
* Added option chkTWtcalcAllTables to allow calculations on all tables, regardless of their class names. Default to false.
* Bug fixes on handling numeric expressions. Thanks to Ton for showing the bug.
!!!! 2012/11/23 [0.7.6]
* Bug fixes for working with {{{TableSortingPlugin}}} and {{{SortableGridPlug}}}.
* Bug fixes for cell title with TWtcalc defined functions.
* Some bug fixes for mixing consecutive and individual cells in a TWtcalc defined function's arguments.
!!!! 2012/11/17 [0.7.5]
* Bug fixes for expressions with CSS style text.
!!!! 2012/11/09 [0.7.4]
* Tests validity of tiddlers before some actions.
!!!! 2012/11/02 [0.7.3]
* Defined {{{ROWS()}}} and {{{COLS()/COLUMNS()}}} functions, which counts the number of rows/columns within a range of cells.
* Bugs fixed for {{{COLUMN()/COL()}}} and {{{COLUMNA()/COLA()}}} functions.
** These bugs were introduced in codes supporting absolute references.
* Bug fixed with {{{ROUND()}}} function.
** It was returning {{{NaN}}} when the argument results in 0.
!!!! 2012/10/26 [0.7.2]
* Defined {{{IF()}}} and modified {{{COUNT()}}} functions, see http://twtable.tiddlyspace.com/#TWtcalc--Example--IF_and_COUNT for example.
** The {{{IF()}}} function works the same way as the {{{IF()}}} function in MS Excel, except that CSS style statements can be included.
*** Syntax: {{{IF(condition, statement_if_true, statement_if_false)}}}
*** Example: IF ($G1>=F1, color:blue;$G1-F1, color:red;$G1-F1) results in a @@color:blue;positive@@ or a @@color:red;negative@@ value of {{{|$G1-F1|}}}.
** The {{{COUNT()}}} function now supports conditional counting. (Note the syntax is not compatible with Excell's countif() function.)
*** Syntax: {{{=COUNT([condition], cell references)}}}
**** condition is optional and if given must be quoted with single or double quotes
**** use the percentage symbol {{{%}}} as a placeholder for the values to test
*** Example, {{{=COUNT('%>60 && %<70', A1:A90)}}} returns the number of numerical values greater than 60 and less than 70 among cells between A1 and A90, inclusively.
* Bug fix for numeric calculations including empty cells
** Previously an error was generated if empty cells are included in numeric calculations. Now those cells are ignored so no more errors in such cases.
* Bug fixes with copy-and-pasting
** The auto-adjustment of cell references upon copy-and-pasting was broken, possibly due to the code restructuring, and is now fixed.
!!!! 2012/10/21 [0.7.1]
* Minor modifications to the prepare_table() function.
!!!! 2012/10/19 [0.7.0]
* Separated from TableCalculator.
* Restructured to better fit the separated code structure.
* Bug fixes for absolute reference support.
!!!! 2012/09/03 [1.3.0]
* Started separating codes from TableCalculator 0.6.14.
!!!! For earlier history please see [[TableCalculator]] or [[its external link|http://twtable.tiddlyspace.com/#TableCalculator]].
!!Code
***/
// //Macro definitions
//{{{
version.extensions.TWtcalc= {major: 1, minor: 0, revision: 10, date: new Date('2014/01/05')};
// Macro for initialization
config.macros.TWtcalc = {
init : function () {
if (config.options.chkTWtcalcEnabled===undefined)
config.options.chkTWtcalcEnabled = true;
if (config.options.chkTWtcalcAllTables===undefined)
config.options.chkTWtcalcAllTables = false;
if (config.options.chkTWtcalcThousandSeparated===undefined)
config.options.chkTWtcalcThousandSeparated =
(config.options.chkTCalcThousandSeparated===undefined?false:
config.options.chkTCalcThousandSeparated);
if (config.options.txtTWtcalcThousandSeparator===undefined)
config.options.txtTWtcalcThousandSeparator =
(config.options.txtTCalcThousandSeparator===undefined?',':
config.options.txtTCalcThousandSeparator);
if (config.options.txtTWtcalcDecimalMark===undefined)
config.options.txtTWtcalcDecimalMark =
(config.options.txtTCalcDecimalMark===undefined?'.':
config.options.txtTCalcDecimalMark);
if (config.options.chkTWtcalcDebugMode===undefined)
config.options.chkTWtcalcDebugMode =
(config.options.chkTCalcDebugMode===undefined?false:
config.options.chkTCalcDebugMode);
merge ( config.optionsDesc, {
chkTWtcalcEnabled:'Enable TWtcalc',
chkTWtcalcAllTables:'Calculate all tables. Otherwise only calculate tables with class "spreadsheet"',
chkTWtcalcThousandSeparated:'Apply thousands separation on numerical results. Default is false.',
txtTWtcalcThousandSeparator:'Thousand separator. Default is comma (,).',
txtTWtcalcDecimalMark:'Decimal mark. Default is period (.).',
chkTWtcalcDebugMode:'Enter debug mode to show error/exception messages. Default is false.'
});
TWtid.col_ref = TWtcalc.col_ref;
TWtcalc.pre_OfficialExample = TWtid.OfficialExample;
TWtid.OfficialExample = TWtcalc.OfficialExample;
TWtcalc.pre_prepare_table = TWtid.prepare_table;
TWtid.prepare_table = TWtcalc.prepare_table;
TWtcalc.pre_start_sorting = TWtid.start_sorting;
TWtid.start_sorting = TWtcalc.start_sorting;
TWtcalc.pre_element_changed = TWtid.element_changed;
TWtid.element_changed = TWtcalc.element_changed;
TWtcalc.pre_paste_in = TWted.paste_in;
TWted.paste_in = TWtcalc.paste_in;
TWtcalc.pre_copy_and_cut = TWted.copy_and_cut;
TWted.copy_and_cut = TWtcalc.copy_and_cut;
TWtcalc.pre_wikify_element = TWtid.wikify_element;
TWtid.wikify_element = TWtcalc.wikify_element;
TWtcalc.pre_system_menu_item = TWtid.system_menu_item;
TWtid.system_menu_item = TWtcalc.system_menu_item;
var txt = config.shadowTiddlers['OptionsPanel'];
var tidopt = 'TWtidOptions';
var p = txt.indexOf(tidopt);
if ( p >= 0 ) {
p += tidopt.length+1;
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'TWtcalcOptions\n'
+txt.substring(p);
} else {
p = txt.indexOf('----');
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'----\n'
+'TWtcalcOptions\n'
+txt.substring(p);
}
merge(config.shadowTiddlers,{
'TWtcalcOptions':'<<TWtcalcOptions>>'
});
}
};
// Macro for option settings.
config.macros.TWtcalcOptions = {
order : {
chkTWtcalcEnabled:0,
chkTWtcalcAllTables:1,
chkTWtcalcThousandSeparated:2,
txtTWtcalcThousandSeparator:3,
txtTWtcalcDecimalMark:4,
chkTWtcalcDebugMode:5
},
handler : function(place,macroName,params,wikifier,paramString) {
// Collect all the TWtid options for users to play with.
config.macros.TWtidOptions.show_options_table(
place
,'TWtcalc Options'
,'TWtcalc'
,config.macros.TWtcalcOptions.order
);
}
};
//}}}
// //TWtcalc
//{{{
TWtcalc = {
$cur_table : null,
col_ref : function ( col ) {
return TWtcalc.index_to_reference_col( col );
},
pre_start_sorting : null,
start_sorting : function(twelem) {
return (TWtcalc.block_update =
TWtcalc.pre_start_sorting.apply(this,arguments));
},
//}}}
/***
!! element_changed
***/
//{{{
pre_element_changed : null,
element_changed : function (twelem,param) {
if ( twelem.$dom.is('table') ) {
switch ( param.what ) {
case 'MODIFIED' :
TWtcalc.re_calculate_table(
twelem,param.text,[param]
);
break;
case 'ROW INSERTED' :
param.text=TWtcalc.inc_cell_reference(
param.text,twelem.start,
twelem.end,param.where,1
);
break;
case 'ROW DELETED' :
param.text=TWtcalc.inc_cell_reference(
param.text,twelem.start,
twelem.end,param.where+1,-1
);
break;
case 'ROW PASTED' :
var start=TWtid.init_pos_obj(twelem.start);
var end=TWtid.init_pos_obj(twelem.end);
start.ndx = TWtid.table_row_starts(
param.text,start,end,param.where
);
end.ndx = TWtid.table_row_ends(
param.text,start.ndx
);
param.text=TWtcalc.inc_cell_reference(
param.text, start, end,
0,(param.where-param.from)
);
break;
case 'COL INSERTED' :
param.text=TWtcalc.inc_cell_reference(
param.text, twelem.start, twelem.end,
null, null, 0, -1, param.where, 1
);
break;
case 'COL DELETED' :
param.text=TWtcalc.inc_cell_reference(
param.text, twelem.start, twelem.end,
null, null, 0, -1, param.where+1, -1
);
break;
case 'COL PASTED' :
param.text=TWtcalc.inc_cell_reference(
param.text, twelem.start,
twelem.end, null, null,
param.where, param.where, 0,
(param.where-param.from)
);
break;
case 'ROW MOVED' :
param.text=TWtcalc.update_cell_reference(
param.text, twelem.start, twelem.end,
param.from, null, param.to, null, true
);
break;
case 'ROW EXCHANGED' :
TWtcalc.block_update = true;
param.text=TWtcalc.update_cell_reference(
param.text, twelem.start, twelem.end,
param.from, null, param.to, null, true
);
param.text=TWtcalc.update_cell_reference(
param.text, twelem.start, twelem.end,
param.to, null, param.from, null, true
);
param.text=TWtcalc.end_block_update
( param.text, twelem.start, twelem.end );
break;
case 'COL EXCHANGED' :
TWtcalc.block_update = true;
param.text=TWtcalc.update_cell_reference(
param.text, twelem.start, twelem.end,
null, param.from, null, param.to, true
);
param.text=TWtcalc.update_cell_reference(
param.text, twelem.start, twelem.end,
null, param.to, null, param.from, true
);
param.text=TWtcalc.end_block_update
( param.text, twelem.start, twelem.end );
break;
case 'SORTED' :
param.text=TWtcalc.end_block_update
( param.text, twelem.start, twelem.end );
TWtcalc.re_calculate_table(twelem,param.text);
break;
}
}
return TWtcalc.pre_element_changed.apply(this,arguments);
},
//}}}
/***
!! end_block_update
***/
//{{{
//Reference adjustment and related functions.
block_update : false,
end_block_update : function ( text, start, end ) {
TWtcalc.block_update = false;
// Unpack the updated cell references.
// All the block-updated cell references were packed into the
// format {A.3} to prevent multiple modification, so we need
// to unpack them for further use.
var cpos = TWtid.all_cells(text,start,end);
var r, c, ctxt, ref, i, pl, pr, pdot;
for ( r=cpos.length-1; r>=0; r-- ) {
for ( c=cpos[r].length-1; c>=0; c-- ) {
ctxt=text.substring(cpos[r][c].open,cpos[r][c].close);
if ( !(ref=ctxt.match(/\{[\$A-Za-z]+\.[\$0-9]+\}/g)) )
continue;
for ( i = 0; i < ref.length; i++ ) {
pl = ctxt.indexOf(ref[i]);
pdot = ctxt.indexOf('.', pl+1);
pr = ctxt.indexOf('}', pl+1);
ctxt = ctxt.substring(0,pl) +
ctxt.substring(pl+1,pdot) +
ctxt.substring(pdot+1,pr) +
ctxt.substring(pr+1);
}
text = text.substring(0,cpos[r][c].open)
+ctxt
+text.substring(cpos[r][c].close);
end.ndx += ctxt.length
-(cpos[r][c].close-cpos[r][c].open);
}
}
return text;
},
//}}}
/***
!! update_cell_reference
***/
//{{{
update_cell_reference : function ( text, start, end,
row_cur, col_cur, row_new, col_new, ignore_abs ) {
// Update cell reference from (row_cur, col_cur) to
// (row_new, col_new). This function is called a table
// row or column index is changed. For example, table row
// index is changing during sorting.
var cpos=TWtid.all_cells(text,start,end);
var r, c, ctxt, chndx, ref;
var cell_changed, ref_changed;
for ( r = cpos.length-1; r >= 0; r-- ) {
for ( c = cpos[r].length-1; c >= 0; c-- ) {
ctxt = text.substring(cpos[r][c].open,cpos[r][c].close);
cell_changed = false;
chndx = TWtcalc.index_of_expression(ctxt);
if (chndx == -1) continue;
chndx++;
while ( chndx < ctxt.length ) {
ref_changed=false;
if( (ref=TWtcalc.cell_reference(ctxt,chndx)) ) {
if ( ctxt.charAt(chndx+ref.length)==':' ) {
// The cell reference is immediately followed
// by a colon, meaning its a block reference.
// Skip it for now.
chndx += ref.length + 1;
ref = TWtcalc.cell_reference(ctxt,chndx);
chndx += ref.length;
} else {
var row = TWtcalc.reference_to_index_row(ref),
col = TWtcalc.reference_to_index_col(ref),
abs_row = TWtcalc.absolute_row(ref),
abs_col = TWtcalc.absolute_col(ref);
// Currently absolute references are not
// changed in any case, but this may not
// be the best choice...
if ( row == row_cur ) {
if ( ignore_abs || !abs_row ) {
row = row_new;
ref_changed = true;
}
}
if ( col == col_cur ) {
if ( ignore_abs || !abs_col) {
col = col_new;
ref_changed = true;
}
}
if ( ref_changed ) {
var newref;
if ( TWtcalc.block_update )
newref='{'
+TWtcalc.index_to_reference_col(
col,abs_col
)+'.'
+TWtcalc.index_to_reference_row(
row,abs_row
)+'}';
else
newref=TWtcalc.index_to_reference_col(
col,abs_col
)+TWtcalc.index_to_reference_row(
row,abs_row
);
ctxt = ctxt.substring(0,chndx) +
newref +
ctxt.substring(chndx+ref.length);
ref = newref; row_changed = true;
cell_changed = true;
}
chndx += ref.length;
}
}
else chndx++;
}
if ( cell_changed ) {
text=text.substring(0,cpos[r][c].open)
+ctxt
+text.substring(cpos[r][c].close);
end.ndx += ctxt.length
-(cpos[r][c].close-cpos[r][c].open);
}
}
}
return text;
},
//}}}
/***
!! inc_cell_reference
***/
//{{{
inc_cell_reference : function(text,start,end,row,dr,c0,cl,col,dc) {
// Increase cell reference by dr and dc for
// row_ndx >= row and col_ndx >= col. This function is Used
// in cases of row/column insertion/deletion.
var cpos = TWtid.all_cells(text,start,end);
var r, c, chndx, ref, ctxt;
if ( !row ) row = 0; if ( !dr ) dr = 0;
if ( !c0 ) c0 = 0; if ( !col ) col = 0; if ( !dc) dc = 0;
var rcl;
for ( r=cpos.length-1; r>=0; r-- ) {
if ( typeof cl != 'number' || cl < 0
|| cl >= cpos[r].length ) {
rcl = cpos[r].length-1;
} else rcl = cl;
for ( c = rcl; c >= c0; c-- ) {
ctxt = text.substring(cpos[r][c].open,cpos[r][c].close);
chndx = this.index_of_expression(ctxt);
if (chndx == -1) continue;
chndx++;
while ( chndx < ctxt.length ) {
if((ref=this.cell_reference(ctxt,chndx))) {
var row_org = this.reference_to_index_row(ref),
col_org = this.reference_to_index_col(ref),
abs_row = this.absolute_row(ref),
abs_col = this.absolute_col(ref);
if ( row_org >= row && col_org >= col ) {
var newref =
this.index_to_reference_col
(col_org+(abs_col?0:dc), abs_col)
+(abs_row?('$'+row_org):(row_org+dr));
ctxt = ctxt.substring(0,chndx)
+newref
+ctxt.substring(chndx+ref.length);
ref = newref;
}
chndx += ref.length;
} else chndx++;
}
text = text.substring(0,cpos[r][c].open)
+ctxt
+text.substring(cpos[r][c].close);
}
}
return text;
},
//}}}
/***
!!
***/
//{{{
copied_from : null,
cut_from : null,
pre_copy_and_cut : null,
copy_and_cut : function (ev, twelem, cut) {
if ( twelem.$dom.is('th,td') ) {
TWtcalc.copied_from = twelem.$dom;
TWtcalc.cut_from = cut ? twelem.$dom : null;
}
TWtcalc.pre_copy_and_cut.apply(this,arguments);
},
//}}}
/***
!! paste_in
***/
//{{{
pre_paste_in : null,
paste_in : function (ev,$ta,$cell) {
TWtcalc.pre_paste_in.apply(this,arguments);
// Auto adjustment of cell reference upon copy-and-pasting
// within the same table.
var $from_cell = TWtcalc.copied_from;
if ( $from_cell ) {
// Something pasted from another cell in the same table.
var title_from = $from_cell[0].title.split('\n');
var r_from = TWtcalc.reference_to_index_row(title_from[0]);
var c_from = TWtcalc.reference_to_index_col(title_from[0]);
var title_to = $cell[0].title.split('\n');
var r_to = TWtcalc.reference_to_index_row(title_to[0]);
var c_to = TWtcalc.reference_to_index_col(title_to[0]);
if ( title_from.length > 1 ) {
// The source cell contains an expression for calculations.
// Obtain the paste-in text and adjust cell reference
// if any.
var txt = $ta.val(), chndx=0, ref, newref,
r, c, abs_r, abs_c;
while ( chndx < txt.length ) {
if ( (ref=TWtcalc.cell_reference(txt,chndx)) ) {
r = TWtcalc.reference_to_index_row(ref);
abs_r = TWtcalc.absolute_row(ref);
c = TWtcalc.reference_to_index_col(ref);
abs_c = TWtcalc.absolute_col(ref);
if (!abs_r) r = r_to + (r-r_from);
if (!abs_c) c = c_to + (c-c_from);
newref = c>=0 ?
TWtcalc.index_to_reference_col(c,abs_c):
'{'+c+'}';
newref += r>=0 ? ((abs_r?'$':'')+r) : '{'+r+'}';
txt=txt.substring(0,chndx)+
newref+
txt.substring(chndx+ref.length);
chndx += newref.length;
} else chndx++;
}
$ta.val(txt);
} else {
// The source cell contains regular wikitext.
TWtcalc.copied_from = null;
}
if ( TWtcalc.cut_from ) {
// The last cell was being cut and then pasted
// to this one.
var $table=$cell.closest('table');
var twtable = new TWElement($table);
var text = twtable.tiddler.text;
text = TWtcalc.update_cell_reference(
text,twtable.start,twtable.end,
r_from,c_from,r_to,c_to
);
TWtid.save_text(twtable.tiddler,text);
}
TWted.preview_editbox($ta);
}
},
//}}}
/***
!! wikify_element
***/
//{{{
pre_wikify_element : null,
wikify_element : function($elem) {
TWtcalc.pre_wikify_element.apply(this,arguments);
if ( TWtid.is_wrapper($elem) ) {
//TWtid.prepare_elements(new TWElement($elem));
$elem.find('table').each(function(n,table){
TWtcalc.calculate_table(jQuery(table));
});
}
},
//}}}
/***
!! re_calculate_table
***/
//{{{
re_calculate_table : function (twtable,text,affected) {
// Recalculate a table represented by twtable.
// Arguments:
// twtable -- (Required) A TWElement object representing the
// table to re-calculate.
// text -- (Optional) The tiddler text (containing
// that table).
// Normally twtable already contains the tiddler
// text so you can leave the 2nd argument undefined.
// However, in cases where the modified text
// hasn't been saved back to twtable.tiddler.text,
// the modified text can be passed in here.
// affected -- an array containing the (row, col) indices of
// all the affected cells. It is usually fine to leave
// this argument undefined. If it is given then TWtcalc
// re-calculates only cells referring to one of these
// affected cells, directly and indirectly.
if ( ! (config.options.chkTWtcalcEnabled
|| config.options.chkTWtcalcAllTables
|| TWtid.hasClass(twtable,'spreadsheet')) )
return false;
function ref_test_object (r, c) {
var refr=TWtcalc.index_to_reference_row(r);
var refc=TWtcalc.index_to_reference_col(c);
return {
ndx:{row:r, col:c},
rel:(refc+refr),
abs:(refc+'$'+refr)
};
}
var $table = twtable.$dom;
if ( ! text ) text = twtable.tiddler.text;
var i, ref = null;
if ( affected ) {
// Convert the (row, col) indices to reference strings.
ref = new Array();
for ( i = 0; i < affected.length; i++ ) {
ref[i] = ref_test_object(
affected[i].row,
affected[i].col
);
}
}
var cpos=TWtid.all_cells(text,twtable.start,twtable.end);
var peq, ctxt;
this.$cur_table = $table;
var $tr = $table.find('tr');
var $trc = new Array();
$tr.each(function(r,tr){
$trc[r] = jQuery(tr).find('th,td');
});
var count;
do {
count = 0;
for ( var r = 0; r < $tr.size(); r++ ) {
$trc[r].each(function(c,cell){
var $cell = jQuery(cell);
c = TWtid.cell_col_index($cell);
ctxt = text.substring(cpos[r][c].open,cpos[r][c].close);
if(ctxt && (peq=TWtcalc.index_of_expression(ctxt))>-1) {
// If the cell text contains expression
if ( ref ) {
// If rel, so as affected, is not null
for ( i = 0; i < ref.length; i++ ) {
// If this cell is one of the affected cells,
// or its expression contains an affected
// cell, break the loop for re-calculation
if (ref[i].ndx.row==r && ref[i].ndx.col==c)
break;
if (ctxt.indexOf(ref[i].rel) > -1) break;
if (ctxt.indexOf(ref[i].abs) > -1) break;
// Check for consecutive references
}
if ( i == ref.length )
// If the expression contains NO directly
// affected cells, leave it.
return;
// This cell needs re-calculations, push it to
// the end of ref so other cells referring to this
// one would also get re-calculated.
ref.push(ref_test_object(r,c));
count++;
// clear this cell to avoid further
// re-calculation
cpos[r][c].close = cpos[r][c].open;
}
// Re-calculate the cell
TWtcalc.calculate_cell($cell,true,ctxt,peq);
} else {
// This cell does not contain an expression, clear
// it to save time in the later runs (if any).
cpos[r][c].close = cpos[r][c].open;
}
});
}
// If more than one cells got recalculated, we search for
// indirectly affected cells.
} while ( count > 0 );
TWtcalc.$cur_table = null;
TWtid.table_resize(twtable);
return true;
},
//}}}
/***
!! Misc. Functions
***/
//{{{
replace_decimal_mark : function ( txt, oldmark, newmark ) {
var p = txt.indexOf(oldmark);
return p == -1 ? txt :
txt.substring(0,p)+newmark+txt.substring(p+1);
},
isnumeric : function ( txt, update, given_mark, to_sep ) {
// See [[Testing a numeric expression -- isnumeric -- The Crazy Version!]]
// or [[its external link|http://twtable.tiddlyspace.com/#%5B%5BTesting%20a%20numeric%20expression%20--%20isnumeric%20--%20The%20Crazy%20Version!%5D%5D]]
// for details.
txt = txt.trim();
var ndig = 0, ncomma = 0, nperiod = 0,
npm = 0, nspace = 0, ndot = 0, i, ch;
for ( i = 0; i < txt.length; i++ )
if ( /[^0-9]/.test((ch=txt.charAt(i))) )
switch ( ch ) {
case '+' :
case '-' :
if ( i > 0 ) return '';
if ( ++npm > 1 ) return '';
break;
case '.' :
if ( ++nperiod > 1 && ncomma > 1)
return '';
break;
case ',' :
if ( ++ncomma > 1 && nperiod > 1)
return '';
break;
case ' ' :
if ( ++nspace > 1 &&
(ndot>1 || nperiod>1 || ncomma>1) )
return '';
break;
case '·' :
if ( ++ndot > 1 &&
(nspace>1 || nperiod>1 || ncomma>1) )
return '';
break;
default :
return '';
}
else ndig++;
if ( ndig == 0 ) return '';
if ( !update && (ncomma == 0 && nperiod == 0 &&
nspace == 0 && ndot == 0) )
return txt;
var mark = '.', separator = ',',
nmark = nperiod, nsep = ncomma;
if ( nperiod > 1 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( nspace > 0 ) {
if ( (nperiod > 0 && ncomma > 0) ||
(nperiod > 1 || ncomma > 1) ) return '';
separator = ' '; nsep = nspace;
if ( ncomma > 0 ) {
mark = ','; nmark = ncomma;
} else {
mark = '.'; nmark = nperiod;
}
} else if ( ndot > 0 ) {
if ( nperiod > 0 || ncomma > 1 ) return '';
separator = '·'; mark = ',';
nsep = ndot; nmark = ncomma;
} else if ( ncomma == 1 && nperiod == 0 ) {
i = txt.indexOf(',');
if ( i==0 || txt.length-i!=4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( txt.length-i < 4 )
return '';
} else if ( nperiod == 1 && ncomma == 1 ) {
i = txt.indexOf(',') - txt.indexOf('.');
if ( i == 4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( i != -4 )
return '';
}
if ( nsep > 0 && ! config.options.chkTWtcalcThousandSeparated )
return '';
if ( nsep > 1 ) {
var p1 = txt.indexOf(separator), p2;
while( (p2=txt.indexOf(separator,p1+1))>-1 ) {
if ( p2 - p1 != 4 ) return '';
p1 = p2;
}
}
if ( (nmark>0 && mark!=config.options.txtTWtcalcDecimalMark)
|| (nsep>0 &&
separator!=config.options.txtTWtcalcThousandSeparator) )
return '';
var opt_mark = given_mark ? given_mark :
config.options.txtTWtcalcDecimalMark;
if ( nsep == 0 ) {
if ( opt_mark != mark )
txt = this.replace_decimal_mark(txt, mark, opt_mark);
return to_sep ? this.separate_number(txt) : txt;
}
var opt_separator = to_sep ?
config.options.txtTWtcalcThousandSeparator : '';
if ( (mark!=opt_mark) || (separator!=opt_separator) ) {
for ( i = 0; i < txt.length; i++ ) {
ch = txt.charAt(i);
if ( ch == separator ) {
if(separator!=opt_separator)
txt=txt.substring(0,i)+
opt_separator+txt.substring(i+1);
}
else if( ch == mark ) {
if(mark!=opt_mark)
txt=txt.substring(0,i)+
opt_mark+txt.substring(i+1);
}
}
}
return txt;
},
unseparate_number : function ( txt ) {
var txt2 = txt.split(config.options.txtTWtcalcThousandSeparator),
txt3 = '', i;
for ( i = 0; i < txt2.length; i++ )
txt3 += txt2[i];
return txt3;
},
// //The following function was obtained from
// //http://www.mredkj.com/javascript/numberFormat.html
separate_number : function (nStr) {
nStr += '';
x = nStr.split(config.options.txtTWtcalcDecimalMark);
x1 = x[0];
x2 = x.length > 1 ?
config.options.txtTWtcalcDecimalMark + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' +
config.options.txtTWtcalcThousandSeparator + '$2');
}
return x1 + x2;
},
absolute_row : function (ref) {
var p = ref.indexOf('$',1);
return (p > 0 && p < 4);
},
reference_to_index_row : function ( ref ) {
var p = ref.indexOf('$',1);
if ( p > 0 && p < 4 )
// Absolute row reference
return parseInt(ref.substring(p+1));
p = /^[\$]/.test(ref) ? 1 : 0;
// Relative row reference
if ( /[a-zA-Z]/.test(ref.charAt(p+1)) )
return parseInt(ref.substring(p+2));
else
return parseInt(ref.substring(p+1));
},
index_to_reference_row : function ( r, abs ) {
return (abs?'$':'')+r;
},
absolute_col : function (ref) {
return /^[\$]/.test(ref);
},
reference_to_index_col : function ( ref ) {
var p = ref.indexOf('$');
if ( p == 0 ) p++; // Absolute col reference
else p = 0; // Relative col reference
var a = 'A'.charCodeAt();
ref = ref.toUpperCase();
if ( /[A-Z]/.test(ref.charAt(p+1)) ) {
var c1 = ref.charAt(p).charCodeAt()-a+1,
c2 = ref.charAt(p+1).charCodeAt()-a;
return c1*26 + c2;
} else {
return ref.charAt(p).charCodeAt()-a;
}
},
index_to_reference_col : function ( c, abs ) {
var a = 'A'.charCodeAt(),
pre = (abs ? '$' : '');
if ( c < 26 )
return pre+String.fromCharCode(c+a);
else {
var c1 = c/26-1, c2 = c%26;
return pre+String.fromCharCode(c1+a)+
String.fromCharCode(c2+a);
}
},
//}}}
// //TWtcalc defined function handlers.
//{{{
index_of_expression : function ( text ) {
var peq = text.indexOf('=');
if ( peq == 0 || peq == -1 ) return peq;
var start = TWtid.skip_style_text(text);
while ( /[\s]/.test(text.charAt(start)) ) start++;
if ( peq == start ) return peq;
return -1;
},
fn_ndx : -1,
isfunction : function ( txt, start ) {
// Returns the function string fn(arg) if any.
this.fn_ndx = -1; if ( !start ) start = 0;
var txt2 = txt.substring(start).toUpperCase(), pfn, pparenl,
fn_name='', i, j;
for ( i = 0; i < this.fn.length; i++ ) {
if ( typeof this.fn[i].name == 'string' ) {
if ( txt2.startsWith(this.fn[i].name) ) {
fn_name = this.fn[i].name;
}
} else for ( j = 0; j < this.fn[i].name.length; j++ )
if ( txt2.startsWith(this.fn[i].name[j]) ) {
fn_name = this.fn[i].name[j]; break;
}
if ( fn_name ) {
pfn = txt2.indexOf(fn_name)+start;
pparenl = txt.indexOf('(',pfn+1);
if ( pparenl == -1 ) return '';
this.fn_ndx = i;
var pparenr = txt.indexOf(')',pparenl+1);
if (pparenr == -1)
return this.is_userfunction(txt,pfn);
while (pparenl > -1) {
pparenl = txt.indexOf('(',pparenl+1);
if ( pparenl>-1 && pparenl<pparenr ) {
// Inner parentheses. Look again.
pparenl = pparenr;
pparenr = txt.indexOf(')',pparenr+1);
if (pparenr==-1)
return this.is_userfunction(txt,pfn);
}
}
return this.is_userfunction(txt,pfn,pparenr+1);
}
}
},
is_userfunction : function ( txt, start, end ) {
if ( start==0 || txt.charAt(start-1)!='.' )
return txt.substring(start,end);
this.fn_ndx = -1;
},
parse_single_term : function (cell_exp, last_one, start) {
var expression = '', tmp_exp = null;
if (this.fn[this.fn_ndx].parse_single_term) {
tmp_exp = this.fn[this.fn_ndx]
.parse_single_term(cell_exp,start);
if ( tmp_exp ) expression += tmp_exp;
} else {
expression += (tmp_exp=cell_exp);
}
if (!last_one && tmp_exp && this.fn[this.fn_ndx].operator)
expression += this.fn[this.fn_ndx].operator;
return expression;
},
consecutive_cells : function (txt) {
// Extract the indices of the starting (top-left) and ending
// (bottom-right) cells in a consecutive range.
var txt2 = txt.split(':');
// Now txt2[0] is the beginning while txt2[1] is the
// ending cell. Find the (row, col) indices of the
// cells and evaluate the cells in between, inclusively.
c1 = this.reference_to_index_col(txt2[0]),
r1 = this.reference_to_index_row(txt2[0]),
c2 = this.reference_to_index_col(txt2[1]),
r2 = this.reference_to_index_row(txt2[1]);
if ( r2 < r1 ) { r = r1; r1 = r2; r2 = r; }
if ( c2 < c1 ) { c = c1; c1 = c2; c2 = c; }
return {
start: {row:r1, col:c1},
end: {row:r2, col:c2}
};
},
parse_consecutive : function ( txt ) {
// calculate over consecutive cells
// The argument txt should be something like cell1..cell2,
// otherwise the behavior of this function is undefined.
var expression = '', tmp_exp,
r, c, fn_ndx = this.fn_ndx, tmp_exp,
$rows = this.$cur_table.find('tr'), $cells, $tr, $cell,
ndx = TWtcalc.consecutive_cells(txt);
for ( r = ndx.start.row; r <= ndx.end.row; r++ ) {
$tr = $rows.eq(r); if ($tr.size ()==0) continue;
$cells = $tr.find('th,td');
for ( c = ndx.start.col; c <= ndx.end.col; c++ ) {
$cell = $cells.eq(c); if ($cell.size()==0) continue;
tmp_exp = this.evaluate_cell($cell,
!this.fn[this.fn_ndx].no_eval);
this.fn_ndx = fn_ndx;
expression += this.parse_single_term
( tmp_exp, (r==ndx.end.row && c==ndx.end.col) );
}
}
if ( expression && this.fn[this.fn_ndx].operator )
if (expression.charAt(expression.length-1)==
this.fn[this.fn_ndx].operator) {
expression =
expression.substring(0,expression.length-1);
}
return expression;
},
//}}}
/***
!! split_line_text
***/
//{{{
split_line_text : function ( txt, sep, brackets ) {
// Split one line of wikitext that contains several items, a table
// row or an array, for example.
//
// Arguments: txt The txt to split.
// sep The separator of items
// brackets An array of pairs of opening/closing
// brackets that should not split.
var result = txt.split (sep), r0 = 0
, rl = result.length-1, popen, mclose, pclose;
while ( ! result[r0] && r0 < rl ) r0++;
while ( ! result[rl] && r0 < rl ) rl--;
for ( var b = 0; b < brackets.length; b++ ) {
for ( var i = r0; i < rl; i++ ) {
if ( ! result[i] ) continue;
if ((popen=result[i].lastIndexOf(brackets[b].open))==-1)
continue;
if ( typeof brackets[b].close == 'string' )
pclose = result[i].lastIndexOf(brackets[b].close);
else {
if ( (mclose=result[i].match(brackets[b].close)) ) {
pclose = result[i]
.lastIndexOf(mclose[mclose.length-1]);
} else pclose = -1;
}
if ( popen > pclose ) {
// Found one part of the text that contains an opening
// bracket(s), but not its closing counterpart. This is
// possibly one of the cases mentioned above. If so then
// the next part may contain the corresponding closing
// bracket(s).
if ( typeof brackets[b].close == 'string' )
pclose = result[i+1].indexOf(brackets[b].close);
else
pclose = result[i+1].search(brackets[b].close);
popen = result[i+1].indexOf(brackets[b].open);
if ( pclose > -1 ) {
if ( popen==-1 || popen>pclose ) {
// Fond the corresponding closing bracket(s).
// Combine the two parts to restore it.
result[i] += sep + result[i+1];
// Remove the second part.
result.splice ( i+1, 1 );
if ( popen > pclose )
// There is another opening bracket(s) after
// the desired closing bracket(s), check this
// cell again.
--i;
}
}
}
}
}
return result;
},
//}}}
/***
!! parse_function
***/
//{{{
parse_function : function ( txt ) {
// Start parsing TWtcalc-defined functions.
// Calculations are done in one of the following ways:
// 1. over consecutive cells
// txt should look like func(cell1:cell2)
// 2. over separated individual cells
// txt should look like func(cell1,cell2,cell3,...)
// 3. mixing both cases
// txt can be like
// func(cell1:cell2,cell3,cell4,cell5:cell7...)
// get rid of parentheses in txt
var pparen_l = txt.indexOf('('),
pparen_r = txt.lastIndexOf(')'),
txt2 = txt.substring(pparen_l+1, pparen_r);
var expression = '', tmp_exp;
if ( this.fn[this.fn_ndx].start_parsing )
if ((tmp_exp=this.fn[this.fn_ndx].start_parsing()))
expression = tmp_exp;
if ( !txt2 ) {
// Functions without arguments.
if ( this.fn[this.fn_ndx].stop_parsing )
if((tmp_exp=this.fn[this.fn_ndx].stop_parsing()))
expression += tmp_exp;
return expression;
}
// Functions with arguments.
var fn_ndx = this.fn_ndx, chndx = 0, fntxt, i;
// If some of the arguments contain function calls,
// evaluate them before further parsing.
txt2 = TWtcalc.split_line_text(txt2,',',[
{'open' : '(', 'close' : ')'}
,{'open' : '[', 'close' : ']'}
,{'open' : '{', 'close' : '}'}
]);
for ( i = 0; i < txt2.length; i++ ) {
var txt3 = txt2[i].trim();
var p0 = TWtid.skip_style_text(txt3), fnval;
chndx = p0;
while ( chndx < txt3.length ) {
if ( (fntxt=this.isfunction(txt3,chndx)) ) {
fnval = this.parse_function(fntxt);
txt3 = txt3.substring(0,chndx)
+ fnval
+ txt3.substring(chndx+fntxt.length);
chndx += fnval.length;
} else chndx++;
}
this.fn_ndx = fn_ndx;
if ( (chndx=txt3.indexOf(':',p0)) > -1
&& this.cell_reference(txt3,chndx+1) ) {
expression += this.fn[this.fn_ndx].no_deref
? this.parse_single_term
(txt3,(i==txt2.length-1))
: this.parse_consecutive(txt3);
if ( i<txt2.length-1 &&
this.fn[this.fn_ndx].operator )
expression+=this.fn[this.fn_ndx].operator;
} else {
tmp_exp=this.evaluate(
txt3
, p0
, !this.fn[this.fn_ndx].no_eval
, this.fn[this.fn_ndx].no_deref
);
this.fn_ndx = fn_ndx;
expression+=this.parse_single_term(
(p0 ? txt3.substring(0,p0) : '')+tmp_exp
, (i==txt2.length-1)
, p0
);
}
}
if (this.fn[this.fn_ndx].stop_parsing)
if ((tmp_exp=this.fn[this.fn_ndx].stop_parsing()))
expression += tmp_exp;
return expression;
},
//}}}
/***
!! cell_reference
***/
//{{{
cell_reference : function ( txt, start ) {
// Returns the cell reference from txt at position start, or an
// empty string if no cell reference is found at that position.
var chndx=(start?start:0), ch, col = '', row = '';
if ( chndx > -1 && chndx < txt.length-1 ) {
if ( (ch=txt.charAt(chndx++)) == '$' ) {
// Absolute column reference
col = ch;
ch = txt.charAt(chndx++);
}
if ( /[\a-zA-Z]/.test(ch) ) {
col += ch;
ch = txt.charAt(chndx++);
if ( /[a-zA-Z]/.test(ch) ) {
col += ch;
ch = txt.charAt(chndx++);
}
}
if ( col ) {
// There is possible column reference,
// check for row reference.
if ( ch == '$' ) {
// Absolute row reference
row = ch;
ch = txt.charAt(chndx++);
}
if (/[0-9]/.test(ch)) {
row += ch;
while (chndx<txt.length &&
/[0-9]/.test((ch=txt.charAt(chndx++))))
row += ch;
return col+row;
}
}
}
return '';
},
//}}}
/***
!! Evaluation functions
***/
//{{{
is_possible_exp : function (txt) {
if ( !txt ) return false;
//if ( /[^\x21-\x7e ]/g.test(txt) ) return false;
var pl = txt.indexOf('(');
if ( pl > -1 ) {
var pr = txt.lastIndexOf(')');
txt = txt.substring(pl+1,pr);
}
var term1 = txt.split(/[\*\/]/);
for ( var i = 0; i < term1.length; i++ ) {
if ( ! term1[i] ) return false;
var term2 = term1[i].split(/[\+\-]/);
for ( var j = 0; j < term2.length; j++ )
if ( term2[j] && ! TWtcalc.isnumeric(term2[j].trim()) )
return false;
}
return true;
},
//}}}
/***
!! evaluate
***/
//{{{
evaluate : function ( txt, start, js_eval, no_deref ) {
// Evaluate a TWtcalc expression.
// A valid TWtcalc expression can be something like
// 1. a simple arithmetic on table cells such as
// =A1+E4 or =(B3+C4)/D7, etc.
// 2. a TWtcalc-defined function such as
// =SUM(A1..D8) or PRODUCT(A1,C3,D5) or
// =AVERAGE(B3..B20;B30..B40;C3,E2), etc.
// 3. a valid Javascript math expression such as
// =Math.sin(Math.PI) or =Math.sqrt(3), etc.
var expression='', cexp = '', chndx=start, subtxt,
$tr = this.$cur_table.find('tr');
while ( chndx < txt.length ) {
if ( (subtxt = this.cell_reference(txt,chndx)) ) {
// cell reference found
if ( !no_deref ) {
var col=this.reference_to_index_col(subtxt),
row = this.reference_to_index_row(subtxt), c2;
c2=$tr.eq(row).find('th,td').eq(col);
cexp = this.evaluate_cell(c2,js_eval);
if ( cexp ) expression += cexp;
else if (/[\+\-\*\/]$/.test(expression))
expression=expression.substring(0,expression.length-1);
} else expression += subtxt;
chndx += subtxt.length;
} else if ( (subtxt=this.isfunction(txt,chndx)) ) {
// No cell reference found, but some
// TWtcalc-defined function found.
expression += this.parse_function(subtxt);
js_eval = !this.fn[this.fn_ndx].no_eval;
chndx += subtxt.length;
} else {
expression += txt.charAt(chndx++);
}
}
if (js_eval && this.is_possible_exp(expression))
try { return ''+eval(expression); }
catch (e) { return 'Err: '+e; }
return expression;
},
//}}}
/***
!!
***/
//{{{
cells_being_evaluated : new Array(),
cyclic_reference : function ( $cell ) {
for ( var c = 0; c < TWtcalc.cells_being_evaluated.length; c++ )
if ( TWtcalc.cells_being_evaluated[c] == $cell )
return 'Error: ' +
(c == TWtcalc.cells_being_evaluated.length-1) ?
'Self reference.' :
'Cyclic reference.';
},
cell_text : function ( $cell, txt ) {
if ( typeof txt == 'undefined' ) {
txt = $cell.text();
if ( txt
&& (config.tableSorting
|| config.macros.sortableGridPlugin) ) {
var p = txt.indexOf('\u2191');
if ( p == -1 ) p = txt.indexOf('\u2193');
if ( config.macros.sortableGridPlugin )
p -= 2;
if ( p > -1 ) txt = txt.substring(0,p);
}
return txt;
} else {
if ( TWtid.cell_row_index($cell) == 0 ) {
if ( config.tableSorting ) {
var $span = $cell.find('span:first');
$cell.text(txt);
if ( $span.size() > 0 ) {
var html = $span.html(),
p = html.indexOf('\u2191');
if ( p == -1 ) p = html.indexOf('\u2193');
if ( p > -1 ) $span.appendTo($cell);
}
} else if ( config.macros.sortableGridPlugin ) {
var $span = $cell.find('a.sortheader span:first');
// This is copied from SortableGridPlugin
// for sorting purposes.
if ( $span.size() > 0 )
$cell.html(TWted.sortable_grid_html(txt))
.find('a.sortheader span:first')
.html($span.html())
.attr('sortdir', $span.attr('sortdir'));
else $cell.text(txt);
} else
$cell.text(txt);
} else {
$cell.text(txt);
}
//displayMessage('cell_text '+TWtid.object_string(TWtid.cell_row_index($cell),TWtid.cell_col_index($cell)));
//TWtid.adjust_width($cell);
}
},
//}}}
/***
!! evaluate_cell
***/
//{{{
evaluate_cell : function ( $cell, js_eval, peq ) {
// Evaluate a table cell
var txt = this.cell_text($cell);
if ( !txt ) return '';
if (!peq) peq = this.index_of_expression(txt);
var txt2, peq0 = peq;
if ( peq > -1 ) {
if ( $cell[0].title ) {
$cell[0].title = $cell[0].title.split('\n')[0]+'\n'+txt;
} else $cell[0].title = txt;
var err = this.cyclic_reference ( $cell );
if ( err ) {
this.cell_text(
$cell
,txt+'<br><br>'+$cell[0].title+'<br>'+err
);
} else {
this.cells_being_evaluated.push($cell);
txt = this.evaluate(txt,peq+1,js_eval);
txt2 = this.isnumeric(txt, true, '',
config.options.chkTWtcalcThousandSeparated);
if ( ! txt2 ) txt2 = txt;
if ( TWtid.skip_style_text(txt2) > 0 ) {
TWtid.wikify_element(
$cell,'@@'+txt2+'@@',true
);
} else {
this.cell_text($cell,txt2);
}
this.cells_being_evaluated.pop();
}
} else if ( $cell.contents().size()==1 &&
(txt2=this.isnumeric(txt,true,'.',false)) ) {
if ( txt.trim() != txt2.trim() ) {
// The .trim() above is necessary because there could be
// white-spaces/line-breaks in the cell content.
this.cell_text(
$cell
,(config.options.chkTWtcalcThousandSeparated
? this.separate_number(txt)
: txt)
);
return txt2;
} else if (config.options.chkTWtcalcThousandSeparated) {
this.cell_text($cell,this.separate_number(txt2));
} else return txt2;
}
return txt;
},
//}}}
/***
!! Main functions
***/
//{{{
pre_system_menu_item : null,
system_menu_item : function(item){
// This function is for other plugins to provide their system menu
// item when present. This function must return an array of menu items
// that are objects consisting of
// {
// text: item text,
// callback: (Optional) callback handler on click,
// prepare: function that prepares the options table
// }
// If the menu item from this plugin is to go after the item from
// TWtid, then here we should call {{{pre_system_menu_item}}}
// first, and push a new item to the returned array.
item = TWtcalc.pre_system_menu_item.apply(this,arguments);
item.push({
text:'TWtcalcOptions',
//callback:TWtcalc.menu_item_handler,
prepare:config.macros.TWtcalcOptions.handler
});
return item;
// If, however, we want to go before the TWtid menu item, we should
// ensure a valid array, push an item, then call the
// {{{pre_system_menu_item}}} and pass the array as the parameter.
//if ( ! item ) item = new Array();
//item.push({
// text:'TWtcalcOptions',
// callback:TWtcalc.menu_item_handler,
// prepare:config.macros.TWtcalcOptions.handler
//});
//return TWtcalc.pre_system_menu_item.call(this,item);
},
cell_reference_title : function ( $cell, r, c ) {
var span = TWtid.cell_spans($cell);
var ref = '';
for ( var rr = 0; rr < span.row; rr++ ) {
for ( var cc = span.col-1; cc >= 0; cc-- ) {
ref += TWtcalc.index_to_reference_col(c-cc)+(r+rr);
if ( cc > 0 ) ref += ' | ';
}
if ( rr < span.row-1 ) ref += '\n';
}
if ( $cell[0].title ) {
var txt = $cell[0].title.split('\n');
if ( txt[0] != ref )
$cell[0].title = ref + '\n' + txt[0];
} else $cell[0].title = ref;
},
//}}}
/***
!! calculate_cell
***/
//{{{
calculate_cell : function($cell,make_title,expression,peq){
if ( make_title )
this.cell_reference_title(
$cell,
TWtid.cell_row_index($cell),
TWtid.cell_col_index($cell)
);
if ( expression ) {
this.cell_text($cell,expression);
}
try {
expression = this.evaluate_cell($cell,true,peq);
} catch ( e ) {
if ( config.options.chkTWtcalcDebugMode )
this.cell_text(
$cell
, this.cell_text($cell)
+'<br><br>'+expression+'<br>'+e
);
}
},
//}}}
/***
!! calculate_table
***/
//{{{
calculate_table : function ( twtable ) {
if ( ! config.options.chkTWtcalcEnabled
|| ! (config.options.chkTWtcalcAllTables
|| TWtid.hasClass(twtable,'spreadsheet')) )
return false;
var $table = twtable.$dom ? twtable.$dom : twtable;
if ( ! $table ) return false;
TWtcalc.$cur_table = $table;
$table.find('tr').each( function (r, tr) {
jQuery(tr).find('th,td').each ( function (c, cell) {
TWtcalc.calculate_cell(jQuery(cell),true);
} );
} );
TWtcalc.$cur_table = null;
if ( twtable.$dom ) TWtid.table_resize(twtable);
return true;
},
//}}}
/***
!!
***/
//{{{
pre_OfficialExample : null,
OfficialExample : function ( title ) {
var result = TWtcalc.pre_OfficialExample(title);
if ( ! result && title )
result = /^TWtcalc--Example/.test(title);
return result;
},
pre_prepare_table : null,
prepare_table : function (twtable) {
var result = TWtcalc.pre_prepare_table.apply(this,arguments);
if ( result && ! twtable.$dom.attr('tcalc') )
if ( TWtcalc.calculate_table ( twtable ) )
twtable.$dom.attr('tcalc', 'true');
return result;
}
};
//}}}
// //TWtcalc defined functions
//{{{
TWtcalc.fn = [
{
name: 'PRODUCT',
operator: '*',
parse_single_term: function ( txt ) {
return TWtcalc.isnumeric(txt)?txt:'1';
}
},
{
name: 'SUM',
operator: '+',
start_parsing: function () {
return '(';
},
stop_parsing: function () {
return ')';
},
parse_single_term: function ( txt ) {
return TWtcalc.isnumeric(txt)?txt:'0';
}
},
{
// COUNTA should be defined before COUNT, otherwise it
// has no chance to be matched. It is so because TWtcalc
// plugin matches a function name with startsWith() method,
// and both functions starts with COUNT, if COUNT is defined
// before COUNTA, then the match will always be COUNT and
// never COUNTA.
name: 'COUNTA',
count: 0,
start_parsing: function () {
this.count = 0;
},
stop_parsing: function () {
return ''+this.count;
},
parse_single_term: function ( txt ) {
if (/\S/.test(txt)) this.count++;
}
},
{
name: 'COUNT',
count: 0,
condition: null,
start_parsing: function () {
this.condition = null;
this.count = 0;
},
stop_parsing: function () {
return ''+this.count;
},
parse_single_term: function ( txt ) {
if (TWtcalc.isnumeric (txt)) {
if ( this.condition ) {
var txt2 = this.condition.replace(/\%/g,txt);
this.count += eval(txt2) ? 1 : 0;
} else this.count++;
} else if ( ! this.condition ) {
txt = txt.trim();
if (/^[\%][\s]*[\>\<\=\!]/.test(txt)) {
this.condition = txt;
}
}
}
},
{
name: ['AVERAGE', 'AVG', 'MEAN'],
operator: '+',
count: 0,
start_parsing: function () {
this.count = 0;
return '(';
},
stop_parsing: function () {
return ')/'+this.count;
},
parse_single_term: function ( txt ) {
if ( (txt=TWtcalc.isnumeric(txt)) ) {
this.count++;
}
return txt;
}
},
{
name: 'TODAY',
no_eval: true,
stop_parsing: function () {
return new Date();
}
},
{
name: 'DAYS',
no_eval: true,
date1: null,
date2: null,
num_dec: null,
start_parsing: function () {
this.date1 = this.date2 = this.num_dec = null;
},
stop_parsing: function () {
var result = (this.date2-this.date1)/1000/86400;
//if ( !this.num_dec || this.num_dec == 0 )
if ( !this.num_dec )
result = Math.round(result);
else {
var factor = Math.pow(10,this.num_dec);
result=Math.round(factor*result)/factor;
}
return result;
},
parse_single_term: function ( txt ) {
if ( !this.date1 ) this.date1 = Date.parse(txt);
else if ( !this.date2 ) this.date2 = Date.parse(txt);
else if ( !this.num_dec ) this.num_dec = txt*1;
}
},
{
name: 'ROUND',
value: null,
num_dec: null,
start_parsing: function () {
this.value = this.num_dec = null;
},
stop_parsing: function () {
if ( this.num_dec===null || this.num_dec===0 ) {
this.value = Math.round(this.value);
} else {
var factor = Math.pow(10,this.num_dec);
this.value=Math.round(factor*this.value)/factor;
}
return this.value?(''+this.value):'0';
},
parse_single_term: function ( txt ) {
if ( this.value === null ) this.value = txt*1;
else if ( this.num_dec === null ) this.num_dec = txt*1;
}
},
{
name: 'RANDOM',
factor: null,
start_parsing: function () {
this.factor = null;
},
stop_parsing: function () {
var result = Math.random();
if ( this.factor ) result *= this.factor;
return result;
},
parse_single_term: function ( txt ) {
if ( !this.factor ) this.factor = txt*1;
}
},
{
name: 'CONCAT',
no_eval: true
},
{
name: 'ISNUMERIC',
no_eval: true,
parse_single_term: function (txt) {
var txt2=TWtcalc.isnumeric(txt);
return txt2 ? txt2 : 'false';
}
},
{
name: ['STDERR', 'STDEV'],
//no_eval: true,
data: null,
sum: 0,
start_parsing: function () {
if (!this.data) this.data = new Array();
this.sum = 0;
},
stop_parsing: function () {
var npt = data.length;
if ( npt == 0 ) return 0;
var mean = this.sum/npt, d, variance = 0;
for ( d = 0; d < npt; d++ ) {
variance += (this.data[d] - mean)^2;
}
return Math.sqrt(variance/(npt-1));
},
parse_single_term: function (txt) {
var v = parseFloat(txt);
this.data.push(v);
this.sum += v;
}
},
{
name: ['COLUMNA', 'COLA'],
no_eval: true,
no_deref: true,
col: null,
start_parsing: function () {
this.col = null;
},
stop_parsing: function () {
if ( this.col === null ) {
var cells = TWtcalc.
cells_being_evaluated;
this.col = TWtcalc.reference_to_index_col
(cells[cells.length-1][0].title);
}
return TWtcalc.col_ref(this.col);
},
parse_single_term: function (txt) {
if ( this.col === null )
this.col = TWtcalc.reference_to_index_col(txt);
}
},
{
name: ['COLUMNS', 'COLS'],
no_eval: true,
no_deref: true,
cols: 0,
start_parsing: function () {
this.cols = 0;
},
stop_parsing: function () {
return ''+this.cols;
},
parse_single_term: function (txt) {
txt = txt.split(':');
switch ( txt.length ) {
case 1 :
this.cols++; break;
case 2 :
// a range of cells
var c0 = TWtcalc.reference_to_index_col(txt[0]),
c1 = TWtcalc.reference_to_index_col(txt[1]);
this.cols += (c1-c0+1);
break;
default :
// Syntax error
throw 'Syntax error: '+txt;
break;
}
}
},
{
name: ['COLUMN', 'COL'],
no_eval: true,
no_deref: true,
col: null,
start_parsing: function () {
this.col = null;
},
stop_parsing: function () {
if ( !this.col ) {
var cells = TWtcalc.cells_being_evaluated;
this.col = TWtcalc.reference_to_index_col(
cells[cells.length-1][0].title);
}
return ''+this.col;
},
parse_single_term: function (txt) {
if ( !this.col )
this.col = TWtcalc.
reference_to_index_col(txt);
}
},
{
name: 'ROWS',
no_eval: true,
no_deref: true,
rows: 0,
start_parsing: function () {
this.rows = 0;
},
stop_parsing: function () {
return ''+this.rows;
},
parse_single_term: function (txt) {
txt = txt.split(':');
switch ( txt.length ) {
case 1 :
this.rows++; break;
case 2 :
// a range of cells
var r0 = TWtcalc.reference_to_index_row(txt[0]),
r1 = TWtcalc.reference_to_index_row(txt[1]);
this.rows += (r1-r0+1);
break;
default :
// Syntax error
throw 'Syntax error: '+txt;
break;
}
}
},
{
name: 'ROW',
no_eval: true,
no_deref: true,
row: null,
start_parsing: function () {
this.row = null;
},
stop_parsing: function () {
if ( !this.row ) {
var cells = TWtcalc.cells_being_evaluated;
this.row = TWtcalc.reference_to_index_row(
cells[cells.length-1][0].title);
}
return ''+this.row;
},
parse_single_term: function (txt) {
if ( !this.row )
this.row = TWtcalc.
reference_to_index_row(txt);
}
},
{
name: 'IF',
no_eval: true,
args: null,
start_parsing: function () {
args = new Array();
},
stop_parsing: function () {
return args[0] ? args[1] : args[2];
},
parse_single_term: function (txt,p0) {
switch ( args.length ) {
case 2 :
case 1 :
var style = txt.substring(0,p0);
args.push(style+eval(txt.substring(p0)));
break;
case 0 :
args.push(eval(txt));
break;
}
}
}
];
//}}}
To fix the top-most rows or left-most columns in a table, the [[TWtable|http://twtable.tiddlyspace.com/#TWtable.2.0.8]] plugin clones those rows/columns, makes them fixed on screen, and put them over the original ones. Therefore the alignment between the cloned and the original ones is an important issue in this plugin.
Naively it seems easy to align two objects: simply give the cloned one the same position and dimensions as the original one. Practically, however, due to different box models used in different browsers, it is not an easy task to produce the same results across browsers.
After quite a few times of trying, the {{{TWtable}}} plugin had accomplished very well alignment for some browsers in possibly all cases, and good results for others in many cases. Still there are situations that the alignment is off and not easy to correct. To further fine tune the plugin in those cases could be very time consuming, therefore I would rather stop here and devote more time into other parts of the development.
Here is a summary of the results for the alignment tests:
----
* ''Opera'', ''Safari for Windows'', and ''~TWEdit for iOS''
** Well aligned.
* ''Chrome''
** __Well aligned in Windows__, could be off in Linux.
*** Chrome in Linux is not using integers for the dimension properties of a DOM object, which might be the reason why it is difficult to align the fixed (cloned) table cells.
*** It seems not giving consistent results for transcluded and non-transcluded tiddlers.
* ~FireFox
** __Seems to be aligned well__ in Windows except that some cells have their borders disappeared.
** Could be slightly off in Linux.
* IE
** __Can be aligned well in some versions__, but
*** different versions need different amount of corrections;
**** Even the //apparently same// version of IE may behave differently. For example, the ~IE9 on my desktop does not do the same as the ~IE9 on my laptop (both in Win7x64 inside ~VirtualBox 4.2.4 r81684 in Ubuntu 12.04 x64).
***** __Actually the {{{navigator.userAgent}}} shows IE 9.0 for the desktop version but IE 7.0 for the laptop version__, even though they both claim to be IE 9.0 in the About dialog.
***** Same for the ~IE8: the __{{{navigator.userAgent}}} shows IE 8.0 on my desktop but IE 7.0 on my laptop__ (both in ~WinXP x86 inside ~VirtualBox 4.2.4 r81684 in Ubuntu 12.04 x64).
*** may not give consistent results for transcluded and non-transcluded tiddlers.
/***
|''Name:''|TableSortingPlugin|
|''Description:''|Dynamically sort tables by clicking on column headers|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Source:''|* (Mod) http://tobibeer.tiddlyspace.com/bags/tobibeer_public/tiddlers/TableSortingPlugin<br>* (Org) http://tiddlywiki.squize.org/#TableSortingPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|2.03|
|''Date:''|24-09-2010|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.3|
Mod by Tobias Beer! Refactored the refresh handler to allow triggering a refresh in other macros without refreshing the tiddler!
!!Usage:
* Make sure your table has a header row
** {{{|Name|Phone Number|Address|h}}}<br> Note the /h/ that denote a header row
* Give the table a class of 'sortable'
** {{{
|sortable|k
|Name|Phone Number|Address|h
}}}<br>Note the /k/ that denotes a class name being assigned to the table.
* To disallow sorting by a column, place {{{<<nosort>>}}} in it's header
* To automatically sort a table by a column, place {{{<<autosort>>}}} in the header for that column
** Or to sort automatically but in reverse order, use {{{<<autosort reverse>>}}}
!!Example:
|sortable|k
|Name |Salary |Extension |Performance |File Size |Start date |h
|ZBloggs, Fred |$12000.00 |1353 |+1.2 |74.2Kb |Aug 19, 2003 21:34:00 |
|ABloggs, Fred |$12000.00 |1353 |1.2 |3350b |09/18/2003 |
|CBloggs, Fred |$12000 |1353 |1.200 |55.2Kb |August 18, 2003 |
|DBloggs, Fred |$12000.00 |1353 |1.2 |2100b |07/18/2003 |
|Bloggs, Fred |$12000.00 |1353 |01.20 |6.156Mb |08/17/2003 05:43 |
|Turvey, Kevin |$191200.00 |2342 |-33 |1b |02/05/1979 |
|Mbogo, Arnold |$32010.12 |2755 |-21.673 |1.2Gb |09/08/1998 |
|Shakespeare, Bill |ã122000.00|3211 |6 |33.22Gb |12/11/1961 |
|Shakespeare, Hamlet |ã9000 |9005 |-8 |3Gb |01/01/2002 |
|Fitz, Marvin |â¬3300.30 |5554 |+5 |4Kb |05/22/1995 |
***/
//{{{
// //!BEGIN-PLUGIN-CODE
config.tableSorting = {
darrow: "\u2193",
uarrow: "\u2191",
getText : function (o) {
var p = o.cells[SORT_INDEX];
return p.innerText || p.textContent || '';
},
sortTable : function (o,rev) {
SORT_INDEX = o.getAttribute("index");
var c = config.tableSorting;
var T = findRelated(o.parentNode,"TABLE");
if(T.tBodies[0].rows.length<=1)
return;
var itm = "";
var i = 0;
while (itm == "" && i < T.tBodies[0].rows.length) {
itm = c.getText(T.tBodies[0].rows[i]).trim();
i++;
}
if (itm == "")
return;
var r = [];
var S = o.getElementsByTagName("span")[0];
c.fn = c.sortAlpha;
if(!isNaN(Date.parse(itm)))
c.fn = c.sortDate;
else if(itm.match(/^[$|£|€|\+|\-]{0,1}\d*\.{0,1}\d+$/))
c.fn = c.sortNumber;
else if(itm.match(/^\d*\.{0,1}\d+[K|M|G]{0,1}b$/))
c.fn = c.sortFile;
for(i=0; i<T.tBodies[0].rows.length; i++) {
r[i]=T.tBodies[0].rows[i];
}
r.sort(c.reSort);
if(S.firstChild.nodeValue==c.darrow || rev) {
r.reverse();
S.firstChild.nodeValue=c.uarrow;
}
else
S.firstChild.nodeValue=c.darrow;
var thead = T.getElementsByTagName('thead')[0];
var headers = thead.rows[thead.rows.length-1].cells;
for(var k=0; k<headers.length; k++) {
if(!hasClass(headers[k],"nosort"))
addClass(headers[k].getElementsByTagName("span")[0],"hidden");
}
removeClass(S,"hidden");
for(i=0; i<r.length; i++) {
T.tBodies[0].appendChild(r[i]);
c.stripe(r[i],i);
for(var j=0; j<r[i].cells.length;j++){
removeClass(r[i].cells[j],"sortedCol");
}
addClass(r[i].cells[SORT_INDEX],"sortedCol");
}
},
stripe : function (e,i){
var cl = ["oddRow","evenRow"];
i&1? cl.reverse() : cl;
removeClass(e,cl[1]);
addClass(e,cl[0]);
},
sortNumber : function(v) {
var x = parseFloat(this.getText(v).replace(/[^0-9.-]/g,''));
return isNaN(x)? 0: x;
},
sortDate : function(v) {
return Date.parse(this.getText(v));
},
sortAlpha : function(v) {
return this.getText(v).toLowerCase();
},
sortFile : function(v) {
var j, q = config.messages.sizeTemplates, s = this.getText(v);
for (var i=0; i<q.length; i++) {
if ((j = s.toLowerCase().indexOf(q[i].template.replace("%0\u00a0","").toLowerCase())) != -1)
return q[i].unit * s.substr(0,j);
}
return parseFloat(s);
},
reSort : function(a,b){
var c = config.tableSorting;
var aa = c.fn(a);
var bb = c.fn(b);
return ((aa==bb)? 0 : ((aa<bb)? -1:1));
},
refresh : function(elem){
var ts = elem.getElementsByTagName("TABLE");
var c = config.tableSorting;
for(var i=0;i<ts.length;i++){
if(hasClass(ts[i],"sortable")){
var x = null, rev, table = ts[i], thead = table.getElementsByTagName('thead')[0], headers = thead.rows[thead.rows.length-1].cells;
for (var j=0; j<headers.length; j++){
var h = headers[j];
if (hasClass(h,"nosort"))
continue;
h.setAttribute("index",j);
h.onclick = function(){c.sortTable(this); return false;};
h.ondblclick = stopEvent;
if(h.getElementsByTagName("span").length == 0)
createTiddlyElement(h,"span",null,"hidden",c.uarrow);
if(x===null && hasClass(h,"autosort")) {
x = j;
rev = hasClass(h,"reverse");
}
}
if(x)
c.sortTable(headers[x],rev);
}
}
}
};
Story.prototype.tSort_refreshTiddler = Story.prototype.refreshTiddler;
Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText){
var elem = this.tSort_refreshTiddler.apply(this,arguments);
if(elem)config.tableSorting.refresh(elem);
return elem;
};
setStylesheet("table.sortable span.hidden {visibility:hidden;}\n"+
"table.sortable thead {cursor:pointer;}\n"+
"table.sortable .nosort {cursor:default;}\n"+
"table.sortable td.sortedCol {background:#ffc;}","TableSortingPluginStyles");
function stopEvent(e){
var ev = e? e : window.event;
ev.cancelBubble = true;
if (ev.stopPropagation) ev.stopPropagation();
return false;
}
config.macros.nosort={
handler : function(place){addClass(place,"nosort");}
}
config.macros.autosort={
handler : function(place,m,p,w,pS){addClass(place,"autosort"+" "+pS);}
}
// //!END-PLUGIN-CODE
//}}}
>這個版本可能是多餘之作,因為絕大部分情況並不需要考慮到這麼多。不過已經花了太多時間,乾脆寫得清楚一點。
>This version is probably unnecessary, for there is no need to consider so much most likely. But I already spent then expected so much longer, might as well just make it clearer.
在電腦語言裡通常 isnumeric 函數都是根據「美式標點」來判斷的,其中句點 (.) 是用來標示小數點,而逗點 (,) 則做為千分位撇節符號。
In computer languages normally the "isnumeric" function follows the US form, in which the period (.) is used as the decimal mark while the comma (,) is the thousands separator.
然而根據維基百科裡[[小數點|http://zh.wikipedia.org/wiki/%E5%B0%8F%E6%95%B8%E9%BB%9E]]網頁的說明,還有許多不同的表示法,甚至在中國與印度還有非千分位的撇節法存在。
According to the Wikipedia [[Decimal mark|http://en.wikipedia.org/wiki/Decimal_mark]] page, however, there exists differen forms of decimal marks and thousands separators. There even exist non-thousands separatoration rules in China and India.
為了能顧及多一點的使用者(其實只是為了自己好玩),我開始構思一個能夠支持多種表示法的 isnumeric 函數。經過一番摧殘腦細胞的過程,其結果就是下面這個__連自己都覺得「''這根本就是瘋了!''」__的版本。而這其實還不夠完整...
In order to benefit more users (actually just for the fun of mine), I started to construct a function that supports multiple forms. After some brain killing periods, I ended up with the following version which __even I myself think is ''totally crazy!''__ And it's not even complete...
{{{
isnumeric : function ( txt, update, given_mark, to_sep ) {
// 檢查傳入的字串 txt 是否為數值,如果是的話,可以選擇性地指定小數點符號,
// 並且選擇是否要使用千分位撇節。預設的小數點及千分位撇節符號可以分別在
// txtTCalcDecimalMark 及 txtTCalcThousandSeparator 兩個系統選項中指定。
// Tests if a string txt is a numeric expression. If so, one gets the option to
// specify the decimal mark and whether to apply thousands separation to the
// expression. Default decimal mark and thousand separator can be given as
// system options txtTCalcDecimalMark and txtTCalcThousandSeparator, respectively.
//
// 參數 Parameters
// txt: 要測試的字串。
// String to test.
// update: 是否要替換小數點符號及使用撇節法。
// Whether to replace the decimal mark and/or
// to apply thousands separation.
// given_mark: 指定的小數點符號,若無指定則使用預設值
// txtTCalcDecimalMark。
// 為何定義這個參數?因為 Javascript 在做計算
// 的時候是採取美式標點,如果使用非美式標點,
// 有可能需要在此轉換以便後續計算。
// User specified decimal mark. Use the default
// value (stored at txtTCalcDecimalMark) if not
// given.
// Why there is such an option? Because the
// Javascript uses the US form for calculations.
// If non-US form is used instead, one might need
// to put it back for later calculations.
// to_sep: 是否使用撇節法。
// Whether to apply thousands separation.
//
// 傳回值 Return value 如果是數值,傳回原字串或是撇節過的字串,
// 如果有指定 given_mark,小數點會被替換。
// 如果不是數值,傳回空字串。
// 為何傳回字串而不是真偽值呢?因為
// TWtcalc(前身為 TableCalculator)的自訂函數設計
// 上是可以交給Javascript 的 eval() 函數來計算出結果
// 的,而這個 eval() 函數接收的參數便是字串,因此
// 在設計可以讓自訂函數使用的函數時都統一以字串
// 為傳回值的型態。
// If numeric, returns the original string or its
// thousands-separated version. Decimal mark will
// be replaced with given_mark if specified.
// If non-numeric, returns an empty string.
// Why returns a string instead of boolean value?
// Because the TWtcalc (formerly TableCalculator)
// allows a user-defined function to pass its return
// value to Javascript for final evaluation. The evaluation
// function, eval(), takes a string as its parameter,
// hence the return values of those functions accessible
// to user-defined functions are in string type.
var ndig = 0, ncomma = 0, nperiod = 0,
npm = 0, nspace = 0, ndot = 0, i, ch;
for ( i = 0; i < txt.length; i++ )
// 一次測試一個字元。
// Test one character at a time.
if ( /[^0-9]/.test((ch=txt.charAt(i))) )
// 如果不是數字的話...
// If not a digit...
switch ( ch ) {
case '+' :
case '-' :
// 正或負號可以沒有或出現一次,而且只能在最前面。
// There can be zero or one appearance of either the
// plus or minus sign, only at the leading position.
if ( i > 0 ) return '';
if ( ++npm > 1 ) return '';
break;
case '.' :
// 句點與逗點都可能是小數點也可能是千分位撇節符,
// 兩者可以都沒有,有的話撇節符可以出現多次,
// 小數點只能出現一次。
// Both the period and comma could be the decimal mark
// or the thousands separator. There could be none of them,
// or more than one separators but just one decimal mark.
if ( ++nperiod > 1 && ncomma > 1)
return '';
break;
case ',' :
// 見上面說明。
// See comments above.
if ( ++ncomma > 1 && nperiod > 1)
return '';
break;
case ' ' :
// 空格可以當做撇節符號,此時句點或逗點都可能是小數點。
// 小數點不能多於一個。
// The white space can be the thousands separator, with
// either the period or comma as the decimal mark. The
// decimal mark cannot appear more than once.
if ( ++nspace > 1 &&
(ndot>1 || nperiod>1 || ncomma>1) )
return '';
break;
case '·' :
// 同上方說明。
// Same as above.
if ( ++ndot > 1 &&
(nspace>1 || nperiod>1 || ncomma>1) )
return '';
break;
default :
// 不是數字又不是以上的符號,不是數值,傳回空字串。
// Neither a digit nor one of the above symbols, not
// a numeric value. Return an empty string.
return '';
}
else
// 這個字元是一個數字,數字個數加一。
// This character is a digit, increase the number of digits by one.
ndig++;
// 如果沒有數字出現,不是數值,傳回空字串。
// Not a numeric value if no digits appear. Returns an emtpy string.
if ( ndig == 0 ) return '';
// 如果沒有任何小數點與撇節符,表示都是數字,不需要替換小數點或
/ 做千分位撇節的話就把字串傳回去。
// No possible decimal marks and thousands separators, the string contains
// all-digit characters. Returns the string itself if no need to replace decimal
// mark and/or apply thousands separation.
if ( !update && (ncomma == 0 && nperiod == 0 &&
nspace == 0 && ndot == 0) )
return txt;
// 需要替換小數點或做千分位撇節,預設採取美式標點。
// Need to replace the decimal mark or apply thousands separation.
// Take the US form as default.
var mark = '.', separator = ',',
nmark = nperiod, nsep = ncomma;
if ( nperiod > 1 ) {
// 有多於一個的句點,表示句點是當做撇節符號,
// 逗點就是小數點符號就是。
// There are more than one period in the string,
// meaning it must be the separator and the comma
// be the decimal mark.
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( nspace > 0 ) {
// 多於一個空格,表示空格是撇節符號,逗點或
// 句點都可能是小數點符號。小數點只能有一個,
// 如果有的話。預設採取句號,因為台灣使用
// 這個符號為小數點。
// There are more than one spaces, meaning it must
// be the separator and either the comma or the
// period be the decimal mark. The decimal mark
// can appear either zero or only one time. Use
// the period as default value because it's used
// in Taiwan as the decimal mark.
if ( (nperiod > 0 && ncomma > 0) ||
(nperiod > 1 || ncomma > 1) ) return '';
separator = ' '; nsep = nspace;
if ( ncomma > 0 ) {
mark = ','; nmark = ncomma;
} else {
mark = '.'; nmark = nperiod;
}
} else if ( ndot > 0 ) {
// 多於一個點號(中央點),表示點號是撇節符號,
// 逗點是小數點符號。
// More than one middle dot, meaning it's the
// separator and the comma is the decimal mark.
if ( nperiod > 0 || ncomma > 1 ) return '';
separator = '·'; mark = ',';
nsep = ndot; nmark = ncomma;
} else if ( ncomma == 1 && nperiod == 0 ) {
// 如果只有一個逗點而沒有句點,這個逗點可能代表
// 小數點也可能是撇節符號,而句點是另外一個符號。
// 但因為預設逗點當做撇節符號,這裡只需要檢查兩
// 種情況:是否為小數點?不是的話有沒有撇錯位置?
// If there is just one comma and no period, this
// comma can be either the decimal mark or the
// separator, while the period be the other. However,
// by default we take the period as the decimal mark,
// here we only need to check two things: Is this
// comma the decimal mark? If not, is it at the right
// place as a separator?
i = txt.indexOf(',');
if ( i==0 || txt.length-i!=4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( txt.length-i < 4 )
return '';
} else if ( nperiod == 1 && ncomma == 1 ) {
// 有一個逗點及一個句點,看是否有辦法判斷撇節符,
// 以及撇節符是否在正確的位置。
// There are one comma and one period, we need to
// check whether possible to tell which is separator,
// and the separator is at the right position.
i = txt.indexOf(',') - txt.indexOf('.');
if ( i == 4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( i != -4 )
return '';
}
if ( nsep > 0 && ! config.options.chkTWtcalcThousandSeparated )
// 如果有撇節符號,但我們並不要使用撇節法,
// If there are thousands separators but we are not applying thousands separation,
return '';
if ( nsep > 1 ) {
// 有多於一個的撇節符號,檢查是否都在正確位置。
// There are more than one separators, make sure
// they are at the right positions.
var p1 = txt.indexOf(separator), p2;
while( (p2=txt.indexOf(separator,p1+1))>-1 ) {
if ( p2 - p1 != 4 ) return '';
p1 = p2;
}
}
if ( (nmark>0 && mark!=config.options.txtTWtcalcDecimalMark)
|| (nsep>0 &&
separator!=config.options.txtTWtcalcThousandSeparator) )
// 如果有小數點但卻和預設值不同,
// 或是有撇節符號但和預設值不同,
// If there is decimal mark but it's not the same as the default,
// or there are thousands separators but they are not the same as the default,
return '';
var opt_mark = given_mark ? given_mark :
config.options.txtTCalcDecimalMark;
if ( nsep == 0 ) {
// 如果沒有撇節符號,視情況置換小數點及使用撇節法後傳回。
// If no separators, replace the decimal mark and apply
// thousands separation if necessary, then returns.
if ( opt_mark != mark )
txt = this.replace_decimal_mark(mark, opt_mark);
return to_sep ? this.separate_number(txt) : txt;
}
// 有撇節符號存在,視情況置換後傳回。
// There are already thousands separators, replace them if necessary.
var opt_separator = to_sep ?
config.options.txtTCalcThousandSeparator : '';
if ( (mark!=opt_mark) || (separator!=opt_separator) ) {
for ( i = 0; i < txt.length; i++ ) {
ch = txt.charAt(i);
if ( ch == separator ) {
if(separator!=opt_separator)
txt=txt.substring(0,i)+
opt_separator+txt.substring(i+1);
}
else if( ch == mark ) {
if(mark!=opt_mark)
txt=txt.substring(0,i)+
opt_mark+txt.substring(i+1);
}
}
}
return txt;
},
}}}
!! The situation
[[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] uses the ~TextArea objects to edit the content of table cells. Usually one ~TextArea is good for one cell that does not span across multiple rows/columns. In cases that a cell does span, however, the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] uses multiple ~TextArea objects, one for each row/column spanned, to edit the content of that cell. One jQuery object is used to represent all ~TextArea objects in the same row, so for a row-spanned cell there are multiple jQuery objects, each of which represents one ~TextArea, while for a column-spanned cell there is one jQuery object representing multiple ~TextArea objects. This way works fine in row-spanned cases but fails for column-spanned cells: No ~TextArea shows up for editing at all.
The cause is unknown but was found to lie in the focus handling mechanism. When a jQuery object, $ta, for example, is representing multiple ~TextArea objects, calling $ta.focus() brings input focus to one of the ~TextArea objects, provided that focusout event is not bound to any handler function. If a handler is bound to the focusout event of any of the ~TextArea objects or the table cell, their common direct parent, $ta.focus() does not seem to do any thing: does not bring focus to any thing, does not throw an exception, does not even show the ~TextArea objects.
>The same may apply to focusin?
!! Solution
Currently I have no solution for this.
!! Workaround
[[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] takes over the focus switching within the tables in a tiddler.
The ''twve'' carries the so-called //focusing borders// to visually remind the users //what you can edit now//.
The codes are implemented in <<slider "" 'twve.extra##editable.box' 'editable.box'>> in the ''twve.extra'' module.
An option in ~TiddlyWiki comtains a {{{value}}} and a {{{description}}} (and a {{{name}}}, of course). The {{{value}}} is for affecting the output of a plugin or the ~TiddlyWiki core, while the {{{description}}} is for the users to understand what the option is affecting. For example,
{{{
config.options.chkTEditorAllTableEditable = false;
config.optionsDesc.chkTEditorAllTableEditable = 'Make all tables in this tiddler editable. If true, all tables will be editable with or without class name "editable". Otherwise only tables with class name "editable" are editable.';
}}}
is a valid ~TiddlyWiki option that affects the behavior of [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] plugin. Normally, options //with// a description will show up in the AdvancedOptions shadow tiddler for further tweaking. Those //without// a description, however, are hidden from the users unless the {{{Show unknown options}}} checkbox is checked.
In the [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] and [[TWtcalc|http://twtable.tiddlyspace.com/#TWtcalc.1.0.10]] plugins, options are initialized in the {{{init}}} function, which are called after all macros are loaded, according to the {{{Macro - init()}}} tiddler in http://tiddlywikidev.tiddlyspace.com/.
>By the way, how to put a ink to {{{http://tiddlywikidev.tiddlyspace.com/#[[Macro%20-%20init%28%29]]}}} in a tiddler? A simple http://tiddlywikidev.tiddlyspace.com/#[[Macro%20-%20init%28%29]] doesn't seem to work for the closing sqaure brackets are not included in the hyper link.
Previously I put the {{{value}}} and {{{description}}} initialization codes in the {{{init}}} function for all the options, such as,
{{{
if ( config.options.chkTEditorAllTableEditable===undefined ) {
config.options.chkTEditorAllTableEditable = false;
config.optionsDesc.chkTEditorAllTableEditable = 'Make all tables in this tiddler editable. If true, all tables will be editable with or without class name "editable". Otherwise only tables with class name "editable" are editable.';
}
}}}
I thought they would just show up in the AdvancedOptions shadow tiddler for users to play with, but it turned out only Google Chrome was doing the expected. All others were not. Worse yet, all browsers except for Chrome did not show the descriptions at all.
After diggin into the ~TiddlyWiki codes, I guessed the reason is that __the descriptions are //not// saved with the correspoinding option values__. Since I only put the description when the option is undefined, browsers that do save the option values will not have the corresponding descriptions once the options are saved (because they are no longer undefined). And my Google Chrome does not save options so it gets the chance to reinitialize the descriptions every time the file is loaded. Realized this, I separated the codes into {{{settting up value}}} when undefined,
{{{
if ( config.options.chkTEditorAllTableEditable===undefined ) {
config.options.chkTEditorAllTableEditable = false;
}
}}}
and {{{giving the description}}} explicitly,
{{{
merge ( config.optionsDesc, {
chkTEditorAllTableEditable:'Make all tables in this tiddler editable. If true, all tables will be editable with or without class name "editable". Otherwise only tables with class name "editable" are editable.'
} );
}}}
Voilà: I have my options shown as they outhgt to be.
> To give a description, simply put <br><br> {{{config.optionsDesc.chkTEditorAllTableEditable = 'Make all tables in this tiddler editable. If true, all tables will be editable with or without class name "editable". Otherwise only tables with class name "editable" are editable.';}}}<br><br>would do the trick, too. Using the {{{merge}}} function, however, saves some keystrokes and so a few bytes in plugin size.
The heart of transclusion support is to find __//where a rendered tiddler is coming from//__, and __//where a tiddler is rendered//__. When we know where a rendered tiddler is coming from, we know how to find its wiki text to edit and how to put it back after changes made. When we know how to find where a tiddler is rendered, we know how to synchronize the output of all transcluded copies.
* To find //where a rendered tiddler is coming from//, the ''twve'' needs to know the tiddler title. To know the title, the ''twve'' relies on the {{{.titleOfWrapper(w)}}} method -- required for each of the [[Editable Wrappers]] -- to do the work. For example, the [[twve.viewer]] -- representing wrappers for normally loaded tiddlers -- defines its version as <<slider '' 'twve.core##twve.viewer.titleOfWrapper' 'twve.viewer.titleOfWrapper(w)'>>.
* To find //where a tiddler is rendered//, the ''twve'' is equipped with a <<slider '' 'twve.core##twve.tiddler.wrapperFromTitle' 'twve.tiddler.wrapperFromTitle(title)'>> method to collect all copies of the registered [[Editable Wrappers]] associated with {{{title}}}. This method does not actually know how to find all kinds of //wrappers//, but rather relies on the specific version defined in each of the [[Editable Wrappers]]. Each of the specific version is expected to find //all the rendered copies of its kind// for a given tiddler title. For example, the [[twve.viewer]] defines its <<slider '' 'twve.core##twve.viewer.wrapperFromTitle' 'twve.viewer.wrapperFromTitle(title,parent)'>> that finds all the {{{div.viewer}}} associated with {{{title}}} in {{{parent}}}.
Partial transclusion is to include only part, not all, of one tiddler's content into another. In Tiddlywiki this is done by applying one of the transclusion macros:
# {{{<<tiddler 'tiddler title'>>}}}
# {{{<<tabs 'tab label' 'cookie' 'tiddler title'>>}}}
# {{{<<slider 'cookie' 'tiddler title' 'label'>>}}}
For partial transclusion one can include either a {{{section}}} or a {{{slice}}} within a tiddler. A {{{section}}} is the collection of content that starts from a heading and ends at the next heading or the end of the tiddler. A {{{slice}}}, however, is just one line of text containing a {{{key:value}}} pair. To specify which part to transclude, one simply appends the title of a section or slice to the tiddler name when using a transclusion macro:
# section -- {{{##section title}}}, to include a //section// of a tiddler;
** A section in a tiddler is the content under a {{{heading}}}, with the {{{heading text}}} as the section title.
# slice -- {{{::sliece title}}}, to include a //slice// of a tiddler.
** A slice is just one line of text containing a {{{key:value}}} pair, with the {{{key}}} as the slice title.
__Partial transclusion for a ''//section//'' is supported in ''twve''__. Elements transcluded this way are editable (if they are designed to be) and synchronized with their other copies, transcluded or not. In general it is not a difficult task to find the transcluded text in a tiddler for editing purposes, the Tiddlywiki already provides the {{{store.getTiddlerText()}}} function to do so. It is, however, not straightforward for ''twve'' to locate an element transcluded this way. The reason is that ''twve'' identifies an element with its order of appearance in a tiddler, while partial transclusion can easily break that order.
Technically there are two orders of appearance for an element -- the rendering order and the defining order. The former is the order of an element rendered in the resulting HTML page, while the latter is that defined in the original wiki text. These two orders are the same in normal transclusion but can be different in partial transclusion. For example, one can include the 2^^nd^^ table in tiddler B and show it as the 1^^st^^ table in tiddler A.
In earlier versions where only normal transclusion was supported and hence the two orders of appearance are identical, the [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] and [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] used only the rendering order to quickly identify an element in the DOM tree (just apply the {{{.eq()}}} method to the result of, for example, {{{.find('table')}}}), and to correctly locate its defining wiki text. In versions 1.4.5 and beyond, however, this scheme no longer works because the two orders of appearance can be inconsistent due to partial transclusion. Therefore the [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] stores for each "partially transcluded" element both numbers, with the rendering order used to identify a rendered table in the DOM tree, while the defining order to find the corresponding wiki text.
>The trade off is that the simple way to identify a table in the DOM tree -- the {{{.eq()}}} method -- is no longer applicable. The [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] needs to compare the defining order to find the rendered copy and the defining text of a table. In that sense it should be possible, and maybe in the future release changed to do so, to use only the defining order for both purposes.
>2014/04/22
>@@color:red;Turned out the 2^^nd^^ version was still NOT correct!@@
>The solution is to @@reverse all the spanned cells before transposing the table@@.
>See <<slider '' 'twve.table##twtable.transpose' 'the correct version'>>
>
In the time around ~TableEditor v1.2.8 there was a suggestion from wolfgang about table transposition. At first I was reluctant to do it because I thought it unnecessary. Later when I was working on large table support, features that I never thought of but convinced useful (thanks to wolfgang again), I started to think it might be also useful to transpose a table.
I spent some time to work out <<slider '' 'Transposing a table -- backward thinking##Transposition -- first version' 'the first version'>>. It worked on simple tables (since transposing a table twice brings it back to its original.) Then I left the codes in the plugin and forgot about it.
>It is so complicated (and too little information) that I don't even understand it after a while.
The ~TableEditor plugin then kept growing larger and richer, later evolved into a View Mode Editor and separated into [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]]+[[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]] combination. This View Mode Editor edits pretty much all kinds of block elements and plain text blocks (adjacent to a block element). After v1.6.0 I started to think about editing single characters within a block. Soon I realized the line-based core codes of the plugins are not good for editing single characters, I needed to rewrite the core codes on a character-by-character base. This transposition function, though not in the core codes, also needed rewriting for this change. I tried to modify the first version but didn't make it because I couldn't understand it any more. Therefore I decided to rewrite it from scratch. For that I had to rethink about the details and, fortunately, figured an easier way to do it: //going backward//.
Normally I start from the top-left corner and go left-to-right and top-down to work on a table. It is perfectly fine with most of the table operations. With transposition, however, it becomes a trouble when there are row-spanned or column-spanned cells. It is so because
# a row-spanned cell shall be transposed into a column-spanned cell (and vice versa),
# they store their content in exactly the opposite ways:
** a ''row''-spanned cell has its content in the __''first''__ cell
*** (top-most row, the first encountered cell when going top-down)
** a ''column''-spanned cell has it in the __''last''__ cell
*** (right-most column, the last encountered cell when going left-to-right)
Because of the 2^^nd^^ reason the first version applied a somewhat complicated way to bookkeep the spanned cells, which made it difficult to understand at a later time. In writing the second version, I figured that simply //going backward// -- start from the bottom-right corner and go right-to-left and bottom-up -- can resolve the problem. In this manner a row-spanned cell will have its content in the last, consistent with a column-cell in the normal manner -- no more bookkeeping needed. The same thing happens to a column-spanned cell. This way we can transpose a table easily -- simply exchange {{{~}}} (row-spanned) with {{{>}}} (column-spanned), and place the content of (//r//, //c//) cell into the (//c//, //r//) cell in the transposed table. Here is <<slider '' 'Transposing a table -- backward thinking##Transposition -- second version' 'the second version'>>.
@@display:none;
!!!! Transposition -- second version
//{{{
twtable.transpose = function () {
// Transpose this table, that is, move cell content from
// (r, c) to (c, r).
// We start from the bottom-right corner, go bottom-up and
// right-to-left, to avoid the complicated, hard-to-remember
// bookkeeping needed to transpose a row-spanned cell into a
// column-spanned one (and vice versa).
// For more details please see http://twve.tiddlyspace.com/#%5B%5BTransposing%20a%20table%20--%20backward%20thinking%5D%5D
var text = twtable.tiddler.text;
var start = twtable.start;
var end = twtable.end;
// Get positions of all cells.
var cpos = twve.table.allCells(text,start,end);
// Prepare a new table.
var newtxt = '';
var r, c = 0, ctxt, head, h;
// Find the maximum number of columns
for ( r = 0; r < cpos.length; r++ ) {
if ( cpos[r].length > c ) c = cpos[r].length;
}
// Start transposition.
var rspan = null;
var cspan = null;
while ( --c >= 0 ) {
newtxt = '|\n' + newtxt;
for ( r=cpos.length-1; r>=0; r-- ) {
if ( c >= cpos[r].length ) continue;
head = /[hH]/.test(
text.charAt(cpos[r][cpos[r].length-1].close+1)
);
ctxt=text.substring(
cpos[r][c].open,cpos[r][c].close
);
if ( ctxt == '~' ) {
// Row-spanned cells. Transpose to column-
// spanned cells. If this is the first cell, we
// record the row index in rspan.start.
if ( ! rspan ) {
rspan = twve.object.create();
rspan.start = r;
}
ctxt = '>';
} else if ( ctxt == '>' ) {
// Column-spanned cells. Transpose to row-
// spanned cells. If this is the first cell, we
// record the column index in cspan.start.
if ( ! cspan ) {
cspan = twve.object.create();
cspan.start = c;
}
ctxt = '~';
} else if (head && ! /^\s*\!/.test(ctxt)) {
if ( (h=ctxt.search(/\S/)) > 0 )
ctxt = ctxt.substring(0,h)+'!'
+ ctxt.substring(h);
else ctxt = '!'+ctxt;
}
newtxt = '|' + ctxt + newtxt;
}
}
var rstart, rend;
// Copy the caption row if any
rend = text.indexOf('|c\n',start.ndx);
if ( rend == -1 )
rend = text.indexOf('|C\n',start.ndx);
if ( rend > -1 ) {
rstart = text.lastIndexOf('|',rend-1);
newtxt = text.substring(rstart,rend+3)+newtxt;
}
// Copy the class row if any
rend = text.indexOf('|k\n',start.ndx);
if ( rend == -1 )
rend = text.indexOf('|K\n',start.ndx);
if ( rend > -1 ) {
rstart = text.lastIndexOf('|',rend-1);
newtxt = text.substring(rstart,rend+3)+newtxt;
}
// Done transposition. Set it back. Note that the length
// of the definition text may change so we call
// twtable.setText() to take care of that.
twtable.setText(newtxt);
newtxt = twtable.changed(
{
what : 'TRANSPOSED',
text : twtable.tiddler.text
}
);
twve.tiddler.saveText(twtable,newtxt);
return twtable.refreshTable();
};
//}}}
!!!! Transposition -- first version
//{{{
transpose_table : function (text, start, end) {
// Transpose a table.
// Arguments
// text: tiddler text.
// r0: first line of table text
// rl: last line of table text
// To transpose a table we do
// cell(r, c) = cell(c, r)
var r, c, rows=0, rowtxt, newtxt = new Array(),
header=false, htxt, h, col_span = null,
row_span = new Array(), col,
row_span_recorded = function(col) {
for ( var c = 0; c < row_span.length; c++ )
if ( row_span[c].col == col )
return c;
return null;
};
for ( r = r0; r <= rl; r++ ) {
if ( /[cCkK]$/.test(wikitxt[r]) ) continue;
++rows; header = /[hH]$/.test(wikitxt[r]);
rowtxt = this.split_table_row(wikitxt[r]);
for ( c = 0; c < rowtxt.length-2; c++ ) {
col = row_span_recorded(c);
htxt = rowtxt[c+1];
if ( htxt == '~' ) {
// Row-spanned cell
htxt = '>';
if ( col === null ) {
row_span.push({'start':(rows-1),'end':-1,'col':c});
col = row_span.length-1;
}
if ( r == rl ) {
row_span[col].end = rows;
}
} else if ( htxt == '>' ) {
// Column-spanned cell
htxt = '~';
if ( !col_span )
col_span = { 'start':c, 'end':-1 };
if ( col !== null )
row_span[col].end = rows-1;
} else {
if ( col !== null ) {
// end of some row-spanned cell
row_span[col].end = rows-1;
}
if ( col_span ) {
// end of col-spanned cell
col_span.end = c;
}
if (header && /^[^\!]/.test(htxt.trim()))
if ( (h=htxt.search(/\S/)) > 0 )
htxt = htxt.substring(0,h)+'!'+
htxt.substring(h);
else htxt = '!'+htxt;
}
if ( rows == 1 ) newtxt[c] = new Array();
if ( col !== null && row_span[col].end > -1) {
var start = row_span[col].start,
end = row_span[col].end;
if ( r == rl ) htxt = newtxt[c][start];
else newtxt[c][end] = newtxt[c][start];
newtxt[c][start] = '>';
row_span.splice(col,1);
}
if ( col_span && col_span.end > -1 ) {
newtxt[col_span.start][rows] = htxt;
newtxt[col_span.end][rows] = '~';
col_span = null;
} else {
newtxt[c][rows] = htxt;
}
}
}
for ( r = 0; r < newtxt.length; r++ ) {
newtxt[r].push('');
newtxt[r]=this.make_table_row(newtxt[r]);
}
var rT;
if ( newtxt.length > rows ) {
rT = 0;
for ( r = r0; r <= rl; r++ ) {
if ( /[cCkK]$/.test(wikitxt[r]) ) continue;
wikitxt[r] = newtxt[rT++];
}
while ( rT < newtxt.length )
wikitxt.splice(r++, 0, newtxt[rT++]);
} else {
rT = r0; r = 0;
while ( r < newtxt.length ) {
if ( /[^cCkK]$/.test(wikitxt[rT]) )
wikitxt[rT++] = newtxt[r++];
else rT++;
}
wikitxt.splice(rT,rl-rT+1);
}
},
//}}}
@@
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'twve';
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != "file:")
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}
// create some shadow tiddler content
merge(config.shadowTiddlers,{
'TspotControls':[
"| tiddlyspot password:|<<option pasUploadPassword>>|",
"| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
"| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),
'TspotOptions':[
"tiddlyspot password:",
"<<option pasUploadPassword>>",
""
].join("\n"),
'TspotSidebar':[
"<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),
'WelcomeToTiddlyspot':[
"This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
"<<tiddler TspotControls>>",
"See also GettingStarted.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n")
});
//}}}
/***
|Name|UndoPlugin|
|Author|Eric Shulman|
|Source|http://www.TiddlyTools.com/#UndoPlugin|
|Documentation|http://www.TiddlyTools.com/#UndoPlugin|
|Version|0.2.1|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|undo/redo changes to tiddlers|
|Status|Experimental - DO NOT DISTRIBUTE|
This plugin records changes to tiddlers edited during the session, allowing you to quickly revert to previous revisions of a tiddler, even after pressing 'done'.
!!!!!Documentation
<<<
TBD
<<<
!!!!!Configuration
<<<
<<option chkEnableUndo>> enable undo handling
<<<
!!!!!Revisions
<<<
2011.09.11 0.2.1 in setmsg(), make sure undo stack is not empty. In go(), make sure index is >0. added disabledmsg with option checkbox. In render(), use wikify() to display static menu content (noundomsg/disabledmsg)
2011.09.07 0.2.0 refactored click handler and added toolbar command wrapper
2011.05.15 0.1.1 edits to message text
2011.05.02 0.1.0 started
<<<
!!!!!Code
***/
//{{{
version.extensions.UndoPlugin= {major: 0, minor: 2, revision: 1, date: new Date(2011,9,11)};
if (config.options.chkEnableUndo===undefined) config.options.chkEnableUndo=true;
config.macros.undo = {
label: 'undo',
prompt: 'undo changes',
tip: 'undo changes to "%0"',
multimsg: 'Undo %0 tiddler changes. Are you sure?',
revertedmsg: '"%0" - previous content restored',
renamedmsg: '"%0" - renamed to "%1"',
deletedmsg: '"%0" - removed',
shadowmsg: '"%0" - default (shadow) content restored',
noundomsg: 'nothing to undo',
disabledmsg: 'undo is disabled\n----\n<<option chkEnableUndo>>enable undo',
undoheading: 'undo tiddler changes:',
dateformat: 'YYYY.0MM.0DD 0hh:0mm:0ss',
popupformat: '%1 %0<div style="font-size:80%"><i>action: </i><b>%2</b></div>',
changes: [], // the list of previous tiddler changes
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var p=paramString.parseParams('name',null,true,false,true);
var label=getParam(p,'label',this.label);
var prompt=getParam(p,'prompt',this.prompt);
createTiddlyButton(place,label,prompt,this.click);
},
click: function(ev){
var p=Popup.create(this); if (!p) return false;
config.macros.undo.render(p);
Popup.show();
ev=ev||window.event; ev.cancelBubble=true;
if(ev.stopPropagation) ev.stopPropagation();
return false;
},
render: function(p) {
var cmu=config.macros.undo; // abbrev
if (!config.options.chkEnableUndo) wikify(cmu.disabledmsg,p);
else if (!cmu.changes.length) wikify(cmu.noundomsg,p);
else {
createTiddlyText(p, cmu.undoheading);
for (var i=cmu.changes.length-1; i>=0; i--) { var c=cmu.changes[i]; var t=c.tiddler;
var b=createTiddlyButton(createTiddlyElement(p,'li'),
c.title, cmu.tip.format([c.title]),
function(ev){return config.macros.undo.go(this.getAttribute('i'));});
b.innerHTML=cmu.popupformat.format(
[c.title,c.when.formatString(cmu.dateformat),c.msg]);
b.setAttribute('i',i);
}
}
},
add: function(title,tiddler,action,msg){
this.changes.push({
title:title,
tiddler:merge({},tiddler),
action: action, // create, rename, change, delete
when: new Date(),
who: config.options.txtUserName,
msg: msg
});
},
setmsg: function(msg) {
if (this.changes.length) this.changes[this.changes.length-1].msg=msg;
},
reset: function(i){
while (this.changes.length) this.changes.pop();
},
go: function(i){
var co=config.options; // abbrev
var steps=this.changes.length-i; if (steps<0) return false;
if (steps>1 && !confirm(this.multimsg.format([steps]))) return false;
var temp=co.chkEnableUndo; co.chkEnableUndo=false; // SUSPEND undo
var msgs=[];
for (var j=this.changes.length; j>i; j--) {
var c=this.changes.pop();
if (c.action=='create') {
store.removeTiddler(c.title);
m=store.isShadowTiddler(c.title)?this.shadowmsg:this.deletedmsg;
msgs.push(m.format([c.title]));
} else {
var t=c.tiddler;
var revert=store.getTiddlerText(c.title)!=t.text;
var rename=c.title!=t.title
store.saveTiddler(t.title,t.title,t.text,
t.modifier,t.modified,t.tags,t.fields);
if (rename) { // RENAME: re-render with previous name
var tidelem=story.getTiddler(c.title);
if (tidelem) { // if displayed, re-render with previous name
story.displayTiddler(tidelem,t.title);
story.closeTiddler(c.title);
}
store.removeTiddler(c.title);
msgs.push(this.renamedmsg.format([c.title,t.title]));
}
if (revert) msgs.push(this.revertedmsg.format([t.title]));
}
}
co.chkEnableUndo=temp; // RESUME undo
while (msgs.length) displayMessage(msgs.shift());
autoSaveChanges();
return false;
}
};
//}}}
// // TOOLBAR COMMAND: undo
//{{{
config.commands.undoChanges = {
text: 'undo',
hideReadOnly: true,
tooltip: 'undo individual document changes since the last save',
handler: function(ev,src,title) { return config.macros.undo.click.call(src,ev); }
};
//}}}
// // HIJACKS - update changes when a tiddler is saved or deleted
//{{{
if (TiddlyWiki.prototype.undo_saveTiddler==undefined) TiddlyWiki.prototype.undo_saveTiddler=TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,text) {
var tiddler=store.getTiddler(title);
if (config.options.chkEnableUndo) {
var msgs=[];
if (!tiddler) {
var action='create';
msgs.push('remove "'+newTitle+'"');
if (store.isShadowTiddler(newTitle))
msgs.push('use default (shadow) content');
} else {
var action=title!=newTitle?'rename':'change';
if (action=='rename') {
msgs.push('rename to "'+title+'"');
}
if (store.getTiddlerText(title)!=text || !msgs.length)
msgs.push('restore previous content');
}
config.macros.undo.add(newTitle,tiddler,action,msgs.join(', '));
}
this.undo_saveTiddler.apply(this,arguments);
}
if (TiddlyWiki.prototype.undo_removeTiddler==undefined) TiddlyWiki.prototype.undo_removeTiddler=TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
var tiddler=store.getTiddler(title);
if (tiddler && config.options.chkEnableUndo) {
var action='delete';
var msg='restore deleted tiddler';
config.macros.undo.add(title,tiddler,action,msg);
}
this.undo_removeTiddler.apply(this,arguments);
}
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 31/03/2016 21:20:37 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 31/03/2016 21:21:51 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 31/03/2016 22:37:49 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 21/04/2016 23:20:51 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 23/04/2016 08:28:15 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 23/04/2016 08:35:41 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 23/04/2016 09:01:30 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 17/05/2016 10:26:48 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
| 21/06/2016 10:32:59 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . | ok |
| 21/06/2016 10:35:34 | VincentYeh | [[/|http://twve.tiddlyspot.com/]] | [[store.cgi|http://twve.tiddlyspot.com/store.cgi]] | . | [[index.html | http://twve.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date("Feb 24, 2008"),
source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
};
//
// Environment
//
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
//
// Upload Macro
//
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: "store.php",
defaultToFilename: "index.html",
defaultUploadDir: ".",
authenticateUser: true // UploadService Authenticate User
};
config.macros.upload.label = {
promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
promptParamMacro: "Save and Upload this TiddlyWiki in %0",
saveLabel: "save to web",
saveToDisk: "save to disk",
uploadLabel: "upload"
};
config.macros.upload.messages = {
noStoreUrl: "No store URL in parmeters or options",
usernameOrPasswordMissing: "Username or password missing"
};
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
return;
var label;
if (document.location.toString().substr(0,4) == "http")
label = this.label.saveLabel;
else
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
}
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};
config.macros.upload.action = function(params)
{
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != "http")
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
alert(config.macros.upload.messages.noStoreUrl);
clearMessage();
return false;
}
if (config.macros.upload.authenticateUser && (!username || !password)) {
alert(config.macros.upload.messages.usernameOrPasswordMissing);
clearMessage();
return false;
}
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
};
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir && uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
};
//
// uploadOptions Macro
//
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,false);
var uploadCaption;
if (document.location.toString().substr(0,4) == "http")
uploadCaption = config.macros.upload.label.saveLabel;
else
uploadCaption = config.macros.upload.label.uploadLabel;
wizard.setButtons([
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
]);
},
options: [
"txtUploadUserName",
"pasUploadPassword",
"txtUploadStoreUrl",
"txtUploadDir",
"txtUploadFilename",
"txtUploadBackupDir",
"chkUploadLog",
"txtUploadLogMaxLine"
],
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i<this.options.length; i++) {
var opt = {};
opts.push();
opt.option = "";
n = this.options[i];
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
opts.push(opt);
}
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
}
}
},
onCancel: function(e)
{
backstage.switchTab(null);
return false;
},
wizardTitle: "Upload with options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br>",
cancelButton: "Cancel",
cancelButtonPrompt: "Cancel prompt",
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
};
//
// upload functions
//
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to upload backup file",
rssSaved: "RSS feed uploaded",
rssFailed: "Failed to upload RSS feed file",
emptySaved: "Empty template uploaded",
emptyFailed: "Failed to upload empty template file",
mainSaved: "Main TiddlyWiki file uploaded",
mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
//specific upload
loadOriginalHttpPostError: "Can't get original file",
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: "The store script '%0' was not found."
};
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
return;
}
if (bidix.debugMode)
alert(original.substr(0,500)+"\n...");
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
bidix.upload.uploadRss(uploadParams,original,posDiv);
};
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
// save on localdisk ?
if (document.location.toString().substr(0,4) == "file") {
var path = document.location.toString();
var localPath = getLocalPath(path);
saveChanges();
}
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == "/")
originalPath = originalPath + "index.html";
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
if (bidix.debugMode)
alert("about to execute Http - GET on "+originalPath);
var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
bidix.upload.uploadMain(params[0],params[1],params[2]);
} else {
displayMessage(bidix.upload.messages.rssFailed);
}
};
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is "file" !!!
if (document.location.toString().substr(0,4) != "file")
rssString = convertUnicodeToUTF8(rssString);
bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
} else {
bidix.upload.uploadMain(uploadParams,original,posDiv);
}
};
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) && (responseText.indexOf("backupfile:") > -1)) {
var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
}
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
store.setDirty(false);
log.endUpload("ok");
} else {
alert(bidix.upload.messages.mainFailed);
displayMessage(bidix.upload.messages.mainFailed);
log.endUpload("failed");
}
};
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
if (xhr.status == 404)
alert(bidix.upload.messages.storePhpNotFound.format([url]));
if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
alert(responseText);
if (responseText.indexOf("Debug mode") >= 0 )
responseText = responseText.substring(responseText.indexOf("\n\n")+2);
} else if (responseText.charAt(0) != '0')
alert(responseText);
if (responseText.charAt(0) != '0')
status = null;
callback(status,params,responseText,url,xhr);
};
// do httpUpload
var boundary = "---------------------------"+"AaB03x";
var uploadFormName = "UploadPlugin";
// compose headers data
var sheader = "";
sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
sheader += uploadFormName +"\"\r\n\r\n";
sheader += "backupDir="+uploadParams[3] +
";user=" + uploadParams[4] +
";password=" + uploadParams[5] +
";uploaddir=" + uploadParams[2];
if (bidix.debugMode)
sheader += ";debug=1";
sheader += ";;\r\n";
sheader += "\r\n" + "--" + boundary + "\r\n";
sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
sheader += "Content-Length: " + data.length + "\r\n\r\n";
// compose trailer data
var strailer = new String();
strailer = "\r\n--" + boundary + "--\r\n";
data = sheader + data + strailer;
if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
};
//
// UploadLog
//
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler("UploadLog");
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = "UploadLog";
this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
}
return this;
};
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
return;
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine >= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length > maxLine + 1)
textArray.splice(1,textArray.length-1-maxLine);
this.tiddler.text = textArray.join('\n');
}
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
// refresh and notifiy for immediate update
story.refreshTiddler(this.tiddler.title);
store.notify(this.tiddler.title, true);
};
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
return;
var now = new Date();
var text = "\n| ";
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
text += config.options.txtUserName + " | ";
text += "[["+filename+"|"+location + "]] |";
text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
text += uploadDir + " | ";
text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
text += backupDir + " |";
this.addText(text);
};
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
return;
this.addText(" "+status+" |");
};
//
// Utilities
//
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &&
((ext.major > major) ||
((ext.major == major) && (ext.minor > minor)) ||
((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
}
};
bidix.dirname = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf("\\"));
}
};
bidix.basename = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("#")) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf("\\")+1);
};
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
};
//
// Initializations
//
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");
//optionsDesc
merge(config.optionsDesc,{
txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
txtUploadUserName: "Upload Username",
pasUploadPassword: "Upload Password",
chkUploadLog: "do Logging in UploadLog (default: true)",
txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});
// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');
// Backstage
merge(config.tasks,{
uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
{{narrow{
* This is a working solution that I don't like much, because I had to break and remove the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] out of the hijacking chain, which I think might not be the best solution. -- 2012/10/19
* I changed my mind because it does not make sense to do the same thing twice. Removing the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] out of the hijacking chain also removes the possibilities of rendering the same math twice^^†^^. Why there was such a chance? Because the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] needed to hijack both {{{displayTiddler()}}} and {{{refreshTiddler()}}}, and {{{displayTiddler()}}} also calls {{{refreshTiddler()}}} during its process. Why it has to hijack both functions? Because the {{{TiddlyWiki}}} does not always call {{{displayTiddler()}}} to render a tiddler, it some times calls the {{{refreshTiddlyer()}}} directly. Removing the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] out of the hijacking chain leaves it no chance to start math rendering by itself, and the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] a chance to manipulate the time of math rendering according to its needs, inside the {{{refreshTiddler()}}} function. -- 2012/10/20
* And that removes the necessity of hijacking both {{{displayTiddler()}}} and {{{refreshTiddler()}}} as well. -- 2012/11/12
----
** ^^†MathJaxPlugin doesn't seem to do the same math twice. See text below.^^
}}}
----
[[MathJax|http://www.mathjax.org/]] is one of my favorite solutions for math display on the web. It has good support for {{{LaTeX}}} syntax, which I use a lot, and generates beautiful output in most of the modern browsers. There are more than one plugins that bring [[MathJax|http://www.mathjax.org/]] into the {{{TiddlyWiki}}} world, among which the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]], the one used in this space, is probably the smallest. The way the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] works is to hijack the {{{displayTiddler()}}} core function and insert math rendering actions after the tiddler being rendered. It has been going well with the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] since the first release until recently that it broke down in {{{TiddlySpace}}} (while still worked for local files).
The breakdown came after the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] switched from hijacking the {{{refreshTiddler()}}} to {{{displayTiddler()}}} core function. The reason for such a change was to obtain the correct width of a math-containing table cell, for it is not possible to obtain in {{{refreshTiddler()}}} where math rendering (with [[MathJax|http://www.mathjax.org/]]) has not yet started. Hijacking the {{{displayTiddler()}}} function gives the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] a chance to wait for the end of math rendering to get the final width of math-containing cells. The reasons why the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] broke down after the change, however, are not all straight forward to me.
The first reason was related to how {{{TiddlyWiki}}} (or Javascript?) handles {{{undefined}}} objects. A simple
{{{
if ( MathJax !== undefined ) { ... }
}}}
worked for local files but failed in {{{TiddlySpace}}}: an {{{undefined object}}} exception (or something like that) was thrown and not caught in [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]], resulting in a silent breakdown of the plugin. __The fix is to test the type of object__ instead of the object itself, as shown below.
{{{
if ( typeof MathJax !== 'undefined' ) { ... }
}}}
> This seems a common practice in other Javascript codes, but what puzzles me is that it works with the local files and fails with TiddlySpace.
>>The [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] needs to test the existence of object {{{MathJax}}}, at least at startup, because the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] loads asynchronously the latest codes of [[MathJax|http://www.mathjax.org/]] from the Internet, which may take a longer time than the {{{Tiddlywiki}}} needs to get ready and start to load the {{{DefaultTiddler}}}. Before the codes are completely loaded, the math renderer object {{{MathJax}}} remains undefined.
The second one was with [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]]: it does not return the {{{tiddlerElement}}} in its hijacking {{{displayTiddler()}}} function so the tiddler rendering stopped there, leaving [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] no chance to do its jobs. This did not cause any trouble before because the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] used to do in the {{{refreshTiddler()}}} function, which is invoked within the {{{displayTiddler()}}} and therefore finished before the math rendering started. Simply adding a statement to return the {{{tiddlerElement}}} could definitely fix the problem, but that will require changes made to the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]], which I would rather choose not to if there are still other possibilities. See the following paragraph for more details.
The third reason took me a while to figure out. The main trouble was an infinite loop of self invocation in [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]]'s {{{displayTiddler()}}} function. This strange behavior was later found mostly due to the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] itself: it broke the hijacking chain by calling the {{{displayTiddler()}}} function hijacked by the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] instead of by itself. This is not the normal way of hijacking a function from another library. Normally a hijacking function should call the one that's hijacked by itself to minimize deviation from the normal flow of code execution. The [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] breaks it with a reason which is explained below. In addition, the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] hijacked the {{{TiddlyWiki}}}'s {{{displayTiddler()}}} function after the codes are completely loaded, which with local files is still before but with TiddlySpace is much later than the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] gets loaded. It was this delay in hijacking time that caused the self invocation loop because the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] was actually hijacking [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]]'s {{{displayTiddler()}}} instead of the {{{TiddlyWiki}}}'s original. Therefore when [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]]'s {{{displayTiddler()}}} function calls [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]]'s hijacked version it's actually calling itself, in which it calls itself again, and again, and again, resulting in an infinite loop of self invocation. This problem, together with the previous one, was avoided by taking the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] out of the {{{displayTiddler()}}}'s hijacking chain. This way the {{{displayTiddler()}}} function that gets executed is never the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]]'s own version, but the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]]'s, in which it manipulates math rendering according to its needs. The [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]] is still needed for its nice and compact formatter to render math expressions.
>The [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] is breaking the normal hijacking chain because it needs to reformat a table some times before, while other times after, the math rendering is done. It seemed feasible to always do table reformatting after math rendering, which works for the after-math kind of situations, then re-invoke the math renderer afterwards for the before-math kind of cases. It was, however, not solving the problem because the __re-invocation of math renderer did not do the expected work__. Actually it did not seem to do anything. __I have no idea why__. After some attempts I figured that I could have both kinds of situations taken care of, without much trouble, by breaking the [[MathJaxPlugin|http://www.guyrutenberg.com/2011/06/25/latex-for-tiddlywiki-a-mathjax-plugin]]'s {{{displayTiddler()}}} function to insert [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]]'s action in accordance with the requirements in each kind of cases.
>>In most of the cases the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] reformats a table after math rendering because table dimensions could be changed after math expressions displayed. But there is one kind of situations where [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] needs to do in the opposite order: when the table caption contains math expressions. The reason is that [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] removes the table caption and reproduces it in a {{{<DIV>}}} element above the table. If the math rendering were done before caption reproduction, the math expression in the caption would never be rendered for, as mentioned above, re-rendering did not do the work.
>>>The [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] removes the table caption for scrolling purposes. To scroll a table without scrolling the window, the table has to be absolutely positioned inside a relatively positioned container. This, however, is not the default case in {{{TiddlyWiki}}} (or maybe in general?), so [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] needs to create the necessary containers to wrap the scrolling part of the table. Most of the times it is the table content, not the table caption, that we want to scroll, hence the [[TableEditor|http://twtable.tiddlyspace.com/#TableEditor]] removes the caption and places it elsewhere to keep it from scrolling with the table body.
<<<
In ~TiddlyWiki blockquotes are complicated in that
# they have two different types
** blockexamples: consecutive lines of text immediately preceded and followed by one single line of mere {{{<<<}}};
** single line blockquotes that consist of one or more (consecutive) lines of text starting with {{{<}}}.
# Both types can carry sub-blockquotes immediately following them.
# Both types can have sub-blockquotes completely inside their bodies.
Because of the last point above, there can be quite complicated blockquotes. The ''twve'' tries to take care of cases as complicated as I can imagine.
<<<
>Single line blockquotes
@@color:blue;To include sub blockquotes to edit, check the option <<option chktwveExtraIncludeSubs>> {{{chktwveExtraIncludeSubs}}}.@@
>Hierarchical blockquotes
>>This one follows immediately after the above one.
>>Blockquotes not started from level 1 are fine, too.
<<<
Block example
<<<
>In-body blockquotes (__blockquotes entirely within another blockquote__),
>>such as this one,
> or
>>this one,
>Works as well.
>Multi-line blockquote
>is fine,
>too.
>>2nd level
>>also
>>works.
>>>3rd level
>>>still works?
<<<
In ~TiddlyWiki {{{code pieces}}} are wrapped with a pair of triple braces """{{{""" and """}}}""" without line breaks.
@@color:blue;With line breaks it would become a blockquote.@@
<<<
{{{1st statement starts here;}}} {{{2nd statement immediately following the 1st;}}}
{{{3rd statement goes in another line;}}}
{{{You want more?}}}
<<<
This tiddler demonstrates that ''twve'' works with [[FoldHeadingsPlugin]]. With ''twve'' you can
# edit the content of the folded section;
** Unfold the section, move the cursor over the content,
** when the focusing borders enclose the content in the section, click to edit.
# edit the section title (the heading text).
** Move the cursor to the right-most area of the heading,
** when the focusing borders enclose the heading, click to edit.
<<<
!! Section 1
Some short text in section 1.
<<<
!!!! A heading within a folded section
Some text in this section-in-a-section
<<<
Some text after that section-in-a-section.
!! Section 2
Some more short text in section 2.
!! Section 3
Some a bit longer text in section 3 to cover more area.
!! Section 4
Some even longer text in section 4 that covers the whole width of this section and results in more than one lines. You will see the focusing borders also spans over multiple lines.
<<foldHeadings>>
<<<
In ~TiddlyWiki there are 6 levels for headings. The ''twve'' works with all 6 levels.
<<<
! You can edit almost everything! Just move your mouse around and see!
!! A table: Hover the mouse cursor over the table, there will be focusing borders around the table, click on one of the table cells to edit.
!! List items: All list items are editable in the view mode. Sublists can be included with <<option chktwveExtraIncludeSubs>> (option chktwveExtraIncludeSubs) set to true.
!!! Hierarchical headings.
!!!! This one follows immediately after the above heading. Works just fine.
!! Last heading
<<<
In ~TiddlyWiki there are virtually unlimited levels for listitems. The ''twve'' may just work fine with all of them.
<<<
@@color:blue;To include sub lists to edit, check the option <<option chktwveExtraIncludeSubs>> {{{chktwveExtraIncludeSubs}}}.@@
* 1
** 1.1
** 1.2
## 1.3
## 1.4
* 2
## 2.1
## 2.2
* 3
** 3.1
** 3.2
** Lists that do not start from level 1 (this one starts from level 2 with {{{**}}}) are fine, too.
* what after?
* are they still correct?
<<<
~MathJax is my favorite choice for math formulas, and it turns out not difficult to edit the displayed math formulas in the view mode. Here are some examples to play with.
<<<
''//Note:// It is easy to edit a math formula for sure'', @@color:red;but the previewer can be very slow with a complicated formula.@@
!!! Enclosed with a pair of {{{$$}}}
$$\boldsymbol{\nabla}f = \frac{\partial f}{\partial r} \boldsymbol{\hat r} + \frac{1}{r} \frac{\partial f}{\partial \theta} \boldsymbol{\hat \theta} + \frac{1}{r\sin\theta} \frac{\partial f}{\partial \phi} \boldsymbol{\hat \phi} .$$
!!! Enclosed with {{{\[}}} and {{{\]}}}
\[\nabla^2 V = \frac{1}{r^2} \frac{\partial}{\partial r} \left(r^2 \frac{\partial V}{\partial r}\right) + \frac{1}{r^2 \sin\theta} \frac{\partial}{\partial \theta} \left(\sin\theta \frac{\partial V}{\partial \theta}\right) + \frac{1}{r^2 \sin^2\theta} \frac{\partial^2 V}{\partial \phi^2}\]
!!! Enclosed with {{{\begin{}}} and {{{\end{}}}
\begin{array}{cccclc} \boldsymbol{\nabla} \times {\bf E} &=&& \frac{1}{r^2 \sin\theta} & \left[ \frac{\partial}{\partial \theta} (r \sin\theta\ E_\phi) - \frac{\partial}{\partial \phi} (r E_\theta) \right] & \boldsymbol{\hat r} \\ &&+& \frac{1}{r \sin\theta} & \left[ \frac{\partial}{\partial \phi} (E_r) - \frac{\partial}{\partial r} (r \sin\theta\ E_\phi) \right] & \boldsymbol{\hat \theta} \\ &&+& \frac{1}{r} & \left[ \frac{\partial}{\partial r} (r\ E_\theta) - \frac{\partial}{\partial \theta} (E_r) \right] & \boldsymbol{\hat \phi} .\end{array}
!!! More complicated style
\[\begin{pmatrix} \hat x' \\ \hat y' \\ \hat z' \end{pmatrix} \sim \begin{pmatrix} 1 + \epsilon_{xx} & \epsilon_{xy} & \epsilon_{xz} \\ \epsilon_{yx} & 1 + \epsilon_{yy} & \epsilon_{yz} \\ \epsilon_{zx} & \epsilon_{zy} & 1 + \epsilon_{zz} \end{pmatrix} \begin{pmatrix} \hat x \\ \hat y \\ \hat z \end{pmatrix}.\]
+++[nested sliders are somewhy]
open on load
===
<<<
An inline element may span over more than one lines if its content is long enough. In such cases the focusing borders are not just a simple rectangle but adjusted to visually comply with the text flow.
<<<
{{{1st statement starts here;}}}{{{2nd statement immediately following the 1st, and is long long long long long long long long long long long enough to span more than one lines;}}}{{{3rd statement that may be long enough to sop in the next line;}}}{{{Another long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long line to span even more lines.}}}
Text after them.
> This tiddler gives information about how ''twve'' handles plain text.
!!! What is plain text?
Plain text in ~TiddlyWiki is a piece of text NOT to define some element, but to describe some information in a tiddler.
!! What is special about it?
There are two things special about plain text.
# In ~TiddlyWiki plain text is rendered as a ~TextNode, an object that is NOT accessible through {{{document.elementfromPoint(x,y)}}} or [[jQuery's .find()|http://api.jquery.com/find/]] methods. Unfortunately these two methods are the main tools that ''twve'' uses to locate the element at the mouse cursor.
# Plain text does not have any signatures in it, while the ''twve'' relies on definite signatures to locate the defining wiki text of an element.
!! What do do about it?
The solution: identify the preceding and following //elements// (with definite signatures).
Since elements with definite signatures can be easily identified and located, it is then possible to locate a piece of plain text by finding its preceding and following elements. The desired plain text is just in between.
When the ''twve'' finds the mouse cursor not over a specific element but just the body of a wrapper, it tries to locate the element that is //previous// and the element //next// to the mouse cursor. The //previous// is somewhere to the left or above, while the //next// is to the right or below. Either of these elements can be null, a regular element, or one of the [[transclusion wrappers]].
With this idea it is then quite simple to locate a piece of plain text:
# Locate the ''end'' of //previous//.
** If the //previous// is __not__ in the same container, look for the __beginning__ of the container instead.
# Locate the ''beginning'' of //next//.
** If the //next// is __not__ in the same container, look for the __end__ of the container instead.
# The desired plain text is just in-between.
>This feature is implemented in <<slider "" 'twve.extra##twwrap.box' 'twwrap.box()'>> method in ''twve.extra'' module.
<<<
In ~TiddlyWiki there are four pairs of tags to create a single type of blockquote. They are all supported in ''twve''. It works with [[SyntaxHighlighterPlugin3]] as well.
<<<
{{{
1st pre-formatted block
starting with {{{
}}}
//{{{
2nd pre-formatted block
starting with //{{{
//}}}
/*{{{*/
3rd pre-formatted block
starting with /*{{{*/
/*}}}*/
<!--{{{-->
4th pre-formatted block
starting with <!--{{{-->
<!--}}}-->
@@color:red;//Note: These can be wrong when there are strings containing """''""" (two single quotes in a row), """//""", """__""" (two underscores in a row), or """--"""//@@, and I have no working solutions yet.
<<<
In ~TiddlyWiki styled text are relatively easy to handle because
# they all have identical opening and closing tags,
# they all have only one pair of tags associated with them.
<<<
Try @@color:blue;Colored text@@, @@inversed text@@, __underlined text__, ''bold text'', //italic text//, @@font-size:2em;larger text@@, etc.
<<<
The ''twve'' carries a floating menu system that shows and hides a menu according to mouse status.
<<<
!!! The Options Menu
For example, there is an Options Menu that carries all the options associated with ''twve'' plugins. This menu hides itself from you most of the time, and shows up only when you __hover the mouse over a tiddler title__. When you do __it shows itself as a small icon__ -- a //hamburger//, according to Ton Gerner -- __at the top-right corner of the focusing borders around the tiddler title__. ''You can bring up the options menu by hovering the mouse over that little icon.''
> This tiddler gives instructions on how to edit transcluded content and their corresponding macros. See [[twve--Example--Transclusion--Content]] and [[twve--Example--Transclusion--Macros]] for examples.
!! The tiddler transcluded
To edit the content transcluded with {{{<<tiddler>>}}} macro, just click on the content text.
!! The slider transcluded
To edit the content transcluded with {{{<<slider>>}}} macro, click on the content text or some empty space within the area holding that content.
!! The tabs transcluded
To edit the content transcluded with {{{<<tabs>>}}} macro, click on the content text or some empty space within the area holding that content.
!! The tiddler macro
To @@edit the {{{<<tiddler>>}}} command@@, one can move the mouse cursor 1) into some empty space in the area of transcluded content, or 2) slightly above or below that area, the focusing borders will then enlarge a little bit, and clicking withing the borders will bring up the edit box with the {{{<<tiddler>>}}} command itself.
!! The slider macro
To @@edit the {{{<<slider>>}}} macro@@, ''move the cursor up until the focusing borders enclose the slider button'', then click within the borders.
!! The tabs macro
To @@edit the {{{<<tabs>>}}} command@@, ''move the mouse up in the tabs area, the focusing border will enclose the tabs and content'', then click within that border.
<<<
This tiddler shows the //transclusion// related features, including:
# editing the content of a transcluded tiddler,
# synchronizing the updated content for all transcluded copies.
<<<
!! {{{<<tiddler>>}}} transcluded
<<tiddler 'twve--Example--Transclusion Instructions##The tiddler transcluded'>>
!! {{{<<slider>>}}} transcluded
<<slider "" 'twve--Example--Transclusion Instructions##The slider transcluded' 'slider transcluded content'>>
!! {{{<<tabs>>}}} transcluded
<<tabs ''
'tabs transcluded content' "" 'twve--Example--Transclusion Instructions##The tabs transcluded'
>>
<<<
This tiddler shows the //transclusion// related features, including:
# editing the content of a transcluded tiddler,
# synchronizing the updated content for all transcluded copies.
<<<
* To @@edit the content@@, click on the content text.
* To @@edit the transclusion macro commands@@, see below (watch the focusing borders).
!! {{{<<tiddler>>}}} transcluded
Text before.
<<tiddler 'twve--Example--Transclusion Instructions##The tiddler macro'>>
Text after.
!! {{{<<slider>>}}} transcluded
Text before.
<<slider "" 'twve--Example--Transclusion Instructions##The slider macro' 'The slider macro'>>Text after.
!! {{{<<tabs>>}}} transcluded
<<tabs ''
'The tabs macro' "" 'twve--Example--Transclusion Instructions##The tabs macro'
>>
<<<
This tiddler shows the plugins work for //self-inclusion// cases.
* This was brought up by Yakov at the time when the plugins did not work for such cases. Now they seem to work.
<<<
!!!!Section
Some section text here to be self-included at the end.
!!!!!!end of section
|editable|k
|let's move the colomns|c
|h-cell4|h-cell1|h-cell3|h-cell2|h
|c14|c13|c12|c11|
|c24|c34|c22|c21|
<<tiddler [[twve--Example--Transclusion--Self Inclusion##Section]]>>
> This tiddler describes information about //wrappers//.
A wrapper is a container that holds all or part of a tiddler's content. In ~TiddlyWiki the built-in wrappers include
<<<
* containers holding __normally loaded__ tiddler's content -- {{{div.viewer}}};
* containers holding all or part of another tiddler's content __transcluded through {{{<<tiddler>>}}} macro__ -- {{{span[tiddler]}}};
* containers holding all or part of another tiddler's content __transcluded through {{{<<tabs>>}}} macro__ -- {{{div.tabContents}}};
* containers holding all or part of another tiddler's content __transcluded through {{{<<slider>>}}} macro__ -- {{{div.sliderPanel}}}.
<<<
The ''twve'' supports view mode editing and transclusion synchronization over all the above built-in wrappers. You can click on some empty space of the wrapper and edit the content held in it. After finished just click away or press {{{Ctrl-Enter}}} to accept changes, the ''twve'' updates its output and synchronizes its other copies, if any.
For example,
* to edit the content of a normally loaded tiddler (held in a {{{div.viewer}}}), simply move your mouse cursor somewhere close to the edge of the wrapper, when the focusing borders enlarges to include the whole wrapper, click to edit.
* To edit transcluded content, see [[twve--Example--Transclusion--Content]].
* To edit transclusion macros, see [[twve--Example--Transclusion--Macros]].
In addition to bulit-in wrappers, the ''twve'' also works with FoldHeadingsPlugin, implemented in ''twve.extra'' module. See [[twve--Example--Folded Sections]] for information and examples.
name: value
|name:|value|
|name|value|
|''name:''|value|
|//~WikiName//|value|
/***
|editable|k
|''Name:''|twve.core|
|''Description:''|Core codes of twve, the ~TiddlyWiki View mode Editor, providing easy access to the defining wiki text corresponding to a DOM element, and the synchronization capability for multiply transcluded copies of a tiddler.|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.core.min / http://twve.tiddlyspace.com/#twve.core.min <br>* (regular) http://twve.tiddlyspot.com/#twve.core / http://twve.tiddlyspace.com/#twve.core|
|''Type:''|plugin|
|''Version:''|3.2.6|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
!!Features:
* Access to the defining wiki text corresponding to a DOM element.
** Locate the wiki text corresponding to a given DOM element.
** Get/Set the wiki text corresponding to a given DOM element.
* Synchronization between multiply transcluded copies in ~TiddlyWiki environment.
** Synchronize the output of multiply transcluded copies of a DOM element after changes.
!!Usage:
!!Options
Look for [[twve.core Options]] in the Options Panel.
!!Examples
[[twve.core Examples]]
!!Todo:
!!!Revision history
!!!! 2016/04/30 [3.2.6]
* Saves options in SystemSettings tiddler.
** If you disable autoSave option, you will need to manually save the tiddler after changing a ''twve'' option.
* Starts using strict mode.
* Better mobile supports.
* Bug fix
** for identification of IE v.11;
** for wiki text of elements;
** for object handling;
** for style text handling.
!!!! 2015/11/06 [3.2.5]
* Bug fix
** for correct location of wiki text within and after block examples;
** for headings.
!!!! 2015/06/15 [3.2.4]
* Added support to regular expression. One can now use regular expressions in the open and close tags.
* Merged {{{twelem.updaText()}}} and {{{twwrap.updaText()}}} into {{{editable.updateText()}}} for they share the same codes.
* Bug fix
** for copying array objects;
** for wiki text searching (minor).
!!!! 2015/03/23 [3.2.3]
* Added supports for CSS wrappers created using """{{Class name{ .... }}}""".
* Bug fix
** for correctly locating spans created with a pair of {{{@@}}}'s.
!!!! 2015/02/13 [3.2.2]
* Replaced regular expression with string operations whenever easy, hoping to save some time.
* Bug fix
** for handling css style text;
** for synchronization of partial transcluded content;
** for identifying IE11;
** for resizing the browser window.
!!!! 2014/12/22 [3.2.1]
* Bug fix
** for handling styled text;
** for editing an empty text line;
** for mobile devices to hide the edit box upon clicking away;
** for locating the first block element at the beginning of a tiddler;
*** A block element usually starts at the beginning of a text line, meaning there would be a newline character before this block element, if it is not the first child of this tiddler. The ~TiddlyWiki consumes this leading newline in such cases (there would be no BR created for that newline).
*** If, however, the block element is the first (non-BR) child of the tiddler, and there are newline characters before it, the ~TiddlyWiki does not consume the newline just before the block element, a behavior different from the above cases.
** for partial refreshing before and after a tiddler;
** for transclusion synchronization.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
* Bug fix
** for refreshing a mixture of partially transcluded sections/slices;
** for transclusion synchronization of transcluded slices;
** for floating menu auto-positioning;
** for keeping a minimum edit box width.
!!!! 2014/08/03 [3.1.0]
* Supports editing of transcluded slices.
* Started to remove jQuery dependencies for migrating to TW5.
* Bug fix
** for occasionally oversize preview box;
*** Sometimes the preview box gets unreasonably larger than the expected height, and the reason seemed related to the width of the previewer: If the width was much smaller in the last time, it gets higher, depending on the length of the content, in this preview.
*** The fix: Set the previewer width to that of the editbox before wikifying the content.
** for obtaining the bounding box of elements;
** for transclusion synchronization with self inclusion and FoldedSectionPlugin.
** for twve.node.info(node) when node is null or undefined.
!!!! 2014/06/02 [3.0.3]
* Added option {{{chktwveCoreEditWrappers}}} to enable/disable wrapper editing (including tiddler title).
** The Options Menu still available at the top-right corner of the tiddler title area.
* Moved out the codes to edit heading, preformatted blocks, and code blocks to ''twve.extra''.
* Bug fixes
** for the width of the bounding box;
*** Previously the ''twve'' used the {{{.clientWidth}}} property of a DOM element to determine the width of the bounding box, which worked fine in most of the browser/OS combinations, except for Opera/Linux. This version uses {{{.innerWidth()}}} method of jQuery and it works fine.
** for compatibility with {{{NestedSlidersPlugin}}};
** for transclusion synchronization with self-inclusion.
!!!! 2014/05/23 [3.0.2]
* Smarter positioning of the floating menu.
** If the menu is too wide to show to the left, it will be shown to the right.
* Moved the following codes from ''twve.extra'':
** that to avoid editing when clicking a link;
** that to show Options Menu for ''twve.core''.
* Bug fixes
** for showing the Options Menu in Andriod/iOS;
** for getting the bounding box of a textnode;
** for adjusting the focusing borders upon switching tabs;
** for editing the tiddler title;
** for distinguishing CODE blocks and PRE blocks that are created with """{{{""" and """}}}""";
** for previewer to work with FoldHeadingsPlugin;
** for """$dom[0] is undefined""" error in node.isVisible() with TiddlySpot.
!!!! 2014/05/14 [3.0.1]
* Moved in the codes for pretty links from ''twve.extra''.
* Removed some debugging codes for handling shadow tiddlers.
!!!! 2014/05/13 [3.0.0]
* ''Easily incorporated into other plugins.''
** Redesigned code structure and largely rewritten code pieces for easy incorporation into other pugins. See [[Edit Your ...]] for more information.
* Improved {{{partial refreshing}}} features.
** Partial refreshing is to refresh only the changed part, instead of the whole of a tiddler, to improve performance of the plugins.
*** In the earliest versions the plugins did not do partial refreshing but only whole tiddler refreshing. This is usually fine when the tiddler is small, but can be slow when is large.
**** In addition, whole tiddler refreshing destroys the original DOM elements and recreates new ones, causing the plugins to pay extra efforts to bookkeep the status of elements for later uses.
*** Later time partial refreshing was implemented for table cells.
*** Even later time this feature was extended to cover other elements, such as list items, headings, blockquotes, etc, for simple cases.
**** For not-so-simple cases, such as changed level in headings or list items, or whole table refreshing, the plugins still did whole tiddler refreshing.
*** This version further extended the feature to cover whole tables and transcluded sections.
* Bug fixes
** for correct section title searching
*** Previously the section title was located with the criteria {{{.indexOf()>-1}}}, which could make mistakes when one section title is part of another preceding it. This version fixed this issue by making sure the section title, after removing the leading !'s and white spaces, does start with the searched pattern.
** for consistent alignment behavior in preview box
** for element level transclusion synchronization
*** Refreshing a table contained in a {{{FoldHeadingsPlugin}}} folded section can mess up the page. It is fixed in this version.
* Changed the return policy of internal functions {{{indexOf}}} and {{{lastIndexOf}}}.
** These two internal functions search in a string for 1) a certain pattern or 2) an array of equivalent patterns. Because of 2) their return value is an object consisting of {{{ndx}}} and {{{matched}}} properties. If a pattern is found, {{{ndx}}} is the index of the matched pattern, while the {{{matched}}} is the actually matched pattern. If, however, no pattern is found, the previous version set the {{{ndx}}} to {{{-1}}} but left the {{{matched}}} untouched as whatever it was, causing confusion some times. This version clears the {{{matched}}} property if no pattern is found.
!!!! 2014/01/18
* Collected from [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] and [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]], adapted to the new code structure.
!!Code
!!!The very beginning
***/
//{{{
var twve = {};
(function(){
"use strict";
// twve.checkOption
twve.checkOption = function(name,def){
var val = config.options[name];
if ( val === undefined )
setOption(name,def);
if(config.optionsSource[name] !== 'setting'){
config.optionsSource[name] = 'setting';
saveOption(name);
}
};
// twve.isMobile
twve.isMobile = function(){
return (config.browser.isAndroid || config.browser.isiOS);
};
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve = {
major: 3, minor: 2, revision: 6,
date: new Date('2016/04/30')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.twveCoreOptions = {
identifyWebkitBrowser : function(){
if ( config.browser.isSafari )
if ( config.browser.isChrome ) {
config.browser.isSafari = false;
if ( config.userAgent.indexOf('opr')>=0 ) {
config.browser.isOpera = true;
config.browser.isChrome = false;
}
}
},
// Initialization procedure
init : function () {
// From TWtid
twve.checkOption(
'chktwveCoreEnabled',
(config.options.chkTWtidEnabled || true)+''
);
twve.checkOption(
'chktwveCoreShowFocus',
(config.options.chkTWtidShowFocus || true)+''
);
merge ( config.optionsDesc, {
chktwveCoreEnabled:"Enable ''twve.core''.",
chktwveCoreShowFocus:'Show focus borders.'
} );
twve.tiddler.prePopupShow = Popup.show;
Popup.show = twve.tiddler.popupShow;
twve.tiddler.preRefreshTiddler = story.refreshTiddler;
story.refreshTiddler = twve.tiddler.refreshTiddler;
// Macro {{{<<tabs ...>>}}} is not using story.refreshTiddler() to
// render contents upon switching tabs, so tables inside don't
// get a chance to reformat. Hijack the function here to take
// care of tab switching.
twve.tiddler.preTabsSwitchTab = config.macros.tabs.switchTab;
config.macros.tabs.switchTab = twve.tiddler.tabsSwitchTab;
// Macro {{{<<tiddler ...>>}}} is not storing tiddler.title
// anywhere, leaving this plugin no way but hijacking its
// transclude() function to find it.
twve.tiddler.preTiddlerTransclude=config.macros.tiddler.transclude;
config.macros.tiddler.transclude=twve.tiddler.tiddlerTransclude;
// Macro {{{<<slider ...>>}}} directly calls wikify() in its
// handler, which does not go through refreshTiddler() function
// so that twve overrides the handler for its own needs.
twve.tiddler.preSliderHandler = config.macros.slider.handler;
config.macros.slider.handler = twve.tiddler.sliderHandler;
twve.tiddler.preOnClickSlider = config.macros.slider.onClickSlider;
config.macros.slider.onClickSlider = twve.tiddler.onClickSlider;
// IE browser
if ( /windows nt (.*?)rv:11/.test(config.userAgent) ) {
config.browser.isIE = config.browser.isIE11 = true;
} else if ( config.browser.isIE ) {
var match = config.userAgent.match(/msie 7(.*?)trident\//);
if ( match ) {
var pos = match.index+match[0].length;
switch ( config.userAgent.charAt(pos) ) {
case '7' :
config.browser.isIE11 = true;
break;
case '6' :
config.browser.isIE10 =
config.browser.isIE1076 = true;
break;
case '5' :
config.browser.isIE975 = true;
break;
case '4' :
config.browser.isIE874 = true;
break;
}
} else {
var pos = config.userAgent.indexOf('msie ');
if ( pos >= 0 )
switch ( config.userAgent.charAt(pos+5) ) {
case '1' :
config.browser.isIE10 = true;
break;
case '9' :
config.browser.isIE9 = true;
break;
case '8' :
config.browser.isIE8 = true;
break;
}
}
if ( config.browser.isIE
&& ! ( config.browser.isIE10
|| config.browser.isIE1076
|| config.browser.isIE9
|| config.browser.isIE975
|| config.browser.isIE8
|| config.browser.isIE874 ) )
displayMessage(
"''twve'' message: Unsupported IE version.\n\n "
+config.userAgent
+'\n\nPlease inform the author at'
+' qmo.wcy2@gmail.com. Thanks a lot.'
);
}
//console.log(config.userAgent+'/'+config.browser.isIE);
// Operating system
var pos = config.userAgent.indexOf('nt ');
if ( pos >= 0 ) {
switch ( config.userAgent.substring(pos+3,pos+6) ) {
case '6.1' :
config.browser.isWin7 = true;
break;
case '6.3' :
config.browser.isWin8 = true;
break;
case '5.1' :
config.browser.isWinXP = true;
break;
}
} else if (config.userAgent.indexOf('android')>=0){
// Andtidwiki would be recognized as Safari on Linux and android.
config.browser.isAndroid = true;
} else if (/ip(.*?)mac/.test(config.userAgent)) {
config.browser.isiOS = true;
} else
// Distinguish webkit browsers.
config.macros.twveCoreOptions.identifyWebkitBrowser();
window.addEventListener(
'resize',
function(ev){
twve.tiddler.resize(ev || window.event);
}
);
/*
window.onscroll = function(ev){
twve.tiddler.scroll(ev || window.event);
};
*/
// Newly defined
twve.checkOption('chktwveCoreEditWrappers','true');
merge ( config.optionsDesc, {
chktwveCoreEditWrappers:"Edit wrappers and tiddler title, default to false<br>(The ''twve.extra'' will automatically enable this feature, regardless of this setting)."
});
// From TWted
twve.checkOption(
'chktwveCorePreview',
(config.options.chkTWtedPreview || true)+''
);
twve.checkOption(
'txttwveCorePreviewHeight',
(config.options.txtTWtedPreviewMaxHeight || '15')
);
twve.checkOption('txttwveCoreMinEditWidth','6');
twve.checkOption(
'txttwveCorePreviewCaret',
(config.options.txtTWtedPreviewCaret || '|')
);
if (/^[0-9]+$/.test(config.options.txttwveCorePreviewCaret))
config.options.txttwveCorePreviewCaret=String.fromCharCode(
config.options.txttwveCorePreviewCaret*1
);
twve.checkOption(
'chktwveCoreConfirmToDelete',
(config.options.chkTWtedConfirmToDelete || true)+''
);
twve.checkOption(
'chktwveCoreClickAway',
(config.options.chkTWtedClickAway || true)+''
);
twve.checkOption(
'chktwveCoreManualSave',
(config.options.chkTWtedManualSave || true)+''
);
twve.checkOption(
'chktwveCoreManualUpload',
(config.options.chkTWtedManualUpload || true)+''
);
merge ( config.optionsDesc, {
chktwveCorePreview:'Enable previewer. Default to true.',
txttwveCoreMinEditWidth:'Minimum edit box width (characters). Default value is 6.',
txttwveCorePreviewHeight:'Previewer max height (lines of text). Default to 15.',
txttwveCorePreviewCaret:'Caret in the previewer. Default to vertical line (|)',
chktwveCoreConfirmToDelete:'Confirm before deleting elements.',
chktwveCoreClickAway:'Click away to accept changes.',
chktwveCoreManualSave:'Save changes to file manually. If true, a button labeled "S" will be provided for manual save. Otherwise the file will be saved each time a change is accepted.',
chktwveCoreManualUpload:'Upload changes to server manually. If true, a button labeled "U" will be provided for manual upload. Otherwise all changes will be uploaded each time a change is accepted.'
} );
//twve.tiddler.preCloseAllTiddlers = story.closeAllTiddlers;
//story.closeAllTiddlers = twve.tiddler.closeAllTiddlers;
twve.tiddler.preEditHandler=config.commands.editTiddler.handler;
config.commands.editTiddler.handler=twve.tiddler.editHandler;
twve.tiddler.precloseTiddler=story.closeTiddler;
story.closeTiddler=twve.tiddler.closeTiddler;
twve.tiddler.preSaveHandler=config.commands.saveTiddler.handler;
config.commands.saveTiddler.handler=twve.tiddler.saveHandler;
twve.tiddler.preCancelHandler=
config.commands.cancelTiddler.handler;
config.commands.cancelTiddler.handler=twve.tiddler.cancelHandler;
twve.tiddler.preSaveChanges = saveChanges;
saveChanges = twve.tiddler.saveChanges;
twve.wrapper.prepareElements = twve.heading.prepareElements;
// Prepare the Options panel
var optionsTitle = 'twve.core Options';
var optionsTiddler = store.getTiddler(optionsTitle);
if ( ! optionsTiddler ){
//optionsTiddler = store.createTiddler(optionsTitle);
//optionsTiddler.creator = config.options.txtUserName;
}
var txt = config.shadowTiddlers['OptionsPanel'];
var p = txt.indexOf('----');
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'----\n'
+'[['+optionsTitle+'|'+optionsTitle+']]\n'
+txt.substring(p);
merge(config.shadowTiddlers,{
'twve.core Options':'<<twveCoreOptions>>'
});
// Register wrappers
twve.tiddler.registerWrapper(twve.tiddlerTitle);
twve.tiddler.registerWrapper(twve.viewer);
twve.tiddler.registerWrapper(twve.tabContent);
twve.tiddler.registerWrapper(twve.tiddlerSpan);
twve.tiddler.registerWrapper(twve.sliderPanel);
},
order : {
// From TWtid
chktwveCoreEnabled:0,
chktwveCoreShowFocus:1,
// Newly defined
chktwveCoreEditWrappers:2,
// From TWted
txttwveCoreMinEditWidth:3,
txttwveCorePreviewHeight:4,
chktwveCorePreview:5,
txttwveCorePreviewCaret:6,
chktwveCoreConfirmToDelete:7,
chktwveCoreClickAway:8,
chktwveCoreManualSave:9,
chktwveCoreManualUpload:10
},
collectOptions : function (key, order) {
// Collect twve options.
var ttopts = [];
for ( var n in config.options ) {
if ( n.indexOf(key) >= 0 ) {
var msg = config.optionsDesc[n];
if ( ! msg ) continue;
var pdot = msg.indexOf('.');
if ( pdot > -1 ) {
if ( msg.substring(pdot-4,pdot) == 'twve' ) {
pdot = msg.indexOf('.',pdot+1);
pdot == -1 ? (pdot=msg.length) : (pdot++);
}
} else pdot = msg.length;
ttopts[ttopts.length] = (
'\n|<<option '+n+'>>|'+msg.substring(0,pdot)+' |'
);
}
}
// Sort them according to that defined in option_order[].
if ( order )
ttopts.sort(function(a,b){
var ka=a.substring(a.indexOf(' ')+1,a.indexOf('>>')),
kb=b.substring(b.indexOf(' ')+1,b.indexOf('>>'));
return order[ka] > order[kb] ? 1 : -1;
});
return ttopts;
},
prepareOptionsTable : function (cap,key,order) {
var opts = '|'+cap+'|c'+'\n| Value | Description |h';
var ttopts = this.collectOptions(key, order);
for ( var i=0,len=ttopts.length; i<len; i++) {
opts += ttopts[i];
}
return opts;
},
showOptionsTable : function (place, cap, key, order) {
var div_opt = document.createElement('div');
place.appendChild(div_opt);
var opts = this.prepareOptionsTable(cap,key,order);
// Render the table.
wikify ( opts, div_opt );
// Adjust width
var table = div_opt.querySelector('table');
twve.node.setDimension(table,'35em');
twve.node.setDimension(
table.querySelectorAll('input'),'5em'
);
},
handler : function(place) {
// Collect all the twve options for users to play with.
config.macros.twveCoreOptions.showOptionsTable(
place,
"''twve.core'' Options",
'twveCore',
config.macros.twveCoreOptions.order
);
}
};
//}}}
/***
!!! twve definitions
The ''twve'' is divided into several components:
# General purpose components:
## ''twve.object'': base for all twve objects;
# Tiddler and wiki text related:
## ''twve.tiddler'': representing the tiddlers;
## ''twve.text'': wiki text handling;
## ''twve.tags'': opening and closing tags of elements;
## ''twve.position'':
# DOM related components:
## ''twve.selector'':
## ''twve.node'': base for all DOM related objects
## ''twve.button'':
## ''twve.menu'': floating menu
## ''twve.editable'': base for twve.element and twve.wrapper;
## ''twve.element'': base for editable elements;
### ''twve.heading'':
### ''twve.pre'': preformatted block
### ''twve.code'': code block
## ''twve.wrapper'': base for editable wrappers;
### ''twve.viewer'': for normally loaded tiddlers;
### ''twve.tabContent'': for {{{<<tabs>>}}} transcluded tiddlers;
### ''twve.tiddlerSpan'': for {{{<<tiddler>>}}} transcluded tiddlers;
### ''twve.sliderPanel'': for {{{<<slider>>}}} transcluded tiddlers;
### ''twve.foldedSection'': for {{{<<foldHeadings>>}}} folded sections (defined in twve.extra);
### ''twve.tiddlerTitle'': for tiddler title;
The following lines of codes show the definition of each part.
!!! twve.object
***/
//{{{
twve.object = {
/*
isArray : function(obj){
return obj.constructor.toString().indexOf("Array") > -1;
return Object.prototype.toString.call(obj).indexOf('Array')>-1;
},
*/
isCollection : function(obj){
return obj && obj.nodeType != 3 &&
obj.length !== undefined &&
typeof obj == 'object';
},
//}}}
/***
!!!! twve.object.toString
***/
//{{{
level : 0,
toString : function (obj) {
switch ( typeof obj ) {
case 'function' :
return 'function';
case 'object' :
if ( ! obj ) return obj;
if ( obj.nodeType ) {
return twve.node.info(obj);
} else if ( obj.title && obj.modifier ) {
// tiddler object
return '{title: '+obj.title+' ...}';
}
var val = '{';
var keys = twve.object.keys(obj);
for ( var n=0,len=keys.length; n<len; n++ ){
val += (n==0?'':', ')+'\n\t'+keys[n]+':';
var v = obj[keys[n]];
if ( v ) {
if ( ++twve.object.level < 5 ) {
val += twve.object.toString(v);
--twve.object.level;
}
} else
val += v;
}
val += '\n}';
return val;
default :
return obj;
}
},
//}}}
/***
!!!! twve.object.keys
***/
//{{{
keys : function(obj,all) {
var keys = [];
for ( var key in obj ) {
if ( all || typeof obj[key] != 'function' ) {
keys[keys.length] = key;
}
}
return keys;
},
//}}}
/***
!!!! twve.object.copyKey
***/
//{{{
copyKey : function(dest,src,key){
switch ( typeof src[key] ) {
case 'string' :
case 'number' :
case 'boolean' :
case 'function' :
dest[key] = src[key];
break;
default :
if ( ! src[key] ) break;
if ( src[key].length !== undefined ) {
// An object with the property "length".
var len = src[key].length;
if (len == 0){
if ( dest[key] && dest[key].clear )
dest[key].clear();
else
dest[key] = [];
} else {
if ( ! dest[key] ) dest[key] = [];
for (var i=0; i<len; i++)
dest[key][i] = src[key][i];
}
} else if ( src[key].getFullYear ) {
// Date
dest[key] = new Date(src[key]);
break;
} else {
// An object without the property "length".
if ( ! dest[key] ) dest[key] = twve.object.create();
}
// Copy src[key] to dest[key].
var keys = twve.object.keys(src[key],true);
for ( var n=0,len=keys.length; n<len; n++ )
twve.object.copyKey(dest[key],src[key],keys[n]);
break;
}
return dest;
},
//}}}
/***
!!!! twve.object.copyKeys
***/
//{{{
copyKeys : function(dest,src,keys){
for ( var i=0, len=keys.length; i<len; i++ ) {
twve.object.copyKey(dest,src,keys[i]);
}
return dest;
},
//}}}
/***
!!!! twve.object.create
***/
//{{{
create : function(src) {
var obj = new Object;
// clone
obj.clone = function(){
return twve.object.create(obj);
};
// copyKey
obj.copyKey = function(src,key){
return twve.object.copyKey(obj,src,key);
};
// keys
obj.keys = function(){
return twve.object.keys(obj);
};
// copyFrom
obj.copyFrom = function(src,keys){
return twve.object.copyKeys(
obj, src,
keys || src.keys()
);
};
// toString
obj.toString = function (){
return twve.object.toString(obj);
}
// created
obj.created = function(src){
return src
? obj.copyFrom(src)
: obj;
}
// End of obj
return obj.created(src);
}
};
//}}}
/***
!!! twve.text
***/
//{{{
twve.text = {
//}}}
/***
!!!! twve.text.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
[ "\'", '\"' ],
[ "\'", '\"' ]
);
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.text.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.text.tiddlerSlice
***/
//{{{
tiddlerSlice : function(title) {
if ( ! title ) return '';
var p = title.indexOf(config.textPrimitives.sliceSeparator);
return p > -1
? title.substring
(p+config.textPrimitives.sliceSeparator.length)
: '';
},
//}}}
/***
!!!! twve.text.sectionTitle
***/
//{{{
sectionTitle : function(sec) {
// Skip the leading !'s and white spaces.
// This is added after 2.0.9
var p = 0;
var regex = /[! \t]/;
while(regex.test(sec.charAt(p))) p++;
return p > 0 ? sec.substring(p) : sec;
},
//}}}
/***
!!!! twve.text.tiddlerSection
***/
//{{{
tiddlerSection : function ( title ) {
if ( ! title ) return '';
var p = title.indexOf(config.textPrimitives.sectionSeparator);
if ( p > -1 ) {
var sec = title.substring(
p + config.textPrimitives.sectionSeparator.length
);
// Skip the leading !'s and white spaces.
sec = twve.text.sectionTitle(sec);
// Restore — (8212=\u2014) back to double dash.
return sec.replace('\u2014','--');
}
return '';
},
//}}}
/***
!!!! twve.text.tiddlerTitle
***/
//{{{
tiddlerTitle : function ( title ) {
if ( ! title ) return '';
var p = title.indexOf(config.textPrimitives.sectionSeparator);
if ( p == -1 )
p = title.indexOf(config.textPrimitives.sliceSeparator);
return p > -1 ? title.substring(0,p) : title;
},
//}}}
/***
!!!! twve.text.headerToSkip
***/
//{{{
headerToSkip : function(title){
var pq = title ? title.lastIndexOf('?') : -1;
if ( pq > 0 && pq < title.length-1 ) {
var to_skip = title.substring(pq+1);
if ( /^\d$/.test(to_skip) )
return to_skip*1;
}
return 0;
},
//}}}
/***
!!!! twve.text.removeHeaderTail
***/
//{{{
removeHeaderTail : function (title) {
var pq = title.lastIndexOf('?');
if ( pq > 0 && pq < title.length-1
&& ! /[\D]/.test(title.substring(pq+1)) ) {
title = title.substring(0,pq);
}
return title;
},
//}}}
/***
!!!! twve.text.lastIndexOf
***/
//{{{
lastIndexOf : function(str,val,start,all){
// Search the last index of val in str, starting from start.ndx.
// (The 3rd argument start MUST be a twve.position object.)
// The being searched val could be a string or an array of
// strings.
// If val is a string, it simply returns
// str.lastIndexOf(val,start.ndx).
// If, however, val is an array of strings, the function
// compares all the elements in the array and considers it
// found if any of the elements is found.
// Returns a twve.object object containing the following
// information:
// {
// ndx: last index of val found, -1 otherwise.
// matched: the matched string
// }
if ( ! val ) {
start.ndx = -1;
start.matched = '';
} else if ( typeof val == 'string' ) {
// val is a string
start.ndx = str.lastIndexOf(val,start.ndx);
start.matched = start.ndx > -1 ? val : '';
} else if ( val.exec ) {
// val is a regular expression, apply binary search on the sub-
// string before start.ndx, and find a match that is closest
// to start.ndx.
var posL = 0;
var posR = start.ndx;
while ( true ) {
var leftstr = str.substring(posL,posR);
if ( ! leftstr ) break;
// Look in the right half (closer to start.ndx).
val.lastIndex = Math.round(leftstr.length/2);
var match = val.exec(leftstr);
if ( match ) {
// found a match in the right half, save its position
// and matched pattern, then look for an even closer
// one if applicable
start.ndx = posL+match.index;
start.matched = match[0];
posL = start.ndx+start.matched.length;
} else {
// try the left half
posR = Math.round(posL+leftstr.length/2);
}
}
} else if ( val.length ) {
// val is an array
var ndx = [], vlen = val.length;
for ( var i = 0; i < vlen; i++ ) {
ndx[i] = twve.position.create(start);
twve.text.lastIndexOf(str,val[i],ndx[i],all);
}
start.ndx = ndx[0].ndx;
start.matched = ndx[0].matched;
var last = start;
for ( var i = 1; i < vlen; i++ ) {
if ( ndx[i].ndx == -1 ) continue;
if ( start.ndx == -1 || (!all && ndx[i].ndx>start.ndx) ) {
start.ndx = ndx[i].ndx;
start.matched = ndx[i].matched;
} else {
last.next = twve.position.create(ndx[i]);
last = last.next;
}
}
}
return start;
},
//}}}
/***
!!!! twve.text.indexOf
***/
//{{{
indexOf : function (str,val,start,all) {
// Search the index of val in str.
// The being searched val could be a string or an array of
// strings.
// If val is a string, it simply returns
// str.indexOf(val,start.ndx). If, however, val is an array
// of strings, the function compares all the elements in the
// array and considers it found if any of the elements is
// found.
// Returns a twve.position object containing the following
// information:
// {
// ndx: index of val found, -1 otherwise.
// matched: the matched string
// }
if ( ! val ) {
start.ndx = -1;
start.matched = '';
} else if ( typeof val == 'string' ) {
// val is a string
start.ndx = str.indexOf(val,start.ndx);
start.matched = start.ndx > -1 ? val : '';
} else if ( val.exec ) {
// val is a regular expression
val.lastIndex = start.ndx;
var match = val.exec(str);
if ( match ) {
start.ndx = match.index;
start.matched = match[0];
} else {
start.ndx = -1;
start.matched = '';
}
} else if ( val.length ) {
// val is an array
var ndx = [], vlen = val.length;
for ( var i = 0; i < vlen; i++ ) {
ndx[i] = twve.position.create(start);
twve.text.indexOf(str,val[i],ndx[i],all);
}
start.ndx = ndx[0].ndx;
start.matched = ndx[0].matched;
var last = start;
for ( var i = 1; i < vlen; i++ ) {
if ( ndx[i].ndx == -1 ) continue;
if ( start.ndx == -1 || (!all && ndx[i].ndx<start.ndx) ) {
start.ndx = ndx[i].ndx;
start.matched = ndx[i].matched;
} else {
// Search for all possible matches and we are finding
// more than one.
last.next = twve.position.create(ndx[i]);
last = last.next;
}
}
}
return start;
},
//}}}
/***
!!!! twve.text.skipToActive
***/
//{{{
skipToActive : function(txt,pattern,start,end,inactiveTags,dir){
// Find pattern in txt, skipping those enclosed by inactive
// tags, along direction dir. The last argument, dir, is a number
// indicating the search direction: a negative number means to
// search backward, otherwise forward. Default direction is forward.
// The pattern to be searched can be either a single string or an
// array of strings.
// Returns a twve.position object that consists of
// {
// ndx: The index of the next pattern that is active
// (not enclosed by any inactive tags) if found, or
// -1 otherwise.
// matched: The matched pattern if found, or empty string if
// not. If found, then this is the pattern itself if
// the pattern is a single string, or the actually
// matched pattern if pattern is an array of strings.
// }
if ( ! start ) start = twve.position.create(0);
else if ( typeof start == 'number' )
start = twve.position.create(start);
if ( typeof dir !== 'number' ) dir = 1;
if ( ! end )
end = twve.position.create(
dir < 0 ? 0 : txt.length
);
else if ( typeof end == 'number' )
end = twve.position.create(end);
var pos = twve.position.create(start);
if ( dir < 0 ){
pos = twve.text.lastIndexOf(txt,pattern,pos);
if ( pos.ndx < end.ndx ) { pos.ndx = -1; return pos; }
} else {
pos = twve.text.indexOf(txt,pattern,pos);
if ( pos.ndx == start.ndx ) return pos;
if ( pos.ndx > end.ndx ) { pos.ndx = -1; return pos; }
}
if ( ! inactiveTags ) inactiveTags = twve.tags.inactiveTags();
var inactive = null;
if ( dir < 0 ){
while(pos.ndx > end.ndx && pos.ndx < start.ndx &&
(inactive=inactiveTags.encloses(txt,pos.ndx,end,start))){
pos.ndx = inactive.start.ndx;
pos = twve.text.lastIndexOf(txt,pattern,pos);
}
if (pos.ndx < end.ndx || pos.ndx > start.ndx) pos.ndx = -1;
} else {
while(pos.ndx > start.ndx && pos.ndx < end.ndx &&
(inactive=inactiveTags.encloses(txt,pos.ndx,start,end))){
pos.ndx = inactive.end.ndx+inactive.end.matched.length;
pos = twve.text.indexOf(txt,pattern,pos);
}
if (pos.ndx < start.ndx || pos.ndx > end.ndx) pos.ndx = -1;
}
return pos;
},
//}}}
/***
!!!! twve.text.consecutiveLinesEnd
***/
//{{{
consecutiveLinesEnd : function (txt,start,alltheway,ch,tags,dir){
// This function is shared by Table and Blockquote for their
// "consecutive lines" characteristic: a single Table or
// Blockquote can be defined by one or more consecutive lines
// of text with the same opening signature. This function
// searches the end of the last line.
// Arguments:
// txt: tiddler text
// start: starting position to search, can be either
// 1. a twve.position object, or
// 2. a number
// alltheway True to go beyond any immediately following
// sub-blockquotes. This is specifically for
// blockquote that contain sub-blockquotes within
// its body. For example,
//
// > Blockquote starts here
// > The second line
// >> A sub blockquote immediately follows here
// >> and ends here
// > Back to the parent blockquote
// > End of the parent blockquote
//
// For blockquotes like the example above, the
// end of the parent blockquote exceeds that of
// its sub-blockquote.
//
// When search for the end of the parent
// blockquote, we go "all the way" to the last
// line. When search for the beginning of the
// sub blockquote, however, we do not go all
// the way but stop when we see the level changes.
var p0 = 0;
switch (typeof start) {
case 'number' :
p0 = start;
break;
case 'object' :
if ( start.ndx ) p0 = start.ndx;
break;
}
var txtlen = txt.length;
var end = twve.position.create(p0);
if ( typeof dir !== 'number' ) dir = 1;
var level = 0, nextlevel = 0, nextstart = null;
// Start the search.
if ( dir < 0 ) {
// Going backward, move to the last end of line.
if (txt.charAt(p0) != '\n') p0--;
// Find the level of this line.
level = twve.leveledElement.getLevel(txt,++p0,ch);
if ( level == 0 )
// This line is not one of the expected consecutive
// lines, return where we started.
return end;
if(!ch) ch = txt.charAt(p0);
// Record the beginning of this line.
nextstart = twve.position.create(
p0-1,('\n'+txt.substring(p0,p0+level))
);
if ( ! tags )
tags = twve.tags.create(nextstart.matched,'\n');
while(true) {
end.copyFrom(nextstart);
nextstart.ndx--;
// Find the beginning of last line.
nextstart = twve.text.lastIndexOf(
txt,tags.open,nextstart
);
if ( nextstart.ndx == -1 ) {
// No more consecutive lines. Check the beginning
// of tiddler text.
tags.firstOpenTag(txt,nextstart);
if ( nextstart.ndx == 0 )
end.copyFrom(nextstart);
break;
}
// Find the level of the last line.
nextlevel = twve.leveledElement.getLevel(
txt,nextstart.ndx+1,ch
);
// Find the end of that line.
p0 = txt.indexOf('\n',nextstart.ndx+1);
if (nextlevel == 0 || p0 != end.ndx ||
// If the last line is not one of these consecutive
// lines, or
(!alltheway && nextlevel!=level))
// the last line has a different level than this
// one, and we are not going all the way to the
// very beginning, return the begining of this line.
break;
// Otherwise go on to the even last line.
}
} else {
// Going forward, move to the beginning of line.
if ( txt.charAt(p0) == '\n' )
p0++;
else while ( p0>0 && txt.charAt(p0-1)!='\n' )
p0--;
// p0 should point at the first character of a line of text.
// Find the level of this line.
level = twve.leveledElement.getLevel(txt,p0,ch);
if ( level == 0 )
// This line is not one of the expected consecutive
// lines, return where we started.
return end;
if(!ch) ch = txt.charAt(p0);
// Record the begining of this line.
nextstart = twve.position.create(
p0-1,('\n'+txt.substring(p0,p0+level))
);
if ( ! tags )
tags = twve.tags.create(nextstart.matched,'\n');
while(true) {
// Find the end of this line.
end.ndx = nextstart.ndx+nextstart.matched.length;
end = twve.text.indexOf(
txt,tags.exactCloseTag(nextstart.matched).close,end
);
if ( end.ndx == -1 ) {
// There is no end of line, return the end of text.
end.ndx = txtlen;
break;
}
// Find the begining of next line.
nextstart.ndx = end.ndx;
nextstart = twve.text.indexOf(txt,tags.open,nextstart);
if ( nextstart.ndx == -1 ||
// No more next lines, or
nextstart.ndx != end.ndx )
// The next line is not an immediately following
// line, return the end of this line.
break;
// Find the level of next line.
nextlevel = twve.leveledElement.getLevel(
txt,nextstart.ndx+1,ch
);
if (nextlevel == 0 ||
// The next line is not one of these consecutive
// lines, or
(!alltheway && nextlevel!=level))
// The next line has a different level than this
// one, and we are not going all the way to the
// very end, return the end of this line.
break;
// Otherwise go on to the even next line.
}
}
return end;
},
//}}}
/***
!!!! twve.text.skipStyleText
***/
//{{{
skipStyleText : function (wikitxt,start,ignoreCSS) {
// Find the position of the first character of the content
// in the wikitext. A wikitext may contain style text at the
// beginning, then followed by its content. This function is
// to find the index of the content's starting character.
// A style text in a table cell must start immediately after
// the beginning '|' that defines that table cell. If the
// wikitxt starts with a blank space then it does not
// contain any style text. If the wikitxt does contain a
// style text, then the style text must follow the
// "attribute:value;" format, in which a colon is inserted
// between attribute and the value text, while a semicolon
// is added to the end of the style text. Therefore to
// identify a style text one needs to
// 1) make sure the wikitxt does not start with a blank space;
// 2) look for the existence of colon-semicolon pairs.
if ( ! wikitxt ) return 0;
if ( typeof start != 'number' || start < 0 ) start = 0;
var regalpha = /[A-Za-z]/;
var regdigit = /[0-9\.]/;
var p0 = start;
var ch = wikitxt.charAt(p0);
if ( ch == '@' ) {
// First character is '@'.
if ( ! ignoreCSS ) return 0;
// Check the 2nd char.
if((ch=wikitxt.charAt(++p0))!='@') return 0;
ch = wikitxt.charAt(++p0);
}
// This char must be an alphabet.
if ( ! regalpha.test(ch) ) return (p0-start);
var p = p0;
var len = wikitxt.length-2;
var pcolon = -1;
while ( p < len ){
ch = wikitxt.charAt(++p);
if ( regalpha.test(ch) ) {
continue;
} else if ( regdigit.test(ch) || ch=='#' ){
if (pcolon==-1) break;
} else if ( ch == '-' ){
if (pcolon>-1) break;
} else if ( ch == ':' ){
if (pcolon > -1) break;
pcolon = p;
} else if ( ch == ';' ){
if (pcolon == -1) break;
// Found one style text, look for the immediately
// following next.
p0 = p+1;
pcolon = -1;
} else
break;
}
return (p0-start);
},
//}}}
/***
!!!! twve.text.removeStyleText
***/
//{{{
removeStyleText : function ( wikitxt ) {
// Remove the style text in the wiki text.
if ( ! wikitxt ) return '';
var p = twve.text.skipStyleText ( wikitxt );
return p < wikitxt.length ? wikitxt.substring(p) : '';
}
};
//}}}
/***
!!! twve.tags
***/
//{{{
twve.tags = {
//}}}
/***
!!!! twve.tags.inactiveTags
***/
//{{{
_inactive_tags : null,
inactiveTags : function(){
// Returns tags of inactiveTags elements: elements that can
// inactivate others, such as preformatted blocks, code, etc.
if ( ! twve.tags._inactive_tags ) {
twve.tags._inactive_tags = twve.tags.create('<<', '>>').merge(
twve.pre.markupTags()
).merge(
twve.code.markupTags()
).merge(
twve.link.markupTags()
).merge(
twve.tags.create('"""', '"""')
);
twve.tags._inactive_tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.tags.create();
}
}
return twve.tags._inactive_tags;
},
//}}}
/***
!!!! twve.tags.create
***/
//{{{
create : function(src,close){
var tags = twve.object.create();
// identicalTag
tags.identicalTag = function(tag1,tag2){
// Return the identical tag among tag1 and tag2.
if ( ! tag1 ) {
// There is no tag1.
if ( tag2 ) return '';
// There is no tag1 nor tag2, compare the opening and
// closing tags of this pair.
tag1 = tags.open;
tag2 = tags.close;
} else if ( ! tag2 )
// There is tag1 but no tag2.
return '';
// There are both tag1 and tag2.
if ( typeof tag1 === 'string' ) {
// tag1 is a string
if ( typeof tag2 === 'string' ) {
// tag2 is also a string
return tag1 === tag2 ? tag1 : '';
} else {
// tag2 is an array
//var pos = twve.position.create(0);
//pos = twve.text.indexOf(tag1,tag2,pos);
//return pos.ndx >= 0 ? tag1 : '';
return tag2.indexOf(tag1)>=0 ? tag1 : '';
}
} else {
// tag1 is an array
if ( typeof tag2 == 'string' ) {
// tag2 is a string
//var pos = twve.position.create(0);
//pos = twve.text.indexOf(tag2,tag1,pos);
//return pos.ndx >= 0;
return tag1.indexOf(tag2)>=0 ? tag2 : '';
} else {
// tag2 is also an array, find the first tag
// string that are both in tag1 and tag2.
//if ( tag1.length != tag2.length ) return false;
//for (var n=0; n<tag1.length; n++)
// if ( tag1[n] != tag2[n] ) return false;
//return true;
for(var n1=0,l1=tag1.length; n1<l1; n1++)
for(var n2=0,l2=tag2.length; n2<l2; n2++)
if(tag1[n1] == tag2[n2]) return tag1[n1];
return '';
}
}
};
// is
tags.is = function(another){
return tags.identicalTag(tags.open,another.open)
&& tags.identicalTag(tags.close,another.close);
};
// notEnclosing
//tags.notEnclosing = function(){
// return tags.open == tags.close;
//};
// clone
tags.clone = function(){
return twve.tiddler.markupTags(tags)
|| twve.tags.create(tags);
};
// firstOpenTag
tags.firstOpenTag = function(txt,start,open){
if ( ! open ) open = tags.open;
// The first open tag at start.ndx.
var p0 = start.ndx <= 0 ? 0 : start.ndx;
if ( typeof open == 'string' ) {
// open is a string
var fullopentag = open;
var len = open.length;
if ( open.charAt(0)=='\n' ) {
// If tag starts with a '\n', remove it.
open = open.substring(1);
len--;
// Skip the leading newline characters.
while ( txt.charAt(p0) == '\n' ) p0++;
}
if ( txt.substring(p0,p0+len) == open ) {
start.ndx = p0;
start.matched = fullopentag;
} else {
start.ndx = -1;
start.matched = '';
}
} else if ( open.exec ) {
// open is a regular expression
open.lastIndex = start.ndx;
var match = open.exec(txt);
if ( match && match.index==start.ndx ) {
start.ndx = match.index;
start.matched = match[0];
} else {
start.ndx = -1;
start.matched = '';
}
} else if ( open.length ) {
// open is an array
for(var n=0,len=open.length; n<len; n++){
tags.firstOpenTag(txt,start,open[n]);
if ( start.ndx != -1 ) return start;
start.ndx = p0;
}
start.ndx = -1;
} else {
// Something wrong
start.ndx = -1;
}
return start;
};
//}}}
/***
!!!!! tags.nextOpenTag
***/
//{{{
tags.nextOpenTag = function(txt,start,inactiveTags) {
if ( start.ndx <= 0 ) {
start.ndx = 0;
tags.firstOpenTag(txt,start);
if ( start.ndx == 0 ) return start;
start.ndx = 0;
} else if (txt.charAt(start.ndx) != '\n' &&
txt.charAt(start.ndx-1) == '\n') {
start.ndx--;
}
return twve.text.skipToActive(
txt,tags.open,start,null,inactiveTags
);
};
//}}}
/***
!!!!! tags.lastOpenTag
***/
//{{{
tags.lastOpenTag = function(txt,start,inactiveTags) {
if ( start.ndx <= 0 ) {
start.ndx = -1;
return start;
}
start = twve.text.skipToActive(
txt,tags.open,start,null,inactiveTags,-1
);
return start.ndx < 0
? tags.firstOpenTag(txt,start)
: start;
};
//}}}
/***
!!!!! tags.exactCloseTag
***/
//{{{
tags.exactCloseTag = function(open){
if ( open && twve.object.isCollection(tags.open) ) {
var exactTags = tags.clone();
exactTags.open = open;
exactTags.close = twve.object.isCollection(tags.close)
? tags.close[tags.open.indexOf(open)]
: tags.close;
return exactTags;
}
return tags;
};
// lastCloseTag
tags.lastCloseTag = function(txt,end,txtlen,close){
// The close tag at the end of text.
if ( ! txtlen ) txtlen = txt.length;
if ( ! close ) close = tags.close;
if ( typeof close === 'string' ) {
// close is a string
var clen = close.length;
if( close.charAt(clen-1)=='\n' &&
txt.charAt(txtlen-1) != '\n' )
// The close tag expects an ending \n, while the
// txt does not have one at the end.
close = close.substring(0,--clen);
if ( txt.substring(txtlen-clen) == close ) {
end.ndx = txtlen-clen;
end.matched = close;
} else {
end.ndx = -1;
end.matched = '';
}
} else if ( close.exec ) {
// close is a regular expression
close.lastIndex = end.ndx;
var match = close.exec(txt);
if(match && match.index+match[0].length<=txtlen){
end.ndx = match.index;
end.matched = match[0];
} else {
end.ndx = -1;
end.matched = '';
}
} else if ( close.length ) {
// close is an array
for(var n=0,len=close.length; n<len; n++){
tags.lastCloseTag(txt,end,txtlen,close[n]);
if ( end.ndx != -1 ) return end;
}
} else {
// Something wrong
end.ndx = -1;
}
return end;
}
//}}}
/***
!!!!! tags.matchedCloseTag
***/
//{{{
tags.matchedCloseTag = function(txt,pos,txtlen,inactiveTags){
// Find the close tag that corresponds to the current
// opening tag. In simple cases we just go for the next
// close tag.
if ( ! tags.close ) return pos;
var ndx = pos.ndx;
pos = twve.text.skipToActive(
txt,tags.close,pos,null,inactiveTags
);
if ( pos.ndx > -1 ) return pos;
pos.ndx = ndx;
return tags.lastCloseTag(txt,pos,txtlen);
};
//}}}
/***
!!!!! tags.encloses
***/
//{{{
tags.encloses = function(txt,ndx,start,end){
// Tests whether this pair of tags encloses the index ndx
// in txt. The last two arguments, start and end, stand for
// the starting and ending indexes of the search.
// If this pair of tags does enclose the index, this function
// returns a twve.position object containing
// {
// ndx: the index of the close tag,
// matched: the matched close tag
// }.
// Otherwise it returns null.
start = twve.position.create(start);
end = twve.position.create(end);
if ( start.ndx < 0 ) start.ndx = 0;
if ( end.ndx <= start.ndx || end.ndx > txt.length )
end.ndx = txt.length;
// Prepare for searching.
if ( ndx <= start || ndx >= end )
return null;
// Search backward for opening tags.
var open = twve.position.create(ndx);
open = twve.text.lastIndexOf(
txt,tags.open,open,true
);
// If no opening tag is found, returns null.
if ( open.ndx < 0 ) return null;
// An opening tag before this ndx is found, look for
// the corresponding close tag.
do {
if ( open.ndx >= start.ndx && open.ndx < ndx ) {
//open.ndx+open.matched.length <= ndx ) {
// The opening index is on or before ndx,
// search forward.
var exactTag = tags.exactCloseTag(open.matched);
var close = twve.position.create(
open.ndx+open.matched.length
);
close = twve.text.indexOf(txt,exactTag.close,close);
if(close.ndx>ndx && close.ndx<=end.ndx){
// The closing tag is after ndx, it is possible
// that ndx is enclosed by this pair of tags,
// if they are not mistaken.
var nextopen = twve.position.create(
open.ndx+open.matched.length
);
nextopen = twve.text.indexOf(
txt,exactTag.open,nextopen
);
return (nextopen.ndx<0 ||
nextopen.ndx>close.ndx)
? {start:open,end:close} : null;
} else {
// Otherwise the closing tag is also before this
// ndx, meaning this pair of tags is enclosing
// some characters but not this ndx. Skip the
// enclosed text.
start.ndx = close.ndx;
}
}
// Otherwise check the next pair, if there is.
open = open.next;
} while ( open );
return null;
};
// mergeTag
tags.mergeTag = function(tag,newtag){
if ( ! newtag ) return tag;
// Merges newtag into tag
if ( ! tag ) {
// The tag in this pair of tags is empty/undefined,
// copy the whole newtag.
if ( typeof newtag == 'string' ) {
// The newtag is a string, just copy it.
tag = newtag;
} else {
// The newtag is an array, clone it.
tag = newtag.slice(0);
}
} else if ( typeof tag == 'string' ) {
// The tag in this pair of tags is a string,
if ( typeof newtag == 'string' ) {
// and newtag is also string, make them an array if
// they are different.
if ( tag != newtag )
tag = new Array(tag,newtag);
} else {
// and newtag is an array, make this tag an array and
// append those all items in newtag.
tag = [tag];
for (var i=0,len=newtag.length; i<len; i++)
if ( tag.indexOf(newtag[i]) == -1 )
tag[tag.length] = newtag[i];
}
} else {
// The tag in this pair of tags is an array,
if ( typeof newtag == 'string' ) {
// and newtag is a string, append newtag to this tag.
if ( tag.indexOf(newtag) == -1 )
tag[tag.length] = newtag;
} else {
// and newtag is also an array, append all its items
// to this tag.
for (var i=0,len=newtag.length; i<len; i++)
if ( tag.indexOf(newtag[i]) == -1 )
tag[tag.length] = newtag[i];
}
}
return tag;
};
// merge
tags.merge = function(t){
// Merges the open and close tags from t (twve.tags object)
// into this one.
tags.open = tags.mergeTag(tags.open,t.open);
tags.close = tags.mergeTag(tags.close,t.close);
return tags;
};
// creation codes
if ( close !== undefined && close != null ) {
// If the 2nd argument close is given, it shall be the
// close tag, and the 1st argument shall be the open tag.
tags.open = src;
tags.close = close;
return tags;
} else
// Otherwise we follow the default behavior.
return tags.created(src);
}
};
//}}}
/***
!!! twve.link
***/
//{{{
twve.link = {
//}}}
/***
!!!! twve.link.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('a');
},
//}}}
/***
!!!! twve.link.markupTags
***/
//{{{
markupTags : function(){
return twve.tags.create(
'[[',
']]'
);
}
};
//}}}
/***
!!! twve.tiddler
***/
//{{{
twve.tiddler = {
//}}}
/***
!!!! twve.tiddler.registered_element
***/
//{{{
registered_element : null,
//}}}
/***
!!!! twve.tiddler.registered_wrapper
***/
//{{{
registered_wrapper : null,
//}}}
/***
!!!! twve.tiddler.registerElement
***/
//{{{
registerElement : function(obj){
// Register one type of editable element to twve.tiddler.
// Elements that are editable should/could do the followings:
// 1. have an "enableEdit" property which enables/disables
// the editable status of its kind;
// 2. have the following methods :
// # "twveSelector" method which
// a. accepts a twve.selector object (sel),
// b. calls
// sel.includeSelector('inc_selector')
// to add the include selector, and
// sel.excludeSelector('exc_selector')
// to add the exclude selector, if
// desired,
// c. returns the twve.selector object (sel).
// # "markupTags" method to return the wiki
// tags of its kind. For example
// return twve.tags.create(
// "opening tag",
// "closing tag"
// );
// (If your editable element do have a pair of
// specific signatures, implement this method to
// return them. Otherwise just skip it.)
// # "create" method to create elements of its kind,
// # (optional) "getText" method to get the text
// of the element in a way different from the
// default (see "getText" in twve.editable.create);
// # (optional) "setText" method to set the text
// of the element in a way different from the
// default (see "setText" in twve.editable.create);
// # (optional) "clone" method to generate an identical
// copy of the element in a way different from the
// default (see twve.tiddler.cloneEditable).
// # (optional) "mouseenter" method to prepare your
// element upon mouseenter event. Return true/false
// to accept/reject the entrance of mouse pointer.
// See the mouseenter method in twve.table for an
// example.
// # (optional) "mousemove" method to have your
// element respond to the mousemove event. See the
// mousemove method in twve.table for an example.
// # (optional) "mouseleave" method to prepare your
// element upon mouseleave event. Return true/false
// to accept/reject the leaving of mouse pointer.
// See the mouseleave method in twve.tiddlerTitle or
// twve.table for examples.
// # (optional) "focus" method to prepare your
// element for getting the focus.
// # (optional) "blur" method to prepare your element for
// losing focus. See the blur method in twve.table
// for an example.
// 3. call twve.tiddler.registerElement(obj) during plugin
// initialization to register the editable element.
if ( ! twve.tiddler.registered_element ) {
twve.tiddler.registered_element = [];
twve.tiddler.registered_element[
twve.tiddler.registered_element.length
] = obj;
} else if(twve.tiddler.registered_element.indexOf(obj)==-1)
twve.tiddler.registered_element[
twve.tiddler.registered_element.length
] = obj;
},
//}}}
/***
!!!! twve.tiddler.registerWrapper
***/
//{{{
registerWrapper : function(obj){
// Register one type of editable wrapper to twve.
// An editable wrapper should/could, like an editable element,
// do the followings:
// 1. have an "enableEdit" property which enables/disables
// the editable status of its kind;
// 2. have the following methods :
// # "twveSelector" method, see "registerElement"
// above for details;
// # "titleOfWrapper" method to return the tiddler
// title associated with that wrapper, see the
// definition of any one of the system wrappers
// near the end of this file for an example;
// # "wrapperFromTitle" method to return the wrappers
// containing the tiddler with a given title, see the
// definition of any one of the system wrappers near
// the end of this file for an example;
// # "create" method to create wrappers of its kind,
// # (Optional) "markupTags" method to return the wiki
// tags of its kind. For example
// return twve.tags.create(
// "opening tag",
// "closing tag"
// );
// (This is necessary when the wrappers of this
// kind contain transcluded content. See the
// definition of any of the system wrappers near
// the end of this file for an example.)
// # (optional) "clone" method to generate an identical
// copy of the element in a way different from the
// default (see twve.tiddler.cloneEditable).
// 3. call twve.tiddler.registerWrapper(obj) during plugin
// initialization to register the editable wrapper.
if ( ! twve.tiddler.registered_wrapper ) {
twve.tiddler.registered_wrapper = [];
twve.tiddler.registered_wrapper[
twve.tiddler.registered_wrapper.length
] = obj;
} else if(twve.tiddler.registered_wrapper.indexOf(obj)==-1)
twve.tiddler.registered_wrapper[
twve.tiddler.registered_wrapper.length
] = obj;
},
//}}}
/***
!!!! twve.tiddler.enableAllEditable
***/
//{{{
enableAllEditable : function(enabled){
// Enable/Disable all registered editable objects at the
// same time.
for ( var obj in twve.tiddler.registered_element )
if ( obj.enableEdit )
obj.enableEdit = (enabled !== false);
},
//}}}
/***
!!!! twve.tiddler.readOnly
***/
//{{{
readOnly : function(title){
var str = 'twve--example';
return (title && title.toLowerCase().substring(0,str.length)==str)
? false
: readOnly;
},
//}}}
/***
!!!! twve.tiddler.registeredEditableObject
***/
//{{{
registeredEditableObject : function(
elem,registered,include_selector,which
){
if ( ! elem || ! registered
|| !config.options.chktwveCoreEnabled )
return null;
// Finds the registered editable object that corresponds
// to the type of elem (DOM node).
var selector = twve.selector.create();
// Go through the registered objects in a backward manner:
// from the last to the first registered.
for(var n = registered.length-1; n>=0; n--){
var obj = registered[n];
if ( obj.enableEdit ){
selector.clear();
if ( obj.is ) {
// If obj has implemented is() function, test it.
if ( obj.is(elem,selector) )
return include_selector
? { obj: obj, selector: selector }
: obj;
} else {
// Otherwise test its selectors.
obj.twveSelector(selector,which);
//var node = elem;
//do {
if ( twve.node.matches(elem,selector.include) &&
(! selector.exclude ||
!twve.node.matches(elem,selector.exclude)) )
return include_selector
? { obj: obj, selector: selector }
: obj;
//node = node.parentNode;
//} while ( node );
}
}
}
return null;
},
//}}}
/***
!!!! twve.tiddler.createEditableElement
***/
//{{{
createEditableElement : function(elem,txt,start,dir){
// Creates an instance of the registered editable object
// that corresponds to the type of elem (DOM node).
var obj_sel = twve.tiddler.registeredEditableObject(
elem,
twve.tiddler.registered_element,
true
);
if ( ! obj_sel ) return null;
var twelem = obj_sel.obj.create
? obj_sel.obj.create(elem,txt,start,dir)
: twve.element.create(elem,txt,start,dir);
return twelem && twve.tiddler.readOnly(twelem.wrapper_title)
? null : twelem;
},
//}}}
/***
!!!! twve.tiddler.twveSelector
***/
//{{{
twveSelector : function(elem,which){
// Finds the selector corresponding to element elem (DOM node).
var obj_sel = twve.tiddler.registeredEditableObject(
elem,
twve.tiddler.registered_element,
true, which
);
if ( ! obj_sel )
obj_sel = twve.tiddler.registeredEditableObject(
elem,
twve.tiddler.registered_wrapper,
true, which
);
return obj_sel ? obj_sel.selector : null;
},
//}}}
/***
!!!! twve.tiddler.twveFoldableSelector
***/
//{{{
twveFoldableSelector : function(){
var selector = twve.sliderPanel.twveSelector();
twve.foldedSection.twveSelector(selector);
return selector;
},
//}}}
/***
!!!! twve.tiddler.twveTranscludedSelectors
***/
//{{{
twveTranscludedSelectors : function(){
var selector = twve.selector.create();
twve.tabContent.twveSelector(selector);
twve.sliderPanel.twveSelector(selector);
twve.tiddlerSpan.twveSelector(selector);
return selector;
},
//}}}
/***
!!!! twve.tiddler.cloneMarkupTags
***/
//{{{
cloneMarkupTags : function(registered,tags){
// Clones a twve.tags object.
for(var n = registered.length-1; n>=0; n--){
var obj = registered[n];
if ( obj.enableEdit && obj.markupTags ){
var objTags = obj.markupTags();
if ( objTags.is(tags) ) return objTags;
}
}
return null;
},
//}}}
/***
!!!! twve.tiddler.markupTags
***/
//{{{
markupTags : function(elem,which){
// Finds the wiki tags corresponding to an element (DOM node),
// or clones a twve.tags object.
if ( elem.nodeType ) {
// A DOM node
var twobj = twve.tiddler.registeredEditableObject(
elem, twve.tiddler.registered_element,
false, which
);
if ( ! twobj ) {
twobj = twve.tiddler.registeredEditableObject(
elem, twve.tiddler.registered_wrapper,
false, which
);
}
return twobj && twobj.markupTags
? twobj.markupTags(elem,which) : null;
} else {
// A twve.tags object
return twve.tiddler.cloneMarkupTags(
twve.tiddler.registered_element,elem
) || twve.tiddler.cloneMarkupTags(
twve.tiddler.registered_wrapper,elem
);
}
},
//}}}
/***
!!!! twve.tiddler.registeredSelectors
***/
//{{{
registeredSelectors : function(registered,selector){
if ( ! selector ) selector = twve.selector.create();
for(var n = registered.length-1; n>=0; n--)
registered[n].twveSelector(selector);
return selector;
},
//}}}
/***
!!!! twve.tiddler.elementSelectors
***/
//{{{
elementSelectors : function(selector){
return twve.tiddler.registeredSelectors(
twve.tiddler.registered_element,selector
);
},
//}}}
/***
!!!! twve.tiddler.wrapperSelectors
***/
//{{{
wrapperSelectors : function(selector){
return twve.tiddler.registeredSelectors(
twve.tiddler.registered_wrapper,selector
);
},
//}}}
/***
!!!! twve.tiddler.directWrapper
***/
//{{{
directWrapper : function(elem,force){
var w = twve.node.closest(
elem.parentNode,
twve.tiddler.wrapperSelectors().include
);
return w
? twve.tiddler.createEditableWrapper(w,force)
: null;
},
//}}}
/***
!!!! twve.tiddler.createEditableWrapper
***/
//{{{
createEditableWrapper : function(w,force,txt,start,dir){
// Creates an instance of the registered editable wrapper
// that corresponds to the type of w (DOM node), and finds
// its wrapper title.
var wrap = twve.tiddler.registeredEditableObject(
w, twve.tiddler.registered_wrapper
);
if ( wrap ) {
var twwrap = wrap.create
? wrap.create(w,txt,start,dir)
: twve.wrapper.create(w,txt,start,dir);
if ( ! twwrap ) return null;
twwrap.wrapper_title = wrap.titleOfWrapper(w);
return force || !twve.tiddler.readOnly(twwrap.wrapper_title)
? twwrap : null;
}
return null;
},
//}}}
/***
!!!! twve.tiddler.createEditable
***/
//{{{
createEditable : function(elem,txt,start,dir){
// Creates an instance of the registered editable element
// or wrapper that corresponds to the type of elem (DOM node).
var editable = twve.tiddler.createEditableWrapper(
elem,null,txt,start,dir
);
if ( ! editable ) {
editable = twve.tiddler.createEditableElement(
elem,txt,start,dir
);
}
return (editable && editable.isSystemShadow())
? null : editable;
},
//}}}
/***
!!!! twve.tiddler.cloneEditable
***/
//{{{
cloneEditable : function(tweditable){
// Clones an instance of the registered editable element
// or wrapper tweditable (twve.editable object).
var twobj = twve.tiddler.registeredEditableObject(
tweditable.dom,
twve.tiddler.registered_element
);
if ( twobj )
return twobj.create
? twobj.create(tweditable)
: twve.element.create(tweditable);
twobj = twve.tiddler.registeredEditableObject(
tweditable.dom,
twve.tiddler.registered_wrapper
);
return twobj
? (twobj.create
? twobj.create(tweditable)
: twve.wrapper.create(tweditable))
: null;
},
//}}}
/***
!!!! twve.tiddler.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// Find the title of a given wrapper w (DOM node).
if ( ! w ) return '';
var twobj = twve.tiddler.registeredEditableObject(
w, twve.tiddler.registered_wrapper
);
return (twobj && twobj.titleOfWrapper)
? twobj.titleOfWrapper(w)
: '';
},
//}}}
/***
!!!! twve.tiddler.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function (title) {
// Find all the registered wrappers with the given tiddler
// title.
// A tiddler may be loaded multiple times and displayed in
// different wrappers with transclusion macros.
// This function finds all the wrappers corresponding to the
// same tiddler title.
// First we go to the top level, the 'div.tiddlerDisplay',
// then call the "wrapperFromTitle" method of each registered
// wrapper to collect all wrappers with that given title.
//
// This function returns an array of DOM object representing all
// the rendered copies of the corresponding tiddler title.
if ( ! title ) return null;
title = twve.text.removeHeaderTail(title);
var tid_title = twve.text.tiddlerTitle(title);
var sec = twve.text.tiddlerSection(title);
var w0 = document.querySelectorAll('div[id=tiddlerDisplay]');
var w = null;
var registered_wrapper = twve.tiddler.registered_wrapper;
for(var n = registered_wrapper.length-1; n>=0; n--){
var wrap = registered_wrapper[n];
if ( wrap.wrapperFromTitle ) {
var w1 = wrap.wrapperFromTitle(
tid_title, w0, sec
);
if ( ! w1 ) continue;
w = w ? w.concat(w1) : w1;
}
}
return w;
},
//}}}
/***
!!!! twve.tiddler.get
***/
//{{{
get : function ( title, where ) {
// Get the tiddler with the given title from
// store or story.
if ( ! where || where != story ) where = store;
return where.getTiddler(twve.text.tiddlerTitle(title));
},
//}}}
/***
!!!! twve.tiddler.displaySelector
***/
//{{{
displaySelector : function(){
return 'div[id=displayArea]';
},
//}}}
/***
!!!! twve.tiddler.getDisplay
***/
//{{{
getDisplay : function(){
return document.querySelector('div[id=displayArea]');
},
//}}}
/***
!!!! twve.tiddler.getOptionsMenu
***/
//{{{
optionsMenu : null,
getOptionsMenu : function(){
if ( ! twve.tiddler.optionsMenu ) {
twve.tiddler.optionsMenu = twve.menu.create(
twve.button.create(
null,
String.fromCharCode(8801),
"''twve'' menu"
)
);
twve.tiddler.optionsMenu.addMenu(
"''twve.core'' Options",
config.macros.twveCoreOptions.handler
);
}
return twve.tiddler.optionsMenu;
},
//}}}
/***
!!!! twve.tiddler.cur_focus
***/
//{{{
cur_focus : null,
//}}}
/***
!!!! twve.tiddler.focusElem
***/
//{{{
focusElem : function (editable,action) {
if ( typeof editable != 'undefined' ) {
// editable is given
if ( twve.tiddler.cur_focus &&
twve.tiddler.cur_focus.blur )
// There is currently focused element which is
// different from the one to get focused (this is
// made sure before calling this method).
// Call its blur() method if it has it
twve.tiddler.cur_focus.blur();
if ( ! editable ) {
// Clear the focus, hide options menu as well.
var menu = twve.tiddler.getOptionsMenu();
if ( menu ) menu.hide(true);
if ( ! action || action.toLowerCase().substring(0,4)!='keep' )
twve.tiddler.drawFocusBorders();
} else if ( editable.focus )
// call the focus() method of the newly focused
// element if it has it
editable.focus();
twve.tiddler.cur_focus = editable;
}
return twve.tiddler.cur_focus;
},
//}}}
/***
!!!! twve.tiddler.focusing_borders
***/
//{{{
border_top : null,
border_bottom : null,
border_left : null,
border_right : null,
//}}}
/***
!!!! twve.tiddler.createFocusBorder
***/
//{{{
createFocusBorder : function(parent,which){
if ( ! parent ) parent = twve.tiddler.getDisplay();
// Create an array to hold each side of the borders.
var border = document.createElement('div');
parent.appendChild(border);
border.style.position = 'absolute';
twve.node.setDimension(border,null,0);
switch ( which ) {
case 'left' :
border.style.borderLeftWidth = '1px';
border.style.borderLeftStyle= 'dashed';
break;
case 'right' :
border.style.borderRightWidth = '1px';
border.style.borderRightStyle = 'dashed';
break;
case 'top' :
border.style.borderTopWidth = '1px';
border.style.borderTopStyle = 'dashed';
break;
case 'bottom' :
border.style.borderBottomWidth = '1px';
border.style.borderBottomStyle = 'dashed';
break;
}
return border;
},
//}}}
/***
!!!! twve.tiddler.focusBorderVisible
***/
//{{{
focusBorderVisible : function(){
return twve.node.isVisible(twve.tiddler.border_top[0]);
},
//}}}
/***
!!!! twve.tiddler.prepareFocusBorders
***/
//{{{
prepareFocusBorders : function(eb){
// Prepare the focus borders and returns the number of boxes
// to draw.
// Left
twve.node.setPosition(
twve.tiddler.border_left[0],eb.left,eb.top
);
twve.node.setDimension(
twve.tiddler.border_left[0],null,eb.height
);
// Top
twve.node.setPosition(
twve.tiddler.border_top[0],eb.left,eb.top
);
twve.node.setDimension(
twve.tiddler.border_top[0],eb.width
);
// Bottom
twve.node.setPosition(
twve.tiddler.border_bottom[0],eb.left,eb.bottom
);
twve.node.setDimension(
twve.tiddler.border_bottom[0],eb.width
);
// Right
twve.node.setPosition(
twve.tiddler.border_right[0],eb.right,eb.top
);
twve.node.setDimension(
twve.tiddler.border_right[0],null,eb.height
);
return 1;
},
//}}}
/***
!!!! twve.tiddler.focusBox
***/
//{{{
focus_box : null,
focusBox : function(){
return twve.tiddler.focus_box;
},
focusBoxVisible : function(){
return twve.node.isVisible(twve.tiddler.border_top[0]);
},
clearFocusBox : function(){
twve.tiddler.focus_box = null;
},
//}}}
/***
!!!! twve.tiddler.drawFocusBorders
***/
//{{{
drawFocusBorders : function(eb){
if ( ! eb ) {
//twve.tiddler.focus_box = null;
twve.node.hide(twve.tiddler.border_top);
twve.node.hide(twve.tiddler.border_bottom);
twve.node.hide(twve.tiddler.border_left);
twve.node.hide(twve.tiddler.border_right);
} else {
twve.tiddler.focus_box = [{}];
// Copy the 1st box.
eb.cloneTo(twve.tiddler.focus_box[0],0,false);
// Copy the outside box.
eb.cloneTo(twve.tiddler.focus_box);
// Prepare and show borders.
var brdlen = twve.tiddler.prepareFocusBorders(eb);
if ( config.options.chktwveCoreShowFocus ) {
twve.node.show(twve.tiddler.border_top,0,brdlen);
twve.node.show(twve.tiddler.border_bottom,0,brdlen);
twve.node.show(twve.tiddler.border_left,0,brdlen);
twve.node.show(twve.tiddler.border_right,0,brdlen);
}
}
},
//}}}
/***
!!!! twve.tiddler.editWrappers
***/
//{{{
editWrappers : function(){
return config.options.chktwveCoreEditWrappers;
},
//}}}
/***
!!!! twve.tiddler.focus
***/
//{{{
focus : function(editable,action,ev,eb) {
var elem = null;
if ( ! editable || ! config.options.chktwveCoreEnabled ) {
action = 'off';
} else {
elem = editable.dom;
if ( twve.node.closest(elem,'.preview') )
return;
if ( config.options.chktwveCoreNoClick ) {
if(! twve.tiddler.cur_editable
|| ! twve.tiddler.cur_editable.is(elem))
twve.tiddler.editInPlace(editable,ev);
}
}
if ( ! twve.tiddler.border_top ) {
// Focusing borders not existing. Create them.
var display = twve.tiddler.getDisplay();
twve.tiddler.border_top = [];
twve.tiddler.border_top[0] =
twve.tiddler.createFocusBorder(display,'top');
twve.tiddler.border_bottom = [];
twve.tiddler.border_bottom[0] =
twve.tiddler.createFocusBorder(display,'bottom');
twve.tiddler.border_left = [];
twve.tiddler.border_left[0] =
twve.tiddler.createFocusBorder(display,'left');
twve.tiddler.border_right = [];
twve.tiddler.border_right[0] =
twve.tiddler.createFocusBorder(display,'right');
}
if ( ! elem || action == 'off' ||
(editable.isWrapper() && ! twve.tiddler.editWrappers()) ) {
twve.tiddler.drawFocusBorders();
twve.tiddler.focusElem(null);
} else {
twve.tiddler.focusElem(editable);
if ( editable.isWrapper() ) {
twve.tiddler.drawFocusBorders();
//if ( ! eb )
eb = editable.box('focus',ev);
} else {
if ( ! eb ) eb = editable.box('focus',ev);
twve.tiddler.drawFocusBorders();
}
if(eb && eb.height>=twve.node.cssSize('font-size',elem)){
if ( editable.drawFocusBorders )
editable.drawFocusBorders(eb);
else
twve.tiddler.drawFocusBorders(eb);
return true;
}
}
return;
},
//}}}
/***
!!!! twve.tiddler.mousePosition
***/
//{{{
mousePosition : function(ev){
var pos = twve.object.create();
if ( ev ) {
pos.x = ev.pageX;
pos.y = ev.pageY;
}
return twve.node.adjustPosition(pos);
},
//}}}
/***
!!!! twve.tiddler.focusTarget
***/
//{{{
focusTarget : function(ev){
// For simple elements/nodes, focus target is just ev.target.
// For complex ones that containing multiple child
// elements/nodes, such as tables, ev.target could be one of
// the children, yet we may want the focus to be on the parent.
return ev.target;
},
//}}}
/***
!!!! twve.tiddler.mouseMove
***/
//{{{
mouseMove : function(ev) {
ev = ev || window.event;
if ( twve.isMobile() &&
ev.target.nodeName == 'A' ) return;
var focus = twve.tiddler.focusElem();
var feb = null;
var tweditable = null;
var target = twve.tiddler.focusTarget(ev);
if ( focus ) {
feb = twve.tiddler.focusBox();
if ( focus.is(target) ) {
// Mouse is still over the same element.
if ( focus.mousemove ) focus.mousemove(ev);
if ( twve.isMobile() ){
twve.tiddler.updateText();
if (feb &&
feb.contains(twve.tiddler.mousePosition(ev)) &&
focus.dom.nodeName!='TABLE'){
if ( twve.tiddler.m_count == 0 )
twve.tiddler.m_count++
else {
twve.tiddler.updateText();
twve.tiddler.m_count = 0;
return ! twve.tiddler.editInPlace(focus,ev);
}
}
}
if(focus.isWrapper()) tweditable = focus;
} else {
// Mouse has come to another element.
var seb = twve.node.box(target);
if ( feb && feb.contains(seb) ) {
// If this other element is within the focused one,
// check if it is a registered editable element.
twve.tiddler.focusElem(null,'keep');
tweditable = twve.tiddler.createEditable(target);
if ( tweditable ) {
// Yield focus to it if it is.
twve.tiddler.focus();
feb = tweditable.differentBox ? null : seb;
} else
// Otherwise restore focus back.
twve.tiddler.focusElem(focus);
} else {
// If this other element is not within the focused
// one, ask the focused one to leave and yield focus
// if it agrees.
var leaving = focus.mouseleave
? focus.mouseleave(ev)
: true;
if ( leaving ) {
twve.tiddler.focus();
tweditable = twve.tiddler.createEditable(target);
if ( tweditable )
feb = tweditable.differentBox ? null : seb;
}
}
}
} else {
if ( twve.isMobile() ) {
twve.tiddler.updateText();
twve.tiddler.down_elem = ev.target;
twve.tiddler.m_count = 0;
}
// There is currently no focused element.
tweditable = twve.tiddler.createEditable(target);
}
if ( tweditable &&
(! tweditable.mouseenter || tweditable.mouseenter(ev)) ) {
twve.tiddler.focus(tweditable,'on',ev,feb);
}
},
//}}}
/***
!!!! twve.tiddler.eventTarget
***/
//{{{
eventTarget : function(ev){
return (ev.button == 0
|| (config.browser.isIE && ev.button == 1)) // left button
//? document.elementFromPoint(ev.clientX,ev.clientY)
? (ev.target.nodeName!=='CANVAS'?ev.target:null)
: null;
},
//}}}
/***
!!!! twve.tiddler.mouseDown
***/
//{{{
m_count : 0,
down_elem : null,
mouseDown : function (ev) {
ev = ev || window.event;
var down_elem = ev.buttons || twve.isMobile()
? twve.tiddler.eventTarget(ev)
: null;
if ( down_elem ) {
var pos = twve.tiddler.mousePosition(ev);
var focus = twve.tiddler.focusElem();
if ( focus && focus.is(down_elem) ) {
var fbox = twve.tiddler.focusBox();
if (fbox &&
(pos.x >= fbox.right ||
pos.y >= fbox.bottom))
return (twve.tiddler.down_elem = down_elem = null);
}
var menu = twve.tiddler.getOptionsMenu();
if((menu.isVisible() && menu.contains(pos)) ||
down_elem.nodeName=='A' ||
twve.node.closest(down_elem,'A')){
down_elem = null;
twve.tiddler.m_count = 0;
} else {
if ( twve.isMobile() ) {
if ( twve.tiddler.m_count > 0 ) menu.hide(true);
} else {
menu.hide(true);
}
if ( down_elem != twve.tiddler.down_elem )
twve.tiddler.m_count = 0;
}
}
return (twve.tiddler.down_elem = down_elem);
},
//}}}
/***
!!!! twve.tiddler.mouseUp
***/
//{{{
mouseUp : function (ev) {
if ( ! twve.tiddler.down_elem ) return;
ev = ev || window.event;
var up_elem = twve.tiddler.eventTarget(ev);
if ( ! up_elem || twve.button.isSystemButton(up_elem) )
return;
if ( up_elem != twve.tiddler.down_elem ){
var fb = twve.tiddler.focusBox();
if(!fb || !fb.contains(ev))
return;
//up_elem = twve.tiddler.down_elem;
}
var edbox = twve.tiddler.getEditBox();
if ( edbox && twve.node.is(up_elem,edbox) ) {
//setTimeout(function(){
twve.tiddler.previewEditBox(up_elem);
//}, 0);
return;
}
if ( twve.node.closest(up_elem,'.preview')
|| twve.node.matches(up_elem,'textarea,input') ) {
return;
}
if ( twve.node.matches(
up_elem,
'html,#displayArea,#contentWrapper,.txtMainTab' +
',.header,.headerShadow,.headerForeground' +
',.siteTitle,.siteSubtitle,#menuBar'
)
) {
twve.tiddler.updateText();
twve.tiddler.focus();
return;
}
var tweditable = twve.tiddler.focusElem();
if ( ! tweditable ) {
if ( edbox )
twve.tiddler.updateText();
return;
}
if ( twve.isMobile() ) {
if ( ++twve.tiddler.m_count < 2 ) return;
twve.tiddler.m_count = 0;
}
if ( edbox ) {
twve.tiddler.updateTextAndIndex(tweditable);
}
if ( tweditable.isEditable(up_elem) ) {
return ! twve.tiddler.editInPlace(tweditable,ev);
}
},
//}}}
/***
!!!! twve.tiddler.resize
***/
//{{{
resize : function (ev) {
if ( twve.tiddler.cur_editable ) return;
if (! (twve.isMobile() ||
(config.browser.isIE &&
!config.browser.isIE9))) {
var scrL = window.pageXOffset;
var scrT = window.pageYOffset;
twve.tiddler.focusElem(null);
var panel = document.querySelectorAll('#sidebar,#mainMenu');
if ( window.innerWidth < screen.width/2 )
twve.node.hide(panel);
else
twve.node.show(panel);
var twview = twve.viewer.create();
var selector = twve.viewer.twveSelector();
panel = document.querySelectorAll(selector.include);
for ( var n=0,len=panel.length; n<len; n++ ) {
if ( twve.node.matches(panel[n],'div.board') ) continue;
twview.setElement(panel[n]);
twview.refreshSelf(twview.getText());
twview.clear();
}
window.scrollTo(scrL,scrT);
}
},
//}}}
/***
!!!! twve.tiddler.scroll
***/
//{{{
/*
scroll : function (ev) {
if ( twve.tiddler.cur_editable ) {
var $ed = twve.tiddler.getEditBox();
if ( ! $ed ) return;
//var shift = twve.node.positionShift();
var box = twve.node.box($ed[0]);
$ed[0].style.left = box.left+'px';
$ed[0].style.top = box.top+'px';
var preview = twve.tiddler.getPreviewer();
if ( preview ) {
preview.style.left = box.left+'px';
preview.style.top =
box.top-preview.offsetHeight;
}
}
},
*/
//}}}
/***
!!!! twve.tiddler.saveText
***/
//{{{
saveText : function(twelem,text,newtitle){
var tiddler = twelem.tiddler || twelem;
var newtext = typeof text == 'string' ? text : tiddler.text;
if ( newtitle ) {
if(store.tiddlerExists(newtitle)) {
if(!confirm(config.messages.overwriteWarning
.format([newtitle])))
return null;
story.closeTiddler(newtitle,false);
}
var tiddlerElem = story.getTiddler(tiddler.title);
tiddlerElem.id = story.tiddlerId(newtitle);
tiddlerElem.setAttribute("tiddler",newtitle);
store.saveTiddler(tiddler.title,newtitle,newtext);
return newtext;
}
var undo_saveTiddler = null;
if ( config.macros.undo ) {
// If UndoPlugin is installed, let it do its job but
// not to save the tiddler.
undo_saveTiddler = store.undo_saveTiddler;
store.undo_saveTiddler = function() {};
store.saveTiddler(tiddler.title,tiddler.title,newtext);
}
// The following lines of code saves the changes just made to a
// DOM element. According to TiddlyWiki web page it is a common
// practice to use store.saveTiddler () to save the content
// of a tiddler. This is usually fine except when
// <<foldHeadings>> macro is used: the open sections get folded
// again. We avoid such situations by calling tiddler.set()
// instead of store.saveTiddler().
tiddler.set(
tiddler.title,newtext,
(tiddler.modifier || config.options.txtUserName),
new Date()
);
store.setDirty(true);
if ( config.macros.undo ) {
// If UndoPlugin is installed, the saveTiddler() function
// was temporarily disabled. Put it back now.
store.undo_saveTiddler = undo_saveTiddler;
}
// Save changes to local file or server
if ( window.location.protocol == "file:" ) {
if ( !config.options.chktwveCoreManualSave )
autoSaveChanges (null,[twelem.tiddler]);
} else if ( !config.options.chktwveCoreManualUpload &&
config.macros.upload ) {
config.macros.upload.action();
}
return newtext;
},
//}}}
/***
!!!! twve.tiddler.refreshTiddler
***/
//{{{
preRefreshTiddler : null,
refreshTiddler : function () {
var elem=twve.tiddler.preRefreshTiddler.apply(this,arguments);
if ( elem && config.options.chktwveCoreEnabled ) {
var viewer = elem.querySelector('.viewer');
if ( ! viewer ) viewer = elem;
twve.viewer.create(viewer).waitAndPrepare();
// Bind the mousemove event handler to the tiddlerDisplay
// area. The unbind calls are necessary to prevent multiple
// invocation upon single event due to multiply bound
// handler.
var display = twve.node.closest(
elem,'div[id=tiddlerDisplay]'
);
//if ( twve.isMobile() ) {
// Nothing to do for mobile devices, yet.
//} else {
display.removeEventListener(
'mousemove', twve.tiddler.mouseMove
);
display.addEventListener(
'mousemove', twve.tiddler.mouseMove
);
//}
// Because FireFox jumps out of the element if text
// selection is finished with mouse released over another,
// the click event is not a good place to determine
// which element to edit. Instead, this plugin compares the
// element in mousedown and mouseup, and goes to edit
// function only if they are the same one.
// The unbind calls are necessary to prevent multiple
// invocation upon single event due to multiply bound
// handler.
/*
if ( twve.isMobile() ) {
document.removeEventListener(
'touchstart', twve.tiddler.mouseDown
);
document.addEventListener(
'touchstart', twve.tiddler.mouseDown
);
document.removeEventListener(
'touchend', twve.tiddler.mouseUp
);
document.addEventListener(
'touchend', twve.tiddler.mouseUp
);
} else {
*/
document.removeEventListener(
'mousedown', twve.tiddler.mouseDown
);
document.addEventListener(
'mousedown', twve.tiddler.mouseDown
);
document.removeEventListener(
'mouseup', twve.tiddler.mouseUp
);
document.addEventListener(
'mouseup', twve.tiddler.mouseUp
);
//}
twve.tiddler.getOptionsMenu().hide(true);
}
return elem;
},
//}}}
/***
!!!! twve.tiddler.popupShow
***/
//{{{
prePopupShow : null,
popupShow : function () {
twve.tiddler.prePopupShow.apply(this,arguments);
var curr = Popup.stack[Popup.stack.length-1];
twve.wrapper.create(curr.popup).waitAndPrepare();
return curr;
},
//}}}
/***
!!!! twve.tiddler.tabSwitchTab
***/
//{{{
preTabsSwitchTab : null,
tabsSwitchTab : function (tabset,tab) {
// Hijacking the switchTab function of <<tabs>> macro.
twve.tiddler.preTabsSwitchTab.apply(this,arguments);
if(twve.node.closest(tabset,twve.tiddler.displaySelector())) {
var tabContent = twve.tabContent.create(
tabset.nextSibling
);
tabContent.waitAndPrepare();
var focus = twve.tiddler.focusElem();
if ( focus ) {
twve.tiddler.drawFocusBorders(focus.box());
}
}
},
//}}}
/***
!!!! twve.tiddler.tiddlerTransclude
***/
//{{{
preTiddlerTransclude : null,
tiddlerTransclude : function (wrapper) {
// Hijacking the handler of <<tiddler>> macro.
twve.tiddler.preTiddlerTransclude.apply(this,arguments);
if(twve.node.closest(wrapper,twve.tiddler.displaySelector())) {
var ts = twve.tiddlerSpan.create(wrapper);
if ( ts.waitAndPrepare ) ts.waitAndPrepare();
}
},
//}}}
/***
!!!! twve.tiddler.sliderHandler
***/
//{{{
preSliderHandler : null,
sliderHandler : function (place) {
// Hijacking the handler of <<slider>> macro.
twve.tiddler.preSliderHandler.apply(this,arguments);
if(twve.node.closest(place,twve.tiddler.displaySelector())) {
var sp = twve.sliderPanel.create(
place.querySelector('div[tiddler]')
);
if ( sp.waitAndPrepare ) sp.waitAndPrepare();
}
},
//}}}
/***
!!!! twve.tiddler.onClickSlider
***/
//{{{
preOnClickSlider : null,
onClickSlider : function () {
// Hijacking the onClick handler of <<slider>> macro.
twve.tiddler.preOnClickSlider.apply(this,arguments);
//twve.sliderPanel.create(this.nextSibling)
// .waitAndPrepare();
},
//}}}
/***
!!!! twve.tiddler.cur_editable
<<<
The element being edited. Note that this may be different from the element being focused.
<<<
***/
//{{{
cur_editable : null,
//}}}
/***
!!!! twve.tiddler.edit_box
***/
//{{{
edit_box : null,
//}}}
/***
!!!! twve.tiddler.getEditBox
***/
//{{{
getEditBox : function(){
return twve.tiddler.edit_box;
},
//}}}
/***
!!!! twve.tiddler.setEditBox
***/
//{{{
setEditBox : function(box){
//box.style.zIndex = 2;
return (twve.tiddler.edit_box = box);
},
//}}}
/***
!!!! twve.tiddler.adjustEditBoxHeight
***/
//{{{
adjustEditBoxHeight : function(ta){
// Adjust the edit box height to fit its containing
// text.
// It is interesting that with Chrome, FireFox,
// and Safari we need to change the height of ta
// to get the correct scrollHeight, while with IE9
// we get it no matter what. The scrH0 is the minimum
// height that we stored upon initialization.
var tah = Math.round(twve.node.height(ta));
twve.node.setDimension(ta,null,0);
var scrH0 = ta.getAttribute('scrH0')*1;
var scrH = ta.scrollHeight;
scrH = Math.max(scrH, scrH0);
twve.node.setDimension(ta,null,scrH+1);
//twve.tiddler.previewEditBox(ta);
},
//}}}
/***
!!!! twve.tiddler.preview
***/
//{{{
preview : null,
//}}}
/***
!!!! twve.tiddler.getPreviewer
***/
//{{{
getPreviewer : function(parent){
if ( ! twve.tiddler.preview ) {
if ( ! parent )
parent = twve.tiddler.getDisplay();
var preview = document.createElement('div');
twve.tiddler.preview = preview;
parent.appendChild(preview);
preview.style.position = 'absolute';
preview.style.overflow = 'auto';
preview.style.zIndex = 2;
preview.style.border = '1px solid black';
preview.style.padding = 0;
preview.style.margin = 0;
preview.style.backgroundColor = '#fff8dc';
preview.classList.add('preview');
var board = document.createElement('div');
preview.appendChild(board);
board.classList.add(
'board','viewer','tiddler','selected'
);
twve.node.hide(preview);
}
return twve.tiddler.preview;
},
//}}}
/***
!!!! twve.tiddler.editInplace
***/
//{{{
editInPlace : function (editable,ev) {
// This name was inherited from GridPlugin v2.0.7
// (http://www.TiddlyTools.com/#GridPlugin) by
// Eric Shulman, for that plugin is one of the two
// seeds of this one.
return (ev && (ev.ctrlKey || ev.shiftKey || ev.altKey))
? false :
(editable.wrapper_title ? editable.editText(ev) : false);
},
//}}}
/***
!!!! twve.tiddler.getEditInfo
***/
//{{{
getEditInfo : function(ta){
if ( ! ta ) ta = twve.tiddler.getEditBox();
var edinfo = {}, txt;
if ( ta ) {
edinfo.text = [];
for ( var r=0,rlen=ta.length; r<rlen; r++ ){
if ( ta[r].nodeType ) {
// single textarea, edinfo.text[r] shall be its text value
txt = ta[r].value;
edinfo.text[r] = txt;
if ( ta[r].getAttribute('cancel')=='true' )
edinfo.canceled = true;
if ( ta[r].defaultValue != txt )
edinfo.modified = true;
} else {
// multiple textareas, edinfo.text[r] shall be an array
edinfo.text[r] = [];
for(var c=0,clen=ta[r].length; c<clen; c++){
txt = ta[r][c].value;
edinfo.text[r][c] = txt;
if ( ta[r][c].getAttribute('cancel')=='true' )
edinfo.canceled = true;
if ( ta[r][c].defaultValue != txt )
edinfo.modified = true;
}
}
}
}
return edinfo;
},
//}}}
/***
!!!! twve.tiddler.updateText
***/
//{{{
updateText : function (ta,editable,refreshed,editing) {
if ( editable === undefined )
editable = twve.tiddler.cur_editable;
if ( ! ta ) ta = twve.tiddler.getEditBox();
if ( ! ta || ! editable ) {
twve.tiddler.closeEditbox();
twve.tiddler.cur_editable = null;
return null;
}
var edinfo = twve.tiddler.getEditInfo(ta);
if ( !edinfo.canceled && edinfo.modified )
editable.updateText(edinfo.text,refreshed);
if ( ! editing ) {
twve.tiddler.closeEditbox();
twve.tiddler.cur_editable = null;
}
return editable;
},
//}}}
/***
!!!! twve.tiddler.updateTextAndIndex
***/
//{{{
updateTextAndIndex : function(twelem){
// Update text of the element currently being edited, and the
// index of the waiting one (twelem).
var editable = twve.tiddler.cur_editable;
var pend = editable.end.ndx;
twve.tiddler.updateText();
if ( ! twelem ) return;
var dlen = editable.end.ndx - pend;
if ( dlen &&
twve.text.tiddlerTitle(editable.wrapper_title) ==
twve.text.tiddlerTitle(twelem.wrapper_title) ) {
// There was change in the text length, indexes of twelem
// need to be updated.
if ( editable.dom == twelem.dom ) {
// Same element, update its end.
twelem.end.ndx = editable.end.ndx;
} else if ( twelem.start.ndx > pend ) {
// Some element after the updated one, update its start
// and end.
twelem.start.ndx += dlen;
twelem.end.ndx += dlen;
}
}
},
//}}}
/***
!!!! twve.tiddler.editBoxCaret
***/
//{{{
editBoxCaret : function(ta, pos_start, pos_end) {
if ( typeof pos_start == 'undefined' ) {
// Get caret position
try {
if ( document.selection ) {
// IE
// The following codes are copied from
// http://jsfiddle.net/FishBasketGordo/ExZM9/
ta.focus();
var sel = document.selection.createRange();
var sel_copy = sel.duplicate();
sel_copy.moveToElementText(ta);
sel_copy.setEndPoint('EndToStart',sel);
return sel_copy.text.length-sel.text.length;
} else if ( ta.selectionStart
|| ta.selectionStart == '0' )
return ta.selectionStart*1;
} catch(e) {
console.log(e);
}
return -1;
} else {
// Set caret position
var len = ta.value.length;
if ( typeof pos_start != 'number' ) {
pos_start = pos_start == 'end' ? len : 0;
} else if ( pos_start < 0 )
pos_start += len;
if ( typeof pos_end != 'number' )
pos_end = pos_start;
else {
if ( pos_end < 0 ) pos_end += len;
if ( pos_start > pos_end ) {
// Make sure that start < end
var tmp = pos_start;
pos_start = pos_end;
pos_end = tmp;
}
}
// The following codes are copied from
// http://www.webdeveloper.com/forum/showthread.php?74982-How-to-set-get-caret-position-of-a-textfield-in-IE
if ( ta.setSelectionRange ) {
ta.focus();
ta.setSelectionRange(pos_start,pos_end);
} else if ( ta.createTextRange ) {
var range = ta.createTextRange();
range.collapse(true);
range.moveEnd('character',pos_end);
range.moveStart('character',pos_start);
range.select();
}
return pos_start;
}
},
//}}}
/***
!!!! twve.tiddler.getSelectionText
***/
//{{{
getSelectionText : function ( ta ) {
// This function is directly copied from
// http://stackoverflow.com/questions/3053542/how-to-get-the-start-and-end-points-of-selection-in-text-area
var s = twve.object.create();
s.start = 0;
s.end = 0;
if (typeof ta.selectionStart == "number"
&& typeof ta.selectionEnd == "number") {
// Firefox (and others)
s.start = ta.selectionStart;
s.end = ta.selectionEnd;
} else if (document.selection) {
// IE
var bookmark =
document.selection.createRange().getBookmark();
var sel = ta.createTextRange();
var bfr = sel.duplicate();
sel.moveToBookmark(bookmark);
bfr.setEndPoint("EndToStart", sel);
s.start = bfr.text.length;
s.end = s.start + sel.text.length;
}
return s;
},
//}}}
/***
!!!! twve.tiddler.closeEditbox
***/
//{{{
closeEditbox : function() {
if ( twve.tiddler.preview )
twve.node.hide(twve.tiddler.preview);
var edbox = twve.tiddler.edit_box;
if ( edbox ) {
for(var r=0,rlen=edbox.length; r<rlen; r++){
if ( edbox[r].nodeType )
// single textarea
edbox[r].parentNode.removeChild(edbox[r]);
else
// array of textareas
for(var c=0,clen=edbox[r].length; c<clen; c++)
edbox[r][c].parentNode.removeChild(edbox[r][c]);
}
twve.tiddler.edit_box = null;
}
},
//}}}
/***
!!!! twve.tiddler.previewEditBox
***/
//{{{
previewEditBox : function (ta,cpos) {
var preview = twve.tiddler.getPreviewer();
if ( ! config.options.chktwveCorePreview ||
! twve.tiddler.cur_editable ) {
twve.node.hide(preview);
return null;
}
twve.node.setDimension(preview,ta.offsetWidth);
if ( typeof cpos != 'number' || cpos == -1 )
cpos = twve.tiddler.editBoxCaret(ta);
var txt = ta.value;
txt = cpos == 0
? (config.options.txttwveCorePreviewCaret+txt)
: (txt.substring(0,cpos)
+config.options.txttwveCorePreviewCaret
+txt.substring(cpos));
twve.node.show(preview);
var board = preview.querySelector('div.board');
// The preview contains the font and color attributes of the
// element being edited. I did not add the next line of code
// until I found that the color attributes can go wrong in
// some cases. One example is moving from one table cell with
// a specific background color to one without, using keyboard
// navigation. 2013/12/27 Vincent
twve.node.copyFontColor(board,preview,true);
twve.node.wikifyAndPrepare(txt,board);
// Adjust previewer height
var h = new String(
config.options.txttwveCorePreviewHeight
).toLowerCase().trim();
var lh = twve.node.cssSize(
window.getComputedStyle(board)
.getPropertyValue('line-height')
) || 17;
if ( /\D$/.test(h) )
// txttwveCorePreviewHeight contains non-digits at
// its end
h = twve.node.cssSize(h);
else
// txttwveCorePreviewHeight is all digits
h = lh*h;
var bh = board.offsetHeight+lh;
if ( bh > h ) bh = h;
var tpos = twve.node.box(ta);
if ( bh > tpos.top ) {
bh = tpos.top;
tpos.top = 0;
} else
tpos.top -= bh;
twve.node.setPosition(preview,tpos.left,tpos.top-2);
// Add 1 in height to avoid unnecessary scrollbar.
twve.node.setDimension(preview,null,bh+1);
// Make the editbox scrollable if too high.
//if ( ta.scrollHeight > h ) {
// twve.node.setDimension(ta,null,h);
//}
return preview
},
//}}}
/***
!!!! twve.tiddler.getPreviewedNodes
***/
//{{{
getPreviewedNodes : function(txt,node){
var preview = twve.tiddler.getPreviewer();
var board = preview.querySelector('div.board');
if ( twve.node.isVisible(preview) )
//return config.options.chktwveCorePreview
// ? board.childNodes
// : twve.node.wikifyAndPrepare(txt,board);
// To avoid redoing the wikification we would simply return the
// board.childNodes if the previewer had been enabled. The current
// way of simulating the caret, however, is preventing this simple
// way because that "fake caret" -- which is simulated by an extra
// vertical bar -- is not part of the text but will also be
// returned. This is not what we want. Therefore we redo the
// wikification no matter what, at least by the time we move on to
// the next mode of editing -- the almost WYSIWYG mode.
// Vincent 2014/12/16
return twve.node.wikifyAndPrepare(txt,board);
// Previewer is hidden, show it, wikify into it, and return.
twve.node.show(preview);
if ( node ) {
twve.node.copyFontColor(preview,node,true);
twve.node.setDimension(preview,node.offsetWidth);
}
twve.node.copyFontColor(board,preview,true);
var nodes = twve.node.wikifyAndPrepare(txt,board);
twve.node.hide(preview);
return nodes;
},
//}}}
/***
!!!! twve.tiddler.caret_pos
***/
//{{{
caret_pos : null,
initCaretPos : function(pos){
if ( ! twve.tiddler.caret_pos )
twve.tiddler.caret_pos = twve.object.create();
if ( pos === undefined ) pos = -100;
twve.tiddler.caret_pos.cur = pos;
twve.tiddler.caret_pos.last = pos;
},
//}}}
/***
!!!! twve.tiddler.setCaretPosition
***/
//{{{
setCaretPosition : function(pos){
twve.tiddler.caret_pos.last = twve.tiddler.caret_pos.cur;
twve.tiddler.caret_pos.cur = pos;
},
//}}}
/***
!!!! twve.tiddler.updatePreviewerCaret
***/
//{{{
updatePreviewerCaret : function(ta,force){
var cpos = twve.tiddler.editBoxCaret(ta);
if ( force || cpos != twve.tiddler.caret_pos.cur ) {
twve.tiddler.previewEditBox(ta,cpos);
}
twve.tiddler.setCaretPosition(cpos);
},
//}}}
/***
!!!! twve.tiddler.keydownCancel
***/
//{{{
keydownCancel : function (ev,ta) {
if ( ev.which == 27 ) {
ta.setAttribute('cancel', 'true');
twve.tiddler.updateText();
return true;
}
return false;
},
//}}}
/***
!!!! twve.tiddler.keydownAfter
***/
//{{{
keydownAfter : function (ev,ta) {
if ( twve.tiddler.keydownCancel(ev,ta) ||
! twve.tiddler.cur_editable )
return false;
var force = false;
switch ( ev.which ) {
case 46 : // delete
force = true;
case 8 : // backspace
if ( ev.alterKey ) return false;
//twve.tiddler.updatePreviewerCaret(ta,force);
break;
case 33 : // page up
case 34 : // page down
case 35 : // end
case 36 : // home
case 37 : // left arrow
case 38 : // up arrow
case 39 : // right arrow
case 40 : // down arrow
if ( ev.alterKey ) return false;
if ( ev.shiftKey ) {
// Selection made. Think about what to do...
return false;
}
//twve.tiddler.updatePreviewerCaret(ta,force);
break;
default :
twve.tiddler.adjustEditBoxHeight(ta);
break;
}
twve.tiddler.updatePreviewerCaret(ta,force);
return true;
},
//}}}
/***
!!!! twve.tiddler.keydown
***/
//{{{
keydown : function (ev) {
ev = ev || window.event;
var ta = this;
var editable = twve.tiddler.cur_editable;
switch ( ev.which ) {
case 13 :
if ( editable.multiLine(ta.value) ) {
if (ev.ctrlKey) {
twve.tiddler.updateText();
return false;
} else if ( twve.tiddler.caret_pos.cur == 0 ) {
twve.tiddler.caret_pos.cur++;
}
} else {
twve.tiddler.updateText();
return false;
}
default :
setTimeout(function(){
twve.tiddler.keydownAfter(ev,ta);
}, 0);
}
},
//}}}
/***
!!!! twve.tiddler.textPasted
***/
//{{{
text_before_paste : null,
selection_at_paste : null,
textPasted : function(ta){
var txt = ta.value;
if ( txt ) {
var len = txt.length
- twve.tiddler.text_before_paste.length
+ twve.tiddler.selection_at_paste.end
- twve.tiddler.selection_at_paste.start;
txt = txt.substring(
twve.tiddler.selection_at_paste.start,
twve.tiddler.selection_at_paste.start+len
);
}
return txt;
},
//}}}
/***
!!!! twve.tiddler.paste
***/
//{{{
paste : function () {
// Something pasted from outside of this tiddler. Need to find
// the paste-in text and replace \n with <br>.
// From http://stackoverflow.com/questions/686995/jquery-catch-paste-input
// I learned that the paste-in text can be obtained in the
// next event loop.
if ( ! twve.tiddler.text_before_paste ) {
var ta = this;
twve.tiddler.text_before_paste = ta.value;
twve.tiddler.selection_at_paste =
twve.tiddler.getSelectionText(ta);
setTimeout(function() {
if ( twve.tiddler.cur_editable.pasteIn )
twve.tiddler.cur_editable.pasteIn(ta);
twve.tiddler.text_before_paste = null;
}, 0);
}
},
//}}}
/***
!!!! twve.tiddler.copyOrCut
***/
//{{{
copyOrCut : function(cut){
var ta = this;
//setTimeout(function() {
if ( twve.tiddler.cur_editable.copyOrCut )
twve.tiddler.cur_editable.copyOrCut(ta,cut);
//}, 0);
},
//}}}
/***
!!!! twve.tiddler.editHandler
***/
//{{{
preEditHandler : null,
editHandler : function(ev,src,title) {
twve.tiddler.updateText();
twve.tiddler.preEditHandler.apply(this,arguments);
return false;
},
//}}}
/***
!!!! twve.tiddler.closeTiddler
***/
//{{{
precloseTiddler : null,
closeTiddler : function () {
twve.tiddler.updateText();
twve.tiddler.focus();
return twve.tiddler.precloseTiddler.apply(this,arguments);
},
//}}}
/***
!!!! twve.tiddler.saveHandler
***/
//{{{
preSaveHandler : null,
saving : false,
saveHandler : function(){
twve.tiddler.saving = true;
var result = twve.tiddler.preSaveHandler.apply(this,arguments);
twve.tiddler.saving = false;
return result;
},
//}}}
/***
!!!! twve.tiddler.cancelHandler
***/
//{{{
preCancelHandler : null,
cancelHandler : function (ev,src,title) {
if(store.isDirty()) {
if(!confirm(this.warning.format([title])))
return false;
}
return twve.tiddler.preCancelHandler.apply(this,arguments);
},
//}}}
/***
!!!! twve.tiddler.saveChanges
***/
//{{{
preSaveChanges : null,
saveChanges : function () {
twve.tiddler.preSaveChanges.apply(this,arguments);
if ( config.macros.upload ) {
config.macros.upload.action();
}
}
};
//}}}
/***
!!! twve.position
***/
//{{{
twve.position = {
//}}}
/***
!!!! twve.position.getPosition
***/
//{{{
getPosition : function(pos){
return typeof pos == 'number'
? (pos < 0 ? 0 : pos)
: ((pos && pos.ndx) ? pos.ndx : 0);
},
//}}}
/***
!!!! twve.position.init
***/
//{{{
init : function(twpos,pos,matched){
switch ( typeof pos ) {
case 'number' :
twpos.ndx = (pos<0?0:pos);
twpos.matched = matched;
break;
case 'object' :
twpos.ndx = (pos.ndx<0?0:pos.ndx);
twpos.matched = pos.matched;
break;
default :
twpos.clear();
break;
}
return twpos;
},
//}}}
/***
!!!! twve.position.ensureValid
***/
//{{{
ensureValid : function(twpos,pos){
// Ensure the validity of the position index, i.e.,
// make sure that twpos.ndx is non-negative.
// The first argument twpos MUST be a twve.position object.
if ( pos ) {
// Copy from pos
twve.position.init(twpos,pos);
}
if ( twpos.ndx < 0 ) twpos.ndx = 0
return twpos;
},
//}}}
/***
!!!! twve.position.create
***/
//{{{
create : function(pos,matched){
var _pos = twve.object.create();
_pos.clear = function() {
_pos.ndx = -1;
_pos.matched = '';
return _pos;
};
_pos.init = function(pos,matched){
return twve.position.init(_pos,pos,matched);
};
_pos.ensureValid = function(pos){
return twve.position.ensureValid(_pos,pos);
};
return _pos.init(pos,matched);
}
};
//}}}
/***
!!! twve.selector
***/
//{{{
twve.selector = {
//}}}
/***
!!!! twve.selector.create
***/
//{{{
create : function(src,exc) {
var sel = twve.object.create();
sel.clear = function() {
sel.include = '';
sel.exclude = '';
return sel;
};
sel.copyFrom = function(src,exc){
if ( typeof exc == 'string' ) {
// The 2nd argument is given, both of them
// are expected to be strings.
sel.include = src;
sel.exclude = exc;
return sel;
} else if ( src.include ) {
// src is a twve.selector object
sel.include = src.include;
sel.exclude = src.exclude;
return sel;
} else if ( src.dom ) {
// src is a twve.element object
sel.include = src.selector.include;
sel.exclude = src.selector.exclude;
return sel;
}
// Otherwise src is expected to be a DOM node.
src = twve.tiddler.twveSelector(src);
if ( src ) {
sel.include = src.include;
sel.exclude = src.exclude;
}
return sel;
};
// include
sel.includeSelector = function(inc){
if ( inc )
sel.include += (sel.include?',':'')+inc;
return sel;
};
// exclude
sel.excludeSelector = function(exc){
if ( exc )
sel.exclude += (sel.exclude?',':'')+exc;
return sel;
};
sel.clear();
return (src || exc) ? sel.copyFrom(src,exc) : sel;
}
};
//}}}
/***
!!! twve.node
***/
//{{{
twve.node = {
//}}}
/***
!!!! twve.node.info
***/
//{{{
info : function(node){
if ( ! node ) return node;
if ( node.nodeType ) return node.nodeName;
var len = node.length;
var msg = '('+len+')[';
--len;
for ( var i=0; i<len; i++ ) {
msg += node[i].nodeName+', ';
}
return msg + node[len].nodeName + ']';
},
//}}}
/***
!!!! twve.node.matches
***/
//{{{
matches : function(node,selector){
if ( ! node || ! selector ) return false;
if ( ! node.nodeType ) {
// Multiple nodes, such as a NodeList.
for ( var i=0,len=node.length; i<len; i++ )
if ( twve.node.matches(node[i],selector) )
return true;
return false;
}
// Tests if a DOM node matches the selector.
var matches = node.matches || node.mozMatchesSelector ||
node.webkitMatchesSelector || node.msMatchesSelector ||
node.oMatchesSelector;
if ( matches ) return matches.call(node,selector);
// None of the above functions are supported, use the parent's
// querySelectorAll to test.
if ( ! node.parentNode ) return false;
var siblings=node.parentNode.querySelectorAll(selector);
if ( ! siblings.length ) return false;
for ( var i=0, n=siblings.length; i<n; i++ )
if ( siblings[i] == node ) return true;
return false;
},
//}}}
/***
!!!! twve.node.closest
***/
//{{{
closest : function(node,selector){
while ( node ) {
if ( twve.node.matches(node,selector) )
return node;
node = node.parentNode;
}
return null;
},
//}}}
/***
!!!! twve.node.parents
***/
//{{{
parents : function(node,selector,filter){
// Collects all parents of node that matches selector or filtered
// by filter function.
var parents = [];
node = node.parentNode;
while ( node ) {
if ( ! selector || twve.node.matches(node,selector) )
if ( ! filter || filter.call(node) )
parents[parents.length] = node;
node = node.parentNode;
}
return parents.length ? parents : null;
},
//}}}
/***
!!!! twve.node.is
***/
//{{{
is : function(node,criteria){
if ( ! node || ! criteria ) return false;
if ( typeof criteria == 'string' ) {
return twve.node.matches(node,criteria);
}
if ( node.nodeType ) {
// node is a single DOM node
if ( criteria.nodeType ) {
// criteria is a single DOM node
return node == criteria;
} else {
// criteria is a collection of DOM nodes
for ( var i=0,len=criteria.length; i<len; i++ )
if ( twve.node.is(node,criteria[i]) ) return true;
}
} else {
// node is a collection of DOM nodes
if ( criteria.nodeType ) {
// criteria is a single DOM node
for ( var i=0,len=node.length; i<len; i++ )
if ( twve.node.is(node[i],criteria) ) return true;
} else {
// criteria is a collection of DOM nodes
for ( var i=0,len=node.length; i<len; i++ )
for ( var j=0,len=criteria.length; j<len; j++ )
if ( twve.node.is(node[i],criteria[j]) )
return true;
}
}
return false;
},
//}}}
/***
!!!! twve.node.not
***/
//{{{
not : function(nodes,toremove){
// Remove some of the nodes out of an array of nodes.
// The 1st argument, nodes, is an array of nodes, while the 2nd
// argument can be one of the followings:
// 1. a CSS string;
// 2. an array of nodes or a NodeList;
// 3. a single DOM node.
if ( typeof toremove == 'string' ) {
// The 2nd argument is a CSS string.
var i = 0;
while ( i < nodes.length )
if ( twve.node.matches(nodes[i],toremove) )
nodes.splice(i,1);
else i++;
return nodes;
}
// The 2nd argument is either a DOM node or an array of DOM
// nodes (or a NodeList).
if ( toremove.nodeType ) {
// The 2nd argument is a DOM node.
var p = nodes.indexOf(toremove);
if ( p > -1 ) nodes.splice(p,1);
} else {
// The 2nd argument is an array of DOM nodes or a NodeList.
for ( var i=0,len=toremove.length; i<len; i++ ) {
var p = nodes.indexOf(toremove[i]);
if ( p > -1 ) nodes.splice(p,1);
}
}
return nodes;
},
//}}}
/***
!!!! twve.node.replace
***/
//{{{
replace : function(newnode,oldnode,parent){
if ( ! parent ) parent = oldnode.parentNode;
if ( newnode.nodeType ) {
// single node
parent.insertBefore(newnode,oldnode);
} else {
// multiple nodes
// In the following codes we keep inserting the first
// node, instead of newnode[i], into the parent, because
// the insertBefore() function removes the inserted node
// out of the NodeList newnode.
while( newnode.length ) {
parent.insertBefore(newnode[0],oldnode);
}
}
parent.removeChild(oldnode);
},
//}}}
/***
!!!! twve.node.scrollBarWidth
***/
//{{{
scw : 0,
scrollBarWidth : function(node){
if ( ! twve.node.scw ) {
if ( ! node ) return 0;
var of = node.style.overflow;
node.style.overflow = 'scroll';
twve.node.scw = node.offsetWidth-node.clientWidth;
node.style.overflow = of;
}
return twve.node.scw;
},
//}}}
/***
!!!! twve.node.adjustPosition
***/
//{{{
adjustPosition : function(pos){
// If the displayArea does not have position:static CSS style,
// the offset positions are incorrectly shifted. Need to correct
// it here.
var display = twve.tiddler.getDisplay();
if ( window.getComputedStyle(display)
.getPropertyValue('position') != 'static' ) {
if ( pos.left !== undefined ) {
pos.left -= display.offsetLeft;
pos.top -= display.offsetTop;
} else {
pos.x -= display.offsetLeft;
pos.y -= display.offsetTop;
}
}
return pos;
},
//}}}
/***
!!!! twve.node.shiftPosition
***/
//{{{
shiftPosition : function(node,left,top){
if ( left === undefined ) return;
var topmissing = top === undefined || top === null;
var postL = (typeof left == 'number'?'px':'');
var postT = (typeof top == 'number'?'px':'');
if ( node.nodeType ) {
// single node
if ( left !== null )
node.style.left = node.offsetLeft+left+postL;
if ( ! topmissing )
node.style.top = node.offsetTop+top+postT;
} else {
// multiple node
for ( var i=0,len=node.length; i<len; i++ ) {
if ( left !== null )
node[i].style.left = node[i].offsetLeft+left+postL;
if ( ! topmissing )
node[i].style.top = node[i].offsetTop+top+postT;
}
}
},
//}}}
/***
!!!! twve.node.setPosition
***/
//{{{
setPosition : function(node,left,top){
if ( left === undefined ) return;
var topmissing = top === undefined || top === null;
var postL = '';
var postT = '';
if ( typeof left == 'number' ) {
left = Math.round(left);
postL = 'px';
}
if ( typeof top == 'number' ) {
top = Math.round(top);
postT = 'px';
}
if ( node.nodeType ) {
// single node
if ( left !== null )
node.style.left = left+postL;
if ( ! topmissing )
node.style.top = top+postT;
} else {
// multiple node
for ( var i=0,len=node.length; i<len; i++ ) {
if ( left !== null )
node[i].style.left = left+postL;
if ( ! topmissing )
node[i].style.top = top+postT;
}
}
},
//}}}
/***
!!!! twve.node.setDimension
***/
//{{{
setDimension : function(node,w,h){
if ( w === undefined ) return;
var hmissing = h === undefined || h === null;
var postW = (typeof w == 'number'?'px':'');
var postH = (typeof h == 'number'?'px':'');
if ( node.nodeType ) {
// single node
if ( w !== null )
node.style.width = w+postW;
if ( ! hmissing )
node.style.height = h+postH;
} else {
// multiple nodes
for(var i=0,len=node.length; i<len; i++){
if ( w !== null )
node[i].style.width = w+postW;
if ( ! hmissing )
node[i].style.height = h+postH;
}
}
},
//}}}
/***
!!!! twve.node.positionShift
***/
//{{{
positionShift : function(){
// ---------------------------------------------------------
// The following few lines of codes are directly copied from
// http://javascript.info/tutorial/coordinates
var body = document.body;
var docElem = document.documentElement;
var scrollTop =
window.pageYOffset ||
docElem.scrollTop ||
body.scrollTop;
var scrollLeft =
window.pageXOffset ||
docElem.scrollLeft ||
body.scrollLeft;
var clientTop =
docElem.clientTop ||
body.clientTop ||
0;
var clientLeft =
docElem.clientLeft ||
body.clientLeft ||
0;
// ---------------------------------------------------------
return twve.node.adjustPosition(
{
left: (scrollLeft - clientLeft),
top: (scrollTop - clientTop)
}
);
},
//}}}
/***
!!!! twve.node.boxInit
***/
//{{{
boxInit : function(src,shift,dest,method){
dest.top = src.top;
dest.right = src.right;
dest.bottom = src.bottom;
dest.left = src.left;
// shift
//if ( ! dest.shift )
dest.shift = function(shift){
// Shift this box by an amount specified in shift.
dest.top += shift.top;
dest.right += shift.left;
dest.bottom += shift.top;
dest.left += shift.left;
return dest;
};
if ( shift ) dest.shift(shift);
if ( method === false ) return dest;
// toString
dest.toString = function(){
return twve.object.toString(dest);
};
// containsX
dest.containsX = function(x){
return x >= dest.left && x <= dest.right;
};
// containsY
dest.containsY = function(y){
return y >= dest.top && y <= dest.bottom;
};
// contains
dest.contains = function(pos){
if ( pos.left )
// The pos should be a box object.
return dest.containsX(pos.left) &&
dest.containsX(pos.right) &&
dest.containsY(pos.top) &&
dest.containsY(pos.bottom);
else
// The pos should be a mouse position object.
return dest.containsX(pos.x) &&
dest.containsY(pos.y);
};
// cloneTo
dest.cloneTo = function(newdest,n,method){
var src = typeof n == 'number'
? dest[n]
: dest;
twve.node.boxInit(src,null,newdest,method);
newdest.width = src.width;
newdest.height = src.height;
return newdest;
};
// clone
dest.clone = function(method){
var cloned = [];
twve.node.boxInit(dest,null,cloned,method);
cloned.width = dest.width;
cloned.height = dest.height;
for ( var i=0, len=dest.length; i<len; i++ ) {
cloned[i] = {};
dest.cloneTo(cloned[i],i,method);
}
return cloned;
};
// End of twve.node.boxInit
return dest;
},
//}}}
/***
!!!! twve.node.box
***/
//{{{
box : function(node,outerw,outerh) {
if ( ! node ) return null;
var box = null;
if (node.nodeType == 3) {
var r = document.createRange();
r.selectNodeContents(node);
box = r.getClientRects();
r.detach();
} else {
if (twve.node.matches(
node,
//twve.tiddler.wrapperSelectors().include
twve.tiddler.twveTranscludedSelectors().include
)) {
box = [];
box[0] = {};
box[0].left = node.offsetLeft;
box[0].top = node.offsetTop;
box[0].width = node.offsetWidth;
box[0].height = node.offsetHeight;
box[0].right = node.offsetLeft+box[0].width;
box[0].bottom = node.offsetTop+box[0].height;
} else if ( node.getClientRects ) {
if ( ! twve.node.isVisible(node) ) return null;
box = node.getClientRects();
} else {
box = [];
box[0] = {};
box[0].left = node.screenX;
box[0].top = node.screenY;
box[0].width = node.innerWidth;
box[0].height = node.innerHeight;
box[0].right = node.screenX+box[0].width;
box[0].bottom = node.screenY+box[0].height;
}
}
//}}}
/***
!!!!! box.initFrom
***/
//{{{
box.initFrom = function(src,shift,dest,method){
return twve.node.boxInit(src,shift,(dest || box),method);
};
//}}}
/***
!!!!! finding the shift, boundingRect, and clientRects
***/
//{{{
// Find the position shift.
var shift = twve.node.positionShift();
// Obtain the outer-most box.
if ( node.getBoundingClientRect ) {
// If the browser provides it, take it.
box.initFrom(node.getBoundingClientRect(),shift);
// Calculate the width and height of the outer-most box.
if ( ! outerw ) {
box.width = node.clientWidth || node.offsetWidth;
box.right = box.left + box.width;
} else {
box.width = box.right - box.left;
}
if ( ! outerh ) {
box.height = node.clientHeight || node.offsetHeight;
box.bottom = box.top + box.height;
} else {
box.height = box.bottom - box.top;
}
} else {
if ( ! box[0] ) return null;
// Otherwise we construct it ourselves.
box.initFrom(box[0]);
for ( var n = 1; n < box.length; n++ ) {
if ( box[n].left < box.left )
box.left = box[n].left;
if ( box[n].right > box.right )
box.right = box[n].right;
if ( box[n].bottom > box.bottom )
box.bottom = box[n].bottom;
}
box.width = box.right - box.left;
box.height = box.bottom - box.top;
box.shift(shift);
}
//}}}
/***
!!!!! End of box.
***/
//{{{
return box;
},
//}}}
/***
!!!! twve.node.cssSize
***/
//{{{
cssSize : function (txt, node) {
if ( ! txt ) {
if ( ! node ) return 0;
return parseFloat(
//node.style.fontSize ||
window.getComputedStyle(node)
.getPropertyValue('font-size')
);
}
// See http://twtable.tiddlyspace.com/#[[Tax%20for%20text%20options]]
// for a description on the change made to the next statement.
// txt = txt.toLowerCase();
txt = new String(txt).toLowerCase().trim();
var unit = txt.substring(txt.length-2);
switch ( unit ) {
case 'px' :
return Math.round(parseFloat(txt));
case 'pt' :
return Math.round(parseFloat(txt)*16/12);
default :
var size = parseFloat(txt);
if ( isNaN(size) ) {
var css = null;
try {
css = window.getComputedStyle(node);
} catch(err) {
node = twve.tiddler.getDisplay();
css = window.getComputedStyle(node);
} finally {
return css
? twve.node.cssSize(
css.getPropertyValue(txt),node
)
: 0;
}
}
if ( unit == 'em' ) {
return Math.round(
size*parseFloat(
window.getComputedStyle(node)
.getPropertyValue(
'font-size'
)
//e.style.fontSize
)*16/12
);
}
if (unit.charAt(1)=='%') {
return Math.round(
size/100*twve.node.width(node,'inner')
);
}
return size;
}
},
//}}}
/***
!!!! twve.node.bgc
***/
//{{{
bgc : function (e,style) {
// Returns the background color of a node e
if ( ! style ) style = window.getComputedStyle(e);
var bgc;
while ( (bgc=style.getPropertyValue(
'background-color'
))=='transparent'
|| bgc == 'rgba(0, 0, 0, 0)'
|| bgc == 'inherit' ) {
e = e.parentNode;
if ( ! e ) {
bgc = '#fff';
break;
}
}
return bgc;
},
//}}}
/***
!!!! twve.node.copyFontColor
***/
//{{{
//copyFontColor : function (dest,src,va) {
copyFontColor : function (dest,src,bgc) {
if ( ! dest || ! src || dest==src ) return;
var srcstyle = window.getComputedStyle(src);
//var deststyle = window.getComputedStyle(dest);
//if ( ! va ) va = srcstyle.getPropertyValue('vertical-align');
dest.style.fontSize=srcstyle.getPropertyValue('font-size');
dest.style.fontFamily=srcstyle.getPropertyValue('font-family');
dest.style.fontStyle=srcstyle.getPropertyValue('font-style');
dest.style.color=srcstyle.getPropertyValue('color');
dest.style.textAlign=srcstyle.getPropertyValue('text-align');
if ( bgc )
dest.style.backgroundColor = twve.node.bgc(src,srcstyle);
//dest.style.verticalAlign=va;
},
//}}}
/***
!!!! twve.node.wikify
***/
//{{{
wikify : function(txt,node,children) {
// Wikifies txt into node if given, or into a newly created
// node if not. Returns the node being wikified.
if(! node) {
var board = twve.tiddler.getPreviewer()
.querySelector('div.board');
board.innerHTML = '';
wikify(txt,board);
return board.childNodes;
} else {
node.innerHTML = '';
wikify(txt,node);
return children ? node.childNodes : node;
}
},
//}}}
/***
!!!! twve.node.wikifyAndPrepare
***/
//{{{
wikifyAndPrepare : function ( txt, board ) {
var node = twve.node.wikify(txt,board,true);
if ( twve.tiddler.cur_editable &&
twve.tiddler.cur_editable.isWrapper() ) {
var twwrap = twve.tiddler.cur_editable.clone();
twwrap.dom = board;
twwrap.tiddler = {text: txt};
twwrap.waitAndPrepare();
}
return node;
},
//}}}
/***
!!!! twve.node.isVisible
***/
//{{{
isVisible : function(node){
if ( ! node ) return false;
if ( node.nodeType == 3 ) return true;
return node.style
? node.style.display != 'none'
: true;
},
//}}}
/***
!!!! twve.node.hide
***/
//{{{
hide : function(node){
if ( ! node ) return;
if ( twve.object.isCollection(node) )
for ( var i=0,len=node.length; i<len; i++) {
node[i].style.display = 'none';
//node[i].style.visibility = 'hidden';
}
else
node.style.display = 'none';
//else
// node.style.visibility = 'hidden';
},
//}}}
/***
!!!! twve.node.show
***/
//{{{
show : function(node,start,len){
if ( ! node ) return;
if ( twve.object.isCollection(node) ) {
if ( typeof len != 'number' || len > node.length )
len = node.length;
if ( typeof start != 'number' || start < 0 )
start = 0;
for ( var i=start; i<len; i++ ) {
node[i].style.display = '';
//node[i].style.visibility = 'visible';
}
} else
node.style.display = '';
//node.style.visibility = 'visible';
},
//}}}
/***
!!!! twve.node.getMargin
***/
//{{{
getMargin : function(node,which,css){
// Get the margin width of node. The 2nd argument which can be
// one of the followings:
// 1. one of 'left', 'top', 'right', and 'bottom'.
// 2. 'horizontal' -- sum of 'left' and 'right' margins.
// 3. 'vertical' -- sum of 'top' and 'bottom' margins.
if ( ! css ) css = window.getComputedStyle(node);
var m1, m2;
var ch = which ? which.charAt(0) : '';
switch ( ch ) {
case 'h' :
case 'H' : // horizontal = left + right
m1 = node.style.marginLeft ||
css.getPropertyValue('margin-left');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.marginRight ||
css.getPropertyValue('margin-right');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'v' :
case 'V' : // vertical = top + bottom
m1 = node.style.marginTop ||
css.getPropertyValue('margin-top');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.marginBottom ||
css.getPropertyValue('margin-bottom');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'l' :
case 'L' : // left
m1 = node.style.marginLeft ||
css.getPropertyValue('margin-left');
return m1 ? parseInt(m1) : 0;
break;
case 't' :
case 'T' : // top
m1 = node.style.marginTop ||
css.getPropertyValue('margin-top');
return m1 ? parseInt(m1) : 0;
break;
case 'r' :
case 'R' : // right
m2 = node.style.marginRight ||
css.getPropertyValue('margin-right');
return m2 ? parseInt(m2) : 0;
break;
case 'b' :
case 'B' : // bottom
m2 = node.style.marginBottom ||
css.getPropertyValue('margin-bottom');
return m2 ? parseInt(m2) : 0;
break;
}
return 0;
},
//}}}
/***
!!!! twve.node.getPadding
***/
//{{{
getPadding : function(node,which,css){
// Get the padding width of node. The 2nd argument which can be
// one of the followings:
// 1. one of 'left', 'top', 'right', and 'bottom'.
// 2. 'horizontal' -- sum of 'left' and 'right' paddings.
// 3. 'vertical' -- sum of 'top' and 'bottom' paddings.
if ( ! css ) css = window.getComputedStyle(node);
var m1, m2;
var ch = which ? which.charAt(0) : '';
switch ( ch ) {
case 'h' :
case 'H' : // horizontal = left + right
m1 = node.style.paddingLeft ||
css.getPropertyValue('padding-left');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.paddingRight ||
css.getPropertyValue('padding-right');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'v' :
case 'V' : // vertical = top + bottom
m1 = node.style.paddingTop ||
css.getPropertyValue('padding-top');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.paddingBottom ||
css.getPropertyValue('padding-bottom');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'l' :
case 'L' : // left
m1 = node.style.paddingLeft ||
css.getPropertyValue('padding-left');
return m1 ? parseInt(m1) : 0;
break;
case 't' :
case 'T' : // top
m1 = node.style.paddingTop ||
css.getPropertyValue('padding-top');
return m1 ? parseInt(m1) : 0;
break;
case 'r' :
case 'R' : // right
m2 = node.style.paddingRight ||
css.getPropertyValue('padding-right');
return m2 ? parseInt(m2) : 0;
break;
case 'b' :
case 'B' : // bottom
m2 = node.style.paddingBottom ||
css.getPropertyValue('padding-bottom');
return m2 ? parseInt(m2) : 0;
break;
}
return 0;
},
//}}}
/***
!!!! twve.node.width
***/
//{{{
width : function(node,which,margin){
// Returns the width of the 1st argument node. The 2nd argument
// specifies 'inner', 'normal', or 'outer' width to return.
// The 'normal' width (default) is the clientWidth, the 'inner'
// width is the clientWidth less the left- and right- paddings,
// while the 'outer' is the offsetWidth.
// The 3rd argument, margin, is a boolean value telling this
// function whether to include the left and right margins.
// It is effective only when retrieving the 'outer' width.
// If the 2nd argument is omitted, 'normal' width is returned.
var w = 0;
which = which ? which.toLowerCase().substring(0,3) : '';
var outer = which == 'out';
var hidden = ! twve.node.isVisible(node);
if ( hidden ) twve.node.show(node);
if ( outer ) {
// outer width
w = node.offsetWidth;
if ( margin )
w += twve.node.getMargin(node,'horizontal');
} else {
w = node.clientWidth;
if ( which == 'inn' )
// inner width
w -= twve.node.getPadding(node,'horizontal');
}
if ( hidden ) twve.node.hide(node);
return w;
},
//}}}
/***
!!!! twve.node.height
***/
//{{{
height : function(node,which,margin){
// Returns the height of the 1st argument node. The 2nd argument
// specifies 'inner', 'normal', or 'outer' height to return.
// The 'normal' height (default) is the clientHeight, the 'inner'
// height is the clientHeight less the top- and bottom-paddings,
// while the 'outer' height is the offsetHeight.
// The 3rd argument, margin, is a boolean value telling this
// function whether to include the top and bottom margins. It is
// effective only when retrieving the 'outer' height.
// If the 2nd argument is omitted, 'normal' height is returned.
var h = 0;
which = which ? which.toLowerCase().substring(0,3) : '';
var outer = which == 'out';
var hidden = ! twve.node.isVisible(node);
if ( hidden ) twve.node.show(node);
if ( outer ) {
h = node.offsetHeight;
if ( margin )
h += twve.node.getMargin(node,'vertical');
} else {
h = node.clientHeight;
if ( which == 'inn' )
// inner height
h -= twve.node.getPadding(node,'vertical');
}
if ( hidden ) twve.node.hide(node);
return h;
},
//}}}
/***
!!!! twve.node.contains
***/
//{{{
contains : function(w0,w1,how){
// Tests if w0 contains w1, hierarchically or geometrically.
// The first two arguments, w0 and w1, are both DOM nodes.
// The last argument, how, is a string specifying how to do
// the test. If it is given 'geo', or any string starting with
// 'geo', the test is done geometrically, i.e., the function
// returns true if the bounding rect of w0 contains that of w1,
// regardless of their hierarchical relation in the DOM tree.
// If, however, the last argument is given any other string or
// left undefined, the function returns true if w0 is one of
// the parents of w1 in the DOM tree.
if ( how && how.toLowerCase().substring(0,3)=='geo' ) {
// Geometrical test.
var box = twve.node.box(w0,true,true);
if ( ! box ) return false;
if ( w1.x ) {
// Tests if w0 contains a position w1
return box ? box.contains(w1) : false;
}
// Tests if w0 contains another twve node or DOM node
if ( w1.dom ) w1 = w1.dom;
return box.contains(twve.node.box(w1));
} else {
// Hierarchical test.
while ( (w1 = w1.parentNode) )
if ( w0 == w1 ) return true;
return false;
}
},
//}}}
/***
!!!! twve.node.index
***/
//{{{
index : function(node){
// Return the index of node within its siblings.
var siblings = node.parentNode.childNodes;
for ( var i=0,len=siblings.length; i<len; i++ )
if ( siblings[i] == node ) return i;
return -1;
},
//}}}
/***
!!!! twve.node.create
***/
//{{{
create : function(src){
var node = twve.object.create();
// clear
node.clear = function(){
node.dom = null;
return node;
};
// copy from
node.copyFrom = function(obj){
node.dom = obj.dom;
return node;
};
// is
node.is = function(criteria){
return criteria && criteria.dom
? node.dom == criteria.dom
: twve.node.is(node.dom,criteria);
};
// not
node.not = function(toremove){
return twve.node.not(node.dom,toremove);
};
// hide
node.hide = function(){
twve.node.hide(node.dom);
return node;
};
// show
node.show = function(pos){
twve.node.show(node.dom);
if ( pos )
twve.node.setPosition(node.dom,pos.left,pos.top);
return node;
};
// isVisible
node.isVisible = function(){
return twve.node.isVisible(node.dom);
};
// width
node.width = function(which,margin){
return twve.node.width(node.dom,which,margin);
};
// height
node.height = function(){
return twve.node.height(node.dom);
};
// get element
node.getElement = function(){
return node.dom;
};
// box
node.box = function(){
return twve.node.box(node.getElement());
};
// contains
node.contains = function(elem,how){
return twve.node.contains(node.dom,elem,how);
};
// add
node.add = function(newnode,where){
if ( node.dom.nodeType ) node.dom = [node.dom];
if ( where=='begin' )
node.dom.unshift(newnode.dom || newnode);
else
node.dom[node.dom.length]=(newnode.dom || newnode);
return node;
};
// creation codes
node.clear();
if ( src )
switch ( typeof src ) {
case 'string' :
node.dom = document.createElement(src);
break;
case 'object' :
if ( src.dom )
node.copyFrom(src);
else if ( src.nodeName )
node.dom = src;
break;
}
// End of node
return node;
}
};
//}}}
/***
!!! twve.nodeList
***/
//{{{
twve.nodeList = {
//}}}
/***
!!!! twve.nodeList.contains
***/
//{{{
contains : function(nodes,node){
if ( typeof node == 'string' ) {
// Search for a node type.
for(var i=0,len=nodes.length; i<len; i++){
if (nodes[i].nodeName == node)
return true;
}
} else {
// Seaerch for a particular DOM node.
for(var i=0,len=nodes.length; i<len; i++){
if (nodes[i] == node)
return true;
}
}
return false;
},
//}}}
/***
!!!! twve.nodeList.toArray
***/
//{{{
toArray : function(node){
// The following lines of codes are copied from the reply posted
// by Thai in
// http://stackoverflow.com/questions/3199588/fastest-way-to-convert-javascript-nodelist-to-array
var twnodelist = [];
if ( node.nodeType )
// Single node
twnodelist[0] = node;
else
// Multiple nodes
for (var i=0,len=node.length;i<len;i++)
twnodelist[i] = node[i];
return twnodelist;
},
//}}}
/***
!!!! twve.nodeList.querySelectorAll
***/
//{{{
querySelectorAll : function(parent,selector){
if ( parent.nodeType ) {
// Single parent
return twve.nodeList.toArray(
parent.querySelectorAll(selector)
);
}
// Multiple parent
var w = null;
for (var i=0,len=parent.length; i<len; i++) {
var w1 = twve.nodeList.toArray(
parent[i].querySelectorAll(selector)
);
w = w ? w.concat(w1) : w1;
}
return w;
}
};
//}}}
/***
!!! twve.button
***/
//{{{
twve.button = {
isSystemButton : function (elem) {
return twve.node.matches(
elem,
'svg.SVGAnimatedString, path.SVGAnimatedString'
);
},
//}}}
/***
!!!! twve.button.isActive
***/
//{{{
isActive : function ( btn ) {
var opacity = btn.style
? btn.style.opacity
: window.getComputedStyle(btn)
.getPropertyValue('opacity');
return opacity == '1';
},
//}}}
/***
!!!! twve.button.activate
***/
//{{{
activate : function ( btn, tf ) {
return btn.style.opacity = (tf?1:0.4);
},
//}}}
/***
!!!! twve.button.create
***/
//{{{
create : function (place, label, tooltip, callback, id) {
var btn = twve.node.create(
createTiddlyButton(place,label,tooltip,callback)
);
// isActive
btn.isActive = function (){
return twve.button.isActive(btn);
};
// activate
btn.activate = function(tf){
return twve.button.activate(btn.dom,tf);
};
// creation codes
btn.dom.setAttribute('id','twveTbtn'+(id?id:label));
btn.dom.style.position = 'absolute';
btn.dom.style.zIndex = 1;
btn.dom.style.textAlign = 'center';
btn.dom.addEventListener(
'mouseenter',
function(ev){
if ( btn.mouseenter )
btn.mouseenter.call(this,ev || window.event);
}
);
btn.dom.addEventListener(
'mouseleave',
function(ev){
if ( btn.mouseleave )
btn.mouseleave.call(this,ev || window.event);
}
);
btn.dom.addEventListener(
'click',
function(ev){
if ( btn.click )
btn.click.call(this,ev || window.event);
}
);
// end of btn
return btn;
},
//}}}
/***
!!!! twve.button.createDelete
***/
//{{{
createDelete : function ( tooltip, callback, id ) {
return twve.button.create(null, '⊗',tooltip,callback,id);
}
};
//}}}
/***
!!! twve.menu
***/
//{{{
twve.menu = {
//}}}
/***
!!!! twve.menu.create
***/
//{{{
create : function(root,autohide){
// Create twve.menu object that will be attached to root, which
// can be an object of one of the following types:
// 1. twve.button,
// 2. twve.editable, or
// 3. twve.element.
var menu = twve.node.create('div');
// hasItem
menu.findItem = function(label){
if ( menu.items )
for ( var i = 0; i < menu.items.length; i++ ) {
var item = menu.items[i];
if ( item.label == label ) return item;
}
return null;
};
// addItem
menu.addItem = function(label,tooltip){
// Adds one item label to this menu. Returns the item just
// added.
if ( ! menu.items ) menu.items = [];
var item = twve.button.create(
createTiddlyElement(menu.dom,'li'),
null,
tooltip
);
item.dom.style.whiteSpace = 'nowrap';
item.label = label;
twve.node.wikify(label,item.dom);
item.mouseenter = function(){
var pos = twve.object.create();
pos.left = item.dom.offsetLeft + menu.dom.offsetLeft;
pos.top = item.dom.offsetTop + menu.dom.offsetTop;
if ( item.submenu ) {
item.submenu.show(pos,'left');
}
};
item.mouseleave = function(ev){
var pos = twve.tiddler.mousePosition(ev);
if ( item.submenu
&& ! item.submenu.contains(pos) )
item.submenu.hide();
};
menu.items[menu.items.length] = item;
menu.dom.removeAttribute('adjusted');
return item;
};
// addMenu
menu.addMenu = function(label,handler){
var item = menu.findItem(label);
if ( ! item )
item = menu.addItem(label);
if ( ! item.submenu ) {
item.submenu = twve.menu.create(item,true);
if ( handler )
handler.call(this,item.submenu.dom);
}
return item.submenu;
};
// contains
var preContains = menu.contains;
menu.contains = function(pos){
if ( preContains.call(this,pos,'geo') )
return true;
if ( menu.items )
for (var i=menu.items.length-1; i>=0; i--)
if ( menu.items[i].submenu
&& menu.items[i].submenu.contains(pos,'geo') )
return true;
return false;
};
// hideRoot
menu.hideRoot = function(){
menu.root.hide();
return menu;
};
// hide
menu.hide = function(hideroot){
if ( hideroot ) menu.hideRoot();
twve.node.hide(menu.dom);
return menu;
};
// adjustPosition
menu.adjustPosition = function(pos,where,w,h,ref){
if ( ! ref ) ref = menu;
if ( ! w ) w = ref.width('outer',true);
where = where ? where.toLowerCase() : '';
if ( where.indexOf('left')>=0 ) pos.left -= w;
if ( ! h ) h = ref.height('outer',true);
if ( where.indexOf('top')>=0 )
pos.top -= h;
else if ( where.indexOf('bottom')>=0 )
pos.top += h;
else
pos.top++;
return pos;
};
// showRoot
menu.showRoot = function(pos,where){
var menupos = twve.object.create();
menupos.left = pos.left;
menupos.top = pos.top;
menu.adjustPosition(menupos,where,null,null,menu.root);
menu.root.show(menupos);
return menupos;
};
// adjustDim
menu.adjustDim = function(){
if ( menu.items ) {
var w = 0;
var h = 0;
for ( var i=0; i<menu.items.length; i++ ){
var iw = twve.node.width(
menu.items[i].dom,'outer',true
);
if ( iw > w ) w = iw;
twve.node.setPosition(menu.items[i].dom,null,h);
h += twve.node.height(
menu.items[i].dom,'outer',true
);
}
twve.node.setDimension(
menu.dom,w,
h + (config.browser.isOpera ||
config.browser.isChrome
? twve.node.scrollBarWidth(menu.dom)+1
: 0)
);
}
menu.dom.setAttribute('adjusted','true');
return menu;
};
// show
menu.show = function(pos,where){
twve.node.show(menu.dom);
if ( ! menu.dom.getAttribute('adjusted') ) {
menu.adjustDim();
}
var w = menu.width('outer',true);
if ( pos ) {
var menupos = twve.object.create();
menupos.left = pos.left+4;
menupos.top = pos.top;
if ( w > pos.left ) {
// Left side is too narrow to put the menu, check
// the right side.
var rw = menu.root.width('outer',true);
var right = twve.node.box(window).width;
if ( w+rw+menupos.left < right ) {
// Right side is enough, put it there.
where = 'right';
menupos.left += rw-8;
} else {
// Right side is also not enough, put it in the
// larger side.
if ( right - w > pos.left ) {
// Right side is larger
twve.node.setDimension(
menu.dom,(w = right - w)
);
where = 'right';
menupos.left += rw-8;
} else {
// Left side is larger
twve.node.setDimension(
menu.dom,(w = pos.left)
);
}
}
}
var h = twve.node.cssSize(
//menu.dom.style.fontSize
window.getComputedStyle(menu.dom)
.getPropertyValue('font-size')
)*20;
if ( menu.height('outer',true) > h )
twve.node.setDimension(menu.dom,null,h);
menu.adjustPosition(menupos,where,w,h);
twve.node.setPosition(
menu.dom,menupos.left,menupos.top
);
}
return menu;
};
// begin of initialization
var parent = twve.tiddler.getDisplay();
if ( ! root.mouseenter ) {
// root.mouseenter
root.mouseenter = function(){
var pos = twve.object.create();
pos.left = this.offsetLeft;
pos.top = this.offsetTop;
menu.show(pos,'left');
return menu;
};
// root.click
root.click = function(){
menu.show();
return menu;
};
parent.appendChild(root.dom);
}
menu.root = root;
parent.appendChild(menu.dom);
menu.dom.style.overflow = 'auto';
menu.dom.style.position = 'absolute';
menu.dom.classList.add('popup');
menu.hide();
if ( autohide )
menu.dom.addEventListener(
'mouseleave',
function(){twve.node.hide(this);}
);
else
menu.dom.addEventListener(
'mouseleave',
function(ev){
if ( menu.mouseleave )
menu.mouseleave.call(this,ev || window.event);
}
);
// end of menu
return menu;
}
};
//}}}
/***
!!! twve.editable
***/
//{{{
twve.editable = {
//}}}
/***
!!!! twve.editable.create
***/
//{{{
create : function(src){
var editable = twve.node.create();
// clear
//preClear = editable.clear;
editable.clear = function(){
//preClear.apply(this);
// title of the direct wrapper
editable.wrapper_title = '';
// tiddler containing this element
editable.tiddler = null;
// Array of DOm object representing all the wrappers
// related to this element, each of the wrappers contains
// one of the copies of this element. See twve.wrapper for
// details of the system wrappers.
editable.wrapper = null;
// The starting and ending information of the element.
if(editable.start){
editable.start.clear();
editable.end.clear();
}else{
editable.start = twve.position.create();
editable.end = twve.position.create();
}
return editable;
};
// copy from
var preCopyFrom = editable.copyFrom;
editable.copyFrom = function(editablex){
preCopyFrom.apply(this,arguments);
editable.wrapper_title = editablex.wrapper_title;
editable.tiddler = editablex.tiddler;
editable.wrapper = editablex.wrapper;
editable.start.copyFrom(editablex.start);
editable.end.copyFrom(editablex.end);
return editable;
};
// created
editable.created = function(src,txt,start,dir){
editable.clear();
return src
? src.dom
? editable.copyFrom(src)
: editable.setElement(src,txt,start,dir)
: editable;
};
// isEditable
editable.isEditable = function(elem){
return editable.is(elem)
|| editable.contains(elem);
};
// beingEdited
editable.beingEdited = function(){
return editable.is(twve.tiddler.cur_editable);
};
// clone
editable.clone = function(){
return twve.tiddler.cloneEditable(editable);
};
// multi line
editable.multiLine = function (txt) {
return (txt && txt.indexOf('\n')>=0);
};
// twveSelector
editable.twveSelector = function(selector,which){
return twve.tiddler.twveSelector(
editable.dom,selector,which
);
};
// is wrapper?
editable.isWrapper = function(){
return false;
};
// direct wrapper
editable.directWrapper = function(){
// Find the direct wrapper of the editable from the
// registered wrappers.
// See twve.tiddler.registerWrapper() for a list of system
// wrappers.
return twve.tiddler.directWrapper(editable.dom,true);
};
// folded wrapper
editable.foldedWrapper = function(){
var twwrap = editable.directWrapper();
return twwrap.isVisible()
? null
: twwrap;
};
// isSystemShadow
editable.isSystemShadow = function(){
return (editable.wrapper_title &&
editable.wrapper_title.indexOf('shadow')<0)
? store.isShadowTiddler(editable.wrapper_title)
: editable.wrapper_title;
};
// findWrappers
editable.findWrappers = function(){
if ( ! editable.wrapper ) {
editable.wrapper = twve.tiddler.wrapperFromTitle(
editable.wrapper_title
);
if ( ! editable.wrapper ) editable.wrapper = [];
}
return editable.wrapper;
};
//}}}
/***
!!!!! editable.setElement
***/
//{{{
editable.setElement = function (elem) {
// Sets element elem (DOM node) to this twve.editable
// object.
// If elem is already set before, this method returns null
// (meaning no further process is needed). Otherwise it
// returns this editable itself.
if ( elem == editable.dom ) return null;
editable.dom = elem;
if ( ! editable.directWrapper() ) return editable;
// Find the wrapper title if not yet.
if( ! editable.wrapper_title ) {
var dw = editable.directWrapper();
if ( dw && dw.titleOfWrapper )
editable.wrapper_title = dw.titleOfWrapper();
}
// Find tiddler and all copies of wrappers if not
// a shadow tiddler
if ( editable.isSystemShadow() ) {
editable.tiddler = null;
editable.wrapper = null;
editable.wrapper_title =
'shadowd -- ' + editable.wrapper_title;
} else {
if ( ! editable.tiddler ) {
editable.tiddler = twve.tiddler.get(
editable.wrapper_title
);
}
}
return editable;
};
//}}}
/***
!!!!! editable.changed
***/
//{{{
editable.changed = function(param){
// This is the event handler in twve.editable.
// See http://twve.tiddlyspace.com/#%5B%5BEvent%20handler%20--%20element_changed%5D%5D
// for more information.
// This handler is called when an element is changed by the
// user. Other plugins may override this one to perform
// further operations, such as recalculating the table,
// after changes made.
// This handler returns the param.text, which contained the
// updated text of the element.
// If other plugins further change the content of the
// element, they should put it back to param.text and
// return it.
return param.text;
};
//}}}
/***
!!!!! editable.wrapperText
***/
//{{{
editable.wrapperText = function(){
return editable.dom
? editable.directWrapper().getText()
: (editable.tiddler
? editable.tiddler.text
: '');
};
//}}}
/***
!!!!! editable.ensureValid
***/
//{{{
editable.ensureValid = function(ndx,txt){
if ( ndx <= 0 ) return 0;
if ( ! txt ) txt = editable.wrapperText();
var max = txt.length;
return ndx >= max ? max : ndx;
};
//}}}
/***
!!!!! editable.setText
***/
//{{{
editable.setText = function (newtxt,open,close) {
var txt = editable.wrapperText();
if ( typeof open != 'number' ) open = editable.start.ndx;
if ( typeof close != 'number' ) close = editable.end.ndx;
open = editable.ensureValid(open,txt);
close = editable.ensureValid(close,txt);
// Adjust the open/close indexes according to the existance
// of leading/ending newlines.
if (txt.charAt(editable.start.ndx)=='\n'){
// If this editable starts from a \n, forward the
// opening index by one to skip it.
open++;
if ( close < editable.end.ndx )
// If we are changing only part of this editable,
// we should also forward the closing index.
close++;
}
if ( close == editable.end.ndx &&
txt.charAt(close-1) == '\n' )
// If this editable ends at a \n, and we are changing
// the whole editable, move the closing index backward
// by one to keep the ending \n.
close--;
var dlen = 0;
if ( ! newtxt ) {
// If we are removing a piece of text,
if ( close > open + 1 &&
txt.charAt(open)=='\n' &&
txt.charAT(close-1)=='\n' ) {
// and that piece of text contains more than one
// newline symbols, forward the open index to keep
// one of them (the opening one).
open++;
}
// The user wants to remove the existing text.
txt = txt.substring(0,open) + txt.substring(close);
// change in length of text
dlen = -(close-open);
} else {
// Set the wiki text of this editable object.
dlen = newtxt.length - (close-open);
txt = txt.substring(0,open)
+ newtxt
+ txt.substring(close);
}
if ( dlen ) {
// The text length has changed, update the ending index
// of this editable.
editable.end.ndx += dlen;
}
// update wrapper text
editable.directWrapper().setText(txt,dlen);
//twve.tiddler.saveText(editable,txt);
// Return the newly set part of text.
return newtxt;
};
//}}}
/***
!!!!! editable.substring
***/
//{{{
editable.substring = function(txt,open,close){
if ( close - open > 1 ) {
// There are more than 1 characters.
if ( txt.charAt(open) == '\n' )
// If the current text contains a leading newline,
// move the opening index forward by one to exclude it.
open++;
if ( txt.charAt(close-1) == '\n' )
// If the current text contains an ending newline,
// move the closing index backward by one to exclude it.
close--;
}
// Get the wiki text of this editable object.
return txt.substring(open,close);
};
//}}}
/***
!!!!! editable.getText
***/
//{{{
editable.getText = function () {
return editable.tiddler
? editable.substring(
editable.wrapperText(),
editable.start.ndx,
editable.end.ndx
)
: '';
};
//}}}
/***
!!!!! editable.focusEditBox
***/
//{{{
editable.focusEditBox = function(ta,txt){
if ( txt ) ta.select();
else ta.focus();
twve.tiddler.initCaretPos();
return editable;
};
//}}}
/***
!!!!! editable.prepareEditBox
***/
//{{{
editable.prepareEditBox = function(ta,txt,eb,talign,fs){
ta.style.position = 'absolute';
ta.style.padding = '0';
ta.style.margin = '0';
ta.style.overflow = 'auto';
ta.style.textAlign = talign;
ta.style.fontFamily = 'courier';
ta.style.fontSize = fs+'px';
twve.node.setPosition(ta,eb.left-1,eb.top-1);
twve.node.setDimension(ta,eb.width,eb.height);
ta.setAttribute('scrH0',eb.height);
ta.setAttribute('spellcheck','true');
ta.setAttribute('cancel','false');
ta.setAttribute(
'title',
'('+(editable.multiLine(txt)
?'Ctrl-'
:'')+'ENTER=accept, ESC=cancel)'
);
ta.addEventListener(
'keydown',
twve.tiddler.keydown
);
ta.addEventListener(
'paste',
twve.tiddler.paste
);
ta.addEventListener(
'copy',
function(){
twve.tiddler.copyOrCut();
}
);
ta.addEventListener(
'cut',
function(){
twve.tiddler.copyOrCut(true);
}
);
};
//}}}
/***
!!!!! editable.previewEditBox
***/
//{{{
editable.previewEditBox = function(ta,elem,display){
// Copy font attributes
twve.node.copyFontColor(
twve.tiddler.getPreviewer(display),elem,true
);
// Output to previewer
return twve.tiddler.previewEditBox(ta,null);
};
//}}}
/***
!!!!! editable.editText
***/
//{{{
editable.editText = function(ev,txt,elem) {
if ( ! editable.tiddler ) return null;
var eb;
if ( ! elem ) {
elem = editable.getElement();
eb = twve.tiddler.focusBox();
//|| editable.box('edit',ev,elem);
} else {
eb = twve.node.box(elem);
}
if ( ! eb ) return null;
twve.tiddler.cur_editable = editable;
if ( txt === undefined ) txt = editable.getText();
var ta = document.createElement('textarea');
ta.value = txt;
ta.defaultValue = txt;
var css = window.getComputedStyle(elem);
var lh = twve.node.cssSize(
css.getPropertyValue('line-height')
);
var fs = twve.node.cssSize(
css.getPropertyValue('font-size')
);
if ( eb.height < lh ) eb.height = lh;
var display = twve.node.closest(
elem,
twve.tiddler.displaySelector()
);
display.appendChild(ta);
editable.prepareEditBox(
ta,txt,eb,css.getPropertyValue('text-align'),fs
);
twve.node.setDimension(ta,null,0);
var scrH = ta.scrollHeight;
if ( scrH < eb.height ) scrH = eb.height;
else eb.height = scrH;
twve.node.setDimension(ta,null,scrH+1);
twve.tiddler.setEditBox([ta]);
editable.focusEditBox(ta,txt,ev,elem);
if ( editable.previewEditBox(ta,elem,display) ) {
var minw = fs*config.options.txttwveCoreMinEditWidth;
twve.node.setDimension(
ta,(eb.width < minw ? minw : eb.width)
);
}
return ta;
};
//}}}
/***
!!!!! editable.updateText
***/
//{{{
editable.updateText = function(txt,refreshed){
// Sets the modified text back to this editable object.
// NOTE: the argument txt is ALWAYS an array of strings.
// This is designed for table cells that span over multiple
// rows/columns. For a column-spanned cell, txt is a single
// array of strings. For a row-spanned cell, txt is a
// double array of strings. For other elements, txt is an
// array of one string (so the modified text would be
// txt[0]).
editable.setText(txt[0]);
return editable.refreshAll(
editable.getText(),null,refreshed
);
};
//}}}
/***
!!!!! editable.hasClass
***/
//{{{
editable.hasClass = function ( cstr ) {
return editable.dom.classList &&
editable.dom.classList.contains(cstr);
};
//}}}
/***
!!!!! editable.saveText
***/
//{{{
editable.saveText = function(newtext,newtitle){
return twve.tiddler.saveText(editable,newtext,newtitle);
};
//}}}
/***
!!!!! end of editable
***/
//{{{
return editable.created(src);
}
};
//}}}
/***
!!! twve.element
***/
//{{{
twve.element = {
//}}}
/***
!!!! twve.element.create
***/
//{{{
create : function(elem,txt,start,dir) {
var twelem = twve.editable.create();
//var preClear = twelem.clear;
twelem.clear = function(){
//preClear.apply(this);
// direct wrapper of the dom
twelem.direct_wrapper = null;
// rendering index
twelem.rIndex = -1;
// defining index
twelem.dIndex = -1;
// opening and closing signatures
twelem.tags = null;
return twelem;
};
// copyFrom
var preCopyFrom = twelem.copyFrom;
twelem.copyFrom = function(elem){
preCopyFrom.apply(this,arguments);
twelem.direct_wrapper = elem.direct_wrapper;
twelem.rIndex = elem.rIndex;
twelem.dIndex = elem.dIndex;
twelem.tags = elem.tags;
return twelem;
};
// wiki tags
twelem.markupTags = function(){
return twve.tiddler.markupTags(twelem.dom);
};
// direct wrapper
var preDirectWrapper = twelem.directWrapper;
twelem.directWrapper = function () {
// Find the direct wrapper of this element if not yet.
if ( ! twelem.direct_wrapper )
twelem.direct_wrapper =
preDirectWrapper.apply(this,arguments);
// Return the direct wrapper.
return twelem.direct_wrapper;
};
//}}}
/***
!!!!! twelem.renderedCopy
***/
//{{{
twelem.renderedCopy = function(w) {
// Find the rendered copy of this twelem in the
// wrapper w (DOM node).
var dw = twelem.directWrapper();
if ( ! dw ) return null;
dw = dw.dom;
if ( dw == w ) {
// Searching in the direct wrapper, the rendered copy
// is just this twve.element object.
return twelem;
}
if ( twve.node.contains(w,dw) ) {
// The wrapper w contains the direct wrapper, meaning
// it's the case of self-inclusion or folded section,
// exclude it.
return null;
}
// We are searching a transcluded copy of this twelem.
// If neither of this twelem or that copy to be searched in w
// is transcluded (non-transcluded or normally transcluded),
// their rendering index shall be the same and there is no need
// to re-find it. If, however, either one of the two is partially
// transcluded, their rendering indexes are not the same
// in general, we need to re-find it in w.
var rndx = -1;
if ( twve.text.tiddlerSection(twelem.wrapper_title)
|| twve.text.tiddlerSection(
twve.tiddler.titleOfWrapper(w)
)
) {
// One of the copies is partially transcluded.
// Clear the rendering index (by setting twelem.rIndex to -1)
// so the twve.wrapper.renderedElement() function will
// re-find it.
rndx = twelem.rIndex;
twelem.rIndex = -1;
}
var the_copy = twve.wrapper.renderedElement(twelem,w);
if ( rndx > -1 )
// We had cleared the rendering index of this object
// to re-find it in w. Restore it here.
twelem.rIndex = rndx;
return the_copy;
};
//}}}
/***
!!!!! twelem.removeSelf
***/
//{{{
twelem.removeSelf = function(){
twelem.dom.parentNode.removeChild(twelem.dom);
twve.tiddler.focusElem(null);
return twelem;
};
//}}}
/***
!!!!! twelem.replaceWith
***/
//{{{
twelem.replaceWith = function(newnode,oldnode){
twve.node.replace(newnode,(oldnode||twelem.dom));
return twelem;
};
//}}}
/***
!!!!! twelem.filter
***/
//{{{
twelem.filter = function(node,selector){
if ( ! selector )
selector = twelem.twveSelector(null,'rendering').include;
if ( node.nodeType )
return twve.node.matches(node,selector) ? node : null;
var filtered = [];
for ( var i=0,len=node.length; i<len; i++ )
if(twve.node.matches(node[i],selector))
filtered[filtered.length] = node[i];
if ( filtered.length == 0 ) return null;
return filtered.length > 1 ? filtered : filtered[0];
};
//}}}
/***
!!!!! twelem.refreshSelf
***/
//{{{
twelem.refreshSelf = function(txt){
// Refresh the element itself by creating a new copy and
// replace this one. This method refreshes only one copy of
// the element and nothing more. It is normally called
// within twelem.refreshAll(), which takes care of
// transclusion synchronization.
// Parameters:
// txt: See twelem.refreshAll below.
var nodes = twve.tiddler.getPreviewedNodes(txt);
var elem = twelem.filter(nodes) || nodes[0];
twelem.replaceWith(nodes);
twelem.dIndex = -1;
twelem.setElement(elem);
return twelem;
};
//}}}
/***
!!!!! twelem.refreshAll
***/
//{{{
twelem.refreshAll = function(txt,param,refreshed){
// Refresh/Re-render the element, and synchronize all
// transcluded copies.
// Parameters:
// txt: An array of text to refresh the element.
// Normally there is only one text in the
// array. Occasionally there are multiple
// text for elements such as spanned table
// cells.
// param: (Optional) Parameter for twelem.changed
// event handler. This parameter, if given,
// should be an object containing
// {
// what: "event message",
// other info that you defined to go with
// the event message...
// }
// The following two variables are for refreshing part or
// whole of a folded wrapper.
var folded = null;
var animated = config.options.chkAnimate;
config.options.chkAnimate = false;
var sec = twve.text.tiddlerSection(
twelem.wrapper_title
);
if ( typeof txt !== 'string' )
// If no text is given, get the text of this twelem.
txt = twelem.getText();
twelem.findWrappers();
for (var n=0,len=twelem.wrapper.length; n<len; n++) {
var w = twelem.wrapper[n];
var w_title = twve.tiddler.titleOfWrapper(w);
var w_sec = twve.text.tiddlerSection(w_title);
if ( sec && w_sec && w_sec != sec ) {
// We are refreshing only one section in the
// tiddler, but this wrapper contains another
// section different from the one to refresh.
// Do nothing.
continue;
}
var twcopy = twelem.renderedCopy(w);
if ( ! twcopy ) {
// There is no such element in this wrapper.
// Check for slices
if ( ! w_sec ) {
w_sec = twve.text.tiddlerSlice(w_title);
if ( w_sec ) {
// Yes, its a transcluded slice, refresh it.
twcopy=twve.tiddler.createEditableWrapper(w);
if ( twcopy ) twcopy.refreshSelf(w_sec);
}
}
continue;
}
folded = twcopy.foldedWrapper();
if ( folded )
// unfold it to refresh the element
twve.node.show(folded.dom);
if ( txt ) {
// Otherwise refresh the element
twcopy.refreshSelf(txt);
if ( param && param.what )
twcopy.changed(param);
} else
// Text is empty, meaning this element should be
// removed
twcopy.removeSelf(param);
// Done refreshing
if ( folded )
// fold it back
twve.node.hide(folded.dom);
}
config.options.chkAnimate = animated;
return twelem;
};
//}}}
/***
!!!!! twelem.renderingIndex
***/
//{{{
twelem.renderingIndex = function(){
// Find the rendering index of a twve.core element object
// in its direct wrapper.
if ( twelem.rIndex < 0 ) {
var elems = twelem.directWrapper().findElements(
twelem.twveSelector(null,'rendered')
);
if ( elems ) {
twelem.rIndex = elems.indexOf(twelem.dom);
}
}
return twelem.rIndex;
};
//}}}
/***
!!!!! twelem.counts
***/
//{{{
twelem.counts = function(searchInfo){
// Check if the currently found element counts as a valid
// one. If so,
// 1. update the ndx and remained properties of
// searchInfo,
// 2. return true.
// Otherwise,
// 1. return false.
// See the counts method in twve.table or twve.blockquote
// for examples.
searchInfo.remained--;
searchInfo.ndx++;
return true;
};
//}}}
/***
!!!!! twelem.skipCloseTag
***/
//{{{
twelem.skipCloseTag = function(){
return twelem.tags &&
twelem.tags.open == twelem.tags.close
? (twelem.tags.close.length+1) : 0;
};
//}}}
/***
!!!!! twelem.ends
***/
//{{{
twelem.ends = function (start,txt,inactiveTags) {
// Find the ending index of this element, starting from
// start. If start is not given or a negative number, the
// search starts from the beginning of this element.
// Returns the index of the character immediately after
// the last char of the closing tag if found, or a negative
// number to indicate the number of enclosed elements
// remained to be searched otherwise.
if ( typeof start == 'number' ) {
start = twve.position.create(start);
} else if ( ! start || start == twelem.start ) {
start = twve.position.create(twelem.start);
} else {
start.ensureValid();
}
// Move the starting position to skip the remaining of the
// opening tag.
start.ndx += start.matched ? start.matched.length : 1;
if (! txt) txt = twelem.wrapperText();
var tags = twelem.tags.exactCloseTag(start.matched);
twelem.end.copyFrom(
tags.matchedCloseTag(txt,start,txt.length,inactiveTags)
);
if ( twelem.end.ndx == -1 ) {
twelem.end.ndx = txt.length;
} else {
twelem.end.ndx += twelem.end.matched.length;
}
return twelem.end;
};
//}}}
/***
!!!!! twelem.starts
***/
//{{{
twelem.starts = function (start,txt,dir) {
// Find the starting position of an element represented
// by this twve.element object, starting from the argument
// start, if given, or the beginning of the direct wrapper.
// The argument start, if given, shall be an object of
// twve.position.
// Returns a twve.position object that contains the
// position of the first char of the opening tag and the
// matched opening tag.
if ( !(txt || twelem.tiddler) ||
!twelem.tags || !twelem.tags.open ) {
twelem.start.ndx = -1;
return twelem.start;
}
if ( ! txt ) txt = twelem.wrapperText();
// Ensure the validity of twelem.start.ndx
twelem.start.ensureValid(start);
var searchInfo = twve.object.create();
// Remaining number of elements to skip before identifying
// this one (twelem.dom).
searchInfo.remained = twelem.rIndex > -1
? (twelem.rIndex+1)
: (twelem.dIndex > -1 ? (twelem.dIndex+1) : 1);
// Index number (order of appearance) of the element
// represented by this twve.element object.
// If the rendering index (twelem.rIndex) was -1 upon
// calling this function, its value would be set to
// searchInfo.ndx upon returning. Same thing would be done to
// the defining index (twelem.dIndex) if it was -1 upon
// calling.
searchInfo.ndx = -1;
var inactiveTags = twve.tags.inactiveTags();
// Do the searching
do {
twelem.start = dir < 0
? twelem.tags.lastOpenTag(
txt,twelem.start,inactiveTags
)
: twelem.tags.nextOpenTag(
txt,twelem.start,inactiveTags
);
if ( twelem.start.ndx < 0 ) {
// Opening tag not found, returns the negative of
// the remaining number of elements to be skipped.
twelem.start.ndx = -searchInfo.remained;
return twelem.start;
}
// Check if this one counts.
if ( ! twelem.counts(searchInfo,txt) ) {
// No it doesn't. Continue searching.
twelem.start.ndx += twelem.start.matched.length;
continue;
}
// Yes, this one does count.
if ( searchInfo.remained == 0 ) {
// If found the wiki text then retunrs it.
if ( twelem.dIndex == -1 )
twelem.dIndex = searchInfo.ndx;
if ( twelem.rIndex == -1 )
twelem.rIndex = searchInfo.ndx;
return twelem.start;
}
// Otherwise search for the next one.
if ( dir < 0 ){
twelem.end.ndx = twelem.start.ndx;
} else {
twelem.start.ndx =
twelem.ends(null,txt,inactiveTags).ndx;
}
} while (true);
};
//}}}
/***
!!!!! twelem.setElement
***/
//{{{
var preSetElement = twelem.setElement;
twelem.setElement = function (elem,txt,start,dir) {
// Sets element elem (DOM node) to this twve.element object.
if ( ! elem ) return twelem.clear();
if ( typeof elem == 'string' ) {
twelem.dom = elem;
twelem.tags = twve.tiddler.markupTags(elem);
} else {
if ( ! preSetElement.apply(this,arguments) )
// Same element, no need to do anything.
return twelem;
if ( ! twelem.tiddler )
// Shadowed or non-existing tiddler
return twelem;
// An element. Look for its rendering index
// if necessary.
if ( twelem.rIndex < 0 && twelem.dIndex < 0 )
if ( txt || start ){
twelem.rIndex = twelem.dIndex = 0;
} else
twelem.renderingIndex();
// Find its corresponding wiki tags if necessary.
if ( ! twelem.tags )
// Find its corresponding wiki tags.
twelem.tags = twelem.markupTags();
if ( typeof dir !== 'number' ) dir = 1;
if ( dir < 0 ) {
// Going backwards, skip the closing tag if
// the element consists of identical opening
// and closing tags.
start -= twelem.skipCloseTag(txt,start);
}
// Search for the beginning of element wiki text.
twelem.starts(start,txt,dir);
if ( twelem.start.ndx >= 0 ) {
// If the beginning is found, search for
// the end.
twelem.ends(null,txt,null);
} else {
// Otherwise set the end position to -1.
twelem.end.ndx = -1;
}
}
return twelem;
};
//}}}
/***
!!!!! End of twelem
***/
//{{{
return twelem.created(elem,txt,start,dir);
}
};
//}}}
/***
!!! twve.pre
***/
//{{{
twve.pre = {
//}}}
/***
!!!! twve.pre.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
var inc = 'pre,div.syntaxhighlighter';
if ( ! which || which.toLowerCase().indexOf(0,6)=='render' )
inc += ',div.alt1,div.alt2'+
',.syntaxhighlighter code'+
',.syntaxhighlighter table';
return selector.includeSelector(inc);
},
//}}}
/***
!!!! twve.pre.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
['\n{{{', '\n\/\/{{{', '\n\/\*{{{\*\/', '\n<!--{{{-->'],
['}}}\n', '\/\/}}}\n', '\/\*}}}*\/\n', '<!--}}}-->\n']
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.pre.markupTags();
};
// lastOpenTag
tags.lastOpenTag = function(txt,start){
if ( start.ndx <= 0 ) {
start.ndx = -1;
return start;
}
// Make a copy of start so we do not change its original
// content.
start = twve.position.create(start);
// Check if we are sitting right on an openning tag of
// a preformmated element. Move backward by 1 if we are.
for(var i=0,len=tags.open.length; i<len; i++)
if( txt.substring(
start.ndx,start.ndx+tags.open[i].length
)==tags.open[i] ){
start.ndx--;
break;
}
start = twve.text.lastIndexOf(txt,tags.open,start);
return start.ndx < 0
? tags.firstOpenTag(txt,start)
: start;
};
// matchedCloseTag
tags.matchedCloseTag = function(txt,start,txtlen) {
var pos = twve.position.create(start);
pos = twve.text.indexOf(txt,tags.close,pos);
if ( pos.ndx > -1 ) return pos;
return tags.lastCloseTag(txt,start,txtlen);
};
return tags;
},
//}}}
/***
!!!! twve.pre.create
***/
//{{{
create : function(src,txt,start,dir){
var pre = twve.element.create();
// markupTags
pre.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.pre.markupTags();
};
// clone
pre.clone = function(){
// Optional, may save a tiny bit of time.
return twve.pre.create(pre);
};
// twveSelector
pre.twveSelector = function(selector,which){
// Optional, may save a tiny bit of time.
return twve.pre.twveSelector(selector,which);
};
// starts
var preStarts = pre.starts;
pre.starts = function(start,txt,dir){
if(dir && dir < 0){
var open = pre.tags.open;
for(var i=0,len=open.length; i<len; i++)
if( txt.substring(
start,start+open[i].length
)==open[i] )
{
start--;
break;
}
}
return preStarts.call(this,start,txt,dir);
}
// setElement
var preSetElement = pre.setElement;
pre.setElement = function(elem,txt,strt,dir){
if ( ! twve.node.matches(elem,'pre') )
elem = twve.node.closest(elem,'div.syntaxhighlighter');
return preSetElement.call(this,elem,txt,start,dir);
};
return pre.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.code
***/
//{{{
twve.code = {
//}}}
/***
!!!! twve.code.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('code')
.excludeSelector('.syntaxhighlighter code');
},
//}}}
/***
!!!! twve.code.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'{{{',
'}}}'
);
// lastOpenTag
tags.lastOpenTag = function(txt,start) {
return twve.text.lastIndexOf(txt,tags.open,start);
};
// nextOpenTag
tags.nextOpenTag = function(txt,start) {
return twve.text.indexOf(txt,tags.open,start);
};
// matchedCloseTag
tags.matchedCloseTag = function(txt,start) {
return twve.text.indexOf(txt,tags.close,start);
};
return tags;
},
//}}}
/***
!!!! twve.code.create
***/
//{{{
create : function(src,txt,start,dir){
var code = twve.element.create();
// markupTags
code.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.code.markupTags();
};
// clone
code.clone = function(){
// Optional, may save a tiny bit of time.
return twve.code.create(code);
};
// twveSelector
code.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.code.twveSelector();
};
// counts
code.counts = function(searchInfo,txt){
if(txt.charAt(
code.start.ndx+code.start.matched.length
)!='\n'){
searchInfo.remained--;
searchInfo.ndx++;
return true;
}
return false;
};
// starts
var preStarts = code.starts;
code.starts = function(start,txt,dir){
if(dir && dir < 0 &&
txt.substring(start,start+3)=='{{{')
start--;
return preStarts.call(this,start,txt,dir);
};
return code.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.leveledElement
***/
//{{{
twve.leveledElement = {
//}}}
/***
!!!! twve.leveledElement.getLevel
***/
//{{{
getLevel : function(txt,start,ch){
var level = 0;
if ( ! ch ) {
ch = txt.charAt(start);
if ( ! ch || /\s/.test(ch) ) {
return 0;
}
level++;
} else if ( /\s/.test(ch) ) {
return 0;
}
var len = txt.length;
while ( (start+level)<len &&
ch.indexOf(txt.charAt(start+level))>-1 )
level++;
return level;
},
//}}}
/***
!!!! twve.leveledElement.create
***/
//{{{
create : function(src,txt,start,dir){
var elem = twve.element.create();
// clear
//var preClear = elem.clear;
elem.clear = function(){
//preClear.apply(this);
elem.level = 0;
return elem;
};
// copyFrom
var preCopyFrom = elem.copyFrom;
elem.copyFrom = function(src){
preCopyFrom.apply(this,arguments);
elem.level = src.level;
return elem;
};
//}}}
/***
!!!!! elem.getLevel
***/
//{{{
elem.getLevel = function(txt,start) {
if ( elem.level < 1 ) {
if ( ! txt ) txt = elem.wrapperText();
if ( ! start ) start = elem.start;
var ndx = start.ndx;
if (txt.charAt(ndx)=='\n') ndx++;
elem.level = twve.leveledElement.getLevel(txt,ndx);
}
return elem.level;
};
//}}}
/***
!!!!! elem.getOpenTag
***/
//{{{
elem.getOpenTag = function(start,txt) {
// Get the actual opening tag
if ( ! txt ) txt = elem.tiddler ? elem.wrapperText() : '';
if ( ! txt ) return '';
if ( ! start ) start = elem.start;
var level = elem.getLevel(txt,start);
var tag = '';
if ( txt.charAt(start.ndx) == '\n' ) level++;
else tag = '\n';
return tag + txt.substring(start.ndx,(start.ndx+level));
};
//}}}
/***
!!!!! elem.starts
***/
//{{{
var preStarts = elem.starts;
elem.starts = function(start,txt,dir){
preStarts.apply(this,arguments);
elem.level = -1;
if ( elem.start.ndx > -1 )
elem.start.matched = elem.getOpenTag(elem.start,txt);
return elem.start;
};
//}}}
/***
!!!!! End of leveledElement
***/
//{{{
return elem.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.heading
***/
//{{{
twve.heading = {
//}}}
/***
!!!! twve.heading.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'h1,h2,h3,h4,h5,h6';
},
getFoldedSelector : function(){
return 'h1.foldable,h2.foldable,h3.foldable' +
',h4.foldable,h5.foldable,h6.foldable';
},
getSpanSelector : function(selector){
return 'span.TiddlyLinkExisting';
},
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
which = which ? which.toLowerCase() : '';
return selector.includeSelector(
which.substring(0,4)=='fold'
? twve.heading.getFoldedSelector()
: (twve.heading.getSelector() +
(which.substring(0,6)=='render'
? ''
: ','+twve.heading.getSpanSelector()))
);
},
//}}}
/***
!!!! twve.heading.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'\n!',
'\n'
);
// blockElement
tags.blockElement = true;
return tags;
},
//}}}
/***
!!!! twve.heading.getTitle
***/
//{{{
getTitle : function(h,title_only){
// Returns the title (content) of a header.
var hnode = null;
if ( h.dom ) {
// h is a twve.heading object
hnode = h.dom;
} else {
// h has to be a DOM node
hnode = h;
h = null;
}
if ( ! hnode ) return null;
if ( hnode.childNodes.length > 0 ){
// If a header contains formatted text or even transcluded
// content, the rendered text will be different from the
// defining wiki text and cause this plugin a failure in
// finding the section content. In such cases we return
// directly the defining wiki text instead of the rendered
// text.
var twh = twve.heading.create(h);
twh.setElement(hnode);
return twve.text.sectionTitle(twh.getText());
} else {
var title = hnode.textContent;
var head = '';
if ( config.macros.foldHeadings ) {
head = config.macros.foldHeadings.hidelabel;
if ( title.indexOf(head) == -1 ) {
head = config.macros.foldHeadings.showlabel;
if ( title.indexOf(head) == -1 )
head = '';
}
}
title = (head
? title.substring(head.length).trim()
: title);
if ( ! title_only ) {
// Look for multiple occurrences of the same title
var twh = twve.heading.create(h);
twh.setElement(hnode);
var hs = twh.directWrapper().findElements(
twve.heading.twveSelector(null,'rendered')
);
if ( hs ) {
var hndx = hs.indexOf(hnode);
var hoccur = 0;
for ( var n = 0; n < hndx; n++ ) {
var hprev = hs[n];
var tprev = twve.heading.getTitle(hprev,true);
if ( tprev == title ) hoccur++;
}
if ( hoccur > 0 ) {
title += ('?'+hoccur);
}
}
}
return title;
}
},
//}}}
/***
!!!! twve.heading.click
***/
//{{{
preClick : null,
click : function (ev) {
ev = ev || window.event;
if ( twve.node.matches(
ev.target,twve.heading.getSelector()
) )
twve.heading.preClick.apply(this,arguments);
},
//}}}
/***
!!!! twve.heading.prepareElements
***/
//{{{
prepareElements : function(twwrap){
if ( config.options.chktwveCoreEnabled &&
! twwrap.isSystemShadow() ) {
var foldable = twwrap.dom.querySelectorAll(
twve.heading.twveSelector(null,'foldable').include
);
for (var i=0,len=foldable.length; i<len; i++){
if ( ! twve.heading.preClick )
twve.heading.preClick = foldable[i].onclick;
foldable[i].onclick = twve.heading.click;
}
return twwrap;
}
return null;
},
//}}}
/***
!!!! twve.heading.create
***/
//{{{
create : function(src,txt,start,dir){
// Create an instance of twve.heading object.
// The argument src can be one of the followings:
// 1. a twve.heading object
// 2. a jQeury object representing a header
// 3. a DOM object (a header)
var h = twve.leveledElement.create();
// twveSelector
h.twveSelector = function(selector,which){
return twve.heading.twveSelector(selector,which);
};
// markupTags
h.markupTags = function(){
return twve.heading.markupTags();
};
// get title
h.getTitle = function (title_only) {
return twve.heading.getTitle(h,title_only);
};
// starts
var preStarts = h.starts;
h.starts = function(start,txt,dir){
if(dir && dir < 0 &&
txt.substring(start,start+2)=='\n!')
start--;
return preStarts.call(this,start,txt,dir);
};
// set element
var preSetElement = h.setElement;
h.setElement = function(elem,txt,start,dir){
preSetElement.call(
this,
(twve.node.matches(elem,twve.heading.getSpanSelector())
? elem.parentNode
: elem),
txt,start,dir
);
return h;
};
// mouseenter
h.mouseenter = function(ev){
var selector = twve.heading.getSpanSelector();
var sp = h.dom.querySelector(selector);
return ! sp
? true
: twve.node.matches(ev.target,selector);
};
// mousemove
h.mousemove = function(ev){
if ( ! h.mouseenter(ev) ) {
twve.tiddler.focus();
}
};
// is
h.is = function(elem){
return twve.node.is(
h.dom,
typeof elem == 'string'
? elem
: ( twve.node.matches(
elem,twve.heading.getSpanSelector()
)
? elem.parentNode
: elem
)
);
};
// isEditable
h.isEditable = function(elem){
var selector = twve.heading.getSpanSelector();
return twve.node.matches((elem || h.dom),selector)
? true
: h.dom.querySelectorAll(selector).length == 0;
};
return h.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.wrapper
***/
//{{{
twve.wrapper = {
//}}}
/***
!!!! twve.wrapper.recordFoldable
***/
//{{{
recordFoldable : function(foldable){
var folded = null;
if ( foldable ) {
folded = [];
for ( var i=0,len=foldable.length; i<len; i++ ) {
folded[i] = ! twve.node.isVisible(foldable[i]);
}
}
return folded;
},
//}}}
/***
!!!! twve.wrapper.restoreFoldable
***/
//{{{
restoreFoldable : function(foldable,folded){
if ( foldable ) {
// Temporarily disable animation
var animated = config.options.chkAnimate;
config.options.chkAnimate = false;
var imax = folded
? Math.min(folded.length, foldable.length)
: foldable.length;
for ( var i = 0; i < imax; i++ ) {
if (
foldable[i] &&
(! folded ||
(folded[i]&&twve.node.isVisible(foldable[i])) ||
(!folded[i]&&!twve.node.isVisible(foldable[i]))) &&
foldable[i].previousSibling
) {
var ev = document.createEvent('MouseEvent');
ev.initEvent('click',true,true);
foldable[i].previousSibling.dispatchEvent(ev);
}
}
config.options.chkAnimate = animated;
}
},
//}}}
/***
!!!! twve.wrapper.renderedElement
<<<
* This function finds a rendered copy of twelem in wrapper w.
** The wrapper can be one of the system wrappers:
*** div.viewer, normally loaded
*** div.tabContents, {{{<<tabs>>}}} transcluded
*** span[tiddler], {{{<<tiddler>>}}} transcluded
*** div.sliderPanel, {{{<<slider>>}}} transcluded
*** span.sliderPanel, {{{<<foldHeadings>>}}} folded
* If none of the indexes (rendering and defining) are given, this function returns null.
* If the defining index is given but the rendering index not (initialized to -1), this function goes through the tiddler text to find its rendering index (matching the defining index), then finds the rendered copy and returns it. This happens during transclusion synchronization when all transcluded copies are to be updated after one of them is changed.
* If the rendering index is given, this function simply finds the rendered element with that rendering index, in a cyclic manner if the 3rd argument cyclic is true, and returns it, regardless of the status of defining index. This happens during keyboard navigation where one definitely knows the rendering index of the current element and wants to move to the next.
<<<
***/
//{{{
renderedElement : function (twelem,w,cyclic) {
// Arguments:
// twelem: A twve.element object representing the element
// to look for.
// w: The wrapper in which to find the element. If
// omitted, the search will be done within
// twelem.directWrapper().
// cyclic: Search in a cyclic manner.
if ( ! twelem ) return null;
if ( twelem.rIndex < 0 && twelem.dIndex < 0 && ! cyclic ) {
return null;
}
if ( ! w ) {
w = twelem.directWrapper();
if ( ! w ) {
return null;
}
w = w.dom;
}
// Make sure there are such kind of elements in the wrapper.
var elements = twve.wrapper.findElements(
w, twelem.twveSelector(null,'rendered')
);
// If no such elements contained in the wrapper, return null.
if ( ! elements ) return null;
var size = elements.length;
// Make a copy of the element
var newelem = twelem.clone();
if ( cyclic ) {
// Find the element in a cyclic manner.
if ( newelem.rIndex < 0 )
newelem.rIndex = size-1;
else if ( newelem.rIndex >= size )
newelem.rIndex = 0;
} else {
// Not in a cyclic manner.
if ( newelem.rIndex < 0 ) {
// Rendering index unknown. Find it from the tiddler
// text.
newelem.wrapper = w;
newelem.direct_wrapper =
twve.tiddler.createEditableWrapper(w,true);
newelem.wrapper_title =
newelem.direct_wrapper.wrapper_title;
// Find the element. The rendering index will be
// determined in newelem.starts() function.
newelem.starts();
if ( newelem.rIndex >= 0 ) {
newelem.ends();
newelem.dom = elements[newelem.rIndex];
return newelem;
}
return null;
} else if ( newelem.rIndex >= size ) {
// This wrapper does not contain a rendered copy of
// twelem.
return null;
}
}
var elem = elements[newelem.rIndex];
if ( newelem.dIndex < 0 ) {
newelem.setElement(elem);
} else {
newelem.dom = elem;
}
if ( newelem.direct_wrapper.dom != w ) {
newelem.wrapper = w;
newelem.direct_wrapper =
twve.tiddler.createEditableWrapper(w,true);
newelem.wrapper_title =
newelem.direct_wrapper.wrapper_title;
}
return newelem;
},
//}}}
/***
!!!! twve.wrapper.findElements
***/
//{{{
findElements : function (w, selector) {
// Find all the rendered elements in wrapper w that are
// specified by selector. The 2nd argument can be either
// a string (valid CSS selector), or an object containing
// the following information:
// {
// include: CSS selector to include
// exclude: CSS selector to exclude
// }
if ( ! w || ! selector ) return null;
if (typeof selector == 'string' ) {
selector = twve.selector.create(selector,'');
} else if ( ! selector.include ) {
// The 2nd argument is not a valid selector object.
return null;
}
var elems = twve.nodeList.toArray(
w.querySelectorAll(selector.include)
);
// In wrapper w there could be transcluded tiddlers,
// which may contain elements of the same type. We shall
// remove them because they are not defined in the tiddler
// directly related to wrapper w.
var t_selector = twve.tiddler.twveTranscludedSelectors();
var t_wrapper = w.querySelectorAll(t_selector.include);
for ( var i=0,len=t_wrapper.length; i<len; i++ ) {
elems = twve.node.not(
elems,
t_wrapper[i].querySelectorAll(selector.include)
);
}
if ( selector.exclude ) {
elems = twve.node.not(elems,selector.exclude);
}
return elems.length > 0 ? elems : null;
},
//}}}
/***
!!!! twve.wrapper.create
***/
//{{{
create : function(wrap,txt,start,dir){
var twwrap = twve.editable.create();
// is wrapper?
twwrap.isWrapper = function(){
return true;
};
// direct wrapper
twwrap.directWrapper = function(){
return twwrap;
};
// title of wrapper
twwrap.titleOfWrapper = function(){
return twve.tiddler.titleOfWrapper(twwrap.dom);
};
// isEditable
twwrap.isEditable = function(elem){
return twwrap.dom == elem
? true
: twve.node.contains(twwrap.dom,elem) &&
! twve.node.matches(
elem,
twve.heading.twveSelector().include
);
};
// findElements
twwrap.findElements = function(selector){
return twve.wrapper.findElements(
twwrap.dom,
selector || twwrap.twveSelector(null,'rendered')
);
};
// ends
twwrap.ends = function(start,txt,dir){
if(!txt) {
if(!twwrap.tiddler) return twwrap.end;
txt = twwrap.tiddler.text;
}
var sec = twve.text.tiddlerSection(twwrap.wrapper_title);
if ( sec ) {
var twh = twve.heading.create(null,txt,start,dir);
twh.dIndex = 0;
twh.tiddler = twwrap.tiddler;
twh.tags = twve.heading.markupTags();
twh.start.ndx = twwrap.start.ndx;
twh.starts(twh.start,txt,dir);
if ( twh.start.ndx < 0 )
twwrap.end.ndx = txt.length;
else twwrap.end.ndx = twh.start.ndx;
} else
twwrap.end.ndx = txt.length;
return twwrap.end;
};
// starts
twwrap.starts = function(sec,start,txt,dir){
twwrap.start.ensureValid(start);
if ( ! sec ){
sec = twve.text.tiddlerSection(twwrap.wrapper_title);
}
if ( ! sec ) return twwrap.start;
// Remove the info that is only used internally.
var to_skip = twve.text.headerToSkip(sec);
if ( to_skip > 0 ) {
sec = twve.text.removeHeaderTail(sec);
}
if(!txt){
if(!twwrap.tiddler) return twwrap.start;
txt = twwrap.tiddler.text;
}
var twh = twve.heading.create(null,txt,start,dir);
twh.dIndex = 0;
twh.tiddler = twwrap.tiddler;
twh.end.ndx = twwrap.start.ndx;
twh.tags = twve.heading.markupTags();
while ( to_skip >= 0 ) {
twh.starts(twh.end,txt,dir);
if ( twh.start.ndx == -1 ) break;
twh.ends(null,txt,null,dir);
var title = twve.text.sectionTitle(twh.getText());
if ( title.substring(0,sec.length)==sec ) {
to_skip--;
}
}
twwrap.start.ndx = twh.end.ndx;
return twwrap.start;
};
// set element
var preSetElement = twwrap.setElement;
twwrap.setElement = function (elem,txt,start,dir) {
// Sets element elem (DOM node) to this twve.wrapper object.
if ( ! elem ) return twwrap.clear();
if ( ! preSetElement.apply(this,arguments) )
// Same element, no need to do anything.
return twwrap;
// Try to locate the wiki text even for non-existing
// tiddler.
twwrap.starts(null,start,txt,dir);
twwrap.ends(null,txt,dir);
return twwrap;
};
// refresh self
twwrap.refreshSelf = function(txt){
if ( ! twwrap.tiddler ) return twwrap;
// Refresh only one wrapper, assuming open and visible.
// Even though this wrapper is open and visible, there
// might be foldable wrappers (created using <<slider>> or
// <<foldHeadings>>) contained in this one. If so, we record
// their folding status, refresh this wrapper, then restore
// their folding status.
// Record folding status, if there are foldable wrappers.
var selector = twve.tiddler.twveFoldableSelector();
var foldable = twwrap.findElements(selector);
var folded = twve.wrapper.recordFoldable(foldable);
// Refresh this wrapper
// First remove all the child nodes.
//while ( twwrap.dom.firstChild )
// twwrap.dom.removeChild(twwrap.dom.firstChild);
twwrap.dom.innerHTML = '';
// Then append all the newly wikified nodes.
var nodes = twve.tiddler.getPreviewedNodes(txt,twwrap.dom);
for ( var i=0,len=nodes.length; i<len; i++ )
twwrap.dom.appendChild(nodes[0]);
// Restore folding status for foldable wrappers
foldable = twwrap.findElements(selector);
// Prepare elements if defined.
return twwrap.checkAndPrepare(foldable,folded);
};
// needsRefresh
twwrap.needsRefresh = function(tworg,sec_org,sec_this){
// Tests if this wrapper needs refresh due to some changes made
// to the original wrapper tworg.
// This wrapper needs refreshing if
// 1. We are refreshing the whole tiddler, or
// 2. this wrapper contains the whole tiddler, or
// 3. we are refreshing one section and w contains
// exactly that section.
if ( ! sec_org || ! sec_this || sec_this == sec_org ) {
// Exclude the situation where tworg contains this
// wrapper, or vice versa.
if ( !(twve.node.contains(tworg.dom,twwrap.dom)
|| twve.node.contains(twwrap.dom,tworg.dom)) ) {
return true;
}
}
return false;
};
// refresh all copies
twwrap.refreshAll = function(txt,param,refreshed){
// Refresh the wrapper twwrap,
// and synchronize all transcluded copies.
// The following two variables are for refreshing
// part or whole of a folded wrapper.
var animated = config.options.chkAnimate;
config.options.chkAnimate = false;
// Extract the section title if any
var sec = twwrap.sliceKey
? twve.text.tiddlerSlice(
twwrap.wrapper_title
)
: twve.text.tiddlerSection(
twwrap.wrapper_title
);
// Record the window scroll positions.
var scrL = window.pageXOffset;
var scrT = window.pageYOffset;
// Loop through all copies of this wrapper
twwrap.findWrappers();
for (var n=0,len=twwrap.wrapper.length; n<len; n++){
var w = twwrap.wrapper[n];
if ( w == twwrap.dom ) {
// Refresh if it's this very one
if ( ! refreshed ) twwrap.refreshSelf(txt);
} else {
// Otherwise find the editable copy
var twcopy = twve.tiddler.createEditableWrapper(w);
if ( ! twcopy ) continue;
// Extract its section title
var w_sec = twve.text.tiddlerSection(
twcopy.wrapper_title
) || twve.text.tiddlerSlice(
twcopy.wrapper_title
);
if ( twcopy.needsRefresh &&
twcopy.needsRefresh(twwrap,sec,w_sec) ) {
// unfold it if necessary
var folded = ! twve.node.isVisible(w)
? w : null;
if ( folded )
twve.node.show(folded);
// refresh the wrapper
twcopy.refreshSelf(
(w_sec == sec
? txt
: (w_sec
? twcopy.getText()
: twcopy.tiddler.text))
);
// fold it back if necessary
if ( folded )
twve.node.hide(folded);
}
}
}
window.scrollTo(scrL,scrT);
config.options.chkAnimate = animated;
return twwrap;
};
// multi line
twwrap.multiLine = function(){
return true;
};
// check and prepare
twwrap.checkAndPrepare = function(subfoldable,subfolded){
// This method can be called
// 1. upon loading,
// 2. upon refreshing.
// In cases of the 2nd type, this wrapper must have been
// unfolded (in twwrap.refreshAll), we don't need to take
// care of that here. In cases of the first, however, this
// wrapper may or may not be unfolded, we need to take care
// of that here.
if ( ! twwrap.dom ) return twwrap;
// Check the folding status of this wrapper
var folded = twwrap.isVisible()
? null
: twwrap.dom;
var animated = config.options.chkAnimate;
config.options.chkAnimate = false;
// Unfold it if folded
if ( folded ) {
// unfold it to refresh the wrapper
twve.node.show(folded);
}
// Unfold sub-wrappers if there are
if ( subfoldable === undefined ) {
subfoldable = twwrap.findElements(
twve.tiddler.twveFoldableSelector()
);
subfolded=twve.wrapper.recordFoldable(subfoldable);
}
if ( subfoldable ) {
twve.node.show(subfoldable);
}
// Prepare it
if ( twwrap.dom )
twve.wrapper.prepareElements(twwrap);
// Fold back the sub-wrappers if necessary
if ( subfoldable ) {
twve.wrapper.restoreFoldable(
subfoldable,subfolded
);
}
// Fold back this wrapper if necessary
if ( folded ) {
twve.node.hide(folded);
}
config.options.chkAnimate = animated;
return twwrap;
};
// wait
var pid = null;
twwrap.wait = function(){
if ( ! anim.running ) {
clearInterval(pid);
twwrap.checkAndPrepare();
}
};
// waitAndPrepare
twwrap.waitAndPrepare = function () {
// If animation is enabled and running, we should wait
// until it's done or the rendering can be wrong.
pid = null;
if ( ! config.options.chktwveCoreEnabled )
return;
if ( config.options.chkAnimate && anim.running ) {
pid = setInterval(twwrap.wait, 50);
} else {
twwrap.checkAndPrepare();
}
};
// twwrap.getText
twwrap.getText = function(){
return twwrap.tiddler.text.substring(
twwrap.start.ndx,twwrap.end.ndx
);
};
// twwrap.setText
twwrap.setText = function(newtxt,dlen){
var txt = twwrap.tiddler.text;
var open = twwrap.start.ndx;
var close = twwrap.end.ndx;
if ( typeof dlen !== 'number' ) {
dlen = newtxt.length - (close-open);
}
txt = txt.substring(0,open)+newtxt+txt.substring(close);
twwrap.end.ndx += dlen;
// update tiddler text
twve.tiddler.saveText(twwrap,txt);
return newtxt;
};
return twwrap.created(wrap,txt,start,dir);
}
};
//}}}
/***
!!! twve.viewer
***/
//{{{
twve.viewer = {
//}}}
/***
!!!! twve.viewer.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('div.viewer');
},
//}}}
/***
!!!! twve.viewer.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// Non-trnascluded wrapper or the tiddler title div.
w = twve.node.closest(w,'[tiddler]');
return w ? w.getAttribute('tiddler') : '';
},
//}}}
/***
!!!! twve.viewer.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function(title, parent){
// Non-transcluded
return twve.nodeList.querySelectorAll(
parent,
'div[tiddler*="'+title+'"] .viewer'
);
},
//}}}
/***
!!!! twve.viewer.create
***/
//{{{
create : function(src){
var viewer = twve.wrapper.create(src);
viewer.clone = function(){
// Optional, may save a tiny bit of time.
return twve.viewer.create(viewer);
};
return viewer;
}
};
//}}}
/***
!!! twve.tabContent
***/
//{{{
twve.tabContent = {
//}}}
/***
!!!! twve.tabContent.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'div.tabContents';
},
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('div.tabContents');
},
//}}}
/***
!!!! twve.tabContent.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// <<tabs>> transcluded wrapper.
// It suffices to use w.previousSibling.querySelectorAll(...) to
// locate the selected tab and retrieve the tiddler title from
// its attribute "content".
// In earlier versions I used jQuery to accomplish this. At that
// time I thought using $w.prev() should be faster than
// $w.parent() and did some test to confirm my idea. In contrary
// I found them pretty much the same performance when the
// content is fully transcluded. More surprisingly, when the
// content is partially transcluded, $w.parent() is better than
// $w.prev() for sure. Therefore $w.parent(), instead of
// $w.prev(), is used here to find the tiddler title.
//return w.previousSibling.querySelector('.tabSelected')
// .getAttribute('content');
w = w.parentNode;
return w
? w.querySelector('.tabSelected').getAttribute('content')
: '';
},
//}}}
/***
!!!! twve.tabContent.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function(title, parent){
// <<tabs>> transcluded
var wrapper = twve.nodeList.querySelectorAll(
parent,
'.tabSelected[content*="'+title+'"]'
);
if ( ! wrapper ) return null;
for(var i=0,len=wrapper.length; i<len; i++)
wrapper[i] = wrapper[i].parentNode.nextSibling;
return wrapper;
},
//}}}
/***
!!!! twve.tabContent.create
***/
//{{{
create : function(src,txt,start,dir){
var tabContent = twve.wrapper.create();
tabContent.clone = function(){
// Optional, may save a tiny bit of time.
return twve.tabContent.create(tabContent);
};
return tabContent.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.transcludedElem
***/
//{{{
twve.transcludedElem = {
//}}}
/***
!!!! twve.transcludedElem.create
***/
//{{{
create : function(src){
if ( src ) {
var focus = twve.tiddler.focusElem();
if ( focus && focus.dom == src.parentNode ) {
return twve.element.create();
}
}
return null;
}
};
//}}}
/***
!!! twve.tiddlerSpan
***/
//{{{
twve.tiddlerSpan = {
//}}}
/***
!!!! twve.tiddlerSpan.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'span[tiddler]';
},
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('span[tiddler]');
},
//}}}
/***
!!!! twve.tiddlerSpan.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// <<tiddler>> transcluded wrapper.
return w.getAttribute('tiddler');
},
//}}}
/***
!!!! twve.tiddlerSpan.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function(title, parent){
// <<tiddler>> transcluded
return twve.nodeList.querySelectorAll(
parent,
'span[tiddler*="'+title+'"]'
);
},
//}}}
/***
!!!! twve.tiddlerSpan.markupTags
***/
//{{{
markupTags : function(){
// <<tiddler>> transcluded
var tags = twve.tags.create(
'<<tiddler',
'>>'
);
// blockElement
tags.blockElement = true;
return tags;
},
//}}}
/***
!!!! twve.tiddlerSpan.create
***/
//{{{
create : function(src,txt,start,dir){
var tiddlerSpan = twve.transcludedElem.create(src)
|| twve.wrapper.create();
// markupTags
tiddlerSpan.markupTags = function(){
// Optional, may save a tiny bit of time.
var tags = twve.tiddlerSpan.markupTags();
tags.blockElement = tiddlerSpan.isWrapper();
return tags;
};
// clone
tiddlerSpan.clone = function(){
// Optional, may save a tiny bit of time.
return twve.tiddlerSpan.create(tiddlerSpan);
};
// starts
var preStarts = tiddlerSpan.starts;
tiddlerSpan.starts = function(){
tiddlerSpan.sliceKey = twve.text.tiddlerSlice(
tiddlerSpan.wrapper_title
);
if ( ! tiddlerSpan.sliceKey )
return preStarts.apply(this,arguments);
var txt = tiddlerSpan.tiddler.text;
store.slicesRE.lastIndex = 0;
while(true) {
var m = store.slicesRE.exec(txt);
if ( ! m ) break;
if ( m[2] ) {
if ( m[2] == tiddlerSpan.sliceKey ) {
tiddlerSpan.sliceValue = m[3];
tiddlerSpan.start.ndx = m.index;
tiddlerSpan.end.ndx = store.slicesRE.lastIndex;
break;
}
} else if ( m[5] == tiddlerSpan.sliceKey ) {
tiddlerSpan.sliceValue = m[6];
tiddlerSpan.start.ndx = m.index;
tiddlerSpan.end.ndx = store.slicesRE.lastIndex-1;
break;
}
}
return tiddlerSpan.start;
};
// ends
var preEnds = tiddlerSpan.ends;
tiddlerSpan.ends = function(){
return tiddlerSpan.sliceKey
? tiddlerSpan.end
: preEnds.apply(this,arguments);
};
// getText
var preGetText = tiddlerSpan.getText;
tiddlerSpan.getText = function(){
return tiddlerSpan.sliceKey
? (tiddlerSpan.sliceValue || '')
: preGetText.apply(this,arguments);
};
// setText
var preSetText = tiddlerSpan.setText;
tiddlerSpan.setText = function(newtxt){
if ( ! tiddlerSpan.sliceKey )
return preSetText.apply(this,arguments);
// There is sliceKey, meaning we are setting the slice
// value.
var txt = tiddlerSpan.tiddler.text;
var p0, p1, len = 0;
if ( tiddlerSpan.sliceValue ) {
p0 = txt.indexOf(
tiddlerSpan.sliceValue,tiddlerSpan.start.ndx
);
len = tiddlerSpan.sliceValue.length;
p1 = p0 + len;
} else {
p1 = p0 = tiddlerSpan.end.ndx;
}
txt = txt.substring(0,p0) +
newtxt +
txt.substring(p1);
tiddlerSpan.end.ndx += (newtxt?newtxt.length:0) - len;
tiddlerSpan.sliceValue = newtxt;
// update tiddler text
twve.tiddler.saveText(tiddlerSpan,txt);
return newtxt;
};
// End of tiddlerSpan
return tiddlerSpan.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.sliderPanel
***/
//{{{
twve.sliderPanel = {
//}}}
/***
!!!! twve.sliderPanel.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'div.sliderPanel';
},
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector(twve.sliderPanel.getSelector());
},
//}}}
/***
!!!! twve.sliderPanel.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// <<slider>> transcluded wrapper.
return w.getAttribute('tiddler');
},
//}}}
/***
!!!! twve.sliderPanel.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function(title, parent){
// <<slider>> transcluded
return twve.nodeList.querySelectorAll(
parent,
'div.sliderPanel[tiddler*="'+title+'"]'
);
},
//}}}
/***
!!!! twve.sliderPanel.markupTags
***/
//{{{
markupTags : function(){
// <<slider>> transcluded
var tags = twve.tags.create(
'<<slider',
'>>'
);
// blockElement
//tags.blockElement = true;
return tags;
},
//}}}
/***
!!!! twve.sliderPanel.create
***/
//{{{
create : function(src,txt,start,dir){
var sliderPanel = twve.transcludedElem.create(src)
|| twve.wrapper.create();
// markupTags
sliderPanel.markupTags = function(){
// Optional, may save a tiny bit of time.
var tags = twve.sliderPanel.markupTags();
tags.blockElement = sliderPanel.isWrapper();
return tags;
};
// clone
sliderPanel.clone = function(){
// Optional, may save a tiny bit of time.
return twve.sliderPanel.create(sliderPanel);
};
return sliderPanel.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.foldedSection
***/
//{{{
twve.foldedSection = {
//}}}
/***
!!!! twve.foldedSection.macro
***/
//{{{
macro : function(){
return '<<foldHeadings>>';
},
//}}}
/***
!!!! twve.foldedSection.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'span.sliderPanel';
},
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return twve.foldedSection.enableEdit
// For this wrapper we do not return selector if disabled.
// See explanations below in titleOfTiddler.
? selector.includeSelector('span.sliderPanel')
: selector;
}
};
//}}}
/***
!!! twve.tiddlerTitle
***/
//{{{
twve.tiddlerTitle = {
//}}}
/***
!!!! twve.tiddlerTitle.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('div.title');
},
//}}}
/***
!!!! twve.tiddlerTitle.titleOfWrapper
***/
//{{{
titleOfWrapper : function(w){
return w.textContent;
},
//}}}
/***
!!!! twve.tiddlerTitle.create
***/
//{{{
create : function(elem){
var tidTitle = twve.wrapper.create();
// setText
tidTitle.updateText = function(txt){
twve.tiddler.saveText(tidTitle,null,txt[0]);
return tidTitle;
};
// ..........................................................
// var preGetText = tidTitle.getText;
tidTitle.getText = function(){
return tidTitle.tiddler.title;
};
// ..........................................................
tidTitle.mouseleave = function(ev){
var menu = twve.tiddler.getOptionsMenu();
if ( menu.dom == ev.relatedTarget )
return false;
menu.hide(true);
return true;
};
// ..........................................................
tidTitle.blur = function(){
twve.tiddler.getOptionsMenu().hide(true);
};
// ..........................................................
tidTitle.multiLine = function(){
return false;
};
// ..........................................................
tidTitle.box = function(action){
var eb = twve.node.box(tidTitle.dom);
if ( action && action.indexOf('focus')>=0 ) {
var pos = twve.object.create();
pos.left = eb.right;
pos.top = eb.top;
twve.tiddler.getOptionsMenu().showRoot(pos,'left');
}
return twve.tiddler.editWrappers() ? eb : null;
}
// ..........................................................
tidTitle.isWrapper = function(){
return false;
};
// ..........................................................
tidTitle.titleOfWrapper = function(){
return tidTitle.dom.textContent;
};
// ..........................................................
return tidTitle.created(elem);
}
};
}());
//}}}
[[twve--Example--The Options Menu]]
[[twve--Example--Preformatted]]
[[twve--Example--Code]]
[[twve--Example--Wrappers]]
[[twve--Example--Transclusion--Content]]
[[twve--Example--Transclusion--Self Inclusion]]
/***
|editable|k
|''Name:''|twve.core|
|''Description:''|Core codes of twve, the ~TiddlyWiki View mode Editor, providing easy access to the defining wiki text corresponding to a DOM element, and the synchronization capability for multiply transcluded copies of a tiddler.|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.core.min / http://twve.tiddlyspace.com/#twve.core.min <br>* (regular) http://twve.tiddlyspot.com/#twve.core / http://twve.tiddlyspace.com/#twve.core|
|''Type:''|plugin|
|''Version:''|3.2.5|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
!!Options
Look for [[twve.core Options]] in the system Options panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve&&(twve={});version.extensions.twve={major:3,minor:2,revision:5,date:new Date("2015/11/06")};
config.macros.twveCoreOptions={identifyWebkitBrowser:function(){config.browser.isSafari&&config.browser.isChrome&&(config.browser.isSafari=!1,0<=config.userAgent.indexOf("opr")&&(config.browser.isOpera=!0,config.browser.isChrome=!1))},init:function(){void 0===config.options.chktwveCoreEnabled&&(config.options.chktwveCoreEnabled=void 0===config.options.chkTWtidEnabled?!0:config.options.chkTWtidEnabled);void 0===config.options.chktwveCoreShowFocus&&(config.options.chktwveCoreShowFocus=void 0===config.options.chkTWtidShowFocus?
!0:config.options.chkTWtidShowFocus);merge(config.optionsDesc,{chktwveCoreEnabled:"Enable ''twve.core''.",chktwveCoreShowFocus:"Show focus borders."});twve.tiddler.prePopupShow=Popup.show;Popup.show=twve.tiddler.popupShow;twve.tiddler.preRefreshTiddler=story.refreshTiddler;story.refreshTiddler=twve.tiddler.refreshTiddler;twve.tiddler.preTabsSwitchTab=config.macros.tabs.switchTab;config.macros.tabs.switchTab=twve.tiddler.tabsSwitchTab;twve.tiddler.preTiddlerTransclude=config.macros.tiddler.transclude;
config.macros.tiddler.transclude=twve.tiddler.tiddlerTransclude;twve.tiddler.preSliderHandler=config.macros.slider.handler;config.macros.slider.handler=twve.tiddler.sliderHandler;twve.tiddler.preOnClickSlider=config.macros.slider.onClickSlider;config.macros.slider.onClickSlider=twve.tiddler.onClickSlider;if(/\bnt\b(.*?)11/.test(config.userAgent))config.browser.isIE=config.browser.isIE11=!0;else if(config.browser.isIE){var a=config.userAgent.match(/msie 7(.*?)trident\//);if(a)switch(a=a.index+a[0].length,
config.userAgent.charAt(a)){case "6":config.browser.isIE10=config.browser.isIE1076=!0;break;case "5":config.browser.isIE975=!0;break;case "4":config.browser.isIE874=!0}else if(a=config.userAgent.indexOf("msie "),0<=a)switch(config.userAgent.charAt(a+5)){case "1":config.browser.isIE10=!0;break;case "9":config.browser.isIE9=!0;break;case "8":config.browser.isIE8=!0}config.browser.isIE&&(!config.browser.isIE10&&!config.browser.isIE1076&&!config.browser.isIE9&&!config.browser.isIE975&&!config.browser.isIE8&&
!config.browser.isIE874)&&displayMessage("''twve'' message: Unsupported IE version.\n\n "+config.userAgent+"\n\nPlease inform the author at qmo.wcy2@gmail.com. Thanks a lot.")}a=config.userAgent.indexOf("nt ");if(0<=a)switch(config.userAgent.substring(a+3,a+6)){case "6.1":config.browser.isWin7=!0;break;case "6.3":config.browser.isWin8=!0;break;case "5.1":config.browser.isWinXP=!0}else 0<=config.userAgent.indexOf("android")?config.browser.isAndroid=!0:/ip(.*?)mac/.test(config.userAgent)?config.browser.isiOS=
!0:config.macros.twveCoreOptions.identifyWebkitBrowser();window.addEventListener("resize",function(b){twve.tiddler.resize(b||window.event)});void 0===config.options.chktwveCoreEditWrappers&&(config.options.chktwveCoreEditWrappers=!1);merge(config.optionsDesc,{chktwveCoreEditWrappers:"Edit wrappers and tiddler title, default to false<br>(The ''twve.extra'' will automatically enable this feature, regardless of this setting)."});void 0===config.options.chktwveCorePreview&&(config.options.chktwveCorePreview=
void 0===config.options.chkTWtedPreview?!0:config.options.chkTWtedPreview);void 0===config.options.txttwveCorePreviewHeight&&(config.options.txttwveCorePreviewHeight=void 0===config.options.txtTWtedPreviewMaxHeight?"15":config.options.txtTWtedPreviewMaxHeight);void 0===config.options.txttwveCoreMinEditWidth&&(config.options.txttwveCoreMinEditWidth=6);void 0===config.options.txttwveCorePreviewCaret&&(config.options.txttwveCorePreviewCaret=void 0===config.options.txtTWtedPreviewCaret?"|":config.options.txtTWtedPreviewCaret);
/^[0-9]+$/.test(config.options.txttwveCorePreviewCaret)&&(config.options.txttwveCorePreviewCaret=String.fromCharCode(1*config.options.txttwveCorePreviewCaret));void 0===config.options.chktwveCoreConfirmToDelete&&(config.options.chktwveCoreConfirmToDelete=void 0===config.options.chkTWtedConfirmToDelete?!0:config.options.chkTWtedConfirmToDelete);void 0===config.options.chktwveCoreClickAway&&(config.options.chktwveCoreClickAway=void 0===config.options.chkTWtedClickAway?!0:config.options.chkTWtedClickAway);
void 0===config.options.chktwveCoreManualSave&&(config.options.chktwveCoreManualSave=void 0===config.options.chkTWtedManualSave?!1:config.options.chkTWtedManualSave);void 0===config.options.chktwveCoreManualUpload&&(config.options.chktwveCoreManualUpload=void 0===config.options.chkTWtedManualUpload?!1:config.options.chkTWtedManualUpload);merge(config.optionsDesc,{chktwveCorePreview:"Enable previewer. Default to true.",txttwveCoreMinEditWidth:"Minimum edit box width (characters). Default value is 6.",
txttwveCorePreviewHeight:"Previewer max height (lines of text). Default to 15.",txttwveCorePreviewCaret:"Caret in the previewer. Default to vertical line (|)",chktwveCoreConfirmToDelete:"Confirm before deleting elements.",chktwveCoreClickAway:"Click away to accept changes.",chktwveCoreManualSave:'Save changes to file manually. If true, a button labeled "S" will be provided for manual save. Otherwise the file will be saved each time a change is accepted.',chktwveCoreManualUpload:'Upload changes to server manually. If true, a button labeled "U" will be provided for manual upload. Otherwise all changes will be uploaded each time a change is accepted.'});
twve.tiddler.preEditHandler=config.commands.editTiddler.handler;config.commands.editTiddler.handler=twve.tiddler.editHandler;twve.tiddler.precloseTiddler=story.closeTiddler;story.closeTiddler=twve.tiddler.closeTiddler;twve.tiddler.preSaveHandler=config.commands.saveTiddler.handler;config.commands.saveTiddler.handler=twve.tiddler.saveHandler;twve.tiddler.preCancelHandler=config.commands.cancelTiddler.handler;config.commands.cancelTiddler.handler=twve.tiddler.cancelHandler;twve.tiddler.preSaveChanges=
saveChanges;saveChanges=twve.tiddler.saveChanges;twve.wrapper.prepareElements=twve.heading.prepareElements;var a=config.shadowTiddlers.OptionsPanel,b=a.indexOf("----");config.shadowTiddlers.OptionsPanel=a.substring(0,b)+"----\n[[twve.core Options|twve.core Options]]\n"+a.substring(b);merge(config.shadowTiddlers,{"twve.core Options":"<<twveCoreOptions>>"});twve.tiddler.registerWrapper(twve.tiddlerTitle);twve.tiddler.registerWrapper(twve.viewer);twve.tiddler.registerWrapper(twve.tabContent);twve.tiddler.registerWrapper(twve.tiddlerSpan);
twve.tiddler.registerWrapper(twve.sliderPanel)},order:{chktwveCoreEnabled:0,chktwveCoreShowFocus:1,chktwveCoreEditWrappers:2,txttwveCoreMinEditWidth:3,txttwveCorePreviewHeight:4,chktwveCorePreview:5,txttwveCorePreviewCaret:6,chktwveCoreConfirmToDelete:7,chktwveCoreClickAway:8,chktwveCoreManualSave:9,chktwveCoreManualUpload:10},collectOptions:function(a,b){var c=[],d;for(d in config.options)if(0<=d.indexOf(a)){var e=config.optionsDesc[d];if(e){var f=e.indexOf(".");-1<f?"twve"==e.substring(f-4,f)&&
(f=e.indexOf(".",f+1),-1==f?f=e.length:f++):f=e.length;c[c.length]="\n|<<option "+d+">>|"+e.substring(0,f)+" |"}}b&&c.sort(function(a,c){var d=a.substring(a.indexOf(" ")+1,a.indexOf(">>")),e=c.substring(c.indexOf(" ")+1,c.indexOf(">>"));return b[d]>b[e]?1:-1});return c},prepareOptionsTable:function(a,b,c){a="|"+a+"|c\n| Value | Description |h";b=this.collectOptions(b,c);c=0;for(var d=b.length;c<d;c++)a+=b[c];return a},showOptionsTable:function(a,b,c,d){var e=document.createElement("div");a.appendChild(e);
a=this.prepareOptionsTable(b,c,d);wikify(a,e);e=e.querySelector("table");twve.node.setDimension(e,"35em");twve.node.setDimension(e.querySelectorAll("input"),"5em")},handler:function(a){config.macros.twveCoreOptions.showOptionsTable(a,"''twve.core'' Options","twveCore",config.macros.twveCoreOptions.order)}};
twve.object={isCollection:function(a){return a&&void 0!==a.length&&"object"==typeof a},level:0,toString:function(a){switch(typeof a){case "function":return"function";case "object":if(!a)return a;if(a.nodeType)return twve.node.info(a);if(a.title&&a.modifier)return"{title: "+a.title+" ...}";for(var b="{",c=twve.object.keys(a),d=0,e=c.length;d<e;d++){var b=b+((0==d?"":", ")+"\n\t"+c[d]+":"),f=a[c[d]];f?5>++twve.object.level&&(b+=twve.object.toString(f),--twve.object.level):b+=f}return b+"\n}";default:return a}},
keys:function(a,b){var c=[],d;for(d in a)if(b||"function"!=typeof a[d])c[c.length]=d;return c},copyKey:function(a,b,c){switch(typeof b[c]){case "string":case "number":case "boolean":case "function":a[c]=b[c];break;default:if(!b[c])break;if(void 0!==b[c].length){a[c]||(a[c]=[]);for(var d=0,e=b[c].length;d<e;d++)a[c][d]=b[c][d]}else if(b[c].getFullYear){a[c]=new Date(b[c]);break}else a[c]||(a[c]=twve.object.create());for(var d=twve.object.keys(b[c],!0),f=0,e=d.length;f<e;f++)twve.object.copyKey(a[c],
b[c],d[f])}return a},copyKeys:function(a,b,c){for(var d=0,e=c.length;d<e;d++)twve.object.copyKey(a,b,c[d]);return a},create:function(a){var b={clone:function(){return twve.object.create(b)},copyKey:function(a,d){return twve.object.copyKey(b,a,d)},keys:function(){return twve.object.keys(b)},copyFrom:function(a,d){return twve.object.copyKeys(b,a,d||a.keys())},toString:function(){return twve.object.toString(b)},created:function(a){return a?b.copyFrom(a):b}};return b.created(a)}};
twve.text={markupTags:function(){var a=twve.tags.create(["'",'"'],["'",'"']);a.clone=function(){return twve.text.markupTags()};return a},tiddlerSlice:function(a){if(!a)return"";var b=a.indexOf(config.textPrimitives.sliceSeparator);return-1<b?a.substring(b+config.textPrimitives.sliceSeparator.length):""},sectionTitle:function(a){for(var b=0,c=/[! \t]/;c.test(a.charAt(b));)b++;return 0<b?a.substring(b):a},tiddlerSection:function(a){if(!a)return"";var b=a.indexOf(config.textPrimitives.sectionSeparator);
return-1<b?(a=a.substring(b+config.textPrimitives.sectionSeparator.length),a=twve.text.sectionTitle(a),a.replace("\u2014","--")):""},tiddlerTitle:function(a){if(!a)return"";var b=a.indexOf(config.textPrimitives.sectionSeparator);-1==b&&(b=a.indexOf(config.textPrimitives.sliceSeparator));return-1<b?a.substring(0,b):a},headerToSkip:function(a){var b=a.lastIndexOf("?");return 0<b&&b<a.length-1&&(a=a.substring(b+1),/^\d$/.test(a))?1*a:0},removeHeaderTail:function(a){var b=a.lastIndexOf("?");0<b&&(b<a.length-
1&&!/[\D]/.test(a.substring(b+1)))&&(a=a.substring(0,b));return a},lastIndexOf:function(a,b,c,d){if(b)if("string"==typeof b)c.ndx=a.lastIndexOf(b,c.ndx),c.matched=-1<c.ndx?b:"";else if(b.exec){d=0;for(var e=c.ndx;;){var f=a.substring(d,e);if(!f)break;b.lastIndex=Math.round(f.length/2);var g=b.exec(f);g?(c.ndx=d+g.index,c.matched=g[0],d=c.ndx+c.matched.length):e=Math.round(d+f.length/2)}}else{if(b.length){e=[];f=b.length;for(g=0;g<f;g++)e[g]=twve.position.create(c),twve.text.lastIndexOf(a,b[g],e[g],
d);c.ndx=e[0].ndx;c.matched=e[0].matched;a=c;for(g=1;g<f;g++)-1!=e[g].ndx&&(-1==c.ndx||!d&&e[g].ndx>c.ndx?(c.ndx=e[g].ndx,c.matched=e[g].matched):(a.next=twve.position.create(e[g]),a=a.next))}}else c.ndx=-1,c.matched="";return c},indexOf:function(a,b,c,d){if(b)if("string"==typeof b)c.ndx=a.indexOf(b,c.ndx),c.matched=-1<c.ndx?b:"";else if(b.exec)b.lastIndex=c.ndx,(d=b.exec(a))?(c.ndx=d.index,c.matched=d[0]):(c.ndx=-1,c.matched="");else{if(b.length){for(var e=[],f=b.length,g=0;g<f;g++)e[g]=twve.position.create(c),
twve.text.indexOf(a,b[g],e[g],d);c.ndx=e[0].ndx;c.matched=e[0].matched;a=c;for(g=1;g<f;g++)-1!=e[g].ndx&&(-1==c.ndx||!d&&e[g].ndx<c.ndx?(c.ndx=e[g].ndx,c.matched=e[g].matched):(a.next=twve.position.create(e[g]),a=a.next))}}else c.ndx=-1,c.matched="";return c},skipToActive:function(a,b,c,d,e){c?"number"==typeof c&&(c=twve.position.create(c)):c=twve.position.create(0);d?"number"==typeof d&&(d=twve.position.create(d)):d=twve.position.create(a.length);var f=twve.position.create(c),f=twve.text.indexOf(a,
b,f);if(f.ndx==c.ndx)return f;if(f.ndx>d.ndx)return f.ndx=-1,f;e||(e=twve.tags.inactiveTags());for(var g=twve.position.create(f);f.ndx>c.ndx&&f.ndx<d.ndx&&(g=e.encloses(a,f.ndx,c,d));)f.ndx=g.ndx+g.matched.length,f=twve.text.indexOf(a,b,f);if(f.ndx<c.ndx||f.ndx>d.ndx)f.ndx=-1;return f},consecutiveLinesEnd:function(a,b,c,d,e){var f=0;switch(typeof b){case "number":f=b;break;case "object":b.ndx&&(f=b.ndx)}b=a.length;if("\n"==a.charAt(f))f++;else for(;0<f&&"\n"!=a.charAt(f-1);)f--;var g=twve.leveledElement.getLevel(a,
f,d),h=twve.position.create(f,"\n");if(0==g)return h;h.ndx=a.indexOf(h.matched,f);if(-1==h.ndx)return h.ndx=b,h.matched="",h;e||(e=twve.tags.create("\n"+a.substring(f,f+g),"\n"));f=twve.position.create(h);do{f=twve.text.indexOf(a,e.open,f);if(f.ndx!=h.ndx||!c&&twve.leveledElement.getLevel(a,f.ndx+1,d)!=g)break;h.ndx=f.ndx+f.matched.length;h=twve.text.indexOf(a,e.exactCloseTag(f.matched).close,h);if(-1==h.ndx){h.ndx=b;h.matched="";break}f.ndx=h.ndx}while(1);return h},skipStyleText:function(a,b){if(!a.length)return 0;
if("number"!=typeof b||0>b)b=0;var c=/[^A-Za-z:;-]/.exec(a);if(c&&c.index==b)return 0;var d=b,e=/[\s\,\.]/;do{var f=a.indexOf(":",d+1);if(0<f){if(c&&c.index>b&&c.index<f)return d-b;var g=a.indexOf(";",f+1);if(g>f){if(e.test(a.substring(f+1,g)))return d-b;d=g+1}else return d-b}else return d-b}while(1)},removeStyleText:function(a){if(!a)return"";var b=twve.text.skipStyleText(a);return b<a.length?a.substring(b):""}};
twve.tags={_inactive_tags:null,inactiveTags:function(){twve.tags._inactive_tags||(twve.tags._inactive_tags=twve.tags.create("<<",">>").merge(twve.pre.markupTags()).merge(twve.code.markupTags()).merge(twve.link.markupTags()).merge(twve.tags.create('"""','"""')),twve.tags._inactive_tags.clone=function(){return twve.tags.create()});return twve.tags._inactive_tags},create:function(a,b){var c=twve.object.create();c.sameTag=function(b,a){if(b){if(!a)return!1}else return a?!1:!0;if("string"==typeof b){if("string"==
typeof a)return b==a;var c=twve.position.create(0),c=twve.text.indexOf(b,a,c);return 0<=c.ndx}if("string"==typeof a)return c=twve.position.create(0),c=twve.text.indexOf(a,b,c),0<=c.ndx;if(b.length!=a.length)return!1;for(c=0;c<b.length;c++)if(b[c]!=a[c])return!1;return!0};c.is=function(b){return c.sameTag(c.open,b.open)&&c.sameTag(c.close,b.close)};c.clone=function(){return twve.tiddler.markupTags(c)||twve.tags.create(c)};c.firstOpenTag=function(b,a,f){f||(f=c.open);if("string"==typeof f){var g=f,
h=f.length,l=0;if("\n"==f.charAt(0)){f=f.substring(1);for(h--;"\n"==b.charAt(l);)l++}b.substring(l,l+h)==f?(a.ndx=l,a.matched=g):(a.ndx=-1,a.matched="")}else if(f.exec)f.lastIndex=0,(b=f.exec(b))?(a.ndx=b.index,a.matched=b[0]):(a.ndx=-1,a.matched="");else if(f.length){g=0;for(h=f.length;g<h&&!(c.firstOpenTag(b,a,f[g]),-1!=a.ndx);g++);}else a.ndx=-1;return a};c.nextOpenTag=function(b,a,f){if(0>=a.ndx){c.firstOpenTag(b,a);if(0==a.ndx)return a;a.ndx=0}else"\n"!=b.charAt(a.ndx)&&"\n"==b.charAt(a.ndx-
1)&&a.ndx--;return twve.text.skipToActive(b,c.open,a,null,f)};c.exactCloseTag=function(b){if(b&&twve.object.isCollection(c.open)){var a=c.clone();a.open=b;a.close=twve.object.isCollection(c.close)?c.close[c.open.indexOf(b)]:c.close;return a}return c};c.lastCloseTag=function(b,a,f,g){f||(f=b.length);g||(g=c.close);if("string"==typeof g){var h=g.length;"\n"==c.close.charAt(h-1)&&"\n"!=b.charAt(f-1)&&(g=g.substring(0,--h));b.substring(f-h)==g?(a.ndx=f-h,a.matched=g):(a.ndx=-1,a.matched="")}else if(g.exec)g.lastIndex=
a.ndx,(b=g.exec(b))?(a.ndx=b.index,a.matched=b[0]):(a.ndx=-1,a.matched="");else if(g.length){f=0;for(h=g.length;f<h&&!(c.lastCloseTag(b,a,g[f]),-1!=a.ndx);f++);}else a.ndx=-1;return a};c.matchedCloseTag=function(b,a,f,g){var h=a.ndx;a=twve.text.skipToActive(b,c.close,a,null,g);if(-1<a.ndx)return a;a.ndx=h;return c.lastCloseTag(b,a,f)};c.encloses=function(b,a,f,g){if(0==a||a>=b.length-1)return null;f=twve.position.create(f);g=twve.position.create(g);0>f.ndx&&(f.ndx=0);if(g.ndx<=f.ndx||g.ndx>b.length)g.ndx=
b.length;var h=twve.position.create(a),h=twve.text.lastIndexOf(b,c.open,h,!0);if(0>h.ndx)return null;do{if(h.ndx>=f.ndx&&h.ndx+h.matched.length<=a){var l=c.exactCloseTag(h.matched),k=twve.position.create(h.ndx+h.matched.length),k=twve.text.indexOf(b,l.close,k);if(-1==k.ndx)break;if(k.ndx>a&&k.ndx<=g.ndx)return a=twve.position.create(h.ndx+h.matched.length),a=twve.text.indexOf(b,l.open,a),0>a.ndx||a.ndx>k.ndx?k:null;f.ndx=k.ndx}h=h.next}while(h);return null};c.mergeTag=function(b,a){if(!a)return b;
if(b)if("string"==typeof b)if("string"==typeof a)b!=a&&(b=[b,a]);else{b=[b];for(var c=0,g=a.length;c<g;c++)-1==b.indexOf(a[c])&&(b[b.length]=a[c])}else if("string"==typeof a)-1==b.indexOf(a)&&(b[b.length]=a);else{c=0;for(g=a.length;c<g;c++)-1==b.indexOf(a[c])&&(b[b.length]=a[c])}else b="string"==typeof a?a:a.slice(0);return b};c.merge=function(b){c.open=c.mergeTag(c.open,b.open);c.close=c.mergeTag(c.close,b.close);return c};return b?(c.open=a,c.close=b,c):c.created(a)}};
twve.link={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("a")},markupTags:function(){return twve.tags.create("[[","]]")}};
twve.tiddler={registered_element:null,registered_wrapper:null,registerElement:function(a){twve.tiddler.registered_element?-1==twve.tiddler.registered_element.indexOf(a)&&(twve.tiddler.registered_element[twve.tiddler.registered_element.length]=a):(twve.tiddler.registered_element=[],twve.tiddler.registered_element[twve.tiddler.registered_element.length]=a)},registerWrapper:function(a){twve.tiddler.registered_wrapper?-1==twve.tiddler.registered_wrapper.indexOf(a)&&(twve.tiddler.registered_wrapper[twve.tiddler.registered_wrapper.length]=
a):(twve.tiddler.registered_wrapper=[],twve.tiddler.registered_wrapper[twve.tiddler.registered_wrapper.length]=a)},enableAllEditable:function(a){for(var b in twve.tiddler.registered_element)b.enableEdit&&(b.enableEdit=!1!==a)},readOnly:function(a){return a&&"twve--example"==a.toLowerCase().substring(0,13)?!1:readOnly},registeredEditableObject:function(a,b,c,d){if(!a||!b||!config.options.chktwveCoreEnabled)return null;for(var e=twve.selector.create(),f=b.length-1;0<=f;f--){var g=b[f];if(g.enableEdit)if(e.clear(),
g.is){if(g.is(a,e))return c?{obj:g,selector:e}:g}else if(g.twveSelector(e,d),twve.node.matches(a,e.include)&&(!e.exclude||!twve.node.matches(a,e.exclude)))return c?{obj:g,selector:e}:g}return null},createEditableElement:function(a){var b=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_element,!0);return!b?null:(a=b.obj.create?b.obj.create(a):twve.element.create(a))&&twve.tiddler.readOnly(a.wrapper_title)?null:a},twveSelector:function(a,b){var c=twve.tiddler.registeredEditableObject(a,
twve.tiddler.registered_element,!0,b);c||(c=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_wrapper,!0,b));return c?c.selector:null},twveFoldableSelector:function(){var a=twve.sliderPanel.twveSelector();twve.foldedSection.twveSelector(a);return a},twveTranscludedSelectors:function(){var a=twve.selector.create();twve.tabContent.twveSelector(a);twve.sliderPanel.twveSelector(a);twve.tiddlerSpan.twveSelector(a);return a},cloneMarkupTags:function(a,b){for(var c=a.length-1;0<=c;c--){var d=
a[c];if(d.enableEdit&&d.markupTags&&(d=d.markupTags(),d.is(b)))return d}return null},markupTags:function(a,b){if(a.nodeType){var c=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_element,!1,b);c||(c=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_wrapper,!1,b));return c&&c.markupTags?c.markupTags():null}return twve.tiddler.cloneMarkupTags(twve.tiddler.registered_element,a)||twve.tiddler.cloneMarkupTags(twve.tiddler.registered_wrapper,a)},registeredSelectors:function(a,
b){b||(b=twve.selector.create());for(var c=a.length-1;0<=c;c--)a[c].twveSelector(b);return b},elementSelectors:function(a){return twve.tiddler.registeredSelectors(twve.tiddler.registered_element,a)},wrapperSelectors:function(a){return twve.tiddler.registeredSelectors(twve.tiddler.registered_wrapper,a)},directWrapper:function(a,b){var c=twve.node.closest(a.parentNode,twve.tiddler.wrapperSelectors().include);return c?twve.tiddler.createEditableWrapper(c,b):null},createEditableWrapper:function(a,b){var c=
twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_wrapper);if(c){var d=c.create?c.create(a):twve.wrapper.create(a);if(!d)return null;d.wrapper_title=c.titleOfWrapper(a);return b||!twve.tiddler.readOnly(d.wrapper_title)?d:null}return null},createEditable:function(a){var b=twve.tiddler.createEditableWrapper(a);b||(b=twve.tiddler.createEditableElement(a));return b&&b.isSystemShadow()?null:b},cloneEditable:function(a){var b=twve.tiddler.registeredEditableObject(a.dom,twve.tiddler.registered_element);
return b?b.create?b.create(a):twve.element.create(a):(b=twve.tiddler.registeredEditableObject(a.dom,twve.tiddler.registered_wrapper))?b.create?b.create(a):twve.wrapper.create(a):null},titleOfWrapper:function(a){if(!a)return"";var b=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_wrapper);return b&&b.titleOfWrapper?b.titleOfWrapper(a):""},wrapperFromTitle:function(a){if(!a)return null;a=twve.text.removeHeaderTail(a);var b=twve.text.tiddlerTitle(a);a=twve.text.tiddlerSection(a);for(var c=
document.querySelectorAll("div[id=tiddlerDisplay]"),d=null,e=twve.tiddler.registered_wrapper,f=e.length-1;0<=f;f--){var g=e[f];g.wrapperFromTitle&&(g=g.wrapperFromTitle(b,c,a))&&(d=d?d.concat(g):g)}return d},get:function(a,b){if(!b||b!=story)b=store;return b.getTiddler(twve.text.tiddlerTitle(a))},displaySelector:function(){return"div[id=displayArea]"},getDisplay:function(){return document.querySelector("div[id=displayArea]")},optionsMenu:null,getOptionsMenu:function(){twve.tiddler.optionsMenu||(twve.tiddler.optionsMenu=
twve.menu.create(twve.button.create(null,String.fromCharCode(8801),"''twve'' menu")),twve.tiddler.optionsMenu.addMenu("''twve.core'' Options",config.macros.twveCoreOptions.handler));return twve.tiddler.optionsMenu},cur_focus:null,focusElem:function(a,b){if("undefined"!=typeof a){twve.tiddler.cur_focus&&twve.tiddler.cur_focus.blur&&twve.tiddler.cur_focus.blur();if(a)a.focus&&a.focus();else{var c=twve.tiddler.getOptionsMenu();c&&c.hide(!0);(!b||"keep"!=b.toLowerCase().substring(0,4))&&twve.tiddler.drawFocusBorders()}twve.tiddler.cur_focus=
a}return twve.tiddler.cur_focus},border_top:null,border_bottom:null,border_left:null,border_right:null,createFocusBorder:function(a,b){a||(a=twve.tiddler.getDisplay());var c=document.createElement("div");a.appendChild(c);c.style.position="absolute";twve.node.setDimension(c,null,0);switch(b){case "left":c.style.borderLeftWidth="1px";c.style.borderLeftStyle="dashed";break;case "right":c.style.borderRightWidth="1px";c.style.borderRightStyle="dashed";break;case "top":c.style.borderTopWidth="1px";c.style.borderTopStyle=
"dashed";break;case "bottom":c.style.borderBottomWidth="1px",c.style.borderBottomStyle="dashed"}return c},focusBorderVisible:function(){return twve.node.isVisible(twve.tiddler.border_top[0])},prepareFocusBorders:function(a){twve.node.setPosition(twve.tiddler.border_left[0],a.left,a.top);twve.node.setDimension(twve.tiddler.border_left[0],null,a.height);twve.node.setPosition(twve.tiddler.border_top[0],a.left,a.top);twve.node.setDimension(twve.tiddler.border_top[0],a.width);twve.node.setPosition(twve.tiddler.border_bottom[0],
a.left,a.bottom);twve.node.setDimension(twve.tiddler.border_bottom[0],a.width);twve.node.setPosition(twve.tiddler.border_right[0],a.right,a.top);twve.node.setDimension(twve.tiddler.border_right[0],null,a.height);return 1},focus_box:null,focusBox:function(){return twve.tiddler.focus_box},clearFocusBox:function(){twve.tiddler.focus_box=null},drawFocusBorders:function(a){a?(twve.tiddler.focus_box=[{}],a.cloneTo(twve.tiddler.focus_box[0],0,!1),a.cloneTo(twve.tiddler.focus_box),a=twve.tiddler.prepareFocusBorders(a),
config.options.chktwveCoreShowFocus&&(twve.node.show(twve.tiddler.border_top,0,a),twve.node.show(twve.tiddler.border_bottom,0,a),twve.node.show(twve.tiddler.border_left,0,a),twve.node.show(twve.tiddler.border_right,0,a))):(twve.tiddler.focus_box=null,twve.node.hide(twve.tiddler.border_top),twve.node.hide(twve.tiddler.border_bottom),twve.node.hide(twve.tiddler.border_left),twve.node.hide(twve.tiddler.border_right))},editWrappers:function(){return config.options.chktwveCoreEditWrappers},focus:function(a,
b,c,d){var e=null;if(!a||!config.options.chktwveCoreEnabled)b="off";else{e=a.dom;if(twve.node.closest(e,".preview"))return;config.options.chktwveCoreNoClick&&(!twve.tiddler.cur_editable||!twve.tiddler.cur_editable.is(e))&&twve.tiddler.editInPlace(a,c)}if(!twve.tiddler.border_top){var f=twve.tiddler.getDisplay();twve.tiddler.border_top=[];twve.tiddler.border_top[0]=twve.tiddler.createFocusBorder(f,"top");twve.tiddler.border_bottom=[];twve.tiddler.border_bottom[0]=twve.tiddler.createFocusBorder(f,"bottom");
twve.tiddler.border_left=[];twve.tiddler.border_left[0]=twve.tiddler.createFocusBorder(f,"left");twve.tiddler.border_right=[];twve.tiddler.border_right[0]=twve.tiddler.createFocusBorder(f,"right")}if(!e||"off"==b||a.isWrapper()&&!twve.tiddler.editWrappers())twve.tiddler.drawFocusBorders(),twve.tiddler.focusElem(null);else if(twve.tiddler.focusElem(a),a.isWrapper()?(twve.tiddler.drawFocusBorders(),d||(d=a.box("focus",c))):(d||(d=a.box("focus",c)),twve.tiddler.drawFocusBorders()),d&&d.height>=twve.node.cssSize("font-size",
e))return a.drawFocusBorders?a.drawFocusBorders(d):twve.tiddler.drawFocusBorders(d),!0},mousePosition:function(a){var b=twve.object.create();a&&(b.x=a.pageX,b.y=a.pageY);return twve.node.adjustPosition(b)},focusTarget:function(a){return a.target},mouseMove:function(a){a=a||window.event;var b=twve.tiddler.focusElem(),c=null,d=null,e=twve.tiddler.focusTarget(a);if(b)if(b.is(e))b.mousemove&&b.mousemove(a);else{(config.browser.isAndroid||config.browser.isiOS)&&twve.tiddler.updateText();var c=twve.tiddler.focusBox(),
f=twve.node.box(e);if(c&&c.contains(f))twve.tiddler.focusElem(null,"keep"),(d=twve.tiddler.createEditable(e))?(twve.tiddler.focus(),c=d.differentBox?null:f):twve.tiddler.focusElem(b);else if(b.mouseleave?b.mouseleave(a):1)twve.tiddler.focus(),(d=twve.tiddler.createEditable(e))&&(c=d.differentBox?null:f)}else(config.browser.isAndroid||config.browser.isiOS)&&twve.tiddler.updateText(),d=twve.tiddler.createEditable(e);d&&(!d.mouseenter||d.mouseenter(a))&&twve.tiddler.focus(d,"on",a,c)},eventTarget:function(a){return 0==
a.button||config.browser.isIE&&1==a.button?a.target:null},m_count:0,down_elem:null,mouseDown:function(a){a=a||window.event;var b=twve.tiddler.eventTarget(a);if(b){a=twve.tiddler.mousePosition(a);var c=twve.tiddler.focusElem();if(c&&c.is(b)&&(c=twve.tiddler.focusBox())&&(a.x>=c.right||a.y>=c.bottom))return twve.tiddler.down_elem=null;c=twve.tiddler.getOptionsMenu();c.isVisible()&&c.contains(a)||"A"==b.nodeName||twve.node.closest(b,"A")?(b=null,twve.tiddler.m_count=0):(config.browser.isAndroid||config.browser.isiOS?
0<twve.tiddler.m_count&&c.hide(!0):c.hide(!0),b!=twve.tiddler.down_elem&&(twve.tiddler.m_count=0))}return twve.tiddler.down_elem=b},mouseUp:function(a){if(twve.tiddler.down_elem){a=a||window.event;var b=twve.tiddler.eventTarget(a);if(b&&b==twve.tiddler.down_elem&&!twve.button.isSystemButton(b)){var c=twve.tiddler.getEditBox();if(c&&twve.node.is(b,c))twve.tiddler.previewEditBox(b);else if(!twve.node.closest(b,".preview")&&!twve.node.matches(b,"textarea,input"))if(twve.node.matches(b,"html,#displayArea,#contentWrapper,.txtMainTab,.header,.headerShadow,.headerForeground,.siteTitle,.siteSubtitle,#menuBar"))twve.tiddler.updateText(),
twve.tiddler.focus();else{var d=twve.tiddler.focusElem();if(d){if(config.browser.isAndroid||config.browser.isiOS){if(2>++twve.tiddler.m_count)return;twve.tiddler.m_count=0}c&&twve.tiddler.updateTextAndIndex(d);if(d.isEditable(b))return!twve.tiddler.editInPlace(d,a)}}}}},resize:function(a){if(!twve.tiddler.cur_editable&&!config.browser.isAndroid&&!(config.browser.isiOS||config.browser.isIE&&!config.browser.isIE9)){a=window.pageXOffset;var b=window.pageYOffset;twve.tiddler.focusElem(null);var c=document.querySelectorAll("#sidebar,#mainMenu");
window.innerWidth<screen.width/2?twve.node.hide(c):twve.node.show(c);for(var d=twve.viewer.create(),c=twve.viewer.twveSelector(),c=document.querySelectorAll(c.include),e=0,f=c.length;e<f;e++)twve.node.matches(c[e],"div.board")||(d.setElement(c[e]),d.refreshSelf(d.getText()),d.clear());window.scrollTo(a,b)}},saveText:function(a,b,c){var d=a.tiddler;b="string"==typeof b?b:d.text;if(c){if(store.tiddlerExists(c)){if(!confirm(config.messages.overwriteWarning.format([c])))return null;story.closeTiddler(c,
!1)}a=story.getTiddler(d.title);a.id=story.tiddlerId(c);a.setAttribute("tiddler",c);store.saveTiddler(d.title,c,b);return b}c=null;config.macros.undo&&(c=store.undo_saveTiddler,store.undo_saveTiddler=function(){},store.saveTiddler(d.title,d.title,b));d.set(d.title,b,d.modifier,new Date);store.setDirty(!0);config.macros.undo&&(store.undo_saveTiddler=c);"file:"==window.location.protocol?config.options.chktwveCoreManualSave||autoSaveChanges(null,[a.tiddler]):!config.options.chktwveCoreManualUpload&&
config.macros.upload&&config.macros.upload.action();return b},preRefreshTiddler:null,refreshTiddler:function(){var a=twve.tiddler.preRefreshTiddler.apply(this,arguments);if(a&&config.options.chktwveCoreEnabled){var b=a.querySelector(".viewer");b||(b=a);twve.viewer.create(b).waitAndPrepare();b=twve.node.closest(a,"div[id=tiddlerDisplay]");b.removeEventListener("mousemove",twve.tiddler.mouseMove);b.addEventListener("mousemove",twve.tiddler.mouseMove);document.removeEventListener("mousedown",twve.tiddler.mouseDown);
document.addEventListener("mousedown",twve.tiddler.mouseDown);document.removeEventListener("mouseup",twve.tiddler.mouseUp);document.addEventListener("mouseup",twve.tiddler.mouseUp);twve.tiddler.getOptionsMenu().hide(!0)}return a},prePopupShow:null,popupShow:function(){twve.tiddler.prePopupShow.apply(this,arguments);var a=Popup.stack[Popup.stack.length-1];twve.wrapper.create(a.popup).waitAndPrepare();return a},preTabsSwitchTab:null,tabsSwitchTab:function(a,b){twve.tiddler.preTabsSwitchTab.apply(this,
arguments);if(twve.node.closest(a,twve.tiddler.displaySelector())){twve.tabContent.create(a.nextSibling).waitAndPrepare();var c=twve.tiddler.focusElem();c&&twve.tiddler.drawFocusBorders(c.box())}},preTiddlerTransclude:null,tiddlerTransclude:function(a){twve.tiddler.preTiddlerTransclude.apply(this,arguments);if(twve.node.closest(a,twve.tiddler.displaySelector())){var b=twve.tiddlerSpan.create(a);b.waitAndPrepare&&b.waitAndPrepare()}},preSliderHandler:null,sliderHandler:function(a){twve.tiddler.preSliderHandler.apply(this,
arguments);if(twve.node.closest(a,twve.tiddler.displaySelector())){var b=twve.sliderPanel.create(a.querySelector("div[tiddler]"));b.waitAndPrepare&&b.waitAndPrepare()}},preOnClickSlider:null,onClickSlider:function(){twve.tiddler.preOnClickSlider.apply(this,arguments)},cur_editable:null,edit_box:null,getEditBox:function(){return twve.tiddler.edit_box},setEditBox:function(a){return twve.tiddler.edit_box=a},adjustEditBoxHeight:function(a){Math.round(twve.node.height(a));twve.node.setDimension(a,null,
0);var b=1*a.getAttribute("scrH0"),c=a.scrollHeight,c=Math.max(c,b);twve.node.setDimension(a,null,c)},preview:null,getPreviewer:function(a){if(!twve.tiddler.preview){a||(a=twve.tiddler.getDisplay());var b=document.createElement("div");twve.tiddler.preview=b;a.appendChild(b);b.style.position="absolute";b.style.overflow="auto";b.style.zIndex=2;b.style.border="1px solid black";b.style.padding=0;b.style.margin=0;b.style.backgroundColor="#fff8dc";b.classList.add("preview");a=document.createElement("div");
b.appendChild(a);a.classList.add("board","viewer","tiddler","selected");twve.node.hide(b)}return twve.tiddler.preview},editInPlace:function(a,b){return b&&(b.ctrlKey||b.shiftKey||b.altKey)?!1:a.wrapper_title?a.editText(b):!1},getEditInfo:function(a){a||(a=twve.tiddler.getEditBox());var b={},c;if(a){b.text=[];for(var d=0,e=a.length;d<e;d++)if(a[d].nodeType)c=a[d].value,b.text[d]=c,"true"==a[d].getAttribute("cancel")&&(b.canceled=!0),a[d].defaultValue!=c&&(b.modified=!0);else{b.text[d]=[];for(var f=
0,g=a[d].length;f<g;f++)c=a[d][f].value,b.text[d][f]=c,"true"==a[d][f].getAttribute("cancel")&&(b.canceled=!0),a[d][f].defaultValue!=c&&(b.modified=!0)}}return b},updateText:function(a,b,c,d){void 0===b&&(b=twve.tiddler.cur_editable);a||(a=twve.tiddler.getEditBox());if(!a||!b)return twve.tiddler.closeEditbox(),twve.tiddler.cur_editable=null;a=twve.tiddler.getEditInfo(a);!a.canceled&&a.modified&&b.updateText(a.text,c);d||(twve.tiddler.closeEditbox(),twve.tiddler.cur_editable=null);return b},updateTextAndIndex:function(a){var b=
twve.tiddler.cur_editable,c=b.end.ndx;twve.tiddler.updateText();if(a){var d=b.end.ndx-c;d&&twve.text.tiddlerTitle(b.wrapper_title)==twve.text.tiddlerTitle(a.wrapper_title)&&(b.dom==a.dom?a.end.ndx=b.end.ndx:a.start.ndx>c&&(a.start.ndx+=d,a.end.ndx+=d))}},caretPosition:function(a,b,c){if("undefined"==typeof b){try{if(document.selection){a.focus();var d=document.selection.createRange(),e=d.duplicate();e.moveToElementText(a);e.setEndPoint("EndToStart",d);return e.text.length-d.text.length}if(a.selectionStart||
"0"==a.selectionStart)return 1*a.selectionStart}catch(f){console.log(f)}return-1}"number"!=typeof b&&(b=0);"number"!=typeof c?c=b:b>c&&(d=b,b=c,c=d);a.setSelectionRange?(a.focus(),a.setSelectionRange(b,c)):a.createTextRange&&(a=a.createTextRange(),a.collapse(!0),a.moveEnd("character",c),a.moveStart("character",b),a.select())},getSelectionText:function(a){var b=twve.object.create();b.start=0;b.end=0;if("number"==typeof a.selectionStart&&"number"==typeof a.selectionEnd)b.start=a.selectionStart,b.end=
a.selectionEnd;else if(document.selection){var c=document.selection.createRange().getBookmark();a=a.createTextRange();var d=a.duplicate();a.moveToBookmark(c);d.setEndPoint("EndToStart",a);b.start=d.text.length;b.end=b.start+a.text.length}return b},closeEditbox:function(){twve.tiddler.preview&&twve.node.hide(twve.tiddler.preview);var a=twve.tiddler.edit_box;if(a){for(var b=0,c=a.length;b<c;b++)if(a[b].nodeType)a[b].parentNode.removeChild(a[b]);else for(var d=0,e=a[b].length;d<e;d++)a[b][d].parentNode.removeChild(a[b][d]);
twve.tiddler.edit_box=null}},previewEditBox:function(a,b){var c=twve.tiddler.getPreviewer();if(!config.options.chktwveCorePreview||!twve.tiddler.cur_editable)return twve.node.hide(c),null;twve.node.setDimension(c,a.offsetWidth);if("number"!=typeof b||-1==b)b=twve.tiddler.caretPosition(a);var d=a.value,d=0==b?config.options.txttwveCorePreviewCaret+d:d.substring(0,b)+config.options.txttwveCorePreviewCaret+d.substring(b);twve.node.show(c);var e=c.querySelector("div.board");twve.node.copyFontColor(e,
c,!0);twve.node.wikifyAndPrepare(d,e);var d=(new String(config.options.txttwveCorePreviewHeight)).toLowerCase().trim(),f=twve.node.cssSize(window.getComputedStyle(e).getPropertyValue("line-height")),d=/\D$/.test(d)?twve.node.cssSize(d):f*d,e=e.offsetHeight+f;e>d&&(e=d);d=twve.node.box(a);e>d.top?(e=d.top,d.top=0):d.top-=e;twve.node.setPosition(c,d.left,d.top-2);twve.node.setDimension(c,null,e+1);return c},getPreviewedNodes:function(a,b){var c=twve.tiddler.getPreviewer(),d=c.querySelector("div.board");
if(twve.node.isVisible(c))return twve.node.wikifyAndPrepare(a,d);twve.node.show(c);b&&(twve.node.copyFontColor(c,b,!0),twve.node.setDimension(c,b.offsetWidth));twve.node.copyFontColor(d,c,!0);d=twve.node.wikifyAndPrepare(a,d);twve.node.hide(c);return d},caret_pos:null,initCaretPos:function(a){twve.tiddler.caret_pos||(twve.tiddler.caret_pos=twve.object.create());void 0===a&&(a=-100);twve.tiddler.caret_pos.cur=a;twve.tiddler.caret_pos.last=a},setCaretPosition:function(a){twve.tiddler.caret_pos.last=
twve.tiddler.caret_pos.cur;twve.tiddler.caret_pos.cur=a},updateCaretPosition:function(a,b){var c=twve.tiddler.caretPosition(a);(b||c!=twve.tiddler.caret_pos.cur)&&twve.tiddler.previewEditBox(a,c);twve.tiddler.setCaretPosition(c)},keydownCancel:function(a,b){return 27==a.which?(b.setAttribute("cancel","true"),twve.tiddler.updateText(),!0):!1},keydownAfter:function(a,b){if(twve.tiddler.keydownCancel(a,b)||!twve.tiddler.cur_editable)return!1;var c=!1;switch(a.which){case 46:c=!0;case 8:if(a.alterKey)return!1;
break;case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:if(a.alterKey||a.shiftKey)return!1;break;default:twve.tiddler.adjustEditBoxHeight(b)}twve.tiddler.updateCaretPosition(b,c);return!0},keydown:function(a){a=a||window.event;var b=this,c=twve.tiddler.cur_editable;switch(a.which){case 13:if(c.multiLine(b.value)){if(a.ctrlKey)return twve.tiddler.updateText(),!1;0==twve.tiddler.caret_pos.cur&&twve.tiddler.caret_pos.cur++}else return twve.tiddler.updateText(),!1;default:setTimeout(function(){twve.tiddler.keydownAfter(a,
b)},0)}},text_before_paste:null,selection_at_paste:null,textPasted:function(a){(a=a.value)&&(a=a.substring(twve.tiddler.selection_at_paste.start,twve.tiddler.selection_at_paste.start+(a.length-twve.tiddler.text_before_paste.length+twve.tiddler.selection_at_paste.end-twve.tiddler.selection_at_paste.start)));return a},paste:function(){if(!twve.tiddler.text_before_paste){var a=this;twve.tiddler.text_before_paste=a.value;twve.tiddler.selection_at_paste=twve.tiddler.getSelectionText(a);setTimeout(function(){twve.tiddler.cur_editable.pasteIn&&
twve.tiddler.cur_editable.pasteIn(a);twve.tiddler.text_before_paste=null},0)}},copyOrCut:function(a){twve.tiddler.cur_editable.copyOrCut&&twve.tiddler.cur_editable.copyOrCut(this,a)},preEditHandler:null,editHandler:function(a,b,c){twve.tiddler.updateText();twve.tiddler.preEditHandler.apply(this,arguments);return!1},precloseTiddler:null,closeTiddler:function(){twve.tiddler.updateText();twve.tiddler.focus();return twve.tiddler.precloseTiddler.apply(this,arguments)},preSaveHandler:null,saving:!1,saveHandler:function(){twve.tiddler.saving=
!0;var a=twve.tiddler.preSaveHandler.apply(this,arguments);twve.tiddler.saving=!1;return a},preCancelHandler:null,cancelHandler:function(a,b,c){return store.isDirty()&&!confirm(this.warning.format([c]))?!1:twve.tiddler.preCancelHandler.apply(this,arguments)},preSaveChanges:null,saveChanges:function(){twve.tiddler.preSaveChanges.apply(this,arguments);config.macros.upload&&config.macros.upload.action()}};
twve.position={init:function(a,b,c){switch(typeof b){case "number":a.ndx=0>b?0:b;a.matched=c;break;case "object":a.ndx=0>b.ndx?0:b.ndx;a.matched=b.matched;break;default:a.clear()}return a},ensureValid:function(a,b){b&&twve.position.init(a,b);0>a.ndx&&(a.ndx=0);return a},create:function(a,b){var c=twve.object.create();c.clear=function(){c.ndx=-1;c.matched="";return c};c.init=function(b,a){return twve.position.init(c,b,a)};c.ensureValid=function(b){return twve.position.ensureValid(c,b)};return c.init(a,
b)}};
twve.selector={create:function(a,b){var c=twve.object.create();c.clear=function(){c.include="";c.exclude="";return c};c.copyFrom=function(b,a){if("string"==typeof a)return c.include=b,c.exclude=a,c;if(b.include)return c.include=b.include,c.exclude=b.exclude,c;if(b.dom)return c.include=b.selector.include,c.exclude=b.selector.exclude,c;if(b=twve.tiddler.twveSelector(b))c.include=b.include,c.exclude=b.exclude;return c};c.includeSelector=function(b){b&&(c.include+=(c.include?",":"")+b);return c};c.excludeSelector=
function(b){b&&(c.exclude+=(c.exclude?",":"")+b);return c};c.clear();return a||b?c.copyFrom(a,b):c}};
twve.node={info:function(a){if(!a)return a;if(a.nodeType)return a.nodeName;var b=a.length,c="("+b+")[";--b;for(var d=0;d<b;d++)c+=a[d].nodeName+", ";return c+a[b].nodeName+"]"},matches:function(a,b){if(!a||!b)return!1;if(!a.nodeType){for(var c=0,d=a.length;c<d;c++)if(twve.node.matches(a[c],b))return!0;return!1}if(c=a.matches||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector||a.oMatchesSelector)return c.call(a,b);if(!a.parentNode)return!1;d=a.parentNode.querySelectorAll(b);if(!d.length)return!1;
for(var c=0,e=d.length;c<e;c++)if(d[c]==a)return!0;return!1},closest:function(a,b){for(;a;){if(twve.node.matches(a,b))return a;a=a.parentNode}return null},parents:function(a,b,c){var d=[];for(a=a.parentNode;a;){if(!b||twve.node.matches(a,b))if(!c||c.call(a))d[d.length]=a;a=a.parentNode}return d.length?d:null},is:function(a,b){if(!a||!b)return!1;if("string"==typeof b)return twve.node.matches(a,b);if(a.nodeType){if(b.nodeType)return a==b;for(var c=0,d=b.length;c<d;c++)if(twve.node.is(a,b[c]))return!0}else if(b.nodeType){c=
0;for(d=a.length;c<d;c++)if(twve.node.is(a[c],b))return!0}else{c=0;for(d=a.length;c<d;c++)for(var e=0,d=b.length;e<d;e++)if(twve.node.is(a[c],b[e]))return!0}return!1},not:function(a,b){if("string"==typeof b){for(var c=0;c<a.length;)twve.node.matches(a[c],b)?a.splice(c,1):c++;return a}if(b.nodeType){var d=a.indexOf(b);-1<d&&a.splice(d,1)}else for(var c=0,e=b.length;c<e;c++)d=a.indexOf(b[c]),-1<d&&a.splice(d,1);return a},replace:function(a,b,c){c||(c=b.parentNode);if(a.nodeType)c.insertBefore(a,b);
else for(;a.length;)c.insertBefore(a[0],b);c.removeChild(b)},scw:0,scrollBarWidth:function(a){if(!twve.node.scw){if(!a)return 0;var b=a.style.overflow;a.style.overflow="scroll";twve.node.scw=a.offsetWidth-a.clientWidth;a.style.overflow=b}return twve.node.scw},adjustPosition:function(a){var b=twve.tiddler.getDisplay();"static"!=window.getComputedStyle(b).getPropertyValue("position")&&(void 0!==a.left?(a.left-=b.offsetLeft,a.top-=b.offsetTop):(a.x-=b.offsetLeft,a.y-=b.offsetTop));return a},shiftPosition:function(a,
b,c){if(void 0!==b){var d=void 0===c||null===c,e="number"==typeof b?"px":"",f="number"==typeof c?"px":"";if(a.nodeType)null!==b&&(a.style.left=a.offsetLeft+b+e),d||(a.style.top=a.offsetTop+c+f);else for(var g=0,h=a.length;g<h;g++)null!==b&&(a[g].style.left=a[g].offsetLeft+b+e),d||(a[g].style.top=a[g].offsetTop+c+f)}},setPosition:function(a,b,c){if(void 0!==b){var d=void 0===c||null===c,e="",f="";"number"==typeof b&&(b=Math.round(b),e="px");"number"==typeof c&&(c=Math.round(c),f="px");if(a.nodeType)null!==
b&&(a.style.left=b+e),d||(a.style.top=c+f);else for(var g=0,h=a.length;g<h;g++)null!==b&&(a[g].style.left=b+e),d||(a[g].style.top=c+f)}},setDimension:function(a,b,c){if(void 0!==b){var d=void 0===c||null===c,e="number"==typeof b?"px":"",f="number"==typeof c?"px":"";if(a.nodeType)null!==b&&(a.style.width=b+e),d||(a.style.height=c+f);else for(var g=0,h=a.length;g<h;g++)null!==b&&(a[g].style.width=b+e),d||(a[g].style.height=c+f)}},positionShift:function(){var a=document.body,b=document.documentElement;
return twve.node.adjustPosition({left:(window.pageXOffset||b.scrollLeft||a.scrollLeft)-(b.clientLeft||a.clientLeft||0),top:(window.pageYOffset||b.scrollTop||a.scrollTop)-(b.clientTop||a.clientTop||0)})},boxInit:function(a,b,c,d){c.top=a.top;c.right=a.right;c.bottom=a.bottom;c.left=a.left;c.shift=function(b){c.top+=b.top;c.right+=b.left;c.bottom+=b.top;c.left+=b.left;return c};b&&c.shift(b);if(!1===d)return c;c.toString=function(){return twve.object.toString(c)};c.containsX=function(b){return b>=c.left&&
b<=c.right};c.containsY=function(b){return b>=c.top&&b<=c.bottom};c.contains=function(b){return b.left?c.containsX(b.left)&&c.containsX(b.right)&&c.containsY(b.top)&&c.containsY(b.bottom):c.containsX(b.x)&&c.containsY(b.y)};c.cloneTo=function(b,a,d){a="number"==typeof a?c[a]:c;twve.node.boxInit(a,null,b,d);b.width=a.width;b.height=a.height;return b};c.clone=function(b){var a=[];twve.node.boxInit(c,null,a,b);a.width=c.width;a.height=c.height;for(var d=0,h=c.length;d<h;d++)a[d]={},c.cloneTo(a[d],d,
b);return a};return c},box:function(a,b,c){if(!a)return null;var d=null;if(3==a.nodeType){var e=document.createRange();e.selectNodeContents(a);d=e.getClientRects();e.detach()}else if(twve.node.matches(a,twve.tiddler.twveTranscludedSelectors().include))d=[{}],d[0].left=a.offsetLeft,d[0].top=a.offsetTop,d[0].width=a.offsetWidth,d[0].height=a.offsetHeight,d[0].right=a.offsetLeft+d[0].width,d[0].bottom=a.offsetTop+d[0].height;else if(a.getClientRects){if(!twve.node.isVisible(a))return null;d=a.getClientRects()}else d=
[{}],d[0].left=a.screenX,d[0].top=a.screenY,d[0].width=a.innerWidth,d[0].height=a.innerHeight,d[0].right=a.screenX+d[0].width,d[0].bottom=a.screenY+d[0].height;d.initFrom=function(b,a,c,e){return twve.node.boxInit(b,a,c||d,e)};e=twve.node.positionShift();if(a.getBoundingClientRect)d.initFrom(a.getBoundingClientRect(),e),b?d.width=d.right-d.left:(d.width=a.clientWidth||a.offsetWidth,d.right=d.left+d.width),c?d.height=d.bottom-d.top:(d.height=a.clientHeight||a.offsetHeight,d.bottom=d.top+d.height);
else{if(!d[0])return null;d.initFrom(d[0]);for(a=1;a<d.length;a++)d[a].left<d.left&&(d.left=d[a].left),d[a].right>d.right&&(d.right=d[a].right),d[a].bottom>d.bottom&&(d.bottom=d[a].bottom);d.width=d.right-d.left;d.height=d.bottom-d.top;d.shift(e)}return d},cssSize:function(a,b){if(!a)return!b?0:parseFloat(window.getComputedStyle(b).getPropertyValue("font-size"));a=(new String(a)).toLowerCase().trim();var c=a.substring(a.length-2);switch(c){case "px":return Math.round(parseFloat(a));case "pt":return Math.round(16*
parseFloat(a)/12);default:var d=parseFloat(a);if(isNaN(d)){c=null;try{c=window.getComputedStyle(b)}catch(e){b=twve.tiddler.getDisplay(),c=window.getComputedStyle(b)}finally{return c?twve.node.cssSize(c.getPropertyValue(a),b):0}}return"em"==c?Math.round(16*d*parseFloat(window.getComputedStyle(b).getPropertyValue("font-size"))/12):"%"==c.charAt(1)?Math.round(d/100*twve.node.width(b,"inner")):d}},bgc:function(a,b){b||(b=window.getComputedStyle(a));for(var c;"transparent"==(c=b.getPropertyValue("background-color"))||
"rgba(0, 0, 0, 0)"==c||"inherit"==c;)if(a=a.parentNode,!a){c="#fff";break}return c},copyFontColor:function(a,b,c){if(a&&b&&a!=b){var d=window.getComputedStyle(b);a.style.fontSize=d.getPropertyValue("font-size");a.style.fontFamily=d.getPropertyValue("font-family");a.style.fontStyle=d.getPropertyValue("font-style");a.style.color=d.getPropertyValue("color");a.style.textAlign=d.getPropertyValue("text-align");c&&(a.style.backgroundColor=twve.node.bgc(b,d))}},wikify:function(a,b,c){if(b)return b.innerHTML=
"",wikify(a,b),c?b.childNodes:b;b=twve.tiddler.getPreviewer().querySelector("div.board");b.innerHTML="";wikify(a,b);return b.childNodes},wikifyAndPrepare:function(a,b){var c=twve.node.wikify(a,b,!0);if(twve.tiddler.cur_editable&&twve.tiddler.cur_editable.isWrapper()){var d=twve.tiddler.cur_editable.clone();d.dom=b;d.waitAndPrepare()}return c},isVisible:function(a){return!a?!1:3==a.nodeType?!0:a.style?"none"!=a.style.display:!0},hide:function(a){if(a)if(twve.object.isCollection(a))for(var b=0,c=a.length;b<
c;b++)a[b].style.display="none";else a.style.display="none"},show:function(a,b,c){if(a)if(twve.object.isCollection(a)){if("number"!=typeof c||c>a.length)c=a.length;if("number"!=typeof b||0>b)b=0;for(;b<c;b++)a[b].style.display=""}else a.style.display=""},getMargin:function(a,b,c){c||(c=window.getComputedStyle(a));switch(b?b.charAt(0):""){case "h":case "H":return b=(b=a.style.marginLeft||c.getPropertyValue("margin-left"))?parseInt(b):0,a=(a=a.style.marginRight||c.getPropertyValue("margin-right"))?
parseInt(a):0,b+a;case "v":case "V":return b=(b=a.style.marginTop||c.getPropertyValue("margin-top"))?parseInt(b):0,a=(a=a.style.marginBottom||c.getPropertyValue("margin-bottom"))?parseInt(a):0,b+a;case "l":case "L":return(b=a.style.marginLeft||c.getPropertyValue("margin-left"))?parseInt(b):0;case "t":case "T":return(b=a.style.marginTop||c.getPropertyValue("margin-top"))?parseInt(b):0;case "r":case "R":return(a=a.style.marginRight||c.getPropertyValue("margin-right"))?parseInt(a):0;case "b":case "B":return(a=
a.style.marginBottom||c.getPropertyValue("margin-bottom"))?parseInt(a):0}return 0},getPadding:function(a,b,c){c||(c=window.getComputedStyle(a));switch(b?b.charAt(0):""){case "h":case "H":return b=(b=a.style.paddingLeft||c.getPropertyValue("padding-left"))?parseInt(b):0,a=(a=a.style.paddingRight||c.getPropertyValue("padding-right"))?parseInt(a):0,b+a;case "v":case "V":return b=(b=a.style.paddingTop||c.getPropertyValue("padding-top"))?parseInt(b):0,a=(a=a.style.paddingBottom||c.getPropertyValue("padding-bottom"))?
parseInt(a):0,b+a;case "l":case "L":return(b=a.style.paddingLeft||c.getPropertyValue("padding-left"))?parseInt(b):0;case "t":case "T":return(b=a.style.paddingTop||c.getPropertyValue("padding-top"))?parseInt(b):0;case "r":case "R":return(a=a.style.paddingRight||c.getPropertyValue("padding-right"))?parseInt(a):0;case "b":case "B":return(a=a.style.paddingBottom||c.getPropertyValue("padding-bottom"))?parseInt(a):0}return 0},width:function(a,b,c){var d=0;b=b?b.toLowerCase().substring(0,3):"";var d="out"==
b,e=!twve.node.isVisible(a);e&&twve.node.show(a);d?(d=a.offsetWidth,c&&(d+=twve.node.getMargin(a,"horizontal"))):(d=a.clientWidth,"inn"==b&&(d-=twve.node.getPadding(a,"horizontal")));e&&twve.node.hide(a);return d},height:function(a,b,c){var d=0;b=b?b.toLowerCase().substring(0,3):"";var d="out"==b,e=!twve.node.isVisible(a);e&&twve.node.show(a);d?(d=a.offsetHeight,c&&(d+=twve.node.getMargin(a,"vertical"))):(d=a.clientHeight,"inn"==b&&(d-=twve.node.getPadding(a,"vertical")));e&&twve.node.hide(a);return d},
contains:function(a,b,c){if(c&&"geo"==c.toLowerCase().substring(0,3)){a=twve.node.box(a,!0,!0);if(!a)return!1;if(b.x)return a?a.contains(b):!1;b.dom&&(b=b.dom);return a.contains(twve.node.box(b))}for(;b=b.parentNode;)if(a==b)return!0;return!1},index:function(a){for(var b=a.parentNode.childNodes,c=0,d=b.length;c<d;c++)if(b[c]==a)return c;return-1},create:function(a){var b=twve.object.create();b.clear=function(){b.dom=null;return b};b.copyFrom=function(a){b.dom=a.dom;return b};b.is=function(a){return a&&
a.dom?b.dom==a.dom:twve.node.is(b.dom,a)};b.not=function(a){return twve.node.not(b.dom,a)};b.hide=function(){twve.node.hide(b.dom);return b};b.show=function(a){twve.node.show(b.dom);a&&twve.node.setPosition(b.dom,a.left,a.top);return b};b.isVisible=function(){return twve.node.isVisible(b.dom)};b.width=function(a,d){return twve.node.width(b.dom,a,d)};b.height=function(){return twve.node.height(b.dom)};b.getElement=function(){return b.dom};b.box=function(){return twve.node.box(b.getElement())};b.contains=
function(a,d){return twve.node.contains(b.dom,a,d)};b.add=function(a){b.dom.nodeType&&(b.dom=[b.dom]);b.dom[b.dom.length]=a.dom||a;return b};b.clear();switch(typeof a){case "string":b.dom=document.createElement(a);break;case "object":a.dom?b.copyFrom(a):a.nodeName&&(b.dom=a)}return b}};
twve.nodeList={toArray:function(a){var b=[];if(a.nodeType)b[0]=a;else for(var c=0,d=b.length=a.length;c<d;c++)b[c]=a[c];return b},querySelectorAll:function(a,b){if(a.nodeType)return twve.nodeList.toArray(a.querySelectorAll(b));for(var c=null,d=0,e=a.length;d<e;d++)var f=twve.nodeList.toArray(a[d].querySelectorAll(b)),c=c?c.concat(f):f;return c}};
twve.button={isSystemButton:function(a){return twve.node.matches(a,"svg.SVGAnimatedString, path.SVGAnimatedString")},isActive:function(a){return"1"==(a.style?a.style.opacity:window.getComputedStyle(a).getPropertyValue("opacity"))},activate:function(a,b){return a.style.opacity=b?1:0.4},create:function(a,b,c,d,e){var f=twve.node.create(createTiddlyButton(a,b,c,d));f.isActive=function(){return twve.button.isActive(f)};f.activate=function(b){return twve.button.activate(f.dom,b)};f.dom.setAttribute("id",
"twveTbtn"+(e?e:b));f.dom.style.position="absolute";f.dom.style.zIndex=1;f.dom.style.textAlign="center";f.dom.addEventListener("mouseenter",function(b){f.mouseenter&&f.mouseenter.call(this,b||window.event)});f.dom.addEventListener("mouseleave",function(b){f.mouseleave&&f.mouseleave.call(this,b||window.event)});f.dom.addEventListener("click",function(b){f.click&&f.click.call(this,b||window.event)});return f},createDelete:function(a,b,c){return twve.button.create(null,"\u2297",a,b,c)}};
twve.menu={create:function(a,b){var c=twve.node.create("div");c.findItem=function(b){if(c.items)for(var a=0;a<c.items.length;a++){var d=c.items[a];if(d.label==b)return d}return null};c.addItem=function(b,a){c.items||(c.items=[]);var d=twve.button.create(createTiddlyElement(c.dom,"li"),null,a);d.dom.style.whiteSpace="nowrap";d.label=b;twve.node.wikify(b,d.dom);d.mouseenter=function(){var b=twve.object.create();b.left=d.dom.offsetLeft+c.dom.offsetLeft;b.top=d.dom.offsetTop+c.dom.offsetTop;d.submenu&&
d.submenu.show(b,"left")};d.mouseleave=function(b){b=twve.tiddler.mousePosition(b);d.submenu&&!d.submenu.contains(b)&&d.submenu.hide()};c.items[c.items.length]=d;c.dom.removeAttribute("adjusted");return d};c.addMenu=function(b,a){var d=c.findItem(b);d||(d=c.addItem(b));d.submenu||(d.submenu=twve.menu.create(d,!0),a&&a.call(this,d.submenu.dom));return d.submenu};var d=c.contains;c.contains=function(b){if(d.call(this,b,"geo"))return!0;if(c.items)for(var a=c.items.length-1;0<=a;a--)if(c.items[a].submenu&&
c.items[a].submenu.contains(b,"geo"))return!0;return!1};c.hideRoot=function(){c.root.hide();return c};c.hide=function(b){b&&c.hideRoot();twve.node.hide(c.dom);return c};c.adjustPosition=function(b,a,d,e,k){k||(k=c);d||(d=k.width("outer",!0));a=a?a.toLowerCase():"";0<=a.indexOf("left")&&(b.left-=d);e||(e=k.height("outer",!0));0<=a.indexOf("top")?b.top-=e:0<=a.indexOf("bottom")?b.top+=e:b.top++;return b};c.showRoot=function(b,a){var d=twve.object.create();d.left=b.left;d.top=b.top;c.adjustPosition(d,
a,null,null,c.root);c.root.show(d);return d};c.adjustDim=function(){if(c.items){for(var b=0,a=0,d=0;d<c.items.length;d++){var e=twve.node.width(c.items[d].dom,"outer",!0);e>b&&(b=e);twve.node.setPosition(c.items[d].dom,null,a);a+=twve.node.height(c.items[d].dom,"outer",!0)}twve.node.setDimension(c.dom,b,a+(config.browser.isOpera||config.browser.isChrome?twve.node.scrollBarWidth(c.dom)-4:0))}c.dom.setAttribute("adjusted","true");return c};c.show=function(b,a){twve.node.show(c.dom);c.dom.getAttribute("adjusted")||
c.adjustDim();var d=c.width("outer",!0);if(b){var e=twve.object.create();e.left=b.left+4;e.top=b.top;if(d>b.left){var k=c.root.width("outer",!0),m=twve.node.box(window).width;d+k+e.left<m?(a="right",e.left+=k-4):m-d>b.left?(twve.node.setDimension(c.dom,d=m-d),a="right",e.left+=k-4):twve.node.setDimension(c.dom,d=b.left)}k=20*twve.node.cssSize(window.getComputedStyle(c.dom).getPropertyValue("font-size"));c.height("outer",!0)>k&&twve.node.setDimension(c.dom,null,k);c.adjustPosition(e,a,d,k);twve.node.setPosition(c.dom,
e.left,e.top)}return c};var e=twve.tiddler.getDisplay();a.mouseenter||(a.mouseenter=function(){var b=twve.object.create();b.left=this.offsetLeft;b.top=this.offsetTop;c.show(b,"left");return c},a.click=function(){c.show();return c},e.appendChild(a.dom));c.root=a;e.appendChild(c.dom);c.dom.style.overflow="auto";c.dom.style.position="absolute";c.dom.classList.add("popup");c.hide();b?c.dom.addEventListener("mouseleave",function(){twve.node.hide(this)}):c.dom.addEventListener("mouseleave",function(b){c.mouseleave&&
c.mouseleave.call(this,b||window.event)});return c}};
twve.editable={create:function(a){var b=twve.node.create();b.clear=function(){b.wrapper_title="";b.tiddler=null;b.wrapper=null;b.start=twve.position.create();b.end=twve.position.create();return b};preCopyFrom=b.copyFrom;b.copyFrom=function(a){preCopyFrom.apply(this,arguments);b.wrapper_title=a.wrapper_title;b.tiddler=a.tiddler;b.wrapper=a.wrapper;b.start.copyFrom(a.start);b.end.copyFrom(a.end);return b};b.created=function(a){b.clear();return a?a.dom?b.copyFrom(a):b.setElement(a):b};b.isEditable=function(a){return b.is(a)||
b.contains(a)};b.beingEdited=function(){return b.is(twve.tiddler.cur_editable)};b.clone=function(){return twve.tiddler.cloneEditable(b)};b.multiLine=function(b){return b&&0<=b.indexOf("\n")};b.twveSelector=function(a,d){return twve.tiddler.twveSelector(b.dom,a,d)};b.isWrapper=function(){return!1};b.foldedWrapper=function(){var a=b.directWrapper();return a.isVisible()?null:a};b.directWrapper=function(){return twve.tiddler.directWrapper(b.dom,!0)};b.isSystemShadow=function(){return b.wrapper_title?
store.isShadowTiddler(b.wrapper_title):!0};b.findWrappers=function(){b.wrapper||(b.wrapper=twve.tiddler.wrapperFromTitle(b.wrapper_title),b.wrapper||(b.wrapper=[]));return b.wrapper};b.setElement=function(a){if(a==b.dom)return null;b.dom=a;if(!b.directWrapper())return b;if(!b.wrapper_title&&(a=b.directWrapper())&&a.titleOfWrapper)b.wrapper_title=a.titleOfWrapper();b.isSystemShadow()?(b.tiddler=null,b.wrapper=null,b.wrapper_title=null):b.tiddler||(b.tiddler=twve.tiddler.get(b.wrapper_title));return b};
b.changed=function(b){return b.text};b.ensureValid=function(a){if(0>=a)return 0;var d=b.tiddler.text.length;return a>=d?d:a};b.checkNewLines=function(a,d,e){b.newline_close=e>d&&"\n"==a.charAt(e-1);b.newline_open="\n"==a.charAt(d)&&(b.newline_close?e>d+1:!0)};b.setText=function(a,d,e){var f=b.tiddler.text;"number"!=typeof d&&(d=b.start.ndx);"number"!=typeof e&&(e=b.end.ndx);d=b.ensureValid(d);e=b.ensureValid(e);var g=0;b.checkNewLines(f,d,e);a?(b.newline_open&&d++,b.newline_close&&e--,g=a.length-
(e-d),f=f.substring(0,d)+a+f.substring(e)):(b.newline_open&&b.newline_close&&d++,f=f.substring(0,d)+f.substring(e),g=-(e-d));d=b.directWrapper();d!=b&&(d.end.ndx+=g);b.end.ndx+=g;twve.tiddler.saveText(b,f);return a};b.getText=function(a,d){"number"!=typeof a&&(a=b.start.ndx);"number"!=typeof d&&(d=b.end.ndx);if(a>=d)return"";var e=b.tiddler.text;b.checkNewLines(e,a,d);b.newline_open&&a++;b.newline_close&&d--;return e.substring(a,d)};b.focusEditBox=function(a,d){d?a.select():a.focus();twve.tiddler.initCaretPos();
return b};b.prepareEditBox=function(a,d,e,f,g){a.style.position="absolute";a.style.padding="0";a.style.margin="0";a.style.overflow="auto";a.style.textAlign=f;a.style.fontFamily="courier";a.style.fontSize=g+"px";twve.node.setPosition(a,e.left-1,e.top-1);twve.node.setDimension(a,e.width,e.height);a.setAttribute("scrH0",e.height);a.setAttribute("spellcheck","true");a.setAttribute("cancel","false");a.setAttribute("title","("+(b.multiLine(d)?"Ctrl-":"")+"ENTER=accept, ESC=cancel)");a.addEventListener("keydown",
twve.tiddler.keydown);a.addEventListener("paste",twve.tiddler.paste);a.addEventListener("copy",function(){twve.tiddler.copyOrCut()});a.addEventListener("cut",function(){twve.tiddler.copyOrCut(!0)})};b.previewEditBox=function(b,a,e){twve.node.copyFontColor(twve.tiddler.getPreviewer(e),a,!0);return twve.tiddler.previewEditBox(b,null)};b.editText=function(a,d,e){if(!b.tiddler)return null;var f;e?f=twve.node.box(e):(e=b.getElement(),f=twve.tiddler.focusBox()||b.box("edit",a,e));twve.tiddler.cur_editable=
b;void 0===d&&(d=b.getText());var g=document.createElement("textarea");g.value=d;g.defaultValue=d;var h=window.getComputedStyle(e),l=twve.node.cssSize(h.getPropertyValue("line-height")),k=twve.node.cssSize(h.getPropertyValue("font-size"));f.height<l&&(f.height=l);l=twve.node.closest(e,twve.tiddler.displaySelector());l.appendChild(g);b.prepareEditBox(g,d,f,h.getPropertyValue("text-align"),k);twve.node.setDimension(g,null,0);h=g.scrollHeight;h<f.height?h=f.height:f.height=h;twve.node.setDimension(g,
null,h);twve.tiddler.setEditBox([g]);b.focusEditBox(g,d,a,e);b.previewEditBox(g,e,l)&&(a=k*config.options.txttwveCoreMinEditWidth,twve.node.setDimension(g,f.width<a?a:f.width));return g};b.updateText=function(a,d){return b.refreshAll(b.setText(a[0]),null,d)};b.hasClass=function(a){return b.dom.classList.contains(a)};b.saveText=function(a,d){return twve.tiddler.saveText(b,a,d)};return b.created(a)}};
twve.element={create:function(a){var b=twve.editable.create();b.clear=function(){b.direct_wrapper=null;b.rIndex=-1;b.dIndex=-1;b.tags=null;return b};var c=b.copyFrom;b.copyFrom=function(a){c.apply(this,arguments);b.direct_wrapper=a.direct_wrapper;b.rIndex=a.rIndex;b.dIndex=a.dIndex;b.tags=a.tags;return b};b.markupTags=function(){return twve.tiddler.markupTags(b.dom)};var d=b.directWrapper;b.directWrapper=function(){b.direct_wrapper||(b.direct_wrapper=d.apply(this,arguments));return b.direct_wrapper};
b.renderedCopy=function(a){var c=b.directWrapper();if(!c)return null;c=c.dom;if(c==a)return b;if(twve.node.contains(a,c))return null;c=-1;if(twve.text.tiddlerSection(b.wrapper_title)||twve.text.tiddlerSection(twve.tiddler.titleOfWrapper(a)))c=b.rIndex,b.rIndex=-1;a=twve.wrapper.renderedElement(b,a);-1<c&&(b.rIndex=c);return a};b.removeSelf=function(){b.dom.parentNode.removeChild(b.dom);twve.tiddler.focusElem(null);return b};b.replaceWith=function(a,c){twve.node.replace(a,c||b.dom);return b};b.filter=
function(a,c){c||(c=b.twveSelector(null,"rendering").include);if(a.nodeType)return twve.node.matches(a,c)?a:null;for(var d=[],e=0,k=a.length;e<k;e++)twve.node.matches(a[e],c)&&(d[d.length]=a[e]);return 0==d.length?null:1<d.length?d:d[0]};b.refreshSelf=function(a){a=twve.tiddler.getPreviewedNodes(a);var c=b.filter(a)||a[0];b.replaceWith(a);b.dIndex=-1;b.setElement(c);return b};b.refreshAll=function(a,c,d){var e=null;d=config.options.chkAnimate;config.options.chkAnimate=!1;var k=twve.text.tiddlerSection(b.wrapper_title);
"string"!=typeof a&&(a=b.getText());b.findWrappers();for(var m=0,r=b.wrapper.length;m<r;m++){var e=b.wrapper[m],p=twve.tiddler.titleOfWrapper(e),n=twve.text.tiddlerSection(p);if(!k||!(n&&n!=k)){var q=b.renderedCopy(e);if(q)(e=q.foldedWrapper())&&twve.node.show(e.dom),a?(q.refreshSelf(a),c&&c.what&&q.changed(c)):q.removeSelf(c),e&&twve.node.hide(e.dom);else if(!n&&(n=twve.text.tiddlerSlice(p)))(q=twve.tiddler.createEditableWrapper(e))&&q.refreshSelf(n)}}config.options.chkAnimate=d;return b};b.renderingIndex=
function(){if(0>b.rIndex){var a=b.directWrapper().findElements(b.twveSelector(null,"rendered"));a&&(b.rIndex=a.indexOf(b.dom))}return b.rIndex};b.counts=function(b){b.remained--;b.ndx++;return!0};b.starts=function(a,c){if(!c&&!b.tiddler||!b.tags||!b.tags.open)return b.start.ndx=-1,b.start;c||(c=b.tiddler.text);b.start.ensureValid(a||b.directWrapper().start);var d=twve.object.create();d.remained=-1<b.rIndex?b.rIndex+1:-1<b.dIndex?b.dIndex+1:1;d.ndx=-1;var e=twve.tags.inactiveTags();do{b.start=b.tags.nextOpenTag(c,
b.start,e);if(0>b.start.ndx)return b.start.ndx=-d.remained,b.start;if(b.counts(d,c)){if(0==d.remained)return-1==b.dIndex&&(b.dIndex=d.ndx),-1==b.rIndex&&(b.rIndex=d.ndx),b.start;b.start.ndx=b.ends(null,c,e).ndx}else b.start.ndx+=b.start.matched.length}while(1)};b.ends=function(a,c,d){!a||a==b.start?a=twve.position.create(b.start):"number"==typeof a?a=twve.position.create(a):a.ensureValid();a.ndx+=a.matched.length||1;c||(c=b.tiddler.text);var e=b.tags.exactCloseTag(a.matched);b.end.copyFrom(e.matchedCloseTag(c,
a,c.length,d));b.end.ndx=-1==b.end.ndx?c.length:b.end.ndx+b.end.matched.length;return b.end};var e=b.setElement;b.setElement=function(a,c){if(!a)return b.clear();if("string"==typeof a)b.dom=a,b.tags=twve.tiddler.markupTags(a);else{if(!e.apply(this,arguments)||!b.tiddler)return b;0>b.rIndex&&0>b.dIndex&&b.renderingIndex();b.tags||(b.tags=b.markupTags());var d=c?twve.position.create():null;b.starts(d,c);0<=b.start.ndx?b.ends(null,c):b.end.ndx=-1}return b};return b.created(a)}};
twve.pre={enableEdit:!0,twveSelector:function(a,b){a||(a=twve.selector.create());var c="pre,div.syntaxhighlighter";if(!b||"render"==b.toLowerCase().indexOf(0,6))c+=",div.alt1,div.alt2,.syntaxhighlighter code,.syntaxhighlighter table";return a.includeSelector(c)},markupTags:function(){var a=twve.tags.create(["\n{{{","\n//{{{","\n/*{{{*/","\n\x3c!--{{{--\x3e"],["}}}\n","//}}}\n","/*}}}*/\n","\x3c!--}}}--\x3e\n"]);a.blockElement=!0;a.clone=function(){return twve.pre.markupTags()};return a},create:function(a){var b=
twve.element.create();b.markupTags=function(){return twve.pre.markupTags()};b.clone=function(){return twve.pre.create(b)};b.twveSelector=function(b,a){return twve.pre.twveSelector(b,a)};var c=b.setElement;b.setElement=function(b){twve.node.matches(b,"pre")||(b=twve.node.closest(b,"div.syntaxhighlighter"));return c.call(this,b)};return b.created(a)}};
twve.code={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("code").excludeSelector(".syntaxhighlighter code")},markupTags:function(){return twve.tags.create("{{{","}}}")},create:function(a){var b=twve.element.create();b.markupTags=function(){return twve.code.markupTags()};b.clone=function(){return twve.code.create(b)};b.twveSelector=function(){return twve.code.twveSelector()};b.counts=function(a,d){return"\n"!=d.charAt(b.start.ndx+b.start.matched.length)?
(a.remained--,a.ndx++,!0):!1};return b.created(a)}};
twve.leveledElement={getLevel:function(a,b,c){var d=0;if(c){if(/\s/.test(c))return 0}else{c=a.charAt(b);if(!c||/\s/.test(c))return 0;d++}for(var e=a.length;b+d<e&&-1<c.indexOf(a.charAt(b+d));)d++;return d},create:function(a){var b=twve.element.create();b.clear=function(){b.level=0;return b};var c=b.copyFrom;b.copyFrom=function(a){c.apply(this,arguments);b.level=a.level;return b};b.getLevel=function(a,c){if(1>b.level){a||(a=b.tiddler.text);c||(c=b.start);var d=c.ndx;"\n"==a.charAt(d)&&d++;b.level=
twve.leveledElement.getLevel(a,d)}return b.level};b.getOpenTag=function(a,c){c||(c=b.tiddler?b.tiddler.text:"");if(!c)return"";a||(a=b.start);var d=b.getLevel(c,a),h="";"\n"==c.charAt(a.ndx)?d++:h="\n";return h+c.substring(a.ndx,a.ndx+d)};var d=b.starts;b.starts=function(a,c){d.apply(this,arguments);b.level=-1;-1<b.start.ndx&&(b.start.matched=b.getOpenTag(b.start,c));return b.start};return b.created(a)}};
twve.heading={enableEdit:!0,getSelector:function(){return"h1,h2,h3,h4,h5,h6"},getSpanSelector:function(a){return"span.TiddlyLinkExisting"},twveSelector:function(a,b){a||(a=twve.selector.create());b=b?b.toLowerCase():"";return a.includeSelector("fold"==b.substring(0,4)?"h1.foldable,h2.foldable,h3.foldable,h4.foldable,h5.foldable,h6.foldable":twve.heading.getSelector()+("render"==b.substring(0,6)?"":","+twve.heading.getSpanSelector()))},markupTags:function(){var a=twve.tags.create("\n!","\n");a.blockElement=
!0;return a},getTitle:function(a,b){var c=null;a.dom?c=a.dom:(c=a,a=null);if(!c)return null;if(0<c.childNodes.length){var d=twve.heading.create(a);d.setElement(c);return twve.text.sectionTitle(d.getText())}var e=c.textContent,d="";config.macros.foldHeadings&&(d=config.macros.foldHeadings.hidelabel,-1==e.indexOf(d)&&(d=config.macros.foldHeadings.showlabel,-1==e.indexOf(d)&&(d="")));e=d?e.substring(d.length).trim():e;if(!b&&(d=twve.heading.create(a),d.setElement(c),d=d.directWrapper().findElements(twve.heading.twveSelector(null,
"rendered")))){for(var c=d.indexOf(c),f=0,g=0;g<c;g++)twve.heading.getTitle(d[g],!0)==e&&f++;0<f&&(e+="?"+f)}return e},preClick:null,click:function(){var a=twve.tiddler.focusElem();(!a||!a.is(this)||a.indexOf&&-1==a.indexOf(this))&&twve.heading.preClick.apply(this,arguments)},prepareElements:function(a){if(config.options.chktwveCoreEnabled&&!a.isSystemShadow()){for(var b=a.dom.querySelectorAll(twve.heading.twveSelector(null,"foldable").include),c=0,d=b.length;c<d;c++)twve.heading.preClick||(twve.heading.preClick=
b[c].onclick),b[c].onclick=twve.heading.click;return a}return null},create:function(a){var b=twve.leveledElement.create();b.twveSelector=function(a,b){return twve.heading.twveSelector(a,b)};b.markupTags=function(){return twve.heading.markupTags()};b.getTitle=function(a){return twve.heading.getTitle(b,a)};var c=b.setElement;b.setElement=function(a){c.call(this,twve.node.matches(a,twve.heading.getSpanSelector())?a.parentNode:a);return b};b.mouseenter=function(a){var c=twve.heading.getSpanSelector();
return!b.dom.querySelector(c)?!0:twve.node.matches(a.target,c)};b.mousemove=function(a){b.mouseenter(a)||twve.tiddler.focus()};b.is=function(a){return twve.node.is(b.dom,"string"==typeof a?a:twve.node.matches(a,twve.heading.getSpanSelector())?a.parentNode:a)};b.isEditable=function(a){var c=twve.heading.getSpanSelector();return twve.node.matches(a,c)?!0:0==b.dom.querySelectorAll(c).length};return b.created(a)}};
twve.wrapper={recordFoldable:function(a){var b=null;if(a)for(var b=[],c=0,d=a.length;c<d;c++)b[c]=!twve.node.isVisible(a[c]);return b},restoreFoldable:function(a,b){if(a){var c=config.options.chkAnimate;config.options.chkAnimate=!1;for(var d=b?Math.min(b.length,a.length):a.length,e=0;e<d;e++)if(a[e]&&(!b||b[e]&&twve.node.isVisible(a[e])||!b[e]&&!twve.node.isVisible(a[e]))&&a[e].previousSibling){var f=document.createEvent("MouseEvent");f.initEvent("click",!0,!0);a[e].previousSibling.dispatchEvent(f)}config.options.chkAnimate=
c}},renderedElement:function(a,b,c){if(!a||0>a.rIndex&&0>a.dIndex&&!c)return null;if(!b){b=a.directWrapper();if(!b)return null;b=b.dom}var d=twve.wrapper.findElements(b,a.twveSelector(null,"rendered"));if(!d)return null;var e=d.length;a=a.clone();if(c)0>a.rIndex?a.rIndex=e-1:a.rIndex>=e&&(a.rIndex=0);else{if(0>a.rIndex)return a.wrapper=b,a.direct_wrapper=twve.tiddler.createEditableWrapper(b,!0),a.wrapper_title=a.direct_wrapper.wrapper_title,a.starts(),0<=a.rIndex?(a.ends(),a.dom=d[a.rIndex],a):null;
if(a.rIndex>=e)return null}c=d[a.rIndex];0>a.dIndex?a.setElement(c):a.dom=c;a.direct_wrapper.dom!=b&&(a.wrapper=b,a.direct_wrapper=twve.tiddler.createEditableWrapper(b,!0),a.wrapper_title=a.direct_wrapper.wrapper_title);return a},findElements:function(a,b){if(!a||!b)return null;if("string"==typeof b)b=twve.selector.create(b,"");else if(!b.include)return null;for(var c=twve.nodeList.toArray(a.querySelectorAll(b.include)),d=twve.tiddler.twveTranscludedSelectors(),d=a.querySelectorAll(d.include),e=0,
f=d.length;e<f;e++)c=twve.node.not(c,d[e].querySelectorAll(b.include));b.exclude&&(c=twve.node.not(c,b.exclude));return 0<c.length?c:null},create:function(a){var b=twve.editable.create();b.isWrapper=function(){return!0};b.directWrapper=function(){return b};b.titleOfWrapper=function(){return twve.tiddler.titleOfWrapper(b.dom)};b.isEditable=function(a){return b.dom==a?!0:twve.node.contains(b.dom,a)&&!twve.node.matches(a,twve.heading.twveSelector().include)};b.findElements=function(a){return twve.wrapper.findElements(b.dom,
a||b.twveSelector(null,"rendered"))};b.starts=function(a,c){b.start.ensureValid(c);a||(a=twve.text.tiddlerSection(b.wrapper_title));if(!a)return b.start;var d=twve.text.headerToSkip(a);0<d&&(a=twve.text.removeHeaderTail(a));var h=twve.heading.create();h.dIndex=0;h.tiddler=b.tiddler;h.end.ndx=b.start.ndx;for(h.tags=twve.heading.markupTags();0<=d;){h.starts(h.end);if(-1==h.start.ndx)break;h.ends();twve.text.sectionTitle(h.getText()).substring(0,a.length)==a&&d--}b.start.ndx=h.end.ndx;return b.start};
b.ends=function(){if(twve.text.tiddlerSection(b.wrapper_title)){var a=twve.heading.create();a.dIndex=0;a.tiddler=b.tiddler;a.tags=twve.heading.markupTags();a.start.ndx=b.start.ndx;a.starts(a.start);b.end.ndx=0>a.start.ndx?b.tiddler.text.length:a.start.ndx}else b.end.ndx=b.tiddler.text.length;return b.end};var c=b.setElement;b.setElement=function(a){if(!a)return b.clear();if(!c.apply(this,arguments)||!b.tiddler)return b;b.starts();b.ends();return b};b.refreshSelf=function(a){if(!b.tiddler)return b;
var c=twve.tiddler.twveFoldableSelector(),d=b.findElements(c),h=twve.wrapper.recordFoldable(d);b.dom.innerHTML="";a=twve.tiddler.getPreviewedNodes(a,b.dom);for(var d=0,l=a.length;d<l;d++)b.dom.appendChild(a[0]);d=b.findElements(c);return b.checkAndPrepare(d,h)};b.needsRefresh=function(a,c,d){return(!c||!d||d==c)&&!twve.node.contains(a.dom,b.dom)&&!twve.node.contains(b.dom,a.dom)?!0:!1};b.refreshAll=function(a,c,d){c=config.options.chkAnimate;config.options.chkAnimate=!1;var h=b.sliceKey?twve.text.tiddlerSlice(b.wrapper_title):
twve.text.tiddlerSection(b.wrapper_title),l=window.pageXOffset,k=window.pageYOffset;b.findWrappers();for(var m=0,r=b.wrapper.length;m<r;m++){var p=b.wrapper[m];if(p==b.dom)d||b.refreshSelf(a);else{var n=twve.tiddler.createEditableWrapper(p);if(n){var q=twve.text.tiddlerSection(n.wrapper_title)||twve.text.tiddlerSlice(n.wrapper_title);n.needsRefresh&&n.needsRefresh(b,h,q)&&((p=!twve.node.isVisible(p)?p:null)&&twve.node.show(p),n.refreshSelf(q==h?a:q?n.getText():n.tiddler.text),p&&twve.node.hide(p))}}}window.scrollTo(l,
k);config.options.chkAnimate=c;return b};b.multiLine=function(){return!0};b.checkAndPrepare=function(a,c){if(!b.dom)return b;var d=b.isVisible()?null:b.dom,h=config.options.chkAnimate;config.options.chkAnimate=!1;d&&twve.node.show(d);void 0===a&&(a=b.findElements(twve.tiddler.twveFoldableSelector()),c=twve.wrapper.recordFoldable(a));a&&twve.node.show(a);b.dom&&twve.wrapper.prepareElements(b);a&&twve.wrapper.restoreFoldable(a,c);d&&twve.node.hide(d);config.options.chkAnimate=h;return b};var d=null;
b.wait=function(){anim.running||(clearInterval(d),b.checkAndPrepare())};b.waitAndPrepare=function(){d=null;config.options.chktwveCoreEnabled&&(config.options.chkAnimate&&anim.running?d=setInterval(b.wait,50):b.checkAndPrepare())};return b.created(a)}};
twve.viewer={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("div.viewer")},titleOfWrapper:function(a){return(a=twve.node.closest(a,"[tiddler]"))?a.getAttribute("tiddler"):""},wrapperFromTitle:function(a,b){return twve.nodeList.querySelectorAll(b,'div[tiddler*="'+a+'"] .viewer')},create:function(a){var b=twve.wrapper.create(a);b.clone=function(){return twve.viewer.create(b)};return b}};
twve.tabContent={enableEdit:!0,getSelector:function(){return"div.tabContents"},twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("div.tabContents")},titleOfWrapper:function(a){return(a=a.parentNode)?a.querySelector(".tabSelected").getAttribute("content"):""},wrapperFromTitle:function(a,b){var c=twve.nodeList.querySelectorAll(b,'.tabSelected[content*="'+a+'"]');if(!c)return null;for(var d=0,e=c.length;d<e;d++)c[d]=c[d].parentNode.nextSibling;return c},create:function(a){var b=
twve.wrapper.create();b.clone=function(){return twve.tabContent.create(b)};return b.created(a)}};twve.transcludedElem={create:function(a){if(a){var b=twve.tiddler.focusElem();if(b&&b.dom==a.parentNode)return twve.element.create()}return null}};
twve.tiddlerSpan={enableEdit:!0,getSelector:function(){return"span[tiddler]"},twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("span[tiddler]")},titleOfWrapper:function(a){return a.getAttribute("tiddler")},wrapperFromTitle:function(a,b){return twve.nodeList.querySelectorAll(b,'span[tiddler*="'+a+'"]')},markupTags:function(){var a=twve.tags.create("<<tiddler",">>");a.blockElement=!0;return a},create:function(a){var b=twve.wrapper.create();b.markupTags=function(){return twve.tiddlerSpan.markupTags()};
b.clone=function(){return twve.tiddlerSpan.create(b)};var c=b.starts;b.starts=function(){b.sliceKey=twve.text.tiddlerSlice(b.wrapper_title);if(!b.sliceKey)return c.apply(this,arguments);var a=b.tiddler.text;for(store.slicesRE.lastIndex=0;;){var d=store.slicesRE.exec(a);if(!d)break;if(d[2]){if(d[2]==b.sliceKey){b.sliceValue=d[3];b.start.ndx=d.index;b.end.ndx=store.slicesRE.lastIndex;break}}else if(d[5]==b.sliceKey){b.sliceValue=d[6];b.start.ndx=d.index;b.end.ndx=store.slicesRE.lastIndex-1;break}}return b.start};
var d=b.ends;b.ends=function(){return b.sliceKey?b.end:d.apply(this,arguments)};var e=b.getText;b.getText=function(){return b.sliceKey?b.sliceValue||"":e.apply(this,arguments)};var f=b.setText;b.setText=function(a){if(!b.sliceKey)return f.apply(this,arguments);var c=b.tiddler.text,d,e,m=0;b.sliceValue?(d=c.indexOf(b.sliceValue,b.start.ndx),m=b.sliceValue.length,e=d+m):e=d=b.end.ndx;c=c.substring(0,d)+a+c.substring(e);b.end.ndx+=(a?a.length:0)-m;b.sliceValue=a;twve.tiddler.saveText(b,c);return a};
return b.created(a)}};
twve.sliderPanel={enableEdit:!0,getSelector:function(){return"div.sliderPanel"},twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector(twve.sliderPanel.getSelector())},titleOfWrapper:function(a){return a.getAttribute("tiddler")},wrapperFromTitle:function(a,b){return twve.nodeList.querySelectorAll(b,'div.sliderPanel[tiddler*="'+a+'"]')},markupTags:function(){return twve.tags.create("<<slider",">>")},create:function(a){var b=twve.transcludedElem.create(a)||twve.wrapper.create();
b.markupTags=function(){return twve.sliderPanel.markupTags()};b.clone=function(){return twve.sliderPanel.create(b)};return b.created(a)}};twve.foldedSection={macro:function(){return"<<foldHeadings>>"},enableEdit:!0,getSelector:function(){return"span.sliderPanel"},twveSelector:function(a){a||(a=twve.selector.create());return twve.foldedSection.enableEdit?a.includeSelector("span.sliderPanel"):a}};
twve.tiddlerTitle={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("div.title")},titleOfWrapper:function(a){return a.textContent},create:function(a){var b=twve.wrapper.create();b.updateText=function(a){twve.tiddler.saveText(b,null,a[0]);return b};b.getText=function(){return b.tiddler.title};b.mouseleave=function(a){var b=twve.tiddler.getOptionsMenu();if(b.dom==a.relatedTarget)return!1;b.hide(!0);return!0};b.blur=function(){twve.tiddler.getOptionsMenu().hide(!0)};
b.multiLine=function(){return!1};b.box=function(a){var d=twve.node.box(b.dom);a&&0<=a.indexOf("focus")&&(a=twve.object.create(),a.left=d.right,a.top=d.top,twve.tiddler.getOptionsMenu().showRoot(a,"left"));return twve.tiddler.editWrappers()?d:null};b.isWrapper=function(){return!1};b.titleOfWrapper=function(){return b.dom.textContent};return b.created(a)}};
//}}}
// @@
/***
|editable|k
|''Name:''|twve.debug|
|''Description:''|Debug codes of twve, the ~TiddlyWiki View mode Editor.|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.debug.min / http://twve.tiddlyspace.com/#twve.debug.min <br>* (regular) http://twve.tiddlyspot.com/#twve.debug / http://twve.tiddlyspace.com/#twve.debug|
|''Type:''|plugin|
|''Version:''|3.2.3|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core|
!!Features:
!!Usage:
!!Options
Look for [[twveDebugOptions]] in the system Options panel.
!!Examples
!!Todo:
!!!Revision history
!!!! 2015/03/23 [3.2.3]
* Added text length in nodeInfo.
!!!! 2015/02/13 [3.2.2]
** adapted for the change in twve.node.copyFontColor
!!!! 2014/12/22 [3.2.1]
* Bug fix
** for node info display.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
!!!! 2014/08/03 [3.1.0]
* Minor bug fixes
!!!! 2014/06/08 [3.0.4]
* Bug fix
** for twve.node.info(node) when node is null or undefined.
!!!! 2014/01/18
* Collected from [[TWElement|http://twtable.tiddlyspace.com/#TWElement]], [[TWtid|http://twtable.tiddlyspace.com/#TWtid]] and [[TWted|http://twtable.tiddlyspace.com/#TWted]].
!!Code
!!!The very beginning
***/
//{{{
if ( typeof twve == 'undefined' ) {
alert('twve.debug: This plugin needs twve.core to function properly. Please install twve.core before this one.');
}
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve.debug = {
major: 3, minor: 2, revision: 3,
date: new Date('2015/03/23')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.twveDebugOptions = {
init : function () {
// twve.tiddler methods
twve.debug.preGetOptionsMenu = twve.tiddler.getOptionsMenu;
twve.tiddler.getOptionsMenu = twve.debug.getOptionsMenu;
// twve.element methods
twve.node.info = twve.debug.nodeInfo;
// From TWtid
if ( config.options.txttwveDebugSnapshotFile===undefined )
config.options.txttwveDebugSnapshotFile =
(config.options.txtTWtidSnapshotFile===undefined ? ''
: config.options.txtTWtidSnapshotFile)
if ( config.options.chktwveDebugSnapshotHideBtn===undefined )
config.options.chktwveDebugSnapshotHideBtn =
(config.options.chkTWtidSnapshotHideBtn===undefined?false
: config.options.chkTWtidSnapshotHideBtn);
merge ( config.optionsDesc, {
txttwveDebugSnapshotFile:'Snapshot file name. Empty string to use the default of SnapshotPlugin, "tiddler.title" to use the tiddler title.',
chktwveDebugSnapshotHideBtn:'Hide buttons when taking a snapshot. Check to hide all the TWtid buttons and the Snapshot button.'
});
// prepare options panel
var txt = config.shadowTiddlers['OptionsPanel'];
var twvecoreopt = '[[twve.core Options';
var p = txt.indexOf(twvecoreopt);
if ( p >= 0 ) {
p = txt.indexOf(']]\n',p+2)+3;
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p) +
'[[twve.debug Options|twve.debug Options]]\n' +
txt.substring(p);
}
merge(config.shadowTiddlers,{
'twve.debug Options':'<<twveDebugOptions>>'
});
},
order : {
txttwveDebugSnapshotFile:1,
chktwveDebugSnapshotHideBtn:2
},
handler : function(place) {
// Collect all the twve.table options for users to play with.
config.macros.twveCoreOptions.showOptionsTable(
place,
"''twve.debug'' Options",
'twveDebug',
config.macros.twveDebugOptions.order
);
}
};
//}}}
/***
!!! twve.debug definitions
***/
//{{{
twve.debug = {
//}}}
/***
!!!! twve.debug.items
***/
//{{{
preGetOptionsMenu : null,
getOptionsMenu : function(){
// Replacing the twve.tiddler.getOptionsMenu for twve.debug.
var menu = twve.debug.preGetOptionsMenu.apply(this,arguments);
menu.addMenu(
"''twve.debug'' Options",
config.macros.twveDebugOptions.handler
);
return menu;
},
//}}}
/***
!!!! twve.debug.nodeInfo
***/
//{{{
nodeInfo : function (node) {
if ( ! node ) return node;
if ( ! node.nodeType ) {
// Multiple nodes
var len = node.length;
var msg = '('+len+')[';
--len;
for ( var i=0; i<len; i++ ) {
msg += twve.debug.nodeInfo(node[i])+', ';
}
return msg + twve.debug.nodeInfo(node[len]) + ']';
}
// Single node.
var title = '';
var twobj = twve.tiddler.registeredEditableObject(
node,
twve.tiddler.registered_wrapper
);
if ( twobj )
if ( twobj.titleOfWrapper ) {
title = twobj.titleOfWrapper(node);
} else {
title = twve.tiddler.titleOfWrapper(
twve.tiddler.directWrapper(node)
);
if ( title )
title = 'contained in '+title;
}
var nName = node.nodeName;
var classes = nName && nName != '#text'
? twve.node.classes(node)
: '';
var attr = nName && nName != '#text'
? twve.node.attributes(node)
: '';
var result = (title ? (' title=['+title+']') : '')
+ (classes ? classes : '')
+ (attr ? ('\n'+attr) : '');
var text = nName == 'TEXTAREA'
? node.nodeValue
: node.textContent;
if ( text && text.length > 50 )
text = text.substring(0,20)+'...'+text.substring(text.length-20);
text += '('+text.length+')';
var display = node && node.style
? node.style.display : '';
return nName+result
+ (display
? (' display=['+display+']')
: '')
+ (text ? (' text=['+text+']') : '');
}
};
//}}}
/***
!!! twve.node extra features
***/
//{{{
merge(twve.node,{
//}}}
/***
!!!! twve.node.classes
***/
//{{{
classes : function (node) {
var classes = node.className;
return classes ? ('[classes:('+classes+')]') : '';
},
//}}}
/***
!!!! twve.node.attributes
***/
//{{{
attributes : function (node) {
var attrs = node.attributes;
if ( ! attrs ) return '';
var str = '';
for(var i = 0; i < attrs.length; i++){
var attr = attrs.item(i);
str += (attr.nodeName+'='+attr.value+'\n');
}
return str ? ('{attributes:'+str+'}') : '';
},
//}}}
/***
!!!! twve.node.copyHtml
***/
//{{{
copyHtml : function ( dest, src ) {
var va = src.style.verticalAlign, factor = 0.5;
switch ( va ) {
case 'bottom' :
factor = 1;
case 'auto' :
case 'middle' :
// The CSS property vertical-align doesn't seem
// to work as it should. The following is a workaround
// to duplicate the vertically centered content of
// a table cell.
var pad = twve.node.cssSize(dest.style.paddingTop);
var sp_t = document.createElement('div');
dest.appendChild(sp_t);
if ( pad == 0 ) pad = 3;
sp_t.innerHTML = src.innerHTML;
sp_t.style.position = 'absolute';
var dh = twve.node.height(dest);
twve.node.setDimension(
sp_t, twve.node.width(dest), dh
);
twve.node.setPosition(
sp_t, null,
pad+(dh-twve.node.height(sp_t))/2
+(config.browser.isIE975 ? 0.5 : 0)
);
break;
default :
dest.innerHTML = src.innerHTML;
break;
}
twve.node.copyFontColor(dest,src,va,true);
},
//}}}
/***
!!!! twve.node.decompose
***/
//{{{
decompose : function(node){
var info = twve.node.info(node);
for(var n=0,len=node.childNodes.length; n<len; n++){
info += ' ---> '+twve.node.info(node.childNodes[n]);
};
return info;
}
});
//}}}
/***
!!! twve.element extra features
***/
//{{{
merge(twve.element,{
//}}}
/***
!!!! twve.element.copyBorder
***/
//{{{
copyBorder : function (dest, src) {
dest.style.borderTop = src.style.borderTop;
dest.style.borderRight = src.style.borderRight;
dest.style.borderBottom = src.style.borderBottom;
dest.style.borderLeft = src.style.borderLeft;
},
//}}}
/***
!!!! twve.element.copyMarginPadding
***/
//{{{
copyMarginPadding : function (dest, src) {
dest.style.margin = src.style.margin;
dest.style.padding = src.style.padding;
},
//}}}
/***
!!!! twve.element.borderTopWidth
***/
//{{{
borderTopWidth : function ( bw ) {
return (config.browser.firefoxDate && config.browser.isLinux
? bw*1.5
: (config.browser.isIE9
? 1
: bw));
}
});
//}}}
/***
|editable|k
|''Name:''|twve.debug|
|''Description:''|Debug codes of twve, the ~TiddlyWiki View mode Editor.|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.debug.min / http://twve.tiddlyspace.com/#twve.debug.min <br>* (regular) http://twve.tiddlyspot.com/#twve.debug / http://twve.tiddlyspace.com/#twve.debug|
|''Type:''|plugin|
|''Version:''|3.2.3|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core|
!!Options
Look for [[twve.debug Options]] in the system Options panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve&&alert("twve.debug: This plugin needs twve.core to function properly. Please install twve.core before this one.");version.extensions.twve.debug={major:3,minor:2,revision:3,date:new Date("2015/03/23")};
config.macros.twveDebugOptions={init:function(){twve.debug.preGetOptionsMenu=twve.tiddler.getOptionsMenu;twve.tiddler.getOptionsMenu=twve.debug.getOptionsMenu;twve.node.info=twve.debug.nodeInfo;void 0===config.options.txttwveDebugSnapshotFile&&(config.options.txttwveDebugSnapshotFile=void 0===config.options.txtTWtidSnapshotFile?"":config.options.txtTWtidSnapshotFile);void 0===config.options.chktwveDebugSnapshotHideBtn&&(config.options.chktwveDebugSnapshotHideBtn=void 0===config.options.chkTWtidSnapshotHideBtn?
!1:config.options.chkTWtidSnapshotHideBtn);merge(config.optionsDesc,{txttwveDebugSnapshotFile:'Snapshot file name. Empty string to use the default of SnapshotPlugin, "tiddler.title" to use the tiddler title.',chktwveDebugSnapshotHideBtn:"Hide buttons when taking a snapshot. Check to hide all the TWtid buttons and the Snapshot button."});var a=config.shadowTiddlers.OptionsPanel,b=a.indexOf("[[twve.core Options");0<=b&&(b=a.indexOf("]]\n",b+2)+3,config.shadowTiddlers.OptionsPanel=a.substring(0,b)+"[[twve.debug Options|twve.debug Options]]\n"+
a.substring(b));merge(config.shadowTiddlers,{"twve.debug Options":"<<twveDebugOptions>>"})},order:{txttwveDebugSnapshotFile:1,chktwveDebugSnapshotHideBtn:2},handler:function(a){config.macros.twveCoreOptions.showOptionsTable(a,"''twve.debug'' Options","twveDebug",config.macros.twveDebugOptions.order)}};
twve.debug={preGetOptionsMenu:null,getOptionsMenu:function(){var a=twve.debug.preGetOptionsMenu.apply(this,arguments);a.addMenu("''twve.debug'' Options",config.macros.twveDebugOptions.handler);return a},nodeInfo:function(a){if(!a)return a;if(!a.nodeType){var b=a.length,d="("+b+")[";--b;for(var c=0;c<b;c++)d+=twve.debug.nodeInfo(a[c])+", ";return d+twve.debug.nodeInfo(a[b])+"]"}d="";if(b=twve.tiddler.registeredEditableObject(a,twve.tiddler.registered_wrapper))b.titleOfWrapper?d=b.titleOfWrapper(a):
(d=twve.tiddler.titleOfWrapper(twve.tiddler.directWrapper(a)))&&(d="contained in "+d);var c=(b=a.nodeName)&&"#text"!=b?twve.node.classes(a):"",e=b&&"#text"!=b?twve.node.attributes(a):"",d=(d?" title=["+d+"]":"")+(c?c:"")+(e?"\n"+e:"");(c="TEXTAREA"==b?a.nodeValue:a.textContent)&&50<c.length&&(c=c.substring(0,20)+"..."+c.substring(c.length-20));c+="("+c.length+")";a=a&&a.style?a.style.display:"";return b+d+(a?" display=["+a+"]":"")+(c?" text=["+c+"]":"")}};
merge(twve.node,{classes:function(a){return(a=a.className)?"[classes:("+a+")]":""},attributes:function(a){a=a.attributes;if(!a)return"";for(var b="",d=0;d<a.length;d++)var c=a.item(d),b=b+(c.nodeName+"="+c.value+"\n");return b?"{attributes:"+b+"}":""},copyHtml:function(a,b){var d=b.style.verticalAlign;switch(d){case "bottom":case "auto":case "middle":var c=twve.node.cssSize(a.style.paddingTop),e=document.createElement("div");a.appendChild(e);0==c&&(c=3);e.innerHTML=b.innerHTML;e.style.position="absolute";
var f=twve.node.height(a);twve.node.setDimension(e,twve.node.width(a),f);twve.node.setPosition(e,null,c+(f-twve.node.height(e))/2+(config.browser.isIE975?0.5:0));break;default:a.innerHTML=b.innerHTML}twve.node.copyFontColor(a,b,d,!0)},decompose:function(a){for(var b=twve.node.info(a),d=0,c=a.childNodes.length;d<c;d++)b+=" ---\x3e "+twve.node.info(a.childNodes[d]);return b}});
merge(twve.element,{copyBorder:function(a,b){a.style.borderTop=b.style.borderTop;a.style.borderRight=b.style.borderRight;a.style.borderBottom=b.style.borderBottom;a.style.borderLeft=b.style.borderLeft},copyMarginPadding:function(a,b){a.style.margin=b.style.margin;a.style.padding=b.style.padding},borderTopWidth:function(a){return config.browser.firefoxDate&&config.browser.isLinux?1.5*a:config.browser.isIE9?1:a}});
//}}}
// @@
This tiddler gives detailed information about the ''twve.editable'', an object representing all editable objects in ~TiddlyWiki.
*** ''getText'' -- retrieves the defining text of an element.
#### Default method is implemented in the <<slider '' 'twve.core##editable.getText' '.getText([open,close])'>> method.
***** Usually you do not need to work with this one, but either [[twve.element]] or [[twve.wrapper]].
*** ''setText''(txt) -- puts back the defining text of an element.
This tiddler gives detailed information about the ''twve.element'', an object representing all editable __elements__ in ~TiddlyWiki.
/***
|editable|k
|''Name:''|twve.extra|
|''Description:''|View mode editing features for block elements (other than tables), in-line elements, and plain text in ~TidfdlyWiki environment|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.extra.min / http://twve.tiddlyspace.com/#twve.extra.min <br>* (regular) http://twve.tiddlyspot.com/#twve.extra / http://twve.tiddlyspace.com/#twve.extra|
|''Type:''|plugin|
|''Version:''|3.2.6|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core|
!!Features:
* View mode editing features for block elements (other than tables) and plain text in ~TiddlyWiki environment
** Headings
** Lists
** Blockquotes
** Blockexamples
** Preformatted blocks
** Plain text
!!Usage:
!!Options
Look for [[twve.extra Options]] in the Options Panel.
!!Examples
[[twve.extra Examples]]
!!Todo:
!!!Revision history
!!!! 2016/04/30 [3.2.6]
* Saves options in SystemSettings tiddler.
** If you disable autoSave option, you will need to manually save the tiddler after changing a ''twve'' option.
* Starts using strict mode.
* Improved support for character-base editing.
** Added support for Home and End keys.
* Bug fix
** for identifying MathJax elements;
** for inline MathJax editing.
!!!! 2015/11/06 [3.2.5]
* Bug fix
** for cssWrappers.
!!!! 2015/06/15 [3.2.4]
* Introduced option {{{chktwveExtraMathAutoNumber}}} to enable/disable MathJax auto numbering feature.
** If turned on, a preview including numbered equations can disturb the numbering of those equations.
*** Moreover, the labeling would fail if labeled equations get previewed. //I don't know how to fix this yet//.
* Bug fix
** for locating the wiki text of HR;
** for correct handling of MathJax {{{\ref}}} and {{{\eqref}}} commands.
!!!! 2015/03/23 [3.2.3]
* Added supports for CSS wrappers created using """{{Class name{ .... }}}""".
* Bug fix
** for correctly locating spans created with a pair of {{{@@}}}'s;
** for displayed math (using MathJax) containing nested {{{\begin}}} commands.
!!!! 2015/02/13 [3.2.2]
* Replaced regular expression with string operations whenever easy, hoping to save some time.
* Bug fix
** for synchronization of partial transcluded content;
** for editing of a paragraph of plain text;
** for editing folded sections;
** for resizing the browser window.
!!!! 2014/12/22 [3.2.1]
* Improved
** for editing of a paragraph of plain text.
* Bug fix
** for editing an empty text line;
** for refreshing mixed type lists;
** for partial refreshing before and after a tiddler;
** for refreshing leveled elements (such as list items);
** for transclusion synchronization.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
* Partial refreshing for wrappers is now possible, which improves a lot the speed of refreshing plain text.
* Automatically enables editing features for wrappers, regardless of the setting of <<option chktwveCoreEditWrappers>>chktwveCoreEditWrappers.
* Bug fix
** for locating blockquotes contained within a block example;
** for disabled wikiwords (e.g., ~TiddlyWiki);
** for moving list items with Ctrl-up / Ctrl-down;
** for editing transcluded slices/sections;
*** Non-empty slices/sections: click on the transcluded text to edit the content, or empty area of its wrapper to edit the macro.
*** Empty slices/sections: click when the mouse pointer is close to the empty wrapper (within the default editbox width given by the option txttwveCoreMinEditWidth) to edit the content, or farther away (to the right) to edit the macro.
**** __The determination of an empty slice could be wrong if that empty slide is located at the very end of a tiddler.__ Workaround: add a newline at the end.
** for plain text following displayed MathJax block defined by {{{\begin{}}} and {{{\end{}}} pairs.
!!!! 2014/08/03 [3.1.0]
* Stopped using Ctrl-left and Ctrl-right in moving contents.
* Started to remove jQuery dependencies for migrating to TW5.
* Bug fix
** for obtaining the bounding box of plain text;
** for transclusion synchronization with self inclusion and FoldedSectionPlugin.
!!!! 2014/06/02 [3.0.3]
* Moved in the codes to edit heading, preformatted blocks, and code blocks from ''twve.core''.
* Enables editing of tiddler title when ''twve.extra'' is installed.
* Bug fixes
** for the {{{.dom is null}}} error upon loading up;
** for focusing borders to correctly surround an in-line element that spans over multiple lines.
*** With FireFox in an Android system, the twve.node.box() function returns the element height much larger than in a desktop/laptop system, yet the 'line-height' CSS value remains the same, resulting in a wrong lines of text calculation.
*** IE11 (and other versions?) gets a wrong bounding box for the leading textnode in a second level (and higher?) list item.
!!!! 2014/05/23 [3.0.2]
* Moved the following codes into ''twve.core'':
** that to avoid editing when clicking on a link;
** that to show Options Menu for ''twve.core''.
* Improved partial refreshing
** for headings when {{{foldHeadingsPlugin}}} is present;
** for lists and blockquotes.
*** A list is difficult to refresh partially, because
**** it can be one of the two types: numbered and unnumbered;
**** change in type will change the DOM structure.
*** A blockquote is difficult to locate in the wiki text, because
**** in HTML it is only blockquote, but in wiki text it has two types: blockexample and blockquote;
**** a blockexample can contain blockquotes within its body;
**** a blockquote can contain sub-blockquotes in two ways: either immediately following it or completely within its body.
**** a blockexample
* More visually consistent focusing borders for in-line elements that span over multiple lines. See [[twve--Example--Over Width Inline Elements]] for examples.
* Bug fix
** for working with {{{foldHeadingsPlugin}}};
** for somewhat complicated blockquote identification;
** for working with styled text.
!!!! 2014/05/14 [3.0.1]
* Moved the codes for pretty links into ''twve.core''.
!!!! 2014/05/13 [3.0.0]
* Inline elements (@@color:red;Unstable!!! Can be very wrong some times!@@):
** styled text such as //italic//, ''bold'', __underlined__, or --strike through--;
*** These can be wrong when there are strings containing """''""" (two single quotes in a row), """//""", """__""" (two underscores in a row), or """--""".
** @@color:blue;colored@@ or @@inversed@@ text enclosed with a pair of """@@""";
*** These are created as SPAN's in ~TiddlyWiki, but there seem to be some SPAN's unknown to me so I can't get them all right yet.
** {{{CODE pieces}}} enclosed with a pair of triple braces, i.e., """{{{""" and """}}}""".
* Bug fixes
** for titles with math expressions (~MathJax).
** for blockquotes
*** It seems to work for almost all kinds of blockquotes, including those containing blockquotes within their bodies.
!!!! 2014/01/18
* Collected from [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] and [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]], adapted to the new code structure.
!!!Code
!!!The very beginning
***/
//{{{
(function(){
"use strict";
if ( typeof twve == 'undefined' ) {
alert('twve.extra: This plugin needs twve.core to function properly. Please install twve.core before this one.');
}
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve.extra = {
major: 3, minor: 2, revision: 6,
date: new Date('2016/04/30')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.twveExtraOptions = {
init : function () {
// twve.tiddler methods
twve.extra.preKeydownAfter = twve.tiddler.keydownAfter;
twve.tiddler.keydownAfter = twve.extra.keydownAfter;
twve.extra.preGetPreviewer = twve.tiddler.getPreviewer;
twve.tiddler.getPreviewer = twve.extra.getPreviewer;
twve.extra.preGetOptionsMenu = twve.tiddler.getOptionsMenu;
twve.tiddler.getOptionsMenu = twve.extra.getOptionsMenu;
twve.extra.preUpdateText = twve.tiddler.updateText;
twve.tiddler.updateText = twve.extra.updateText;
// twve.text methods
twve.MathJax.displayed.preLastIndexOf = twve.text.lastIndexOf;
twve.text.lastIndexOf = twve.MathJax.displayed.lastIndexOf;
// twve.tags methods
twve.extra.preInactiveTags = twve.tags.inactiveTags;
twve.tags.inactiveTags = twve.extra.inactiveTags;
// twve.node methods
twve.MathJax.preWikify = twve.node.wikify;
twve.node.wikify = twve.MathJax.wikify;
twve.heading.preWikify = twve.node.wikify;
twve.node.wikify = twve.heading.wikify;
twve.MathJax.preBox = twve.node.box;
twve.node.box = twve.MathJax.box;
twve.sliderPanel.preBox = twve.node.box;
twve.node.box = twve.sliderPanel.box;
// twve.heading methods
twve.foldedSection.preHeadingCreate = twve.heading.create;
twve.heading.create = twve.foldedSection.headingCreate;
twve.MathJax.preHeadingClick = twve.heading.click;
twve.heading.click = twve.MathJax.headingClick;
// twve.wrapper methods
twve.MathJax.preFold = twve.wrapper.fold;
twve.wrapper.fold = twve.MathJax.fold;
twve.MathJax.prePrepareElements = twve.wrapper.prepareElements;
twve.wrapper.prepareElements = twve.MathJax.prepareElements;
twve.MathJax.preRestoreFoldable = twve.wrapper.restoreFoldable;
twve.wrapper.restoreFoldable = twve.MathJax.restoreFoldable;
// twve.extra methods
twve.extra.preSetOption = setOption;
setOption = twve.extra.setOption;
// From TWted
twve.checkOption(
'chktwveExtraIncludeSubs',
(config.options.chkTWtedIncludeSubs || false)+''
);
twve.checkOption('chktwveExtraInline','true');
twve.checkOption('chktwveExtraCyclickNavi','false');
twve.checkOption('chktwveExtraLocateChar','false');
twve.checkOption(
'chktwveExtraNoClick',
(config.options.chkTWtedNoClick || false)+''
);
twve.checkOption(
'txttwveExtraPreviewOpacity',
(config.options.txtTWtedPreviewOpacity || '1.00')
);
twve.checkOption('chktwveExtraMathAutoNumber','false');
merge ( config.optionsDesc, {
//txttwveExtraMinEditWidth:'Minimum editbox width (characters). Default to 6.',
chktwveExtraIncludeSubs:'Include sub-elements to edit. For example, sub lists are included in the editbox if this option is set to true.',
chktwveExtraInline:'Edit inline elements.',
chktwveExtraCyclickNavi:'Cyclick keyboard navigation.',
chktwveExtraLocateChar:'(@@color:red;Experimental@@) Character by character editing.',
chktwveExtraNoClick:'Edit element content without clicking it.',
txttwveExtraPreviewOpacity:'Opacity of the previewer. Default to 0.9.',
chktwveExtraMathAutoNumber:'~MathJax auto numbering.'
});
// prepare options panel
var optionsTitle = 'twve.extra Options';
var txt = config.shadowTiddlers['OptionsPanel'];
var twvecoreopt = '[[twve.core Options';
var p = txt.indexOf(twvecoreopt);
if ( p >= 0 ) {
p = txt.indexOf(']]\n',p+2)+3;
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'[['+optionsTitle+'|'+optionsTitle+']]\n'
+txt.substring(p);
}
merge(config.shadowTiddlers,{
'twve.extra Options':'<<twveExtraOptions>>'
});
// Register editable elements
twve.tiddler.registerElement(twve.blockquote);
twve.tiddler.registerElement(twve.cssWrapper);
twve.tiddler.registerElement(twve.listitem);
twve.tiddler.registerElement(twve.tabset);
twve.tiddler.registerElement(twve.heading);
twve.tiddler.registerElement(twve.img);
twve.tiddler.registerElement(twve.pre);
twve.tiddler.registerElement(twve.code);
twve.tiddler.registerElement(twve.span);
twve.tiddler.registerElement(twve.MathJax.inline);
twve.tiddler.registerElement(twve.MathJax.displayed);
twve.tiddler.registerElement(twve.HR);
twve.tiddler.registerElement(twve.styledText.strike);
twve.tiddler.registerElement(twve.styledText.under);
twve.tiddler.registerElement(twve.styledText.italic);
twve.tiddler.registerElement(twve.styledText.bold);
// Register wrappers
twve.tiddler.registerWrapper(twve.foldedSection);
},
order : {
chktwveExtraIncludeSubs:1,
chktwveExtraInline:2,
chktwveExtraCyclickNavi:3,
chktwveExtraLocateChar:4,
chktwveExtraNoClick:5,
txttwveExtraPreviewOpacity:6,
chktwveExtraMathAutoNumber:7
},
handler : function(place) {
// Collect all the twve.extra options for users to play with.
config.macros.twveCoreOptions.showOptionsTable(
place,
"''twve.extra'' Options",
'twveExtra',
config.macros.twveExtraOptions.order
);
}
};
//}}}
/***
!!!! twve.extra
***/
//{{{
twve.extra = {
//}}}
/***
!!!! twve.extra.setOption
***/
//{{{
preSetOption : null,
setOption : function(name,value){
twve.extra.preSetOption.apply(this,arguments);
//if ( name.indexOf('twveExtra') >= 0 ) {
// One of the twve.extra options
// switch ( name ) {
// }
//}
},
//}}}
/***
!!!! twve.extra.getPreviewer
***/
//{{{
preGetPreviewer : null,
getPreviewer : function(){
var preview = twve.extra.preGetPreviewer.apply(this,arguments);
preview.style.opacity=config.options.txttwveExtraPreviewOpacity;
return preview;
},
//}}}
/***
!!!! twve.extra.getOptionsMenu
***/
//{{{
preGetOptionsMenu : null,
getOptionsMenu : function(){
// Replacing the twve.tiddler.getOptionsMenu for twve.extra.
var menu = twve.extra.preGetOptionsMenu.apply(this,arguments);
menu.addMenu(
"''twve.extra'' Options",
config.macros.twveExtraOptions.handler
);
return menu;
},
//}}}
/***
!!!! twve.extra.inactiveTags
***/
//{{{
preInactiveTags : null,
inactiveTags : function(){
return twve.extra.preInactiveTags.apply().merge(
twve.MathJax.displayed.markupTags()
).merge(
twve.MathJax.inline.markupTags()
);
},
//}}}
/***
!!!! twve.extra.keydownChar
***/
//{{{
keydownChar : function (ev,ta) {
// WYSIWYG keydown handler
if ( twve.tiddler.keydownCancel(ev,ta) ) return false;
var caret_pos = twve.tiddler.caret_pos;
var editable = twve.tiddler.cur_editable;
switch ( ev.which ) {
case 46 : // delete
if ( editable.selection.end > editable.selection.start ) {
// Some text is being selected, remove it.
twve.tiddler.updateText([ta],editable,false,true);
return true;
}
var next = editable.selection.nextNode;
switch(next.nodeValue.length){
case 1 :
// The next node has only one character left, remove
// it and move to the even next node.
editable.selection.nextNode = next.nextSibling;
next.parentNode.removeChild(next);
// Adjust the offset
break;
case 0 :
// The next node is empty, move to the even next node
// and remove its first character.
break;
default :
// The next node has more than one characters left,
// remove its first character.
editable.selection.nextNode = next.splitText(1);
next.parentNode.removeChild(next);
// Remove one character in the text.
editable.selection.end++;
twve.tiddler.updateText([ta],editable,true,true);
if ( editable.editNode )
editable.editNode.end.ndx--;
editable.setFocusOffset(ta);
editable.selection.end--;
break;
}
break;
case 8 : // backspace
if ( ev.alterKey ) return false;
if ( editable.selection.end > editable.selection.start ) {
// Some text is being selected, remove it.
twve.tiddler.updateText([ta],editable,false,true);
return true;
}
var cur = editable.selection.focusNode;
var curlen = cur.nodeValue.length;
switch(curlen){
case 1 :
// The cur node has only one character left, remove
// it and move to the end of its previous node.
editable.selection.nextNode = cur.nextSibling;
editable.selection.focusNode = cur.previousSibling;
cur.parentNode.removeChild(cur);
// Adjust the offset
break;
case 0 :
// The cur node is empty, move to its previous node
// and remove its last character.
break;
default :
// The cur node has more than one characters left,
// remove its first character.
//var dlen = editable.selection.end -
// editable.selection.start + 1;
cur = cur.splitText(curlen-1);
cur.parentNode.removeChild(cur);
// Move caret to the left.
// Remove one character in the text.
editable.selection.start--;
twve.tiddler.updateText([ta],editable,true,true);
if ( editable.editNode )
editable.editNode.end.ndx--;
editable.setFocusOffset(ta,null,-1);
// Update caret position.
editable.selection.end--;
twve.tiddler.setCaretPosition(editable.selection.end);
break;
}
break;
case 33 : // page up
case 34 : // page down
case 35 : // end
if ( ev.ctrlKey ) {
// Ctrl-End, move to the end of tiddler.
return false;
}
// End is pressed, move to the end of this line
// or this block.
twve.tiddler.updateText([ta],editable,true,true);
editable.setFocusOffset(
ta,editable.editNode.end.ndx-editable.editNode.start.ndx
);
// Update caret position.
//editable.selection.start = editable.selection.end =
//editable.editNode.start.ndx +
//editable.selection.focusOffset;
twve.tiddler.setCaretPosition(editable.selection.end);
break;
case 36 : // home
if ( ev.ctrlKey ) {
// Ctrl-Home, move to the beginning of tiddler.
return false;
}
// Home is pressed, move to the begining of this line
// or this block.
twve.tiddler.updateText([ta],editable,true,true);
editable.setFocusOffset(ta,0);
// Update caret position.
editable.selection.start = editable.selection.end = 0;
twve.tiddler.setCaretPosition(0);
break;
case 37 : // left arrow
if ( ev.ctrlKey ) {
// Move the cursor if necessary.
return false;
}
//if (caret_pos.cur == 0 && caret_pos.last == 0) {
// At the beginning of a non-empty element, go to its
// left neighbor.
//} else {
// Otherwise move caret to the left.
twve.tiddler.updateText([ta],editable,true,true);
editable.setFocusOffset(ta,null,-1);
// Update caret position.
//editable.selection.start = editable.selection.end =
//editable.editNode.start.ndx +
//editable.selection.focusOffset;
// caret_pos.cur-1;
twve.tiddler.setCaretPosition(editable.selection.end);
//}
break;
case 38 : // up arrow
if ( ev.ctrlKey ) {
return false;
}
break;
case 39 : // right arrow
if ( ev.ctrlKey ) {
// Move cursor if necessary.
return false;
}
//if (caret_pos.cur == txt.length &&
// caret_pos.last == caret_pos.cur) {
// At the end of a non-empty element, move to its right
// neighbor.
//} else {
// Otherwise move caret to the right.
twve.tiddler.updateText([ta],editable,true,true);
editable.setFocusOffset(ta,null,1);
// Update caret position.
//editable.selection.start = editable.selection.end =
//editable.editNode.start.ndx +
//editable.selection.focusOffset;
// caret_pos.cur+1;
twve.tiddler.setCaretPosition(editable.selection.end);
//}
break;
case 40 : // down arrow
if ( ev.ctrlKey ) {
return false;
}
break;
default :
// Normal typing. Insert the rendered nodes at the caret
// position.
var txt = ta.value;
ta.value = '';
editable.typed += txt;
var nodes = twve.tiddler.getPreviewedNodes(txt);
var parent = editable.selection.nodeNdx.e0;
var next = editable.selection.nextNode;
while( nodes.length ) {
editable.inserted[editable.inserted.length] =
parent.insertBefore(nodes[0],next);
}
var len = txt.length;
//editable.setFocusOffset(ta,null,len,next);
editable.setFocusOffset(ta,0,null,next);
//editable.selection.start = editable.selection.end =
// caret_pos.cur+len;
twve.tiddler.setCaretPosition(editable.selection.end);
break;
}
return true;
},
//}}}
/***
!!!! twve.extra.keydownAfter
***/
//{{{
preKeydownAfter : null,
keydownAfter : function (ev,ta) {
if (config.options.chktwveExtraLocateChar)
return twve.extra.keydownChar(ev,ta);
if ( ! twve.extra.preKeydownAfter.apply(this,arguments) )
return false;
var caret_pos = twve.tiddler.caret_pos;
var editable = twve.tiddler.cur_editable;
var txt = ta.value;
switch ( ev.which ) {
case 33 : // page up
case 34 : // page down
case 35 : // end
case 36 : // home
break;
case 46 : // delete
if ( txt == '\n' ) {
// Empty element, go straight to its right
// neighbor.
editable.editNeighbor('right');
} else if (caret_pos.cur == txt.length ) {
// We are at the end of the text being edited.
if ( txt || caret_pos.cur == caret_pos.last )
editable.combineEditNeighbor(ta,'right');
}
break;
case 8 : // backspace
if ( txt == '\n' ) {
// Empty element, go straight to its left
// neighbor.
editable.editNeighbor('left');
} else if ( caret_pos.cur == 0 ) {
// We are at the begining of the paragraph being
// edited.
if ( txt || caret_pos.cur == caret_pos.last )
editable.combineEditNeighbor(ta,'left');
}
break;
case 37 : // left arrow
if ( ev.ctrlKey ) {
return false;
}
if ( ! txt || txt == '\n' ||
(caret_pos.cur == 0 && caret_pos.last == 0) ) {
// Empty element, or at the beginning of an element,
// go to its left neighbor.
editable.editNeighbor('left');
}
break;
case 38 : // up arrow
if ( ev.ctrlKey ) {
//editable = editable.move('up',1);
//if ( editable ) {
// twve.tiddler.focusElem(editable);
// editable.editText();
//}
return false;
}
if ( ! txt || txt == '\n' ||
(caret_pos.cur == 0 && caret_pos.last == 0) ) {
editable.editNeighbor('up');
}
break;
case 39 : // right arrow
if ( ev.ctrlKey ) {
return false;
}
if ( ! txt || txt == '\n' ) {
// Empty element, move straight to its right neighbor.
editable.editNeighbor('right');
} else if (caret_pos.cur == txt.length &&
caret_pos.last == caret_pos.cur) {
// At the end of a non-empty element, move to its right
// neighbor.
editable.editNeighbor('right');
}
break;
case 40 : // down arrow
if ( ev.ctrlKey ) {
//editable = editable.move('down',1);
//if ( editable ) {
// twve.tiddler.focusElem(editable);
// editable.editText();
//}
return false;
}
if ( ! txt || txt == '\n' ||
(caret_pos.cur == txt.length &&
caret_pos.last == caret_pos.cur) ) {
editable.editNeighbor('down');
}
break;
}
return true;
},
//}}}
/***
!!!! twve.extra.updateText
***/
//{{{
preUpdateText : null,
updateText : function (ta,editable,refreshed,editing) {
if ( ! editable ) editable = twve.tiddler.cur_editable;
if ( editable ) {
if ( config.options.chktwveExtraLocateChar ) {
// Normalize the element
editable.getElement().normalize();
if ( ! ta ) ta = twve.tiddler.getEditBox();
if ( ta ) {
ta[0].value =
ta[0].defaultValue.substring(
0,editable.selection.start
) + editable.typed +
ta[0].defaultValue.substring(
editable.selection.end
);
twve.extra.preUpdateText.call(
this,ta,editable,refreshed,editing
);
if ( editing ) {
if ( editable.editNode )
editable.editNode.end.ndx +=
editable.typed.length;
ta[0].defaultValue = ta[0].value;
editable.typed = editable.deleted = ta[0].value = '';
}
}
} else {
// Update text
twve.extra.preUpdateText.call(
this,ta,editable,refreshed,editing
);
}
}
return editable;
}
};
//}}}
/***
!!! twve.tags.nested
***/
//{{{
if ( twve.tags.nested === undefined )
twve.tags.nested = {
//}}}
/***
!!!! twve.tags.nested.matchedCloseTag
***/
//{{{
matchedCloseTag : function(txt,start,open,close){
if ( typeof start === 'number' ) {
start = twve.position.create(start);
if ( typeof open ==='string' )
start.matched = open;
} else if ( start.ndx === undefined ) {
start = twve.position.create(0);
if ( typeof open ==='string' )
start.matched = open;
}
return twve.tags.nested.create(open,close)
.matchedCloseTag(txt,start);
},
//}}}
/***
!!!! twve.tags.nested.create
***/
//{{{
create : function(src,close){
var tags = twve.tags.create(src,close);
// clone
tags.clone = function(){
// Required, for this object has no markupTags() method.
return twve.tags.nested.create();
};
//}}}
/***
!!!!! tags.matchedCloseTag
***/
//{{{
tags.matchedCloseTag = function(txt,start){
var remained = 1;
var txtlen = txt.length;
var end = twve.position.create(start.ndx);
do {
// Look for next closing tag.
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
// Look for next opening tag.
start = twve.text.indexOf(txt,tags.open,start);
if ( start.ndx == -1 ) {
// No more opening but still closing tags.
// Check for remaining closing tags.
start.ndx = end.ndx;
do {
if ( --remained == 0 ) return end;
end.ndx += end.matched.length;
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
} while ( true );
}
if ( end.ndx == start.ndx ) {
if ( --remained == 0 ) {
// This situation is possible for elements that
// must start from the beginning of a line and
// end at a line break, such as list items.
return end;
}
start.ndx += start.matched.length;
end.ndx += end.matched.length;
} else if ( end.ndx < start.ndx ) {
// The next closing tag is before the next opening tag.
do {
// If this closing tag is the matched one, return
// its position info.
if ( --remained == 0 ) return end;
// Otherwise look for all closing tags before the
// next opening tag.
end.ndx += end.matched.length;
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
} while ( end.ndx < start.ndx );
// The matched closing tag is not found yet, add one
// more and go on.
++remained;
start.ndx += start.matched.length;
end.ndx = start.ndx;
} else { // end.ndx > start.ndx
// The next opening tag is before the next closing
// tag. Look for all opening tags before the next
// closing tag.
do {
++remained;
start.ndx += start.matched.length;
start = twve.text.indexOf(txt,tags.open,start);
} while ( start.ndx > -1 && start.ndx < end.ndx );
--remained;
end.ndx += end.matched.length;
start.ndx = end.ndx;
}
} while (true);
};
//}}}
/***
!!!!! End of tags
***/
//{{{
return tags;
}
};
//}}}
/***
!!! twve.BR
***/
//{{{
twve.BR = {
enableEdit : true,
//}}}
/***
!!!! twve.BR.is
***/
//{{{
is : function(node){
return node.nodeName == 'BR';
},
//}}}
/***
!!!! twve.BR.markupTags
***/
//{{{
markupTags : function(){
return twve.tags.create(
['\n', '<br'],
['', '>']
);
},
//}}}
/***
!!!! twve.BR.create
***/
//{{{
create : function(src,txt,start,dir){
var br = twve.element.create();
br.err = function(start,txt,dir){
br.start.clear();
var msg = dir < 0
? txt.substring(start-50,start)
: txt.substring(start,start+50);
console.log('Internal Error('+(dir<0?'left':'right')+'): BR encountered but neither newline nor <br> was found. start='+start+'/'+txt.length+' subtxt=['+msg+']');
};
br.onBlockElement = function(txt,start){
if ( br.dom.nextSibling.nodeName != 'BR' &&
txt.charAt(start) == '\n' ) {
if("|*#{<>!".indexOf(txt.charAt(start+1))>=0){
br.start.ndx = start;
br.end.ndx = start+1;
br.end.matched = '\n';
return br.start;
}
}
};
br.onNewLine = function(txt,start){
if ( txt.charAt(start) == '\n' ) {
br.start.ndx = start;
br.end.ndx = start+1;
br.end.matched = '\n';
// Check if there is <<foldHeadings>>
if(txt.charAt(start-1)=='>'){
if((start=txt.indexOf('<<',start-1))>=0){
br.start.matched = txt.substring(
start,br.end.ndx-1
);
if(br.start.matched=='<<foldHeadings>>'){
br.start.ndx = start;
}
}
}
return br.start;
}
};
br.starts = function(start,txt,dir){
if ( ! txt || start==txt.length ){
//if(twve.node.matches(
// br.dom.parentNode,twve.foldedSection.getSelector()
//)){
br.start.ndx = br.end.ndx = start;
//}
return br.start;
}
if ( dir < 0 ){
// Going left (backward), first check if we are sitting
// on the end of a block element.
if ( br.onBlockElement(txt,start) ){
// Yes we are, return it.
return br.start;
}
// No, move backward by one chararacter.
start--;
while(txt.charAt(start)=='>'){
// We are sitting on a '>',
br.end.ndx = start;
br.start.ndx = txt.lastIndexOf('<',start-1);
if ( br.start.ndx >= 0 ){
br.start.matched = txt.substring(
br.start.ndx,br.start.ndx+3
);
if(br.start.matched.toLowerCase()=='<br'){
// This is a <br> or <br />
br.end.ndx = start+1;
return br.start;
}
start = br.start.ndx-1;
} else
start = br.end.ndx-1;
}
if ( br.onNewLine(txt,start) ){
// We are sitting right on a new line char,
return br.start;
}
} else {
// Going right (forward),
while ( txt.charAt(start)=='<' ) {
// and we are sitting on a '<',
br.end.ndx = txt.indexOf('>',start+1)+1;
if ( br.end.ndx > start+1 ){
br.start.matched = txt.substring(start,start+3);
if(br.start.matched.toLowerCase()=='<br'){
// This is a <br> or <br />
br.start.ndx = start;
br.end.matched = '>';
return br.start;
}
// Check if there is <<foldHeadings>>
br.end.ndx++;
br.start.matched = txt.substring(
start,br.end.ndx
);
if(br.start.matched=='<<foldHeadings>>'){
br.start.ndx = start;
if(txt.charAt(br.end.ndx)=='\n'){
br.end.ndx++;
return br.start;
}
}
start = br.end.ndx;
} else
start++;
}
if ( br.onNewLine(txt,start) )
// or we are sitting right on a new line char,
return br.start;
}
br.err(start,txt,dir);
return br.start;
};
br.ends = function(){
return br.end;
};
return br.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.HR
***/
//{{{
twve.HR = {
enableEdit : true,
//}}}
/***
!!!! twve.HR.is
***/
//{{{
is : function(node){
return node.nodeName == 'HR';
},
//}}}
/***
!!!! twve.HR.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
['\n--', '<HR'],
['--\n', '>']
);
tags.blockElement = true;
return tags;
},
//}}}
/***
!!!! twve.HR.create
***/
//{{{
create : function(src,txt,start,dir){
var hr = twve.element.create();
hr.err = function(start,txt,dir){
hr.start.clear();
var msg = dir < 0
? txt.substring(start-50,start)
: txt.substring(start,start+50);
console.log('Internal Error('+(dir<0?'left':'right')+'): HR encountered but neither \n----\n nor <hr> was found. start='+start+'/'+txt.length+' subtxt=['+msg+']');
};
hr.starts = function(start,txt,dir){
if ( ! txt || start==txt.length ){
hr.start.ndx = hr.end.ndx = start;
return hr.start;
}
var ch;
if ( dir < 0 ){
// Going left (backward),
hr.end.ndx = start;
ch = txt.charAt(start-1);
if ( ch == '\n' ) {
if(txt.substring(start-6,start)=='\n----\n'){
hr.end.matched = hr.tags.close[0];
hr.start.ndx = start - 6;
hr.start.matched = hr.tags.open[0];
return hr.start;
}
} else if ( ch == '>' ) {
// <HR>
hr.end.matched = hr.tags.close[1];
hr.start.ndx = txt.lastIndexOf(
'<',start-1
);
hr.start.matched = hr.tags.open[1];
if ( hr.start.ndx >= 0 ) return hr.start;
}
} else {
// Going right (forward),
hr.start.ndx = start;
ch = txt.charAt(start);
if ( ch == '\n' ) {
if(txt.substring(start+6,start)=='\n----\n'){
hr.start.ndx++;
hr.start.matched = hr.tags.open[0];
hr.end.ndx = start + 6;
hr.end.matched = hr.tags.close[0];
return hr.start;
}
} else if ( ch == '-' ) {
if(txt.substring(start+5,start)=='----\n'){
hr.start.matched = hr.tags.open[0];
hr.end.ndx = start + 5;
hr.end.matched = hr.tags.close[0];
return hr.start;
}
} else if ( ch == '<' ) {
// <HR>
hr.start.matched = hr.tags.open[1];
hr.end.ndx = txt.indexOf('>',start+1);
hr.end.matched = hr.tags.close[1];
if ( hr.end.ndx > 0 ) return hr.start;
}
}
hr.err(start,txt,dir);
return hr.start;
};
hr.ends = function(){
return hr.end;
};
return hr.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.tiddler extra features
***/
//{{{
merge(twve.tiddler, {
//}}}
/***
!!!! twve.tiddler.editWrappers
***/
//{{{
editWrappers : function(){
return true;
},
//}}}
/***
!!!! twve.tiddler.registerWrapper
***/
//{{{
wrap_sel : null,
preRegisterWrapper : twve.tiddler.registerWrapper,
registerWrapper : function(){
twve.tiddler.wrap_sel = null;
twve.tiddler.preRegisterWrapper.apply(this,arguments);
},
//}}}
/***
!!!! twve.tiddler.focusTarget
***/
//{{{
focusTarget : function(ev){
if ( twve.tiddler.wrap_sel === null )
twve.tiddler.wrap_sel =
twve.tiddler.wrapperSelectors();
return twve.node.closest(
ev.target,twve.tiddler.wrap_sel.include
) || ev.target;
//return (config.options.chktwveExtraLocateChar &&
// ! twve.isMobile())
// ? (twve.node.closest(
// ev.target,twve.tiddler.wrapperSelectors()
// ) || ev.target)
// : ev.target;
},
//}}}
/***
!!!! twve.tiddler.prepareFocusBorders
***/
//{{{
prePrepareFocusBordersExtra : twve.tiddler.prepareFocusBorders,
prepareFocusBorders : function(eb){
var eblen = eb.length;
var brdlen = twve.tiddler.border_top.length;
var parent = twve.tiddler.border_top[0].parentNode;
if ( eblen < 2 )
twve.tiddler.prePrepareFocusBordersExtra.apply(
this,arguments
);
else {
var shift = twve.node.positionShift();
eb.top = eb[0].top + shift.top;
eb.right = eb[0].right + shift.left;
eb.bottom = eb[0].bottom + shift.top;
eb.left = eb[0].left + shift.left;
eb.height = eb[0].height;
eb.width = eb[0].width;
twve.tiddler.prePrepareFocusBordersExtra.call(this,eb);
// Prepare borders for the inner nodes.
for ( var n = 1; n < eblen; n++ ) {
// Left border of the first line.
if ( n >= brdlen ) {
twve.tiddler.border_left[n] =
twve.tiddler.createFocusBorder(parent,'left');
twve.tiddler.border_top[n] =
twve.tiddler.createFocusBorder(parent,'top');
twve.tiddler.border_bottom[n] =
twve.tiddler.createFocusBorder(parent,'bottom');
twve.tiddler.border_right[n] =
twve.tiddler.createFocusBorder(parent,'right');
}
twve.node.setPosition(
twve.tiddler.border_left[n],
eb[n].left+shift.left,
eb[n].top+shift.top
);
twve.node.setDimension(
twve.tiddler.border_left[n],null,eb[n].height
);
// Top border of the nth line.
twve.node.setPosition(
twve.tiddler.border_top[n],
eb[n].left+shift.left,
eb[n].top+shift.top
);
twve.node.setDimension(
twve.tiddler.border_top[n],eb[n].width
);
// Bottom border of the nth line.
twve.node.setPosition(
twve.tiddler.border_bottom[n],
eb[n].left+shift.left,
eb[n].bottom+shift.top
);
twve.node.setDimension(
twve.tiddler.border_bottom[n],eb[n].width
);
// Right border of the last line.
twve.node.setPosition(
twve.tiddler.border_right[n],
eb[n].right+shift.left,
eb[n].top+shift.top
);
twve.node.setDimension(
twve.tiddler.border_right[n],null,eb[n].height
);
}
}
if ( eblen < brdlen ) {
// If # of borders is larger than that of the boxes, drop
// the extra borders.
for ( var n = brdlen-1; n>=eblen; n-- ) {
parent.removeChild(twve.tiddler.border_left[n]);
parent.removeChild(twve.tiddler.border_top[n]);
parent.removeChild(twve.tiddler.border_bottom[n]);
parent.removeChild(twve.tiddler.border_right[n]);
}
brdlen -= eblen;
twve.tiddler.border_left.splice(eblen,brdlen);
twve.tiddler.border_top.splice(eblen,brdlen);
twve.tiddler.border_bottom.splice(eblen,brdlen);
twve.tiddler.border_right.splice(eblen,brdlen);
}
return eblen;
},
//}}}
/***
!!!! twve.tiddler.editBoxShown
***/
//{{{
editBoxShown : function(ta){
return ta.offsetWidth > 4;
},
//}}}
/***
!!!! twve.tiddler.previewEditBox
***/
//{{{
prePreviewEditBox : twve.tiddler.previewEditBox,
previewEditBox : function (ta) {
if ( ! twve.tiddler.editBoxShown(ta) ) {
twve.node.hide(twve.tiddler.getPreviewer());
return null;
}
return twve.tiddler.prePreviewEditBox.apply(this,arguments);
},
//}}}
/***
!!!! twve.tiddler.nodeWiki
***/
//{{{
nodeWiki : function (node,txt,start,toskip,dir){
// Returns an object containing the index (within txt) of the
// first character (starting from start), the matched string,
// and the actual length of the opening and closing tags
// corresponding to node.
// In some cases of leveled elements, such as lists, headings
// or blockquotes, the matched string could be just part of
// the tag string, therefore the actual length is needed to
// work with them correctly.
// {
// start: {
// ndx: index of the opening tag,
// matched: matched tag string
// },
// end: {
// ndx: index of the closing tag,
// matched: matched tag string
// }
// }
var nodewiki = twve.object.create();
nodewiki.start = twve.position.create(start,'');
nodewiki.end = twve.position.create(start,'');
// A positive dir means to search forward, while a negative
// dir means backward.
if(typeof dir !== 'number') dir = 1;
if ( node.nodeType == 3 ) {
// A text node, include the beginning tilde (~) if any.
if ( dir < 0 ) {
// If there is \n at start then it must be the
// beginning the next block element. Skip it.
//if(txt.charAt(nodewiki.end.ndx)=='\n')
// nodewiki.end.ndx--;
// Set the open tag position to the beginning of text
// in this text node.
nodewiki.start.ndx =
nodewiki.end.ndx-node.nodeValue.length;
// If there is a leading tilde, which will be removed
// upon rendering, add it back.
if(txt.charAt(nodewiki.start.ndx-1) == '~'){
nodewiki.start.matched = '~';
nodewiki.start.ndx--;
}
} else {
// If there is \n at start then it must be the end
// of the previous block element. Skip it.
if(txt.charAt(nodewiki.start.ndx)=='\n')
nodewiki.start.ndx++;
// If there is a leading tilde, which will be removed
// upon rendering, add it back.
nodewiki.start.matched =
txt.charAt(nodewiki.start.ndx) == '~' ? '~' : '';
// Set the close tag position to the end of text in this
// text node.
nodewiki.end.ndx =
nodewiki.start.ndx + nodewiki.start.matched.length +
node.nodeValue.length;
}
} else {
// Not a text node. Look for its opening tag (should be
// right at txt[nodewiki.start.ndx]).
var twelem=twve.tiddler.createEditable(node,txt,start,dir);
if ( twelem )
return twelem;
if ( node.nodeName == 'BR' ) {
return twve.BR.create(node,txt,start,dir);
}
if(txt.charAt(nodewiki.start.ndx)=='\n')
// There is \n but no BR.
if ( ! (dir < 0) ){
// Going forward, this \n must be the end of the
// previous block element. Skip it.
nodewiki.start.ndx++;
}
// Find the tags of the node.
var tags = null, ch;
if ( node.nodeName == 'SUP' ) {
if ( dir < 0 ) {
ch = txt.charAt(nodewiki.end.ndx-1);
if ( ch == '^' ) {
// '^^', superscript
nodewiki.start.ndx = txt.lastIndexOf(
'^^',nodewiki.end.ndx-2
);
return nodewiki;
}
} else {
ch = txt.charAt(nodewiki.start.ndx);
if ( ch == '^' ) {
// '^^', superscript
nodewiki.end.ndx = txt.indexOf(
'^^',nodewiki.end.ndx+2
)+2;
return nodewiki;
}
}
} else if ( node.nodeName == 'SUB' ) {
if ( dir < 0 ) {
ch = txt.charAt(nodewiki.end.ndx-1);
if ( ch == '~' ) {
// '^^', superscript
nodewiki.start.ndx = txt.lastIndexOf(
'~~',nodewiki.end.ndx-2
);
return nodewiki;
}
} else {
ch = txt.charAt(nodewiki.start.ndx);
if ( ch == '~' ) {
// '~~', subscript
nodewiki.end.ndx = txt.indexOf(
'~~',nodewiki.end.ndx+2
)+2;
return nodewiki;
}
}
} else if ( node.nodeName == 'SPAN' ) {
// A span, could be one of the followings:
// 1. HTML entity
// 2. -- (long dash)
// 3. ...
if ( dir < 0 ){
ch = txt.charAt(nodewiki.end.ndx-1);
if ( ch == ';' ) {
// HTML entity
nodewiki.start.ndx = txt.lastIndexOf(
'&',nodewiki.end.ndx-1
);
return nodewiki;
} else if ( ch == '-' ) {
// '--', long dash
nodewiki.start.ndx = nodewiki.end.ndx-2;
return nodewiki;
}
} else {
ch = txt.charAt(nodewiki.start.ndx);
if ( ch == '&' ) {
// HTML entity
nodewiki.end.ndx = txt.indexOf(
';',nodewiki.start.ndx+1
) + 1;
return nodewiki;
} else if ( ch == '-' ) {
// '--', long dash
nodewiki.end.ndx = nodewiki.start.ndx+2;
return nodewiki;
}
}
// Otherwise try itself or its parent
tags = twve.tiddler.markupTags(node,'parent');
} else if ( node.nodeName == 'A' ) {
if(twve.sliderPanel.isButton(node)){
// A sliderPanel button
nodewiki.tags = twve.sliderPanel.markupTags();
return nodewiki;
}
// Regular links
tags = twve.link.markupTags();
if ( dir < 0 ) {
// Going backward
if (
txt.substring(
nodewiki.end.ndx-tags.close.length,
nodewiki.end.ndx
) == tags.close
) {
// A TiddlyWiki link
nodewiki.end.matched = tags.close;
nodewiki.start.ndx = nodewiki.end.ndx -
nodewiki.end.matched.length;
nodewiki.start = twve.text.lastIndexOf(
txt, tags.open, nodewiki.start
);
nodewiki.tags = tags;
} else {
// A URL link.
nodewiki.start.ndx = nodewiki.end.ndx -
node.textContent.length;
}
} else {
// Going forward
if (
txt.substring(
nodewiki.start.ndx,
nodewiki.start.ndx+tags.open.length
) == tags.open
) {
// A TiddlyWiki link
nodewiki.start.matched = tags.open;
nodewiki.end.ndx = nodewiki.start.ndx +
nodewiki.start.matched.length;
nodewiki.end = twve.text.indexOf(
txt, tags.close, nodewiki.end
);
nodewiki.end.ndx += nodewiki.end.matched.length;
nodewiki.tags = tags;
} else {
// A URL link.
nodewiki.end.ndx = nodewiki.start.ndx +
node.textContent.length;
}
}
return nodewiki;
} else {
// Try the node itself or its parent.
tags = twve.tiddler.markupTags(node,'parent');
if ( ! tags && node.nodeName == 'DIV' ) {
// For non-editable DIV's try its firstChild.
tags = twve.tiddler.markupTags(
node.firstChild,'parent'
);
}
}
if ( tags ) {
// There is a pair of tags corresponding to the
// current node.
nodewiki.tags = tags;
if ( typeof toskip != 'number' ) toskip = 0;
do {
// Find the starting position and the matched string.
nodewiki.start = dir < 0
? tags.lastOpenTag(txt,nodewiki.start)
: tags.nextOpenTag(txt,nodewiki.start);
if ( nodewiki.start.ndx == -1 )
return nodewiki;
} while ( --toskip > 0 );
// Now look for the closing tag.
// Find the exact close tag. We do so because there are
// cases where multiple opening/closing pairs
// correspond to the same type of elements. In such
// cases the tags.close is an array of strings
// instead of a single string, so we need to find the
// exact closing tag.
tags = tags.exactCloseTag(nodewiki.start.matched);
// Find the starting position of the closing tag.
nodewiki.end.ndx =
nodewiki.start.ndx+nodewiki.start.matched.length;
// The following assignment is needed in the matchedCloseTag()
// of some elements.
nodewiki.end.matched = nodewiki.start.matched;
// Look for the matched close tag.
nodewiki.end = tags.matchedCloseTag(
txt, nodewiki.end
);
if ( nodewiki.end.ndx == -1 ) {
// Close tag not found, should be the outer-most
// element. Set the index and matched to that of the
// opening tag.
nodewiki.end.ndx = nodewiki.start.ndx;
nodewiki.end.matched = nodewiki.start.matched;
} else
nodewiki.end.ndx += nodewiki.end.matched.length;
} else {
// Unrecognized node
if ( node.nextSibling ) {
// If there is a next node, look for its starting
// position.
var next_pos = twve.tiddler.nodeWiki(
node.nextSibling, txt, nodewiki.end.ndx
);
nodewiki.end.ndx = next_pos.start.ndx;
} else {
// This node is the last node in its parent.
nodewiki.end.ndx = txt.length;
}
}
}
return nodewiki;
},
//}}}
/***
!!!! twve.tiddler.wikiTextSelectionOffset
***/
//{{{
wikiTextSelectionOffset : function(
sel,nodes,nodeNdx,offset,txt,which
){
// Find the starting and/or ending selection offset within a
// wiki text txt.
// Arguments:
// sel: selection information obtained from window.getSelection(),
// nodes: array of nodes defined by the wiki text txt,
// nodeNdx: indexes of the starting and ending nodes of the
// selection, obtained from twve.editable.nodeIndex(),
// offset: starting and ending offset indexes (to be found),
// txt: the wiki text that defines nodes,
// which: start, end, or omitted.
var ch = which ? which.toLowerCase().charAt(0) : '';
// Whether or not to look for the starting index of offset.
var _start = ch != 'e';
// Whether or not to look for the ending index of offset.
var _end = ch != 's';
// Index of the starting node (within the array nodes).
var ndx_s = typeof nodeNdx.start === 'number'
? nodeNdx.start
: nodeNdx.start.outer;
// Index of the ending node (within the array nodes).
var ndx_e = typeof nodeNdx.end === 'number'
? nodeNdx.end
: nodeNdx.end.outer;
// Obtain the wikitext of each node in the array nodes.
var nodeWiki = editable.editNode.nodeWiki;
if ( _start ) {
// We are searching for the starting offset, which
// is within the wiki text of nodes[ndx_s].
if ( ndx_s === nodeNdx.start ) {
// The nodeNdx.start is a number, meaning nodes[ndx_s]
// is the sel.anchoNode.
offset.start = nodeWiki[ndx_s].start.ndx +
nodeWiki[ndx_s].start.matched.length +
sel.anchorOffset;
} else {
// The nodeNdx.start is NOT a number, meaning the
// sel.anchorNode is some child node of nodes[ndx_s].
offset.start = nodewiki.start.ndx +
nodewiki.start.mtched.length;
twve.tiddler.wikiTextSelectionOffset(
sel,nodes[ndx_s].childNodes,
{
start: nodeNdx.start.inner,
end: nodeNdx.start.inner
},
offset,txt,'start'
);
}
}
if ( _end ) {
// We are searching for the ending offset, which
// is within the wiki text of nodes[ndx_e].
if ( sel.focusNode == sel.anchorNode &&
sel.focusOffset == sel.anchorOffset ) {
// If no selection made.
offset.end = offset.start;
} else if ( ndx_e === nodeNdx.end ) {
// The nodeNdx.end is a number, meaning nodes[ndx_e]
// is the sel.focusNode.
offset.end = nodeWiki[ndx_e].start.ndx +
nodeWiki[ndx_e].start.matched.length +
sel.focusOffset;
} else {
// The nodeNdx.end is NOT a number, meaning the
// sel.focusNode is some child node of nodes[ndx_e].
offset.end = nodewiki.start.ndx +
nodewiki.start.matched.length;
twve.tiddler.wikiTextSelectionOffset(
sel,nodes[ndx_e].childNodes,
{
start: nodeNdx.end.inner,
end: nodeNdx.end.inner
},
offset,txt,'end'
);
}
}
return offset;
}
});
//}}}
/***
!!! twve.node extra features
***/
//{{{
merge(twve.node,{
//}}}
/***
!!!! twve.node.box
***/
//{{{
preBoxExtra : twve.node.box,
box : function(node,outerw,outerh){
//if (twve.node.matches(
// node,twve.heading.twveSelector().include
//)) return null;
if ( twve.node.matches(node,twve.heading.getSpanSelector()) )
node = node.parentNode;
var box = twve.node.preBoxExtra.call(this,node,outerw,outerh);
if ( ! box ) return null;
//}}}
/***
!!!!! box.containsXShifted
***/
//{{{
box.containsXShifted = function(x,rect,shift){
if ( ! shift ) shift = twve.node.positionShift();
return (x >= rect.left+shift.left &&
x <= rect.right+shift.left);
};
//}}}
/***
!!!!! box.containsX
***/
//{{{
var preContainsX = box.containsX;
box.containsX = function(x,which){
if ( which === undefined )
return preContainsX.apply(this,arguments);
if ( typeof which == 'number' )
return which >= 0 && which < box.length
? box.containsXShifted(x,box[which])
: false;
if ( which.toLowerCase().substring(0,5)=='inner' ) {
// Goes through all the rectangles, returns the
// rectangle that contains x or null otherwise.
var shift = twve.node.positionShift();
for (var n = 0; n < box.length; n++ )
if ( box.containsXShifted(x,box[n],shift) )
//return box.cloneN(n,twve.node.positionShift());
return box[n];
}
return false;
};
//}}}
/***
!!!!! box.containsYShifted
***/
//{{{
box.containsYShifted = function(y,rect,shift){
if ( ! shift ) shift = twve.node.positionShift();
return (y >= rect.top+shift.top &&
y <= rect.bottom+shift.top);
};
//}}}
/***
!!!!! box.containsY
***/
//{{{
var preContainsY = box.containsY;
box.containsY = function(y,which){
if ( which === undefined )
return preContainsY.apply(this,arguments);
if ( typeof which == 'number' )
return which >= 0 && which < box.length
? box.containsYShifted(y,box[which])
: null;
if ( which.toLowerCase().substring(0,5)=='inner' ) {
// Goes through all the rectangles, returns the
// rectangle that contains y or null otherwise.
var shift = twve.node.positionShift();
for (var n = 0; n < box.length; n++ )
if ( box.containsYShifted(y,box[n],shift) )
//return box.cloneN(n,twve.node.positionShift());
return box[n];
}
return false;
};
//}}}
/***
!!!!! box.containsShifted
***/
//{{{
box.containsShifted = function(pos,rect,shift){
if ( ! shift ) shift = twve.node.positionShift();
if ( pos.left )
return box.containsXShifted(pos.left,rect,shift) &&
box.containsXShifted(pos.right,rect,shift) &&
box.containsYShifted(pos.top,rect,shift) &&
box.containsYShifted(pos.bottom,rect,shift);
else
return box.containsXShifted(pos.x,rect,shift) &&
box.containsYShifted(pos.y,rect,shift);
};
//}}}
/***
!!!!! box.contains
***/
//{{{
var preContains = box.contains;
box.contains = function(pos,which){
if ( which === undefined )
return preContains.apply(this,arguments);
if ( typeof which == 'number' )
return which >= 0 && which < box.length
? box.containsShifted(pos,box[which])
: false;
if ( which.toLowerCase().substring(0,5)=='inner' ) {
// Goes through all the rectangles, returns the
// rectangle that contains pos or null otherwise.
var shift = twve.node.positionShift();
for (var n = 0; n < box.length; n++ )
if ( box.containsShifted(pos,box[n],shift) )
//return box.cloneN(n,twve.node.positionShift());
return box[n];
}
return false;
};
//}}}
/***
!!!!! box.toString
***/
//{{{
box.toString = function(){
return '(' +
Math.round(box.left) + ',' +
Math.round(box.top) + ')-(' +
Math.round(box.right) + ',' +
Math.round(box.bottom) + ') (w,h)=(' +
Math.round(box.width) + ',' +
Math.round(box.height) + ')';
};
//}}}
/***
!!!!! End of box.
***/
//{{{
if ( box.height == 1 ) {
box.top--;
box.bottom++;
box.height += 2;
}
return box;
}
});
//}}}
/***
!!! twve.editable extra features
***/
//{{{
merge(twve.editable,{
//}}}
/***
!!!! twve.editable.nodeIndex
***/
//{{{
nodeIndex : function (nodes,node){
// Find the index of a node within nodes (an array of DOM nodes
// or a NodeList) contained in some element.
// Returns the index number if found, -1 otherwise.
if ( ! node ) return -1;
for ( var n=0,len=nodes.length; n<len; n++ ){
var c = nodes[n];
if (node.nodeType == c.nodeType) {
// Same type, compare them.
if ( node == c ) {
// Found it. Return the index of this node.
return n;
}
}
}
// Otherwise we check if we look further into the child nodes
for ( var n=0,len=nodes.length; n<len; n++ ){
var c = nodes[n];
if (c.nodeType != 3) {
// This c is not a text node but an element,
// see if it contains the node we are looking
// for.
var ndx2 = twve.editable.nodeIndex(
c.childNodes,node
);
if ( ndx2 != -1 ) {
// Yes, it contains the node. Set the index
// number of this element and stop the loop.
ndx = twve.object.create();
ndx.outer = n;
ndx.inner = ndx2;
return ndx;
}
}
}
return -1;
},
//}}}
/***
!!!! twve.editable.nodeFromIndex
***/
//{{{
nodeFromIndex : function (nodes,ndx){
// Find the corresponding node, in an array of nodes, that is
// specified by the index ndx. The ndx can be either
// 1. a number, meaning the index of the expected node itself
// within the array nodes, or
// 2. an object of {
// outer: index number of one of the parents,
// inner: index number/object in the child nodes of that
// parent
// }
return ndx.outer
// The ndx is an object
? twve.editable.nodeFromIndex(
nodes[ndx.outer].childNodes,ndx.inner
)
// The ndx is a number
: nodes[ndx];
},
//}}}
/***
!!!! twve.editable.create
***/
//{{{
preCreateExtra : twve.editable.create,
create : function(){
var editable=twve.editable.preCreateExtra.apply(this,arguments);
//}}}
/***
!!!!! editable.lineHeight
***/
//{{{
editable.lineHeight = function(css){
return parseInt(
editable.dom.style.lineHeight ||
window.getComputedStyle(editable.dom)
.getPropertyValue('line-height')
);
};
//}}}
/***
!!!!! editable.setFocusOffset
***/
//{{{
editable.setFocusOffset = function(ta,offset,shift,next){
if ( typeof offset === 'number' )
editable.selection.focusOffset = offset;
else if ( typeof shift === 'number' )
editable.selection.focusOffset += shift;
if ( ! next ) {
next = (editable.editNode || editable.focusNode).dom;
if ( twve.object.isCollection(next) )
next = twve.editable.nodeFromIndex(
next,editable.selection.nodeNdx.end
);
}
var len = next.nodeValue.length;
var box;
if ( editable.selection.focusOffset <= 0 ) {
// We are setting offset at the beginning of next.
editable.selection.focusOffset = 0;
box = twve.node.box(next);
twve.node.setPosition(ta,box.left-1,box.top);
} else if ( editable.selection.focusOffset >= len ) {
// We are setting offset at the end of next.
editable.selection.focusOffset = len;
if ( next.nextSibling ) {
// If there is an even next sibling, take it as the
// new next, and put the edit box at its left end.
next = next.nextSibling;
box = twve.node.box(next);
twve.node.setPosition(ta,box.left-1,box.top);
} else {
// If there is no more next sibling, put the edit box
// at the right end of the current next.
box = twve.node.box(next);
twve.node.setPosition(ta,box.right+1,box.top);
}
} else {
// We are setting offset in the middle of the current next,
// split it to obtain the new next and put the edit box
// at the left end of the new next.
next = next.splitText(editable.selection.focusOffset);
box = twve.node.box(next);
twve.node.setPosition(ta,box.left-1,box.top);
}
editable.selection.nextNode = next;
};
//}}}
/***
!!!!! editable.focusEditBox
***/
//{{{
var preFocusEditBox = editable.focusEditBox;
editable.focusEditBox = function(ta,txt,ev,elem){
if ( txt && config.options.chktwveExtraLocateChar ) {
// Find the index of the char that the mouse just
// clicked at.
var skiplen = ! editable.start.matched
? 0
: editable.start.matched.length -
(editable.start.matched.charAt(0)=='\n'?1:0);
skiplen += twve.text.skipStyleText(txt,skiplen);
// Find selection information.
var sel = getSelection();
if ( ! elem ) elem = editable.getElement();
var e0 = elem.length ? elem[0] : elem;
editable.selection = twve.object.create();
editable.selection.end = editable.selection.start = 0;
editable.selection.nodeNdx = twve.object.create();
var nodes = null;
// Find the starting (anchor) and ending (focus) node
// indexes.
if ( editable.editNode ) {
// We are focusing on some of the child nodes.
nodes = editable.editNode.dom;
if ( nodes.nodeType ) nodes = [nodes];
editable.selection.nodeNdx.end =
twve.editable.nodeIndex(
nodes,
(sel.focusNode == e0
? e0.childNodes[sel.focusOffset]
: sel.focusNode)
);
if ( editable.selection.nodeNdx.end < 0 )
editable.selection.nodeNdx.end = nodes.length-1;
editable.selection.nodeNdx.start =
twve.editable.nodeIndex(
nodes,
(sel.anchorNode == e0
? e0.childNodes[sel.anchorOffset]
: sel.anchorNode)
);
if ( editable.selection.nodeNdx.start < 0 )
editable.selection.nodeNdx.start = 0;
} else {
// We are focusing on the whole editable object.
nodes = e0.childNodes;
editable.selection.nodeNdx.end = sel.focusNode == e0
? sel.focusOffset
: twve.editable.nodeIndex(nodes,sel.focusNode);
if ( editable.selection.nodeNdx.end < 0 )
editable.selection.nodeNdx.end = nodes.length-1;
if ( sel.anchorNode == sel.focusNode ) {
editable.selection.nodeNdx.start =
editable.selection.nodeNdx.end;
} else {
editable.selection.nodeNdx.start =
sel.anchorNode == e0
? sel.anchorOffset
: twve.editable.nodeIndex(
nodes,sel.anchorNode
);
if ( editable.selection.nodeNdx.start < 0 )
editable.selection.nodeNdx.start = 0;
}
}
// Get the offset.
//editable.selection.anchorNode = sel.anchorNode;
//editable.selection.anchorOffset = sel.anchorOffset;
editable.selection.focusNode = sel.focusNode;
editable.selection.focusOffset = sel.focusOffset;
twve.tiddler.wikiTextSelectionOffset(
sel,nodes,
editable.selection.nodeNdx,
editable.selection,
txt.substring(skiplen)
);
if ( skiplen ) {
editable.selection.start += skiplen;
editable.selection.end += skiplen;
}
// Set caret position to (0, 0) in the editbox.
twve.tiddler.editBoxCaret(ta,0,0);
// Store the current caret position.
twve.tiddler.initCaretPos(editable.selection.start);
// Clear editbox text
editable.typed = editable.deleted = ta.value = '';
editable.inserted = [];
// Adjust editbox and put it at the right place.
editable.selection.nodeNdx.e0 = e0;
if ( editable.selection.focusNode != e0 ) {
ta.style.borderWidth = '0px';
ta.style.resize = 'none';
ta.style.width = '1px';
ta.style.height = window.getComputedStyle(e0)
.getPropertyValue('line-height');
editable.setFocusOffset(ta);
}
} else {
editable.selection = null;
preFocusEditBox.apply(this,arguments);
}
return ta;
};
//}}}
/***
!!!!! editable.boxCopy
***/
//{{{
editable.boxCopy = function(dest,src,lh,where){
var copied = false;
if ( where == 'top' ) {
if ( src.top - dest.top >= lh ) {
dest.bottom = src.top;
copied = true;
}
} else if ( where == 'bottom' ) {
if ( dest.bottom - src.bottom >= lh ) {
dest.top = src.bottom;
copied = true;
}
} else {
// Copy the src to dest
// Copy top and bottom first.
dest.top = src.top;
dest.bottom = src.bottom - src.top < lh
? dest.top + lh
: src.bottom;
if ( where == 'all' ) {
dest.width = src.width;
if ( src.left > dest.left )
dest.left = src.left;
dest.right = dest.left + dest.width;
}
// Then copy the clientRects.
//dest.splice(0,dest.length).concate(src);
copied = true;
}
dest.height = dest.bottom - dest.top;
return copied;
};
//}}}
/***
!!!!! editable.createFocusNode
***/
//{{{
editable.createFocusNode = function(node,nodewiki){
var focusNode = twve.node.create(node);
if ( nodewiki ) {
focusNode.start =
twve.position.create(nodewiki.start);
focusNode.end =
twve.position.create(nodewiki.end);
}
return focusNode;
};
//}}}
/***
!!!!! editable.setFocusNode
***/
//{{{
editable.setFocusNode = function(node,index,nodewiki,where){
if (editable.focusNode && where) {
// Add this node to the end or the beginning.
where = where.toLowerCase();
editable.focusNode.add(node,where);
//if ( ! editable.focusNode.index )
// editable.focusNode.index = [];
switch (where){
case 'end' :
if ( nodewiki ) {
editable.focusNode.end.ndx=nodewiki.end.ndx;
//if ( config.options.chktwveExtraLocateChar ) {
// In the WYSIWYG mode we also record the
// tags and positions of the current node.
editable.focusNode.nodeWiki[
editable.focusNode.nodeWiki.length
] = nodewiki;
//}
}
editable.focusNode.index[
editable.focusNode.index.length
] = index;
return editable;
case 'begin' :
if ( nodewiki ) {
editable.focusNode.start.ndx=nodewiki.start.ndx;
//if ( config.options.chktwveExtraLocateChar ) {
// In the WYSIWYG mode we also record the
// tags and positions of the current node.
editable.focusNode.nodeWiki.unshift(
nodewiki
);
//}
}
editable.focusNode.index.unshift(index);
return editable;
}
}
// Otherwise set this node as the first one.
editable.focusNode = editable.createFocusNode(node,nodewiki);
editable.focusNode.index = [index];
if ( nodewiki ) {
// Record the positions of the current node.
editable.focusNode.nodeWiki = [nodewiki];
}
return editable;
};
//}}}
/***
!!!!! editable.collectNode
***/
//{{{
editable.collectNode = function(node,index,nodewiki,eb,nb,dir){
if ( eb && nb ) {
if ( nb.left < eb.left ) {
eb.left = nb.left;
eb.width = eb.right - eb.left;
}
if ( nb.right > eb.right ) {
eb.right = nb.right;
eb.width = eb.right - eb.left;
}
if ( dir < 0 ){
if ( ! editable.focusNode ) eb.bottom = nb.bottom;
eb.top = nb.top;
} else {
if ( ! editable.focusNode ) eb.top = nb.top;
eb.bottom = nb.bottom;
}
eb.height = eb.bottom - eb.top;
}
editable.setFocusNode(
node,index,nodewiki,(dir<0?'begin':'end')
);
return editable;
};
//}}}
/***
!!!!! editable.collectThenStop
***/
//{{{
editable.collectThenStop = function(
nodes,p,nodewiki,eb,nb,mpos,lh,dir,force
){
if ( nodes[p].nodeName == 'BR' ) {
// End of paragraph, prepare to return.
if ( ! editable.focusNode ) {
// We haven't collected any nodes, check
// for hidden element.
if(p<nodes.length-1 && nodes[p+1].nodeName=='EM'){
// There is EM following BR, could be a
// hidden span.
var hidden = '@@display:none;';
var pn = txt.indexOf(
hidden,nodewiki.end.ndx
);
if ( pn > -1 ) {
nodewiki.start.ndx = pn;
nodewiki.start.matched = hidden;
pn = txt.lastIndexOf('//',pn);
if ( pn > -1 ){
nodewiki.start.matched =
txt.substring(
pn,nodewiki.start.ndx
)+hidden;
nodewiki.start.ndx = pn;
}
p++;
nb = twve.node.box(nodes[p]);
pn = txt.indexOf('\n//}}}\n',pn);
pn = txt.indexOf('\n//',pn);
if ( pn > -1 )
pn = txt.indexOf('@@',pn);
nodewiki.end.ndx = pn == -1
? txt.length
: (pn+2);
}
}
editable.collectNode(
nodes[p],p,nodewiki,eb,nb,dir
);
if(nb) eb.left = nb.left;
eb.width = eb.right - eb.left + 1;
}
return eb;
} else if ( nodewiki.tags &&
nodewiki.tags.blockElement ) {
// This is a block element, collect it if desired,
// and return.
if ( ! editable.focusNode || force )
editable.collectNode(
nodes[p],p,nodewiki,eb,nb,dir
);
return eb;
} else if ( nodewiki.tags && nodewiki.tags.is(
twve.sliderPanel.markupTags()
)) {
// A slider panel button, return its bounding box.
//if ( (nodewiki.start.ndx > 0 && txt.charAt(
// nodewiki.start.ndx-1
// ) == '\n') &&
// (nodewiki.end.ndx==txt.length ||
// txt.charAt(
// nodewiki.end.ndx+1
// ) == '\n') ) {
editable.setFocusNode(nodes[p],p,nodewiki);
return nb;
//}
// Otherwise collect and continue.
//editable.collectNode(nodes[p],p,nodewiki,eb,nb,dir);
} else {
// A node to collect.
if ( nb && nb.width == 0 &&
nodes[p].nodeType != 3 ) {
// Empty element
var css = window.getComputedStyle(nodes[p]);
var minw = twve.node.cssSize(
css.getPropertyValue('font-size')
//nodes[p].style.fontSize
) * config.options.txttwveCoreMinEditWidth;
editable.setFocusNode(nodes[p],p,nodewiki,'end');
// If the mouse pointer is within
// the default edit box width, set the
// box width to the default edit width
// and return.
if ( mpos.x > nb.left &&
mpos.x - nb.left <= minw ) {
nb.width = minw;
nb.right = nb.left + nb.width;
return nb;
}
// Otherwise set the height to enclose
// the node and leave the width unchanged.
editable.boxCopy(eb,nb,lh);
//return eb;
} else {
// Collect this node.
editable.collectNode(
nodes[p],p,nodewiki,eb,nb,dir
);
//eb.width = twve.node.width(editable.dom);
//eb.right = eb.left + eb.width;
}
}
};
//}}}
/***
!!!!! editable.skipOpenTag
***/
//{{{
editable.skipOpenTag = function(txt,start){
if ( ! editable.start.matched ) return 0;
if ( typeof start != 'number' )
start = (start && start.ndx && start.ndx > 0)
? start.ndx : 0;
else if ( start < 0 ) start = 0;
var len = editable.start.matched.length;
return len -
(editable.start.matched.charAt(0) == '\n' &&
txt.charAt(start) != '\n')
? 1 : 0;
};
//}}}
/***
!!!!! editable.collectParagraphNodes
***/
//{{{
editable.collectParagraphNodes = function(nodes,start,dir,add){
// Collect a paragraph of nodes in the collection nodes,
// starting from index start, along direction dir,
// and stops when a BR is encountered or all nodes are
// consumed.
//
// Arguments:
// nodes: a collection of nodes to search for;
// start: starting index;
// dir: direction to collect, either 'left' or 'right',
// default is 'right';
// add: true to add nodes to this editable, false or
// omitted to re-collect nodes.
//
// Returns: The editable object itself, with
// editable.focusNode hodling the collected child
// and their corresponding information (indexes,
// starting/ending positions, etc).
var txt = editable.getText('content');
var dp = (dir === 'left' || dir === 'up') ? -1 : 1;
var p = twve.position.getPosition(start);
if ( !nodes ) nodes = editable.dom.childNodes;
var plen = nodes.length;
var lh = editable.lineHeight();
var eb = twve.tiddler.focusBox() ||
twve.node.box(editable.dom);
//var eb = twve.node.box(editable.dom);
var nb = twve.node.box(nodes[p]);
var mpos = {
x: (nb?(nb.left+nb.right)/2:0),
y: (nb?(nb.top+nb.bottom)/2:0)
};
var nodewiki = twve.object.create();
if ( dp < 0 ) {
// Going left, the current beginning would be the new end.
nodewiki.end = twve.position.create(
editable.focusNode.start
);
nodewiki.start = twve.position.create(nodewiki.end);
} else {
// Going right, the current end would be the new start.
nodewiki.start = twve.position.create(
(editable.focusNode ? editable.focusNode.end : 0)
);
nodewiki.end = twve.position.create(nodewiki.start);
}
// Start collecting
if ( ! add ) editable.focusNode = null;
do {
nodewiki = twve.tiddler.nodeWiki(
nodes[p], txt,
(dp>0?nodewiki.end.ndx:nodewiki.start.ndx),
0, dp
);
//console.log('nodes['+p+']@('+nodewiki.start.ndx+','+nodewiki.end.ndx+'):'+twve.node.info(nodes[p]));
if(editable.collectThenStop(
nodes,p,nodewiki,eb,nb,mpos,lh,dp,add
)) {
break;
}
p += dp;
if ( p < 0 || p >= plen ) break;
nb = twve.node.box(nodes[p]);
} while ( true );
//if ( dp<0 && editable.focusNode &&
// editable.focusNode.index.length > 1 ) {
// editable.focusNode.dom.reverse();
// editable.focusNode.index.reverse();
//}
twve.tiddler.drawFocusBorders(eb);
return editable;
};
//}}}
/***
!!!!! editable.inCombinable
***/
//{{{
editable.inCombinable = function(next,txt,startndx,dir){
switch ( next.nodeName ) {
case 'TABLE' :
case 'PRE' :
return true;
case 'DIV' :
if ( !twve.table || twve.table.is(next) )
return true;
if ( !config.macros.highlightSyntax )
return true;
if ( txt ) {
var tags = twve.pre.markupTags();
for(var i=0,len=tags.open.length; i<len; i++){
if ( dir < 0 ) {
// Going backward, look for tags.close[i].
if ( txt.substring(
startndx-tags.close[i].length,startndx
) == tags.close[i] )
return true;
} else {
// Going forward, look for tags.open[i].
if ( txt.substring(
startndx,startndx+tags.open[i].length
) == tags.open[i] )
return true;
}
}
} else {
var tags = editable.editNode.nodeWiki[0].tags;
return tags && tags.open.length &&
tags.open[0]=='\n{{{';
}
break;
case 'BLOCKQUOTE' :
// The next node is a blockquote, return true
// if it is a block example
if ( txt ) {
var sig = '';
if ( dir < 0 ) {
// Going backward
sig = txt.substring(startndx-4,startndx);
} else {
// Going forward.
sig = txt.substring(startndx,startndx+4);
}
if ( sig == '<<<\n' || sig == '\n<<<' )
return true;
} else {
var nodewiki = editable.editNode.nodeWiki[0];
return nodewiki.blockExample &&
nodewiki.blockExample();
}
}
return false;
};
//}}}
/***
!!!!! editable.collectNeighboringBlock
***/
//{{{
editable.collectNeighboringBlock = function(txt,which){
// Collect immediately following BR and/or block element.
if ( ! editable.editNode ) return false;
var next, start, startndx, dir, p;
if ( which == 'left' ) {
next = (editable.editNode.dom.nodeType
? editable.editNode.dom
: editable.editNode.dom[0])
.previousSibling;
if ( ! next ) return false;
start = editable.editNode.start;
startndx = start.ndx;
p = editable.editNode.index[0]-1;
dir = -1;
} else {
next = (editable.editNode.dom.nodeType
? editable.editNode.dom
: editable.editNode.dom[
editable.editNode.dom.length-1
])
.nextSibling;
if ( ! next ) return false;
start = editable.editNode.end;
startndx = start.ndx;
p = editable.editNode.index[
editable.editNode.index.length-1
]+1;
dir = 1;
}
if( ! txt ) txt = editable.getText('content');
if ( editable.inCombinable(next,txt,startndx,dir) ) {
return false;
} else if ( next.nodeName == 'BR' ) {
// There is a BR,
var pnext = p + (which == 'left' ? -1 : 1);
if ( editable.inCombinable(
editable.dom.childNodes[pnext],txt,startndx,dir
) )
// and we don't wnat to collect its next node,
// return false;
return false;
// Otherwise collect this BR.
editable.collectNode(
next,p,
twve.tiddler.nodeWiki(next,txt,startndx,0,dir),
null,null,dir
);
// Move on to the next node.
p = pnext;
}
// Collect the next paragraph.
editable.collectParagraphNodes(
editable.dom.childNodes,p,which,true
);
// Check the wiki text of the first node.
var nodewiki = editable.editNode.nodeWiki[0];
if(nodewiki.tags && nodewiki.tags.blockElement &&
! twve.nodeList.contains(editable.editNode.dom,'BR')){
// The first node is a block element, meaning
// all the to-be-edited text after it will be merged
// into that block element. In such cases extra
// care is needed: we shall remove the BR
// immediately following the last node being
// edited, if there is one.
next = editable.editNode.dom[
editable.editNode.dom.length-1
].nextSibling;
if ( next.nodeName == 'BR' ) {
// There is one, collect it.
p = editable.editNode.index[
editable.editNode.index.length-1
]+1;
editable.collectNode(
next,p,
twve.tiddler.nodeWiki(
next,txt,
editable.editNode.end.ndx,
0,1
)
);
// The reason for such an extra care is that
// 1. block elements end with a \n, and
// 2. that ending \n is not rendered as a BR.
// Therefore if there is a BR immediately following
// the last node being edited, we should remove it
// to avoid extra BR.
if ( (next = next.nextSibling) ){
// There is more to follow that BR, collect the
// next paragraph to have the correct spacing.
editable.collectParagraphNodes(
editable.dom.childNodes,++p,'right',true
);
}
}
}
// Check the wiki text of the last node.
nodewiki = editable.editNode.nodeWiki[
editable.editNode.nodeWiki.length-1
];
if ( nodewiki.tags && nodewiki.tags.blockElement &&
txt.charAt(editable.editNode.end.ndx-1)=='\n' ) {
// The last node is a block element, keep its ending
// \n so the wiki text after it will be rendered
// correctly.
editable.editNode.end.ndx--;
}
return startndx != start.ndx;
},
//}}}
/***
!!!!! editable.combineEditNeighbor
***/
//{{{
editable.combineEditNeighbor = function(ta,which){
// Combine the remaining text in this paragraph
// with neighbor, then bring it up to edit.
var dom = editable.editNode.dom;
if ( ! dom ) return editable;
if ( dom.length !== undefined )
dom = which == 'left' ? dom[0] : dom[dom.length-1];
if ( !editable.inCombinable(dom) &&
editable.collectNeighboringBlock(
editable.getText('content'),which
)){
// The neighboring block is collected.
// Adjust text for update.
var txt = editable.getText();
var caret_pos = 0;
if ( which == 'left' ) {
// Combine with the left neighbor.
var ndx = txt.indexOf(ta.defaultValue);
var prev = txt.substring(ndx);
txt = txt.substring(0,ndx);
if ( txt.length > 1 &&
txt.charAt(txt.length-1)=='\n' )
txt = txt.substring(0,txt.length-1);
caret_pos = txt.length;
txt += prev;
} else {
// Combine with the right neighbor.
txt = txt.substring(ta.defaultValue.length);
if ( ! txt ) {
// The combined text is the same as before,
// no update actiion will be triggered.
// However, this could mean that either only
// one \n is collected, or nothing is collected.
// In the former case we should do something to
// trigger the update action.
dom = editable.focusNode.dom;
if ( dom[dom.length-1].nodeName == 'BR' ) {
// Yes we have just collected a BR, change
// the defaultValue to trigger update action.
ta.defaultValue += '\n';
}
} else if(txt.length > 1 && txt.charAt(0)=='\n'){
txt = txt.substring(1);
}
caret_pos = ta.value.length;
txt = ta.value + txt;
}
// Update the paragraph for visual feedback.
ta.value = txt;
twve.tiddler.updateText();
// Edit text and restore the caret position
ta = editable.editText();
twve.tiddler.editBoxCaret(ta,caret_pos);
twve.tiddler.updatePreviewerCaret(ta);
}
return editable;
};
//}}}
/***
!!!!! editable.box
***/
//{{{
editable.box = function(action,ev){
// The wrapper box of this editable.
var eb = twve.node.box(editable.dom);
// The child nodes of this editable.dom.
var nodes = editable.dom.childNodes;
// If no children, do nothing.
if ( ! nodes ) return eb;
var size = nodes.length;
// If only one child, do nothing.
if ( size < 2 ) return eb;
// The wikitext corresponding to this editable.dom.
var txt = editable.getText('content');
var mpos = twve.tiddler.mousePosition(ev);
if ( (mpos.x >= eb.left && mpos.x <= eb.left + 4) ||
(mpos.y >= eb.top && mpos.y <= eb.top + 4) ||
(mpos.x <= eb.right && mpos.x >= eb.right - 4) ||
(mpos.y <= eb.bottom && mpos.y >= eb.bottom - 4) ) {
// If mouse position is very close to one of the four
// borders of the wrapper box, return the whole wrapper
// box without any specific focusNode.
editable.focusNode = null;
//editable.prev = editable.next = null;
return eb;
}
// Now we are sure the mouse position is within the wrapper
// box, we are going to find the node and the corresponding
// wikitext to start the search.
// First, we check if there is currently a focus box. If so,
// we start from there, otherwise we start from either the
// beginning or the end of the wikitext.
var nodewiki = null;
var p = 0, dp = 1;
var ebp = twve.tiddler.focusBox();
if ( ebp ){
// If we have a focus box, figure out where we are with
// respect to this focus box, and start from the closest
// focusNode.
//if (config.options.chktwveExtraLocateChar)
// Do nothing if we are in the WYSIWYG mode.
// return ebp;
// Not in the WYSIWYG mode,
if ( (nodewiki = editable.focusNode) ){
// and we are having fucusNode, figure out where
// we are.
if ( config.options.chktwveExtraLocateChar ) {
// We are in the WYSIWYG mode.
if ( ebp.contains(mpos) ) {
// Mouse position is within the current box,
// search none.
return ebp;
}
} else if ( mpos.y < ebp.top ){
// We are NOT in the WYSIWYG mode.
// Mouse position is above the current box,
// search backward from the beginning of
// focusNode.
p = editable.focusNode.index[0]-1;
dp = -1;
} else if ( mpos.y > ebp.bottom ) {
// Mouse position is below the current box,
// search forward from the end of focusNode.
p = editable.focusNode.index[
editable.focusNode.index.length-1
]+1;
} else if (ebp.height != eb.height)
// Mouse position is within the current box,
// and the current box is not the wrapper box,
// search none.
return ebp;
// Return the whole wrapper area if there is nothing
// to search for.
if ( p < 0 || p >= size ) {
editable.focusNode = null;
return eb;
}
}
} else {
// We do not currently have a focus box, figure out
// where we are with respect to the whole wrapper area.
// If we are in the upper half of the wrapper area,
// search forward from the beginning of this editable's
// wikitext.
// If, however, we are in the lower half, search backward
// from the end of this editable's wikitext.
var yhalf = (eb.top+eb.bottom)/2;
if (mpos.y > yhalf){
// We are in the lower half.
p = size - 1;
dp = -1;
}
}
// We have deteremined which node to start, we are now to
// determine where in the wikitext to start.
var lastFocusNode = editable.focusNode;
editable.focusNode = null;
var lh = editable.lineHeight();
var plast = dp < 0 ? 0 : size-1;
if ( (p==0 && dp>0) || (p==size-1 && dp<0) ) {
//console.log('search over');
// If we are starting from the very beginning (forward),
// or the very end (backward) of the, we need to locate
// the wikitext of the first/last child node with a
// definite bounding box.
nodewiki = twve.tiddler.nodeWiki(
nodes[p],txt,
(dp<0?txt.length:editable.skipOpenTag(txt)),
0,dp
);
while ( true ) {
//if ( (ebp = nodewiki.box
// ? nodewiki.box(action,ev)
// : twve.node.box(nodes[p])) ) {
//console.log('ini p='+p+': ebp='+ebp);
// break;
//}
if ((ebp=twve.node.box(nodes[p])))
break;
p += dp;
if ( p < 0 || p >= size ) return eb;
nodewiki = twve.tiddler.nodeWiki(
nodes[p],txt,nodewiki.end.ndx,0,dp
);
}
/*
// Then we compare the mouse position with that
// bounding box.
if ( dp > 0 && mpos.y < ebp.top ) {
// We are searching forward from the begging and
// the mouse position is above the first child node,
// return a box enclosing the empty area at the top
// of the wrapper area.
if ( editable.boxCopy(eb,ebp,lh,'top') ) {
editable.prev = null;
if ( ! editable.next )
editable.next = twve.element.create();
editable.next.start.ndx = nodewiki.start.ndx;
//if ( ! editable.next )
// editable.next = twve.object.create(nodewiki);
//else
// editable.next.copyFrom(nodewiki);
}
return eb;
} else if ( dp < 0 && mpos.y > ebp.bottom ) {
// We are searching backward from the end and the
// mouse position is below the last child node,
// return a box enclosing the empty area at the
// bottom of the wrapper area.
if ( editable.boxCopy(eb,ebp,lh,'bottom') ) {
editable.next = null;
if ( ! editable.prev )
editable.prev = twve.element.create();
editable.prev.end.ndx = nodewiki.end.ndx;
}
return eb;
}
*/
} else {
// Otherwise we are starting from somewhere in the
// middle, locate the correct wikitext to start with.
//console.log('search continued');
nodewiki = twve.tiddler.nodeWiki(
nodes[p],txt,
(dp < 0
? nodewiki.start.ndx
: nodewiki.end.ndx),
0,dp
);
//if(twve.tiddler.getEditBox())console.log('Start from nodes['+p+']='+nodes[p]+' txt=['+txt.substring(nodewiki.start.ndx,nodewiki.end.ndx)+']');
//ebp = nodewiki.box
// ? nodewiki.box(action,ev)
// : twve.node.box(nodes[p]);
ebp = twve.node.box(nodes[p]);
//console.log('middle p='+p+': ebp='+ebp);
}
// No we are good to start searching.
var lastebp = null, last_nodewiki = null;
//if(nodes[p].nodeName=='HR')console.log(ebp);
//console.log('editable.wrapper_title='+editable.wrapper_title);
while ( p >= 0 && p < size ) {
//if(nodewiki.start.ndx<0||nodewiki.end.ndx<0){
//console.log('nodes['+p+'] '+nodes[p].nodeName+'('+nodes[p].classList+')id=('+(nodes[p].getAttribute?nodes[p].getAttribute('id'):'')+')=['+twve.tcalc.nodeText(nodes[p])+'], wiki at ('+nodewiki.start.ndx+','+nodewiki.end.ndx+')=['+txt.substring(nodewiki.start.ndx,nodewiki.start.ndx+10)+' ... '+txt.substring(nodewiki.end.ndx-10,nodewiki.end.ndx)+']');
//if(last_nodewiki)console.log('last node at ('+last_nodewiki.start.ndx+','+last_nodewiki.end.ndx+')=['+txt.substring(last_nodewiki.start.ndx,last_nodewiki.start.ndx+10)+' ... '+txt.substring(last_nodewiki.end.ndx-10,last_nodewiki.end.ndx)+']');
//}
if ( nodes[p].nodeName == 'BR' ) {
// We are NOT in the WYSIWYG mode.
// If this node is a BR,
if ( editable.focusNode ) {
// and we currently have focusNode, we might
// want to stop collecting nodes, or to restart
// collecting nodes.
if ( dp < 0 ){
// If we are going backward.
if((lastebp &&
(lastebp.bottom < mpos.y ||
lastebp.top < mpos.y)) ||
(ebp.bottom+ebp.height?0:lh)<mpos.y){
// and we've passed the mouse y position,
// meaning this BR is the beginning of a
// paragraph, stop collecting nodes and
// return.
return eb;
}
} else {
// If we are going forward,
if ( (lastebp &&
(lastebp.top > mpos.y ||
lastebp.bottom > mpos.y)) ||
ebp.top > mpos.y ) {
// and we've passed the mouse y position,
// meaning this BR is the end of a
// paragraph, stop collecting nodes and
// return.
return eb;
}
}
// Otherwise this BR is just the beggning or
// the end of a paragraph that we are not
// collecting. Clear the focusNode and restart
// collecting nodes.
lastFocusNode = editable.focusNode;
editable.focusNode = null;
} else {
// Otherwise we do not currently have focusNode,
// collect this BR and return.
//editable.collectNode(
// nodes[p],p,nodewiki,eb,ebp,dp
//);
}
} else if ( nodes[p].nodeName == 'HR' ) {
if (ebp.containsY(mpos.y)){
editable.collectNode(
nodes[p],p,nodewiki,eb,ebp,dp
);
return eb;
}
} else if ( ebp && ebp.containsY(mpos.y) ) {
// The mouse vertical position is within the
// vertical range of this node.
if(config.macros.foldHeadings &&
twve.node.matches(
nodes[p],twve.heading.getFoldedSelector()
)
){
// If the current node is the heading of a folded
// section,
if (twve.node.matches(
ev.target,twve.heading.getSpanSelector()
)) {
// and the mouse is over its right corner,
// collect this heading and return.
editable.collectNode(
nodes[p],p,nodewiki,eb,ebp,dp
);
return eb;
}
// Otherwise do nothing.
return null;
} else {
// This node is not a folded section, collect
// it and continue.
if ((last_nodewiki && last_nodewiki.tags &&
last_nodewiki.tags.blockElement) ||
(nodewiki.tags &&
nodewiki.tags.blockElement)) {
// If either last node or this node is
// a block element, restart the collecting.
editable.focusNode = null;
}
editable.collectNode(
nodes[p],p,nodewiki,eb,ebp,dp
);
}
// If last node, return whatever we have collected.
if ( p == plast ) return eb;
// Otherwise continue to the next node.
} else if ( ebp ) {
// The mouse y position is not within the height of
// this node, we might want to
// 1. stop collecting and return,
// 2. restart collecting nodes, or
// 3. collect this node and continue,
// depending on the following conditions:
if ( (dp>0 && ebp.top>mpos.y &&
ebp.top>eb.bottom) ||
(dp<0 && ebp.bottom<mpos.y &&
ebp.bottom<eb.top) ) {
// We have already passed the mouse y position,
// should stop and return.
if ( ! editable.focusNode )
// If we have collected nothing, return
// with the previous collection of ndoes.
editable.focusNode = lastFocusNode;
return eb;
} else {
// We have not yet passed the mouse y position,
// meaning we are not yet to the nodes to collect.
if (last_nodewiki && last_nodewiki.tags &&
last_nodewiki.tags.blockElement) {
// If last node is a block element,
// restart the collecting.
editable.focusNode = null;
}
// Collect this node and continue.
editable.collectNode(
nodes[p],p,nodewiki,eb,ebp,dp
);
}
}
// Continue to the next node.
var mp = dp < 0 ? p-2 : p;
if ( p>=0 && twve.node.matches(
nodes[mp],'span.MathJax_Preview'
)){
if (editable.focusNode) {
editable.collectNode(
nodes[(p+=dp)],p,null,null,null,dp
);
editable.collectNode(
nodes[(p+=dp)],p,null,null,null,dp
);
} else
p += (dp+dp);
}
lastebp = ebp; last_nodewiki = nodewiki;
while ( true ) {
p += dp;
if ( p < 0 || p == size ) break;
if ( dp > 0 &&
(ebp = twve.node.box(nodes[p])) == null &&
// ! twve.node.isVisible(nodes[p]) &&
twve.node.matches(
nodes[p],
twve.foldedSection.getSelector()
)
) {
// Next node is a folded section, skip its
// content by looking for the next heading.
if ( (p+1 < size) &&
twve.node.matches(
nodes[p+1],
twve.heading.getSelector()
)
) {
//twve.foldedSection.hijackInactiveTags();
nodewiki = twve.tiddler.nodeWiki(
nodes[p+1], txt,
nodewiki.end.ndx,
nodes[p].querySelectorAll(
twve.heading.getSelector()
).length, dp
);
//ebp = nodewiki.box
// ? nodewiki.box(action,ev)
// : twve.node.box(nodes[p]);
ebp = twve.node.box(nodes[p]);
nodewiki.end.ndx = nodewiki.start.ndx;
nodewiki.end.matched = '';
//twve.foldedSection.releaseInactiveTags();
} else {
var macro = twve.foldedSection.macro();
var foldedEnd = txt.indexOf(macro);
if ( foldedEnd > -1 ) {
nodewiki.end.ndx = foldedEnd;
nodewiki.end.matched = macro;
} else {
nodewiki.end.ndx = txt.length;
nodewiki.end.matched = '';
}
}
} else {
nodewiki = twve.tiddler.nodeWiki(
nodes[p],txt,
(dp < 0
? nodewiki.start.ndx
: nodewiki.end.ndx),
0,dp
);
//ebp = nodewiki.box
// ? nodewiki.box(action,ev)
// : twve.node.box(nodes[p]);
ebp = twve.node.box(nodes[p]);
//console.log('p='+p+': ebp='+ebp)
break;
}
}
}
// Nothing collected, just return.
//editable.prev = editable.next = null;
return eb;
};
//}}}
/***
!!!!! editable.scrollIntoView
***/
//{{{
editable.scrollIntoView = function(scroller){
// Scrolls the element, within scroller, into view if
// currently not. If scroller is not given, the system
// window would be used.
var wrapper = editable.isWrapper();
var ebox = wrapper
? twve.tiddler.focusBox()
: editable.box('edit');
var scr_box;
if ( ! scroller ) {
scroller = window;
scr_box = twve.node.box(scroller);
if ( ! wrapper ){
var d = scroller.pageYOffset;
scr_box.top += d;
scr_box.bottom += d;
d = scroll.pageXOffset;
scr_box.left += d;
scr_box.width += d;
}
} else {
scr_box = twve.node.box(scroller);
}
var dx = 0, dy = 0;
if(ebox.right > scr_box.right){
dx = Math.round((ebox.right-scr_box.right)*1.1);
} else if ( ebox.left < scr_box.left ) {
dx = Math.round((ebox.left-scr_box.left)*1.1);
}
if(ebox.bottom > scr_box.bottom){
dy = (ebox.bottom-scr_box.bottom);
} else if ( ebox.top < scr_box.top ) {
dy = (ebox.top-scr_box.top);
}
if ( dx != 0 || dy != 0 )
if ( scroller == window ){
window.scrollTo(pageXOffset+dx,pageYOffset+dy);
} else {
scroller.scrollLeft += dx;
scroller.scrollTop += dy;
}
return editable;
};
//}}}
/***
!!!!! editable.mousemove
***/
//{{{
editable.mousemove = function(ev){
if ( ! editable.is(twve.tiddler.cur_editable) )
twve.tiddler.focus(editable,'on',ev);
};
//}}}
/***
!!!!! End of editable extra
***/
//{{{
return editable;
}
});
//}}}
/***
!!! twve.element extra features
***/
//{{{
merge(twve.element,{
//}}}
/***
!!!! twve.element.create
***/
//{{{
preCreateExtra : twve.element.create,
create : function(){
// create the element
var twelem = twve.element.preCreateExtra.apply(this,arguments);
//}}}
/***
!!!!! twelem.getNeighbor
***/
//{{{
twelem.getNeighbor = function (which,delta) {
// Retrieve the neighboring element separated by delta, in
// the direction specified by which.
switch ( which ) {
case 'left' :
case 'up' :
delta = delta < 0 ? delta : -delta;
break;
case 'right' :
case 'down' :
delta = delta > 0 ? delta : -delta;
break;
}
// Find that neighbor.
twelem.rIndex += delta;
var dIndex = twelem.dIndex;
twelem.dIndex = -1;
var twnext = twve.wrapper.renderedElement(twelem,null,true);
twelem.rIndex -= delta;
twelem.dIndex = dIndex;
return twnext;
};
//}}}
/***
!!!!! twelem.editNeighbor
***/
//{{{
twelem.editNeighbor = function (which,delta) {
// Edit the neighboring element of twelem. The direction is
// specified in the first argument which: 'left', 'right',
// 'up', or 'down'.
// The second rgument, delta, specifies how far away
// the neighbor we are looking for. Default value is 1,
// meaning the immediate neighbor in the specified direction.
// Save changes to this one.
twve.tiddler.updateText();
twve.tiddler.focus();
// Find the neighbor.
if ( delta === undefined ) delta = 1;
var twnext = twelem.getNeighbor(which,delta);
// Then edit the next one.
twnext.editText();
twnext.scrollIntoView();
return twnext;
};
//}}}
/***
!!!!! twelem.switchWith
***/
//{{{
twelem.switchWith = function(twnext){
// Switch the wiki text and DOM content of this twelem
// with twnext.
var twearlier, twlater;
// Determine which is earlier.
if ( twelem.start.ndx < twnext.start.ndx ) {
twearlier = twelem;
twlater = twnext;
} else {
twearlier = twnext;
twlater = twelem;
}
// Switch their text.
var txt = {
earlier : twearlier.getText(),
later : twlater.getText()
};
var save = config.options.chktwveCoreManualSave;
var upload = config.options.chktwveCoreManualUpload;
config.options.chktwveCoreManualSave = true;
config.options.chktwveCoreManualUpload = true;
twlater.setText(txt.earlier);
config.options.chktwveCoreManualSave = save;
config.options.chktwveCoreManualUpload = upload;
twearlier.setText(txt.later);
// And adjust position of the later
var dlen = txt.later.length-txt.earlier.length;
twlater.start.ndx += dlen;
twlater.end.ndx += dlen;
// Then switch their DOM content.
var html_next = twnext.dom.innerHTML;
twnext.dom.innerHTML = twelem.dom.innerHTML;
twelem.dom.innerHTML = html_next;
return twelem;
};
//}}}
/***
!!!!! twelem.move
***/
//{{{
twelem.move = function (which,delta) {
// Move this element so its index is changed by delta.
var twnext = twelem.getNeighbor(which,delta);
if ( ! twnext ) return null;
twve.tiddler.updateText();
twve.tiddler.focusElem(null);
// The next element found, switch the wiki text and DOM
// content with the current one (twelem).
twelem.switchWith(twnext);
if ( Math.abs(twelem.rIndex-twnext.rIndex) == 1 )
return twnext;
// This element has been moved from the first to the last
// (cyclic manner), or the opposite way, special cares are
// needed to get the correct result. For example, suppose
// we have a series 0123, and we move the 0 to the left in a
// cyclic manner, we would expect to have a result of 1230.
// This method, however, simply switches 0 and 3 and so
// produces 3120, different from our expectation. What we
// need is to further switch the 3 with 1 and 2 to produce
// the expected 1230.
var ndx;
if ( twnext.rIndex == 0 ) {
ndx = 1;
if ( which == 'down' ) which = 'up';
else which = 'left';
} else {
ndx = twnext.rIndex - 1;
if ( which == 'up' ) which = 'down';
else which = 'right';
}
while ( twelem.rIndex != ndx ) {
var next = twelem.getNeighbor(which,1);
twelem.switchWith(next);
twelem = next;
}
return twnext;
};
//}}}
/***
!!!!! End of twelem.
***/
//{{{
return twelem;
}
});
//}}}
/***
!!! twve.leveledElement extra features
***/
//{{{
merge(twve.leveledElement, {
preCreate : twve.leveledElement.create,
create : function(src,txt,start,dir){
var elem = twve.leveledElement.preCreate.call(this);
// clear
//var preClear = elem.clear;
elem.clear = function(){
//preClear.apply(this);
elem.sub_elem = null;
return elem;
};
// copyFrom
var preCopyFrom = elem.copyFrom;
elem.copyFrom = function(src){
preCopyFrom.apply(this,arguments);
elem.sub_elem = src.sub_elem;
return elem;
};
// get sub element
elem.getSubElement = function(){
return elem.dom
? elem.dom.querySelector(elem.dom.nodeName)
: null;
};
// subEelementEnds
elem.subElementEnds = function(start,txt){
return twve.text.consecutiveLinesEnd(
(txt || elem.wrapperText()),
(start || elem.start),
true
);
};
// dummyLevel
elem.dummyLevel = function(elem){
var selector = elem.twveSelector(null,'parent');
if(!twve.node.matches(elem,selector.include)) return 0;
var level = 0;
while ( true ) {
elem = elem.childNode[0];
if(twve.node.matches(elem,selector.include)) level++;
else break;
}
return level;
};
// firstChildOfDummy
elem.firstChildOfDummy = function(node){
// Tests if node is a dummy element. Returns its first child
// if it is, null otherwise.
var sub = node && node.childNodes ? node.childNodes[0] : null;
return (!sub || sub.nodeName!=node.nodeName)
? null
: sub;
};
// renderedCopy
var preRenderedCopy = elem.renderedCopy;
elem.renderedCopy = function(){
var the_copy = preRenderedCopy.apply(this,arguments);
if ( elem.sub_elem && the_copy && elem != the_copy ) {
// There is sub_elem, and the_copy is not elem itself.
// Find the corresponding sub_elem in the_copy.
the_copy.sub_elem = the_copy.getSubElement();
}
return the_copy;
};
// box
var preBox = elem.box;
elem.box = function(){
var eb = preBox.apply(this,arguments);
if ( ! config.options.chktwveExtraIncludeSubs ) {
// We are not including the sub element, reduce
// the height (bottom) of the enclosing box.
var sub = elem.getSubElement();
if ( sub ) {
eb.bottom = twve.node.box(sub).top;
eb.height = eb.bottom - eb.top;
}
}
return eb;
};
// removeSelf
elem.removeSelf = function(){
// The user just removed this leveledElement.
if ( elem.sub_elem ) {
// This removed item had a sub element. Put it back.
// If there is previous leveledElement, and it is of the
// same level as this one, append the sub element to the
// previous one. Otherwise replace this element with its
// sub element.
var prev = twve.position.create();
if ( elem.dIndex > 0 ) {
prev = twve.position.create(elem.start);
prev.ndx--;
var txt = elem.wrapperText();
prev = twve.text.lastIndexOf(
txt, elem.start.matched, prev
);
if ( prev.ndx == -1 && elem.dIndex == 1 ) {
// We did not find the very first leveled element,
// it could be the missing \n at the beginning. Search
// again without it.
prev.ndx = elem.start.ndx - 1;
prev = twve.text.lastIndexOf(
txt, elem.start.matched.substring(1), prev
);
}
if ( prev.ndx > -1 &&
twve.leveledElement.getLevel(
txt,prev.ndx
) == elem.getLevel() ) {
// A previous leveledElement of the same level
// is found. Make sure it is immediately
// preceding this one.
var end = twve.position.create(prev);
end.ndx++;
end = twve.text.indexOf(txt,'\n',end);
if ( end.ndx != elem.start.ndx ) {
// If NOT immediately preceding this one,
// abandon it.
prev.ndx = -1;
}
}
}
if ( prev.ndx > -1 ) {
// The previous item is at the same level as
// this one. Append the sub element to it.
var dom = elem.dom;
elem.dom = dom.previousSibling;
dom.parentNode.removechild(dom);
elem.dom.appendChild(elem.sub_elem);
elem.rIndex--;
elem.dIndex--;
elem.start.matched = prev.matched;
elem.end.ndx = elem.start.ndx + 1;
elem.start.ndx = prev.ndx;
} else {
// Otherwise replace this element with its sub
// element.
elem.dom.parentNode.replaceChild(
elem.sub_elem,elem.dom
);
elem.dom = elem.sub_elem;
elem.ends();
}
} else {
// This item did not have a sub list. Just remove it.
elem.dom.parentNode.removeChild(elem.dom);
twve.tiddler.focusElem(null);
}
return elem;
};
//}}}
/***
!!!!! elem.topMostParent
***/
//{{{
elem.topMostParent = function(){
var dom = elem.dom;
var selector = elem.twveSelector(null,'parent');
do {
var parent = twve.node.closest(
dom.parentNode,selector.include
);
if ( ! parent ) break;
dom = parent;
} while ( true )
return dom;
};
//}}}
/***
!!!!! elem.refreshTopLevel
***/
//{{{
elem.refreshTopLevel = function(txt){
var parent = elem.topMostParent();
// Refresh this element from its top level.
if ( elem.dom == elem.firstChildOfDummy(parent)) {
// There are dummy elements containing this one.
elem.dom = parent;
elem.preRefreshSelf.call(this,txt);
} else {
// There are no dummy elements.
var selector = elem.twveSelector();
// Save the number of children,
var children = twve.nodeList.toArray(
parent.querySelectorAll(selector.include)
);
var nch = children.length;
// and the index of elem.dom.
var elemndx = nch > 0
? children.indexOf(elem.dom) : -1;
// Find the wiki text of the parent.
if ( elemndx >= 0 ) elem.rIndex -= elemndx+1;
var include_sub = config.options.chktwveExtraIncludeSubs;
config.options.chktwveExtraIncludeSubs = true;
elem.dIndex = -1;
elem.setElement(parent);
var ch0 = txt.charAt(0);
var ch1 = elem.start.matched.charAt(1);
if ( ch0 != ch1 ) {
// This element shall be destroyed, the remaining
// part originally after this element will not be
// found in the setElement() above. We need to
// find it here.
elem.end.ndx += txt.length+1;
var pos = twve.position.create(elem.end.ndx+1);
elem.end.copyFrom(
twve.text.consecutiveLinesEnd(
elem.wrapperText(),pos,true,ch1
)
);
}
var elemtxt = elem.getText();
// Refresh the parent.
elem.preRefreshSelf.call(this,elemtxt);
// Relocate the changed elem.dom, if it's still there.
elem.start.ndx += elemtxt.indexOf(txt);
elem.end.ndx = elem.start.ndx + txt.length;
children = twve.nodeList.toArray(
elem.dom.querySelectorAll(selector.include)
);
var chnew = children.length;
if ( ch0 == elem.start.matched.charAt(1) ) {
// The changed element got recreated (still there).
// Find it.
// If the number of children is different, the
// difference must be the change in level of the
// changed element.
if ( elemndx > 0 && chnew > 0 ) {
elemndx += chnew-nch;
elem.rIndex += elemndx+1;
elem.dom = children[elemndx];
}
twve.tiddler.focus(elem);
} else {
// The element is destroyed.
// Yield focus to the direct wrapper.
var wrap = elem.directWrapper();
// Find the element preceding the txt.
wrap.prev = elem.clone();
wrap.prev.dom = children[elemndx-1];
wrap.prev.end.ndx = elem.start.ndx-1;
// Find the element following the txt.
wrap.next = elem.clone();
wrap.next.dom = children[elemndx];
wrap.next.start.ndx = elem.end.ndx+1;
twve.tiddler.focus(wrap);
}
config.options.chktwveExtraIncludeSubs = include_sub;
}
return elem;
};
//}}}
/***
!!!!! elem.refreshThisLevel
***/
//{{{
elem.refreshThisLevel = function(txt){
// Refresh this leveledElement without changing its level.
// We need to remove the leading signatures to have the
// correct output.
twve.node.wikify(txt.substring(elem.level),elem.dom);
return elem;
};
//}}}
/***
!!!!! elem.refreshSelf
***/
//{{{
elem.preRefreshSelf = elem.refreshSelf;
elem.refreshSelf = function(txt){
// Determine whether to refresh this level or the top level.
if (txt.indexOf('\n') > -1 ||
elem.getLevel() != twve.leveledElement.getLevel(txt,0))
elem.refreshTopLevel(txt);
else
elem.refreshThisLevel(txt);
// Then restore the sub elements if there were any.
if ( elem.sub_elem ) {
elem.dom.appendChild(elem.sub_elem);
elem.sub_elem = null;
}
return elem;
};
//}}}
/***
!!!!! elem.ends
***/
//{{{
var preEnds = elem.ends;
elem.ends = function(start,txt,inactiveTags){
preEnds.apply(this,arguments);
if ( elem.rIndex < 0 || elem.dIndex < 0 )
return elem.end;
if ( config.options.chktwveExtraIncludeSubs ) {
// Include sub element to edit. No need to keep the
// sub element now.
elem.sub_elem = null;
// Look for the end of sub element.
elem.end.copyFrom(
elem.subElementEnds(null,txt)
);
} else {
// Don't include sub element to edit. Keep the
// sub element, if there is, for later use.
elem.sub_elem = elem.getSubElement();
}
return elem.end;
};
// End of elem.
return elem.created(src,txt,start,dir);
}
});
//}}}
/***
!!! twve.heading extra features
***/
//{{{
merge(twve.heading, {
//}}}
/***
!!!! twve.heading.wikify
***/
//{{{
preWikify : null,
wikify : function(txt,node,children){
if ( node && twve.node.matches(node,'div.board') ) {
// Preview something
if ( twve.tiddler.cur_editable ) {
var macro = twve.foldedSection.macro();
if ( twve.node.matches(
twve.tiddler.cur_editable.dom,
twve.foldedSection.getSelector()+',.foldable'
) ) {
// Either a folded section that contains headings, or a
// heading of a folded section, and no <<foldHeadings>>
// macro in the text, add it to get the correct preview.
if ( txt.indexOf('\n!') > -1 &&
txt.indexOf(macro) < 0 )
txt += '\n'+macro;
}
}
}
return twve.heading.preWikify.call(this,txt,node,children);
},
//}}}
/***
!!!! twve.heading.create
***/
//{{{
preCreateExtra : twve.heading.create,
create : function(src,txt,start,dir){
var h = twve.heading.preCreateExtra.call(this);
// subElementEnds
h.subElementEnds = function(start,txt,action){
if ( ! action ) return h.end;
};
// refreshSelf
//var preRefreshSelf = h.refreshSelf;
h.refreshSelf = function(txt){
var content = null;
if (twve.node.matches(h.dom,'.foldable')) {
// If this heading is one of a folded section,
// add <<foldHeadings>> macro to the refreshing text,
// and save the section content.
txt += '\n'+twve.foldedSection.macro();
content = h.dom.nextSibling;
}
// Refresh this heading.
h.preRefreshSelf.call(this,txt);
if ( content ) {
// If we had saved the section content, meaning this is
// a heading of a folded section. Replace its onclick
// and restore the sectin content.
h.dom.onclick = twve.heading.click;
h.dom.parentNode.replaceChild(
content,h.dom.nextSibling
);
}
return h;
};
return h.created(src,txt,start,dir);
}
});
//}}}
/***
!!! twve.listitem
***/
//{{{
twve.listitem = {
//}}}
/***
!!!! twve.listitem.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('ol,ul');
/*
which = which ? which.toLowerCase() : '';
return ( which.substring(0,9)=='rendering' ||
which.substring(0,6)=='parent' )
// In rendering phase or finding the parent.
? selector.includeSelector('ol,ul')
// Not in rendering phase nor finding the parent.
: selector.includeSelector('li').excludeSelector(
// In multi-line rendering mode lists can be created
// inside table cells. Exclude them because a table
// cell is treated as a single element in twve.
'td li,th li' +
// Tiddler tags shown in the tagged (or tagging?) box
// are also list items. These are not defined in the
// tiddler text and should be excluded from search.
',.tagged li,.tidTags li' +
// This is new in 2.8.x?
',.listTitle'
);
*/
},
//}}}
/***
!!!! twve.listitem.leadingChars
***/
//{{{
leadingChars : function(){
return '*#';
},
//}}}
/***
!!!! twve.listitem.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
['\n*','\n#'],
'\n'
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.listitem.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.listitem.switchType
***/
//{{{
switchType : function(prefix){
return prefix.charAt(prefix.length-1)=='*'
? prefix.replace(/\*/g,'#')
: prefix.replace(/\#/g,'*')
},
//}}}
/***
!!!! twve.listitem.create
***/
//{{{
create : function(src,txt,start,dir){
var li = twve.leveledElement.create();
// index
li.index = function(contents){
// Find the index of this list item within contents.
if ( ! contents )
contents = li.directWrapper().dom.childNodes;
return contents.indexOf(li.dom.parentNode);
};
// markupTags
li.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.listitem.markupTags();
};
// clone
li.clone = function(){
// Optional, may save a tiny bit of time.
return twve.listitem.create(li);
};
// twveSelector
li.twveSelector = function(selector,which){
// Optional, may save a tiny bit of time.
return twve.listitem.twveSelector(selector,which);
};
// starts
var preStarts = li.starts;
li.starts = function(start,txt,dir){
if(dir && dir<0 && txt.charAt(start)=='\n'){
var ch = txt.charAt(start+1);
if(ch=='*' || ch=='#') start--;
}
return preStarts.call(this,start,txt,dir);
};
// get sub list
li.getSubElement = function(){
return li.dom.querySelectorAll('ol,ul');
};
// setElement
var preSetElement = li.setElement;
li.setElement = function(elem,txt,start,dir){
if ( twve.node.matches(elem,'ol,ul') ) {
if ( elem.nodeType ) {
// If elem is a single node, set it.
preSetElement.call(
this,elem.querySelector('li'),txt,start,dir
);
li.dom = elem;
// Go all the way to the end/begining of this ol/ul.
var end = twve.text.consecutiveLinesEnd(
txt, li.start, true,
twve.listitem.leadingChars(), li.tags, dir
);
if ( typeof dir !== 'number' || dir < 0 )
// Backward
li.start.copyFrom(end);
else
// Forward
li.end.copyFrom(end);
}
// Otherwise we must be midway in refreshTopLevel(),
// leave it for that function to take care.
} else preSetElement.call(this,elem,txt,start,dir);
return li;
};
//}}}
/***
!!!!! li.subElementEnds
***/
//{{{
li.subElementEnds = function(start,txt){
var sub_open = li.start.matched;
sub_open += sub_open.charAt(sub_open.length-1);
var twsublist = twve.listitem.create(li);
twsublist.tags = twve.tags.create(
[sub_open, twve.listitem.switchType(sub_open)],'\n'
);
if ( start ) twsublist.end.copyFrom(start);
if ( ! txt ) txt = li.wrapperText();
do {
twsublist.rIndex = -1;
twsublist.dIndex = 0;
twsublist.starts(twsublist.end,txt);
if ( twsublist.start.ndx == twsublist.end.ndx-1 ) {
// The sublist is immediately following the previous,
// go on to find its end.
twsublist.ends(null,txt);
if ( twsublist.end.ndx < 0 ) {
twsublist.end.ndx = txt.length;
break;
}
} else break;
} while (true);
return twsublist.end;
};
//}}}
/***
!!!!! li.immediatelyFollowingEnds
***/
//{{{
li.immediatelyFollowingEnds = function(li2){
if ( ! li2 ) {
li2 = li.clone();
li2.rIndex = li2.dIndex = 0;
}
while (true) {
li2.starts(li2.end);
if ( li2.start.ndx == -1 ||
// If no more lists
li2.start.ndx+1 > li.end.ndx )
// or the next list is not immediately following,
break;
// Copy the end of the immediately following list.
li.end.ndx = li2.ends().ndx;
}
return li;
};
//}}}
/***
!!!!! li.refreshTopLevel
***/
//{{{
li.refreshTopLevel = function(txt){
// Refresh this listitem from its top level. This is needed
// when the type of list is changed, or when there are
// sub-lists included in the refreshing.
var include_sub = config.options.chktwveExtraIncludeSubs;
config.options.chktwveExtraIncludeSubs = true;
// Find the top most parent.
var parent = li.topMostParent();
// find the rendering index of the parent's first listitem.
li.rIndex -= twve.nodeList.toArray(
parent.querySelectorAll('li')
).indexOf(li.dom);
var selector = li.twveSelector(null,'parent');
// Find immediately preceding lists
while ( true ) {
var prev = parent.previousSibling;
if ( ! twve.node.matches(prev,selector.include) )
break;
var items = prev.querySelectorAll('li').length;
li.rIndex -= items;
parent = prev;
}
// Remove immediately following lists
while ( true ) {
var next = parent.nextSibling;
if ( ! twve.node.matches(next,selector.include) )
break;
next.parentNode.removeChild(next);
}
// Find the wiki text of parent.
li.dIndex = -1;
li.setElement(parent);
// Find the end of immediately following lists.
var li2 = li.clone();
li2.rIndex = li2.dIndex = 0;
li.immediatelyFollowingEnds(li2);
var ch0 = txt.charAt(0);
if ( ch0 != '*' && ch0 != '#' ) {
// This listitem shall be destroyed. Include its text
// and look for its immediately following listitems.
li2.end.ndx += txt.length+1;
li.end.ndx = li2.end.ndx;
li.immediatelyFollowingEnds(li2);
}
// The end of all the items is found. Refresh these
// immediately packed lists.
var litxt = li.getText();
li.preRefreshSelf.call(this,litxt);
// Relocate the changed listitem, if it's still there.
li.start.ndx += litxt.indexOf(txt);
li.end.ndx = li.start.ndx + txt.length;
litxt = litxt.split('\n');
var linendx = litxt.indexOf(txt);
if ( ch0 == '*' || ch0 == '#' ) {
// The list item is recreated (still there). Find it.
li.rIndex += linendx;
li.start.matched = '\n'+ch0;
li.dom = li.dom.querySelectorAll('li')[linendx];
twve.tiddler.focus(li);
} else {
// The list item is destroyed.
var wrap = li.directWrapper();
var lis = wrap.dom.querySelectorAll('li');
if ( linendx > 0 ) {
wrap.prev = twve.listitem.create();
wrap.prev.dom = lis[linendx-1];
wrap.prev.end.ndx = li.start.ndx-1;
}
if ( linendx < litxt.length-1 ) {
wrap.next = twve.listitem.create();
wrap.next.dom = lis[linendx];
wrap.next.start.ndx = li.end.ndx+1;
}
twve.tiddler.focus(wrap);
}
config.options.chktwveExtraIncludeSubs = include_sub;
return li;
};
//}}}
/***
!!!!! li.refreshThisLevel
***/
//{{{
var preRefreshThisLevel = li.refreshThisLevel;
li.refreshThisLevel = function(txt){
// Refresh this listitem without changing its level.
return ( txt.charAt(0) == li.start.matched.charAt(1) )
// The type of this list remains the same, just refresh
// its content.
? preRefreshThisLevel.call(this,txt)
// Otherwise refresh from the top level
: li.refreshTopLevel(txt);
};
//}}}
/***
!!!!! End of listitem
***/
//{{{
return li.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.blockquote
***/
//{{{
twve.blockquote = {
//}}}
/***
!!!! twve.blockquote.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'blockquote';
},
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector(twve.blockquote.getSelector());
},
//}}}
/***
!!!! twve.blockquote.blockExampleOpen
***/
//{{{
blockExampleOpen : function(){
return '\n<<<';
},
//}}}
/***
!!!! twve.blockquote.blockExampleClose
***/
//{{{
blockExampleClose : function(){
return '<<<\n';
},
//}}}
/***
!!!! twve.blockquote.encloses
***/
//{{{
encloses : function(txt,ndx,start,end){
start = twve.position.create(start);
end = twve.position.create(end);
if ( start.ndx < 0 ) start.ndx = 0;
if ( end.ndx <= start.ndx || end.ndx > txt.length )
end.ndx = txt.length;
txt = txt.substring(start.ndx,end.ndx);
var tagsBlockExample = twve.tags.create(
twve.blockquote.blockExampleOpen(),
twve.blockquote.blockExampleClose()
);
var pos1=twve.position.create(start.ndx,tagsBlockExample.open);
var pos2=twve.position.create(pos1);
// The blockexample has identical opening and closing tags,
// therefore we must always start searching from the
// beginning (start) to ensure we have found a pair of
// identical tags.
while (true) {
pos1 = tagsBlockExample.nextOpenTag(txt,pos1);
if ( pos1.ndx == -1 ) return null;
pos2.ndx = pos1.ndx + pos1.matched.length;
if ( pos2.ndx > ndx ) return null;
// An opening tag found before ndx. Look for its closing
// tag.
pos2.matched = tagsBlockExample.close;
pos2 = tagsBlockExample.matchedCloseTag(txt,pos2);
if ( pos2.ndx == -1 ) {
// If no closing tag found, return the end of text.
pos2.ndx = end.ndx;
return {start:pos1, end:pos2};
} else if ( pos2.ndx > ndx )
// If a closing tag is found after ndx, (enclosing),
// return that position.
return {start:pos1, end:pos2};
}
return null;
},
//}}}
/***
!!!! twve.blockquote.markupTags
***/
//{{{
markupTags : function(which){
var tags = typeof which ==='string' &&
which.toLowerCase().substring(0,5)=='inact'
? twve.tags.create()
: twve.tags.create(
['\n<<<', '\n>'],
['<<<\n', '\n']
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.blockquote.markupTags();
};
// blockExample
tags.blockExample = function(pos){
return pos.matched == twve.blockquote.blockExampleOpen();
};
// notEnclosing
tags.notEnclosing = function(){
return typeof tags.open == 'string' &&
tags.open != twve.blockquote.blockExampleOpen();
};
// exactCloseTag
var preExactCloseTag = tags.exactCloseTag;
tags.exactCloseTag = function(){
var exactTags = preExactCloseTag.apply(this,arguments);
if ( ! exactTags.close ){
exactTags.open = '\n>';
exactTags.close = '\n';
}
return exactTags;
};
// matchedCloseTag
var preMatchedCloseTag = tags.matchedCloseTag;
tags.matchedCloseTag = function(txt,pos){
if ( tags.blockExample(pos) ) {
// This blockquote is a blockexample. Go find the next
// closing tag.
pos.ndx++;
return preMatchedCloseTag.call(this,txt,pos);
} else {
// This blockquote consist of consecutive lines of text.
// Go to the very end of these consecutive lines of text
var all_end =
twve.text.consecutiveLinesEnd(txt,pos,true,'>');
if ( config.options.chktwveExtraIncludeSubs ) {
// If we want to include sub-blockquotes, this is
// the end.
return all_end;
}
// Otherwise we look for the end before the
// sub-blockquote
var cur_end = twve.text.consecutiveLinesEnd(
txt,pos,false,'>'
);
if ( all_end.ndx == cur_end.ndx )
// If this end is the same as the last one, there
// are no sub-blockquotes.
return all_end;
// Otherwise we check for in-body sub-blockquotes.
// If the blockquote level after cur_end is the same as
// the level of this blockquote, there must be an in-body
// sub-blockquote. In that case we return the all_end to
// include that sub-blockquote. Otherwise either this
// blockquote is an in-body sub-blockquote of another, or
// it does not have in-body sub-blockquote. We return
// cur_end in such cases.
var level = twve.leveledElement.getLevel(txt,pos.ndx,'>');
var level_end = twve.leveledElement.getLevel(
txt,cur_end.ndx+1,'>'
);
return level_end == level
? all_end
: cur_end;
}
};
// encloses
var preEncloses = tags.encloses;
tags.encloses = function(txt,ndx,start,end){
// This method is mainly for the situation where a heading
// is enclosed in a blockexample. It needs special care
// because a blockexample have the same opening and closing
// tags, we need to start from the beginning every time to
// really make sure the enclosing of the index ndx within
// a blockexample.
var pos = preEncloses.apply(this,arguments);
if ( pos ) return pos;
if(ndx == 0 || ndx >= txt.length-1 || tags.notEnclosing())
return null;
return twve.blockquote.encloses(txt,ndx,start,end);
};
return tags;
},
//}}}
/***
!!!! twve.blockquote.create
***/
//{{{
create : function(src,txt,start,dir){
var bq = twve.leveledElement.create();
// markupTags
bq.markupTags = function(){
return twve.blockquote.markupTags();
};
// twveSelector
bq.twveSelector = function(selector,which){
return twve.blockquote.twveSelector(selector,which);
};
// is
var preIs = bq.is;
bq.is = function(elem){
if ( preIs.apply(this,arguments) ) return true;
var sub = bq.firstChildOfDummy(elem);
return sub ? bq.is(sub) : false;
};
// renderingIndex
var preRenderingIndex = bq.renderingIndex;
bq.renderingIndex = function(){
// Obtain the rendering index first.
preRenderingIndex.apply(this);
// Then check for necessary adjustments.
// If a blockquote starts from the 2nd level, there will be
// an dummy top-level blockquote to contain it, hence its
// rendering index is added by 1. The same applies to
// deeper-level-started blockquotes, with one extra dummy
// blockquote introduced at each level.
// Hence, if the user clicks on one of the dummy containing
// blockquotes, which are NOT DEFINED in the tiddler text,
// we shall find the actually defined blockquote for further
// use.
var dum_level = 0;
// Search for dummy blockquotes in children
while( (bq.sub_elem = bq.firstChildOfDummy(bq.dom)) ){
dum_level++;
bq.dom = bq.sub_elem;
};
return (bq.rIndex += dum_level);
};
// skipOpenTag
var preSkipOpenTag = bq.skipOpenTag;
bq.skipOpenTag = function(txt){
var p = preSkipOpenTag.apply(this,arguments);
if ( bq.blockExample() && txt.charAt(p) == '\n' ) p++;
return p;
};
// getNeighbor
var preGetNeighbor = bq.getNeighbor;
bq.getNeighbor = function(which,delta){
var bqnext = preGetNeighbor.apply(this,arguments);
return (bqnext && bqnext.firstChildOfDummy(bqnext.dom))
? bqnext.getNeighbor(which,1)
: bqnext;
};
// blockExample
bq.blockExample = function(){
return bq.tags.blockExample(bq.start);
};
// counts
bq.counts = function(searchInfo,txt){
// Check if the currently found element counts as a valid
// one. If so,
// 1. update the ndx and remained properties of
// searchInfo,
// 2. return true.
// Otherwise,
// 1. return false.
// Blockquotes are different from others in that they can
// have sub-bloquotes within their bodies. Special care is
// needed to correctly count the indexes of them.
// When we see a blockquote, count it once (dlevel = 1).
// Then we check its level to adjust the counts.
// 1. If this blockqoute does not start from level 1, add
// the level difference.
// 2. If this blockquote is the one we are looking for,
// update the researchInfo and return true.
// 3. Otherwise we check for sub-blockqoute contained
// within its body.
// a. If there is sub-blockquote after the current
// starting index, check if that sub-blockquote is
// what we are looking for.
// i. If so, move the start.ndx to the beginning
// of the sub-blockquote, update the searchInfo
// and return true.
// ii. Otherwise, move the start.ndx to the end of
// that sub-blockquote, repeat 3.
// b. Otherwise update the searchInfo and return true.
var parent_level = 1;
var dlevel = 1;
if ( bq.blockExample() ) {
// Update the searchInfo.
searchInfo.remained -= dlevel;
searchInfo.ndx += dlevel;
if ( searchInfo.remained == 0 )
// This blockexample is the one we are looking
// for, return true.
return true;
// Blockexamples may or may not contain leveled
// blockquotes, check it.
var next = twve.position.create(bq.start);
next.ndx += next.matched.length;
next = twve.text.indexOf(txt,'\n>',next);
if ( next.ndx == -1 )
// There is no leveled blockquotes contained in
// this blockexample, return true to count this
// one.
return true;
// Otherwise update bq.start.ndx and do the followings.
bq.start.copyFrom(next);
}
// Blockquotes that consist of consecutive lines of text.
var cur_level = twve.leveledElement.getLevel(
txt,
bq.start.ndx+(txt.charAt(bq.start.ndx)=='\n'?1:0),
'>'
);
dlevel = parent_level = cur_level;
if ( dlevel < searchInfo.remained ) {
// This blockquote is NOT the one we are looking for.
// Check for sub-blockquotes within its body.
var all_end = twve.text.consecutiveLinesEnd(
txt,bq.start.ndx,true,'>'
);
var cur_end = twve.text.consecutiveLinesEnd(
txt,bq.start.ndx,false,'>'
);
// Now all_end is at the end of all these
// consecutive lines of text, while cur_end is
// at the end of the lines of the same level.
// There are sub-blockquotes following or within this
// one, check if one of them is what we are looking
// for.
do {
if ( all_end.ndx == cur_end.ndx ) {
// There is no more sub-blockquotes
// following or within the body of this
// one, meaning this is not the one
// we are looking for. Move the start.ndx
// to the end of this blockquote for next
// search, and return.
break;
}
// There are sub-blockquotes.
// Find the level (next_level) after cur_end.
var next_level = twve.leveledElement.getLevel(
txt, cur_end.ndx+1,'>'
);
if ( next_level > cur_level ) {
// The next_level is even deeper, meaning
// there are still sub-blockquotes after
// this one. Check for this one.
dlevel += (next_level - cur_level);
if(dlevel == searchInfo.remained){
// If this is the one we are looking for,
// return its beginning.
bq.start.ndx = cur_end.ndx;
break;
}
// Otherwise move search for the next.
bq.start.ndx = cur_end.ndx;
cur_level = next_level;
cur_end = twve.text.consecutiveLinesEnd(
txt,bq.start.ndx,false,'>'
);
} else {
// If next_level is shallower than cur_level,
// skip to the next sub-blockquote if any.
var next_end = twve.text.consecutiveLinesEnd(
txt,cur_end.ndx+1,false,'>'
);
if ( next_end.ndx == all_end.ndx ) {
// If there is no more sub-blockquotes
// in the parent body, return.
bq.start.ndx = all_end.ndx-1;
break;
}
// There is another sub-blockquote. Update the
// indexes for next search.
bq.start.ndx = next_end.ndx;
cur_end = next_end;
cur_level = next_level;
}
} while (true);
}
// Update the searchInfo.
searchInfo.remained -= dlevel;
if ( searchInfo.remained < 0 ) searchInfo.remained = 0;
searchInfo.ndx += dlevel;
return true;
};
// subElementEnds
var preSubElementEnds = bq.subElementEnds;
bq.subElementEnds = function(){
return bq.blockExample()
? bq.end
: preSubElementEnds.apply(this,arguments);
};
// ends
var preEnds = bq.ends;
bq.ends = function(){
return bq.found
? bq.end
: preEnds.apply(this,arguments);
};
// starts
var preStarts = bq.starts;
bq.starts = function(start,txt,dir){
if ( dir && dir < 0 ){
// Going backwards,
preStarts.call(this,start-1,txt,dir);
var tags = bq.tags.exactCloseTag(bq.start.matched);
if ( ! txt ) txt = bq.wrapperText();
// Locate the end of this blockquote.
bq.end.ndx = txt.indexOf(
tags.close,bq.start.ndx+1
)+tags.close.length;
bq.end.matched = tags.close;
// Find the begining of this blockquote.
if ( bq.blockExample() ){
bq.start.ndx--;
bq.start = tags.lastOpenTag(txt,bq.start);
} else {
bq.start = twve.text.consecutiveLinesEnd(
txt,bq.start,true,'>',tags,-1
);
if(bq.start.ndx > 0) bq.start.ndx++;
}
bq.found = true;
return bq.start;
}
return preStarts.apply(this,arguments);
};
return bq.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.tabset
***/
//{{{
twve.tabset = {
//}}}
/***
!!!! twve.tabset.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector(
//((which && which.toLowerCase().substring(0,6)=='parent')
// ? 'div.tabsetWrapper' : 'div.tabset')
'div.tabsetWrapper'
);
},
//}}}
/***
!!!! twve.tabset.markupTags
***/
//{{{
markupTags : function(){
// <<tabs>> transcluded
var tags = twve.tags.create(
'<<tabs',
'>>'
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.tabset.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.tabset.is
***/
//{{{
is : function(node,selector){
return twve.node.matches(
node,twve.tabset.twveSelector(selector).include
);
},
//}}}
/***
!!!! twve.tabset.includeNext
***/
//{{{
includeNextBox : function(node,box){
if ( ! box ) box = twve.node.box(node);
if ( node.nextSibling ) {
var box2 = twve.node.box(node.nextSibling);
box.height += box2.height;
box.bottom += box2.height;
}
return box;
},
//}}}
/***
!!!! twve.tabset.create
***/
//{{{
create : function(src,txt,start,dir){
var tabset = twve.element.create();
// markupTags
tabset.markupTags = function(){
return twve.tabset.markupTags();
};
// twveSelector
tabset.twveSelector = function(){
return twve.tabset.twveSelector();
};
// box, include the tabContents
tabset.box = function(){
return twve.tabset.includeNextBox(tabset.dom);
};
// refreshSelf
var preRefreshSelf = tabset.refreshSelf;
tabset.refreshSelf = function(){
tabset.dom.parentNode.removeChild(tabset.dom.nextSibling);
return preRefreshSelf.apply(this,arguments);
};
// end of tabset
return tabset.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.span
***/
//{{{
twve.span = {
//}}}
/***
!!!! twve.span.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector(
'span.marked,span[style*=color],span[style*=font]'
).excludeSelector(
twve.MathJax.getSpanSelector()+
','+twve.heading.getSpanSelector()
);
},
//}}}
/***
!!!! twve.span.markupTags
***/
//{{{
markupTags : function(){
return twve.tags.create(
'@@',
'@@'
);
},
//}}}
/***
!!!! twve.span.create
***/
//{{{
create : function(src,txt,start,dir){
if ( ! config.options.chktwveExtraInline ) return null;
var span = twve.element.create(src,txt,start,dir);
// markupTags
span.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.span.markupTags();
};
// clone
span.clone = function(){
// Optional, may save a tiny bit of time.
return twve.span.create(span);
};
// twveSelector
span.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.span.twveSelector();
};
// starts
var preStarts = span.starts;
span.starts = function(start,txt){
preStarts.apply(this,arguments);
start = span.start.ndx + 2;
var skipped = twve.text.skipStyleText(txt,start);
if ( skipped )
span.start.matched +=
txt.substring(start,start+skipped);
return span.start;
};
return span;
}
};
//}}}
/***
!!! twve.styledText
***/
//{{{
twve.styledText = {
//}}}
/***
!!!! twve.styledText.inactiveTags
***/
//{{{
preInactiveTags : null,
inactiveTags : function(){
return twve.styledText.preInactiveTags.apply(this).merge(
twve.text.markupTags()
);
},
//}}}
/***
!!!! twve.styledText.create
***/
//{{{
create : function(src,txt,start,dir){
if ( ! config.options.chktwveExtraInline ) return null;
var styledText = twve.element.create();
/*
// setElement
var preSetElement = styledText.setElement;
styledText.setElement = function(){
// Temporarily disables styledText wiki tags within strings.
if ( ! twve.styledText.preInactiveTags ) {
twve.styledText.preInactiveTags=twve.tags.inactiveTags;
twve.tags.inactiveTags=twve.styledText.inactiveTags;
preSetElement.apply(this,arguments);
// Restore back the previous inactive checking procedure.
twve.tags.inactiveTags=twve.styledText.preInactiveTags;
twve.styledText.preInactiveTags = null;
} else
preSetElement.apply(this,arguments);
return styledText;
};
*/
return styledText.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.styledText.bold
***/
//{{{
twve.styledText.bold = {
//}}}
/***
!!!! twve.styledText.bold.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('strong');
},
//}}}
/***
!!!! twve.styledText.bold.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
"''",
"''"
);
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.bold.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.styledText.bold.create
***/
//{{{
create : function(src,txt,start,dir){
var bold = twve.styledText.create();
if ( ! bold ) return null;
// markupTags
bold.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.bold.markupTags();
};
// clone
bold.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.bold.create(bold);
};
// twveSelector
bold.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.bold.twveSelector();
};
return bold.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.styledText.under
***/
//{{{
twve.styledText.under = {
//}}}
/***
!!!! twve.styledText.under.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('u');
},
//}}}
/***
!!!! twve.styledText.under.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'__',
'__'
);
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.under.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.styledText.under.create
***/
//{{{
create : function(src,txt,start,dir){
var under = twve.styledText.create();
if ( ! under ) return null;
// markupTags
under.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.under.markupTags();
};
// clone
under.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.under.create(under);
};
// twveSelector
under.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.under.twveSelector();
};
return under.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.styledText.italic
***/
//{{{
twve.styledText.italic = {
//}}}
/***
!!!! twve.styledText.italic.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('em');
},
//}}}
/***
!!!! twve.styledText.italic.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
"\/\/",
"\/\/"
);
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.italic.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.styledText.italic.create
***/
//{{{
create : function(src,txt,start,dir){
var italic = twve.styledText.create();
if ( ! italic ) return null;
// markupTags
italic.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.italic.markupTags();
};
// clone
italic.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.italic.create(italic);
};
// twveSelector
italic.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.italic.twveSelector();
};
// counts
var preCounts = italic.counts;
italic.counts = function(searchInfo,txt){
return (
italic.start.ndx > 5 &&
txt.substring(
italic.start.ndx-5,italic.start.ndx
).toLowerCase()=='http:'
) ? false
: preCounts.apply(this,arguments);
};
return italic.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.styledText.strike
***/
//{{{
twve.styledText.strike = {
//}}}
/***
!!!! twve.styledText.strike.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('strike');
},
//}}}
/***
!!!! twve.styledText.strike.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'--',
'--'
);
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.strike.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.styledText.strike.create
***/
//{{{
create : function(src,txt,start,dir){
var strike = twve.styledText.create();
if ( ! strike ) return null;
// markupTags
strike.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.strike.markupTags();
};
// clone
strike.clone = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.strike.create(strike);
};
// twveSelector
strike.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.styledText.strike.twveSelector();
};
// counts
strike.counts = function(searchInfo,txt){
if(txt.charAt(
strike.start.ndx+strike.start.matched.length
)!=' '){
searchInfo.remained--;
searchInfo.ndx++;
return true;
}
return false;
};
return strike.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.img
***/
//{{{
twve.img = {
//}}}
/***
!!!! twve.img.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('img');
},
//}}}
/***
!!!! twve.img.markupTags
***/
//{{{
markupTags : function(){
return twve.tags.create(
[ '[img', '[>img', '[<img', '<img' ],
[ ']]', ']]', ']]', '>' ]
);
},
//}}}
/***
!!!! twve.img.create
***/
//{{{
create : function(src,txt,start,dir){
var img = twve.element.create();
return img.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.cssWrapper
***/
//{{{
twve.cssWrapper = {
enableEdit : true,
//}}}
/***
!!!! twve.cssWrapper.is
***/
//{{{
is : function(node,selector){
var result = node && node.classList &&
node.classList.length == 1 &&
(node.nodeName=='SPAN' || node.nodeName=='DIV');
if ( result ){
var css = node.classList.item(0);
if(css=='toolbar' || css=='subtitle' || css=='tiddler')
return false;
if ( selector )
selector.include =
node.nodeName+'.'+css;
}
return result;
},
//}}}
/***
!!!! twve.cssWrapper.HTMLtagCloseMark
***/
//{{{
HTMLtagCloseMark : function(txt,pos){
if (pos.matched.charAt(0) == '<')
pos.matched = txt.substring(
pos.ndx, txt.indexOf('>',pos.ndx+1)+1
);
return pos;
},
//}}}
/***
!!!! twve.cssWrapper.markupTags
***/
//{{{
markupTags : function(node){
var possible = (node && node.classList.length==1);
var classStr = (possible?node.classList.item(0):'');
var tags = twve.tags.create(
[ ('{{'+classStr+'{'), '<div' ],
[ '}}}', '</div' ]
);
// classStr
tags.classStr = classStr;
// blockElement
tags.blockElement = possible && (node.nodeName == 'DIV');
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
var cloned = twve.tags.create(
tags.open,tags.close
);
cloned.classStr = tags.classStr;
cloned.blockElement = tags.blockElement;
return cloned;
};
// nextOpenTag
var preNextOpenTag = tags.nextOpenTag;
tags.nextOpenTag = function(txt,start){
return twve.cssWrapper.HTMLtagCloseMark(
txt,preNextOpenTag.call(this,txt,start)
);
};
// exactCloseTag
tags.exactCloseTag = function(open){
var exactTags = null;
switch (open.substring(0,2)){
case '<d':
exactTags = twve.tags.nested.create(
tags.open[1],tags.close[1]
);
exactTags.classStr = tags.classStr;
exactTags.blockElement = tags.blockElement;
exactTags.preMatchedCloseTag =
exactTags.matchedCloseTag;
exactTags.matchedCloseTag = function(txt,pos){
return twve.cssWrapper.HTMLtagCloseMark(
txt,
exactTags.preMatchedCloseTag.apply(
this,arguments
)
);
};
return exactTags;
case '{{':
exactTags = tags.clone();
exactTags.open = tags.open[0];
exactTags.close = tags.close[0];
return exactTags;
default:
return tags.clone();
}
};
// matchedCloseTag
var preMatchedCloseTag = tags.matchedCloseTag;
tags.matchedCloseTag = function(txt,pos){
return twve.cssWrapper.HTMLtagCloseMark(
txt,preMatchedCloseTag.apply(this,arguments)
);
};
return tags;
},
//}}}
/***
!!!! twve.cssWrapper.create
***/
//{{{
create : function(src,txt,start,dir){
var cssWrapper = twve.element.create();
// is
cssWrapper.is = function(node){
return twve.cssWrapper.is(node);
};
// counts
var preCounts = cssWrapper.counts;
cssWrapper.counts = function(searchInfo){
var matched = cssWrapper.start.matched;
if ( matched.length > 4 &&
matched.substring(0,4) == '<div' &&
matched.indexOf(cssWrapper.tags.classStr) < 0 )
return false;
return preCounts.apply(this,arguments);
};
return cssWrapper.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.MathJax
***/
//{{{
twve.MathJax = {
enabled : true,
//}}}
/***
!!!! twve.MathJax.getSpanSelector
***/
//{{{
getSpanSelector : function(){
return 'span.MathJax_Preview,span[id*=MathJax] span';
},
//}}}
/***
!!!! twve.MathJax.nextFormula
***/
//{{{
nextFormula : function(txt,start){
var tags = twve.MathJax.displayed.markupTags().merge(
twve.MathJax.inline.markupTags()
);
var formula = {};
formula.start = twve.position.create(start);
formula.start = tags.nextOpenTag(txt,formula.start);
if ( formula.start.ndx > -1 ) {
formula.end = twve.position.create(formula.start);
formula.end.ndx += formula.start.matched.length;
formula.end = tags.exactCloseTag(formula.start.matched)
.matchedCloseTag(txt,formula.end);
formula.expression = txt.substring(
formula.start.ndx,
formula.end.ndx+formula.end.matched.length
);
}
formula.getSource = function(){
return formula.start.matched != '\\begin{'
? formula.expression.substring(
formula.start.matched.length,
formula.expression.length-
formula.end.matched.length
)
: formula.expression;
};
return formula;
},
//}}}
/***
!!!! twve.MathJax.box
***/
//{{{
preBox : null,
box : function(node,outerw,outerh){
var mnode = twve.MathJax.displayed.getMathNode(node);
if ( ! mnode )
mnode = twve.MathJax.inline.getMathNode(node);
return twve.MathJax.preBox.call(this,(mnode||node),outerw,outerh);
},
//}}}
/***
!!!! twve.MathJax.beforeMath
***/
//{{{
folded : null,
subfoldable : null,
subfolded : null,
beforeMath : function (msg) {
// Things to do before MathJax does math processing on an element.
var fsel = twve.tiddler.twveFoldableSelector();
twve.MathJax.folded = null;
if ( twve.node.matches(msg[1],fsel.include) ) {
// We are processing math in a foldable
// panel.
if (!twve.node.isVisible(msg[1])){
twve.MathJax.folded = msg[1];
twve.node.show(msg[1]);
}
} else {
// We are not in a foldable panel, check its parents.
var parents = twve.node.parents(
msg[1], fsel.include
);
if ( parents ) {
// There are foldable parents containing this element.
for(var i=0,len=parents.length;i<len;i++){
if(! twve.node.isVisible(parents[i])){
twve.MathJax.folded=parents[i];
twve.node.show(parents[i]);
break;
}
}
}
// Check for folded sections in this element, if it is a wrapper.
if ( ! twve.node.closest(
msg[1],twve.tiddler.displaySelector()
) || ! twve.node.matches(
twve.tiddler.wrapperSelectors().include
) ) return;
twve.MathJax.subfoldable =
twve.wrapper.findElements(msg[1],fsel);
twve.MathJax.subfolded =
twve.wrapper.recordFoldable(
twve.MathJax.subfoldable
);
if ( twve.MathJax.subfoldable ) {
twve.node.show(twve.MathJax.subfoldable);
}
}
},
//}}}
/***
!!!! twve.MathJax.afterMath
***/
//{{{
afterMath : function(msg){
// Things to do after MathJax done math processing on an element.
if ( twve.MathJax.subfoldable ) {
twve.wrapper.restoreFoldable(
twve.MathJax.subfoldable,
twve.MathJax.subfolded
);
}
if ( twve.MathJax.folded ) {
twve.node.hide(twve.MathJax.folded);
}
},
//}}}
/***
!!!! twve.MathJax.checkReady
***/
//{{{
checkReady : function () {
if ( config.extensions.MathJax ) {
if ( typeof MathJax!=='undefined' ) {
// The MathJax was loaded completely. Remove the
// MathJaxPlugin if it's still in the hijacking chain.
if ( story.displayTiddler ==
config.extensions.MathJax.displayTiddler ) {
story.displayTiddler =
config.extensions.MathJax.displayTiddler_old;
MathJax.Hub.Config({
"HTML-CSS": { linebreaks: { automatic: true } },
SVG: { linebreaks: { automatic: true } }
});
//MathJax.Hub.signal.Interest(function(message){
// console.log("Hub: "+message)
//});
// Listen to the Begin Process signal
MathJax.Hub.Register.MessageHook(
'Begin Process',twve.MathJax.beforeMath
);
// Listen to the End Process signal
MathJax.Hub.Register.MessageHook(
'End Process', twve.MathJax.afterMath
);
}
return true;
}
}
return false;
},
//}}}
/***
!!!! twve.MathJax.render
***/
//{{{
render : function (w) {
if ( ! w ) return;
var t = w.parentNode
? w.parentNode.querySelector('.title')
: null;
if ( t ) {
MathJax.Hub.Queue(['Typeset',MathJax.Hub,t]);
}
// Enable auto-numbering if necessary
if ( config.options.chktwveExtraMathAutoNumber ) {
if ( twve.node.matches(w,'div.viewer') ) {
MathJax.Hub.Config({
TeX: { equationNumbers: { autoNumber: "AMS" } }
});
MathJax.Hub.Queue(
["resetEquationNumbers", MathJax.InputJax.TeX]
);
}
} else {
MathJax.Hub.Config({
TeX: { equationNumbers: { autoNumber: "none" } }
});
}
MathJax.Hub.Queue(['Typeset',MathJax.Hub,w]);
},
//}}}
/***
!!!! twve.MathJax.prepareElements
***/
//{{{
prePrepareElements : null,
prepareElements : function(twwrap){
if ( twve.MathJax.checkReady() &&
! twve.node.matches(twwrap.dom,'div.board') ) {
twve.MathJax.render(twwrap.dom);
}
twve.MathJax.prePrepareElements.call(this,twwrap);
return twwrap;
},
//}}}
/***
!!!! twve.MathJax.apply
***/
//{{{
apply : function(callback,obj,args){
if ( typeof MathJax !== 'undefined' ) {
MathJax.Hub.Queue(function(){
callback.apply(obj,args);
});
} else
callback.apply(obj,args);
},
//}}}
/***
!!!! twve.MathJax.fold
***/
//{{{
preFold : null,
fold : function(){
twve.MathJax.apply(
twve.MathJax.preFold,this,arguments
);
},
//}}}
/***
!!!! twve.MathJax.headingClick
***/
//{{{
preHeadingClick : null,
headingClick : function(){
twve.MathJax.apply(
twve.MathJax.preHeadingClick,this,arguments
);
},
//}}}
/***
!!!! twve.MathJax.restoreFoldable
***/
//{{{
preRestoreFoldable : null,
restoreFoldable : function(){
twve.MathJax.apply(
twve.MathJax.preRestoreFoldable,this,arguments
);
},
//}}}
/***
!!!! twve.MathJax.wikify
***/
//{{{
preWikify : null,
wikify : function(txt,node){
var node = twve.MathJax.preWikify.apply(this,arguments);
if ( twve.MathJax.enabled && typeof MathJax != 'undefined' ) {
var elem = null;
if ( node.nodeType ) {
elem = node.nodeType == 3
? node.parentNode
: node;
} else if ( node.length > 0 )
elem = node[0].parentNode;
//if ( typeof katex != 'undefined' &&
// twve.node.matches(node,'div.board') ) {
// var formula = twve.MathJax.nextFormula(txt);
// if ( formula.start.ndx == 0 ) {
// katex.render(formula.getSource(),node);
// return node;
// }
if ( elem )
try {
MathJax.Hub.Queue(['Typeset', MathJax.Hub, elem]);
} catch(e) {
console.log(e);
}
}
return node;
},
//}}}
/***
!!!! twve.MathJax.hasID
***/
//{{{
hasID : function(node){
return node && node.id && node.id.substring(0,7)=='MathJax';
},
//}}}
/***
!!!! twve.MathJax.create
***/
//{{{
create : function(src,txt,start,dir){
var math = twve.element.create();
// filter
math.filter = function(node){
if ( node.nodeType )
return math.getMathNode(node);
var filtered = [];
for ( var i=0,len=node.length; i<len; i++ ) {
var mnode = math.getMathNode(node[i]);
if ( mnode ) {
filtered[filtered.length] = mnode;
if ( mnode == node[i] ) i++;
else if ( mnode == node[i].nextSibling ) i += 2;
}
}
if ( filtered.length == 0 ) return null;
return filtered.length > 1 ? filtered : filtered[0];
};
// End of math.
return math.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.MathJax.displayed
***/
//{{{
twve.MathJax.displayed = {
//}}}
/***
!!!! twve.MathJax.displayed.lastIndexOf
***/
//{{{
preLastIndexOf : null,
lastIndexOf : function(str){
var start = twve.MathJax.displayed
.preLastIndexOf.apply(this,arguments);
// If a square bracket ([) is immediately following the opening
// tag of a displayed math (\[), it would result in \[[ in the
// wiki text and confuse the ''twve'' to mistake it as a TW
// link. We make sure such cases won't happen here.
var cur = start;
var curprev = cur;
while ( cur ) {
if ( cur.matched=='[[' && str.charAt(cur.ndx-1)=='\\' ) {
// This is the case we want to avoid. Remove cur.
if ( cur == start )
// Remove the first one
curprev = start = cur.next;
else
// Remove this one
curprev.next = cur.next;
} else {
// Otherwise check the next.
curprev = cur;
}
cur = cur.next;
}
return start;
},
//}}}
/***
!!!! twve.MathJax.displayed.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'div.MathJax_Display';
},
twveSelector : function(selector,which){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
var displayed = twve.MathJax.displayed.getSelector();
return selector.includeSelector(
displayed +
(which && which.toLowerCase().substring(0,6)=='render'
? '' : (','+displayed+' span'))
);
},
//}}}
/***
!!!! twve.MathJax.displayed.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
//[ '\\[', '\\begin{' ],
//[ '\\]', '\\end{' ]
[ '\\[', '$$', '\\begin{' ],
[ '\\]', '$$', '\\end{' ]
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.displayed.markupTags();
};
// nextOpenTag
//var preNextOpenTag = tags.nextOpenTag;
//tags.nextOpenTag = function(txt,start){
// start = preNextOpenTag.apply(this,arguments);
// if (start.matched == '\\begin{'){
// var pl = start.ndx+start.matched.length;
// start.matched += txt.substring(
// pl,txt.indexOf('}',pl)+1
// );
// }
// return start;
//};
// matchedCloseTag
var preMatchedCloseTag = tags.matchedCloseTag;
tags.matchedCloseTag = function(txt,pos,txtlen,inactiveTags){
if ( tags.close == '\\end{' ){
var cmd = txt.substring(
pos.ndx,txt.indexOf('}',pos.ndx)+1
);
tags.close += cmd;
pos.ndx += cmd.length;
}
return preMatchedCloseTag.call(
this,txt,pos,txtlen,inactiveTags
);
};
return tags;
},
//}}}
/***
!!!! twve.MathJax.displayed.getMathNode
***/
//{{{
getMathNode : function(node){
var selector = twve.MathJax.displayed.getSelector();
switch ( node.nodeName ) {
case 'SPAN' :
if ( twve.MathJax.hasID(node) ) {
return twve.node.closest(node.parentNode,selector);
} else {
var ch = node.childNodes;
if ( ch && ch.length > 2 )
node = ch[1];
else if ( ch && ch.length == 1 ) {
var txt = ch[0].nodeValue;
if ( txt ) {
var open = txt.substring(0,2);
var close = txt.substring(txt.length-2);
if ( (open =='\\[' && close == '\\]') ||
(open == '$$' && close =='$$') ) {
return node;
}
}
node = node.nextSibling;
} else
node = node.nextSibling;
return twve.node.matches(node,selector) ? node : null;
}
case 'DIV' :
return twve.node.matches(node,selector) ? node : null;
case 'SCRIPT' :
if ( twve.MathJax.hasID(node) ) {
node = node.previousSibling;
return twve.node.matches(node,selector) ? node : null;
}
}
return null;
},
//}}}
/***
!!!! twve.MathJax.displayed.is
***/
//{{{
is : function(node){
return twve.MathJax.displayed.getMathNode(node);
},
//}}}
/***
!!!! twve.MathJax.displayed.create
***/
//{{{
create : function(src,txt,start,dir){
var math = twve.MathJax.create();
// markupTags
math.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.displayed.markupTags();
};
// clone
math.clone = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.displayed.create(math);
};
// twveSelector
math.twveSelector = function(selector,which){
// Optional, may save a tiny bit of time.
return twve.MathJax.displayed.twveSelector(selector,which);
};
// multiLine
math.multiLine = function(){
return true;
};
// skipCloseTag
math.skipCloseTag = function(txt,start){
return txt.substring(start-2,start)=='$$' ? 3 : 0;
};
// setElement
var preSetElement = math.setElement;
math.setElement = function(elem,txt,start,dir){
// Set element
return preSetElement.call(
this,twve.MathJax.displayed.getMathNode(elem),
txt,start,dir
);
};
// getMathNode
math.getMathNode = function(node){
return twve.MathJax.displayed.getMathNode(node);
};
// removeSiblings
math.removeSiblings = function(){
if ( math.end.matched.substring(0,5)=='\\end{' ) {
var parent = math.dom.parentNode;
parent.removeChild(math.dom.previousSibling);
parent.removeChild(math.dom.nextSibling);
return true;
}
return false;
};
// replaceWith
var preReplaceWith = math.replaceWith;
math.replaceWith = function(newnode,oldnode){
if ( ! math.removeSiblings() && ! oldnode ) {
oldnode = math.dom.parentNode;
}
return preReplaceWith.call(this,newnode,oldnode);
};
// removeSelf
var preRemoveSelf = math.removeSelf;
math.removeSelf = function(){
if ( ! math.removeSiblings() )
math.dom = math.dom.parentNode;
return preRemoveSelf.apply(this,arguments);
};
// End of math.
return math.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.MathJax.inline
***/
//{{{
twve.MathJax.inline = {
//}}}
/***
!!!! twve.MathJax.inline.twveSelector
***/
//{{{
enableEdit : true,
getSelector : function(){
return 'span.MathJax_Preview';
//return 'span.MathJax';
},
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector(
twve.MathJax.inline.getSelector()
).excludeSelector(
twve.MathJax.displayed.getSelector()+' span'
);
},
//}}}
/***
!!!! twve.MathJax.inline.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
[ '\\(', '\\ref{', '\\eqref{' ],
[ '\\)', '}', '}' ]
);
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.inline.markupTags();
};
return tags;
},
//}}}
/***
!!!! twve.MathJax.inline.getMathNode
***/
//{{{
getMathNode : function(node){
//var selector = twve.MathJax.inline.getSelector();
switch ( node.nodeName ) {
case 'SPAN' :
if ( twve.MathJax.hasID(node) ) {
do {
if ( node.id.substring(7,15)=='-Element' )
return node;
do {
node = node.parentNode;
} while ( node && ! node.id );
} while ( node );
} else {
var ch = node.childNodes;
if ( ch && ch.length > 2 )
node = ch[1];
else if ( ch && ch.length == 1 ) {
var txt = ch[0].nodeValue;
if ( txt &&
txt.substring(0,2)=='\\(' &&
txt.substring(txt.length-2)=='\\)' )
return node;
else
node = node.nextSibling;
} else
node = node.nextSibling;
return twve.MathJax.hasID(node) ? node : null;
}
case 'SCRIPT' :
if ( twve.MathJax.hasID(node) ||
node.getAttribute('type').indexOf('math')>-1 )
return node.previousSibling;
}
return null;
},
//}}}
/***
!!!! twve.MathJax.inline.is
***/
//{{{
is : function(node){
return twve.MathJax.inline.getMathNode(node);
},
//}}}
/***
!!!! twve.MathJax.inline.create
***/
//{{{
create : function(src,txt,start,dir){
var math = twve.MathJax.create();
// markupTags
math.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.inline.markupTags();
};
// clone
math.clone = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.inline.create(math);
};
// twveSelector
math.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.MathJax.inline.twveSelector();
};
//}}}
/***
!!!!! math.renderingIndex
***/
//{{{
math.renderingIndex = function(){
// Find the rendering index of a twve.core element object
// in its direct wrapper.
if ( math.rIndex < 0 ) {
var elems = math.directWrapper().findElements(
math.twveSelector(null,'rendered')
);
var ndx = 0;
var displayed = twve.MathJax.displayed.getSelector();
if ( elems ) {
for(var i=0,len=elems.length; i<len; i++){
if (twve.node.matches(
elems[i].nextSibling,displayed
)) continue;
if(elems[i]==math.dom){
math.rIndex = ndx;
break;
}
ndx++;
}
}
}
return math.rIndex;
};
// setElement
var preSetElement = math.setElement;
math.setElement = function(elem,txt,start,dir){
// Set element
var elem = twve.MathJax.inline.getMathNode(elem);
preSetElement.call(
this,elem.previousSibling,txt,start,dir
);
math.dom = elem;
return math;
};
// getMathNode
math.getMathNode = function(node){
return twve.MathJax.inline.getMathNode(node);
};
// replaceWith
var preReplaceWith = math.replaceWith;
math.replaceWith = function(newnode,oldnode){
return preReplaceWith.call(
this,newnode,(oldnode||math.dom.parentNode)
);
};
// removeSelf
var preRemoveSelf = math.removeSelf;
math.removeSelf = function(){
math.dom = math.dom.parentNode;
return preRemoveSelf.apply(this,arguments);
};
// End of math.
return math.created(src,txt,start,dir);
}
};
//}}}
/***
!!! twve.tiddlerTitle extra features
***/
//{{{
merge(twve.tiddlerTitle, {
//}}}
/***
!!!! twve.tiddlerTitle.titleOfWrapper
***/
//{{{
titleOfWrapper : function(w,n){
var title = '';
var inline = twve.MathJax.inline.twveSelector();
var displayed = twve.MathJax.displayed.twveSelector();
var tags = null;
if ( ! w.childNodes ) return title;
for ( var i=0,len=w.childNodes.length; i<len; i++ ) {
var c = w.childNodes[i];
if ( twve.node.matches(c,inline.include) ) {
tags = twve.MathJax.inline.markupTags();
} else if ( twve.node.matches(c,displayed.include) ) {
tags = twve.MathJax.displayed.markupTags();
} else if ( twve.node.matches(c,'script') && tags ) {
title += tags.open[n] + c.textContent + tags.close[n];
tags = null;
} else {
title += c.textContent || c.nodeValue;
}
}
return title;
},
//}}}
/***
!!!! twve.tiddlerTitle.create
***/
//{{{
preCreateExtra : twve.tiddlerTitle.create,
create : function(src){
var tidTitle = twve.tiddlerTitle.preCreateExtra.call(this);
// titleOfWrapper
tidTitle.titleOfWrapper = function(){
var title = twve.tiddlerTitle.titleOfWrapper(
tidTitle.dom,0
);
tidTitle.tiddler = twve.tiddler.get(title);
if ( ! tidTitle.tiddler )
title = twve.tiddlerTitle.titleOfWrapper(
tidTitle.dom,1
);
return title;
};
return tidTitle.created(src);
}
});
//}}}
/***
!!! twve.wrapper extra features
***/
//{{{
merge(twve.wrapper,{
//}}}
/***
!!!! twve.wrapper.create
***/
//{{{
preCreateExtra : twve.wrapper.create,
create : function(){
var twwrap = twve.wrapper.preCreateExtra.apply(this,arguments);
//}}}
/***
!!!!! twwrap.isEditable
***/
//{{{
twwrap.isEditable = function(elem){
return twwrap.dom == elem ||
twve.node.contains(twwrap.dom,elem);
};
//}}}
/***
!!!!! twwrap.editNeighbor
***/
//{{{
twwrap.editNeighbor = function(which,delta){
// Edit the neighboring element(s) of editNode. The direction
// is specified in the first argument which: 'left', 'right',
// 'up', or 'down'.
// The second rgument, delta, specifies how far away
// the neighbor we are looking for. Default value is 1,
// meaning the immediate neighbor in the specified direction.
// Gather information.
if ( typeof delta !== 'number' ) delta = 1;
which = which ? which.toLowerCase() : 'right';
var nodes = twwrap.dom.childNodes;
var plen = nodes.length;
var index = twwrap.editNode.index;
var isBR = twwrap.editNode.dom.nodeName === 'BR';
var p = -1, cpos = 0;
switch(which){
case 'up':
//which = 'left';
case 'left':
p = index[0];
//if(p>2 && twve.node.matches(
// nodes[p-2],'span.MathJax_Preview'
//))
// p-=2;
p -= delta;
cpos = 'end';
break;
case 'down':
//which = 'right';
case 'right':
p = index[index.length-1];
if(twve.node.matches(
nodes[p],'span.MathJax_Preview'
))
p += 2;
p += delta;
// If the next neighbor is
if ( p < plen )
twwrap.editNode.next = nodes[p];
break;
}
if ( config.options.chktwveExtraCyclickNavi )
if ( p < 0 ) {
p = plen - 1;
twwrap.editNode.start.ndx = twwrap.end.ndx;
} else if ( p >= plen ) {
p = 0;
twwrap.editNode.end.ndx = twwrap.start.ndx;
}
if ( p >= 0 && p < plen ){
// Check if the current editNode contains only BR
// (an empty line).
// Save changes to this one.
twwrap.focusNode = twwrap.editNode;
twve.tiddler.updateText();
if(twwrap.focusNode.next &&
twwrap.focusNode.next != nodes[p])
p = twve.editable.nodeIndex(
nodes,twwrap.focusNode.next
);
twwrap.collectParagraphNodes(nodes,p,which);
if ( which === 'left' || which === 'up' ) {
// We are going backwrad,
if ( twwrap.focusNode.dom.nodeName === 'BR' ) {
// and the collected node is a mere BR, then
// that BR could be either
// 1. the end of the text to edit, or
// 2. an empty line.
// Skip to the even previous node(s) if case 1,
// or edit that empty line if case 2.
if ( twwrap.focusNode.dom
.previousSibling.nodeName !== 'BR' ) {
twwrap.editNode = twwrap.focusNode;
return twwrap.editNeighbor(which,1);
}
}
} else if ( ! isBR ) {
// We are going forward, and we are not editing
// an empty line.
if ( twwrap.focusNode.dom.nodeName === 'BR' ) {
// If the newly collected node is a mere BR,
// then that BR shall be the end of the text
// being currently edited. Skip to the even
// next node(s).
twwrap.editNode = twwrap.focusNode;
return twwrap.editNeighbor(which,1);
}
}
// Then edit the next one.
var ta = twwrap.editText();
cpos = twve.tiddler.editBoxCaret(ta,cpos);
twve.tiddler.setCaretPosition(cpos);
twwrap.scrollIntoView();
}
return twwrap;
};
//}}}
/***
!!!!! twwrap.adjustFocusNode
***/
//{{{
twwrap.adjustFocusNode = function(ndxstart,dn){
var focusNode = twwrap.focusNode;
if ( dn && focusNode ) {
// We have changed the number of nodes in the last
// refresh, and we have focusNode, then we need to
// adjust the focusNode if it is after the refreshed
// nodes.
var ndx0 = focusNode.index[0];
if ( ndx0 >= ndxstart ) {
// Yes the focusNode is after the refreshed nodes,
// recollect the nodes and update their indexes.
var newdom = twwrap.dom.childNodes;
var newlen = focusNode.index.length;
ndx0 += dn;
focusNode.dom = [];
focusNode.index = [];
for(var i=0; i<newlen; i++){
focusNode.dom[i] = newdom[ndx0+i];
focusNode.index[i] = ndx0+i;
}
}
}
return twwrap;
};
//}}}
/***
!!!!! twwrap.refreshSelf
***/
//{{{
var preRefreshSelf = twwrap.refreshSelf;
twwrap.refreshSelf = function(txt){
if ( twwrap.editNode ) {
// There is twwrap.editNode
//if ( txt === undefined ) txt = twwrap.getText();
var newdom = twve.tiddler.getPreviewedNodes(txt);
var newdomary = twve.nodeList.toArray(newdom);
// Get newdom to refresh.
var editdom = twwrap.editNode.dom;
var ndx0 = twwrap.editNode.index[0];
var dn = 0;
// Refresh the nodes.
if ( editdom ) {
if ( editdom.nodeType ) {
dn = newdom.length - 1;
// Single node
if(twve.node.matches(
editdom,'span.MathJax_Preview'
)){
editdom.parentNode.removeChild(
editdom.nextSibling
);
editdom.parentNode.removeChild(
editdom.nextSibling
);
}
twve.node.replace(newdom,editdom);
} else {
// Multiple newdom
dn = newdom.length - editdom.length;
for(var i=1,len=editdom.length; i<len; i++)
twwrap.dom.removeChild(editdom[i]);
twve.node.replace(newdom,editdom[0],twwrap.dom);
}
var len = newdomary.length;
twwrap.editNode.dom = len == 1
? newdomary[0] : newdomary;
twwrap.editNode.index = [ndx0];
for(var i=1; i<len; i++){
twwrap.editNode.index[i] = ndx0+i;
}
if ( twwrap.focusNode !== twwrap.editNode )
twwrap.adjustFocusNode(ndx0,dn);
}
/*
else {
if ( twwrap.editNode.next ) {
dn = newdom.length;
// Insert newdom before editNode.next.
for(var i=0,len=newdom.length; i<len; i++)
twwrap.dom.insertBefore(
newdom[0],twwrap.editNode.next
);
twwrap.adjustFocusNode(dn);
} else {
// Append newdom to twwrap.dom.
for(var i=0,len=newdom.length; i<len; i++)
twwrap.dom.appendChild(newdom[0]);
}
}
*/
return twwrap;
}
/*
else if ( twwrap.next ) {
// We are editing before the beginning of tiddler.
var newdom = twve.tiddler.getPreviewedNodes(txt);
var dn = newdom.length;
var first = twwrap.dom.firstChild;
for ( var i=0,len=newdom.length; i<len; i++ )
twwrap.dom.insertBefore(newdom[0],first);
twwrap.next = null;
twwrap.adjustFocusNode(dn);
return twwrap;
} else if ( twwrap.prev ) {
// We are editing after the end of tiddler.
var newdom = twve.tiddler.getPreviewedNodes(txt);
for ( var i=0,len=newdom.length; i<len; i++ )
twwrap.dom.appendChild(newdom[0]);
twwrap.prev = null;
return twwrap;
}
*/
return preRefreshSelf.apply(this,arguments);
};
//}}}
/***
!!!!! twwrap.needsRefresh
***/
//{{{
var preNeedsRefresh = twwrap.needsRefresh;
twwrap.needsRefresh = function(tworg,sec_org,sec_this){
var result = preNeedsRefresh.apply(this,arguments);
if ( result && tworg.editNode ) {
// This wrapper needs refresh and the original wrapper has
// editNode, find its copy in this one.
var nodes = twwrap.dom.childNodes;
var index = tworg.editNode.index;
if ( sec_org == sec_this ) {
// This wrapper contains exactly the same content as the
// original.
if ( index.length == 1 ) {
var p = index[0];
twwrap.collectNode(nodes[p],p);
} else {
var len = index.length;
for(var i=0; i<len; i++){
twwrap.collectNode(
nodes[index[i]],index[i]
);
}
}
} else {
// This wrapper contains different content as the
// original, one of them must contain partial while
// the other contains full content.
var p0 = index[0];
var p1 = index[index.length-1];
if ( ! sec_org ) {
// The original contains full while this one
// contains partial (section) content. Determine
// whether editNode lies in this section or not.
if ( twwrap.rIndex === undefined ) return result;
if(p0 < twwrap.rIndex ||
p1>(twwrap.rIndex+twwrap.dom.childNodes.length))
// The node to refresh is either before or after
// this section, no need to refresh.
return false;
// Otherwise find the corresponding index within
// this section and collect them to refresh.
for ( var p=p0,n=0; p<=p1; p++,n++ )
twwrap.collectNode(nodes[n],n);
return true;
} else {
// Otherwise, the original contains partial (section)
// while this one contains full content. Check if
// there is also a folded section containing that
// section. If so, that folded section will take, or
// already took care of the refresh, no need to do it
// here.
if ( twve.node.matches(
nodes[tworg.rIndex],
twve.foldedSection.getSelector()
) )
return false;
// Otherwise collect the nodes to refresh.
for ( var p=p0,n=p0+tworg.rIndex; p<=p1; p++,n++ )
twwrap.collectNode(nodes[n],n);
return true;
}
}
}
return result;
};
//}}}
/***
!!!!! twwrap.getOpen
***/
//{{{
twwrap.getOpen = function(){
//var start = twwrap.start.ndx;
return twwrap.editNode && twwrap.editNode.start
//? (twwrap.editNode.start.ndx+start)
? (twwrap.editNode.start.ndx)
: (twwrap.focusNode && twwrap.focusNode.start
//? (twwrap.focusNode.start.ndx+start)
? (twwrap.focusNode.start.ndx)
: 0);
};
//}}}
/***
!!!!! twwrap.getClose
***/
//{{{
twwrap.getClose = function(){
var start = twwrap.start.ndx;
return twwrap.editNode && twwrap.editNode.end
//? (twwrap.editNode.end.ndx+start)
? (twwrap.editNode.end.ndx)
: (twwrap.focusNode && twwrap.focusNode.end
//? (twwrap.focusNode.end.ndx+start)
? (twwrap.focusNode.end.ndx)
: (twwrap.end.ndx-start));
};
//}}}
/***
!!!!! twwrap.getText
***/
//{{{
twwrap.getText = function(action){
var txt = twwrap.tiddler.text;
var start = twwrap.start.ndx;
return (action && action.toLowerCase().indexOf('content')>=0)
? txt.substring(start,twwrap.end.ndx)
: twwrap.substring(
txt,twwrap.getOpen()+start,twwrap.getClose()+start
);
};
//}}}
/***
!!!!! twwrap.wrapperText
***/
//{{{
//twwrap.wrapperText = function(){
// return twwrap.getText('content');
//};
//}}}
/***
!!!!! twwrap.setText
***/
//{{{
var preSetText = twwrap.setText;
twwrap.setText = function(newtxt,dlen){
if ( typeof dlen === 'number' )
// Called from a child node, set the text directly.
return preSetText.call(this,newtxt,dlen);
// Called from this wrapper itself, prepare the text.
var txt = twwrap.getText('content');
var open = twwrap.getOpen();
var close = twwrap.getClose();
var charblock = '!|#*>';
if ( close > open + 1 ) {
// If the original text is more than one characters,
// check for leading and ending newlines.
if ( txt.charAt(open) == '\n' ) {
// This editable starts with a \n,
if ( txt.charAt(close-1) == '\n' ) {
// and ends with a \n as well, we are editing some
// of its child blockelement.
if ( newtxt ) {
var p = newtxt.lastIndexOf('\n');
// We are not removing this blockelement,
if (charblock.indexOf(newtxt.charAt(0))>-1) {
// and the newtxt starts with a
// bockelement, keep the opening \n;
open++;
if ( p < newtxt.length-1 )
// There is no closing \n in the newtxt,
// keep the original one.
close--;
} else if(p>-1 && p<newtxt.length &&
charblock.indexOf(newtxt.charAt(p+1))>-1){
// The newtxt ends with a blockelement
// and does not have a closing \n,
// keep the original one.
close--;
}
} else {
// We are removing this blockelement, keep
// one of them.
open++;
}
} else {
// and ends not with a \n, we could be editing
// some plain text and/or inline elements,
// possibly mixing with a beginning block
// element. Check it.
if ( charblock.indexOf(txt.charAt(open+1)) > -1 ) {
// Yes there is a begining block element,
// keep the beginning \n.
open++;
}
}
/*
} else if ( open == 0 ) {
// We are editing something at the very beginning
// of a tiddler,
if ( newtxt &&
charblock.indexOf(txt.charAt(open)) > -1 &&
txt.charAt(close-1) == '\n' ) {
// and we are not removing the whole text that
// is a block element, keep the ending \n if
// there is one.
close--;
}
*/
} else if ( newtxt &&
charblock.indexOf(txt.charAt(open)) > -1 &&
txt.charAt(close-1) == '\n' ) {
// We are not removing the whole text that
// is a block element, keep the ending \n if
// there is one.
close--;
}
}
dlen = newtxt.length - (close-open);
txt = txt.substring(0,open)
+ newtxt
+ txt.substring(close);
if ( dlen ) {
if ( twwrap.editNode ) {
twwrap.editNode.end.ndx += dlen;
}
if ( twwrap.focusNode &&
twwrap.focusNode != twwrap.editNode ) {
if ( twwrap.focusNode.end.ndx > open )
twwrap.focusNode.end.ndx += dlen;
if ( twwrap.focusNode.start.ndx >= close )
twwrap.focusNode.start.ndx += dlen;
//console.log('setText: focus=('+twwrap.focusNode.start.ndx+','+twwrap.focusNode.end.ndx+')');
}
}
// Set it back
return preSetText.call(this,txt,dlen);
};
//}}}
/***
!!!!! twwrap.updateText
***/
//{{{
var preUpdateText = twwrap.updateText;
twwrap.updateText = function(txt,refreshed){
var result = preUpdateText.call(this,txt,refreshed);
twwrap.editNode = null;
return result;
};
//}}}
/***
!!!!! twwrap.editText
***/
//{{{
var preEditText = twwrap.editText;
twwrap.editText = function(ev,txt,elem){
if ( ! twve.tiddler.focusBoxVisible() ) return null;
if ( twwrap.focusNode ) {
var eb = twve.tiddler.focusBox();
if ( eb && eb[0].width == 0 ) {
// Focusing on an empty node. Check if it's a
// transcluded content.
var wrap = twve.tiddler.createEditableWrapper(
twwrap.focusNode.dom
);
if (wrap) return wrap.editText(ev);
}
twwrap.editNode = twwrap.focusNode;
if ( twwrap.editNode.editText )
return twwrap.editNode.editText(ev,txt,elem);
} else if ( ! twwrap.tiddler ) {
twwrap.tiddler = store.createTiddler(
twwrap.wrapper_title
);
twwrap.tiddler.tags.push('autoCreate');
twwrap.tiddler.creator = config.options.txtUserName;
}
return preEditText.apply(this,arguments);
};
//}}}
/***
!!!!! end of twwrap
***/
//{{{
return twwrap;
}
});
//}}}
/***
!!! twve.sliderPanel extra features
***/
//{{{
merge(twve.sliderPanel, {
//}}}
/***
!!!! twve.sliderPanel.isButton
***/
//{{{
isButton : function(node,skip){
return (skip || node.nodeName == 'A') &&
twve.node.matches(
node.nextSibling,twve.sliderPanel.getSelector()
);
},
//}}}
/***
!!!! twve.sliderPanel.box
***/
//{{{
preBox : null,
box : function(node){
var eb = twve.sliderPanel.preBox.apply(this,arguments);
if ( twve.sliderPanel.isButton(node) ) {
// A slider panel button, includes the box of the content
// wrapper if it is visible.
var ebc=twve.sliderPanel.preBox.call(this,node.nextSibling);
if ( ebc ) {
if ( eb.left > ebc.left ) eb.left = ebc.left;
if ( eb.right < ebc.right ) eb.right = ebc.right;
eb.bottom = ebc.bottom
eb.width = eb.right - eb.left;
eb.height = eb.bottom - eb.top;
}
}
return eb;
}
});
//}}}
/***
!!! twve.foldedSection extra features
***/
//{{{
merge(twve.foldedSection, {
//}}}
/***
!!!! twve.foldedSection.titleOfTiddler
***/
//{{{
titleOfTiddler : function (w) {
// <<foldHeadings>> folded wrapper.
// The <<foldHeadings>> folded wrapper does not store the tiddler
// and section title. We need to construct it. This method
// obtains its tiddler title
// The following lines of codes cause "too much recursion" error
// for reasons unknown to me yet. Fortunately the workaround is
// easy: temporarily disable this type of wrapper.
var status = twve.foldedSection.enableEdit;
twve.foldedSection.enableEdit = false;
var p = twve.node.closest(
w.parentNode,
twve.tiddler.wrapperSelectors().include
);
var title = twve.tiddler.titleOfWrapper(p);
twve.foldedSection.enableEdit = status;
return title;
},
//}}}
/***
!!!! twve.foldedSection.titleOfSection
***/
//{{{
titleOfSection : function (w) {
// <<foldHeadings>> folded wrapper.
// The <<foldHeadings>> folded wrapper does not store the tiddler
// and section title. We need to construct it. This method
// obtains the section title.
var prev = w.previousSibling;
if ( ! prev ) return '';
var status = twve.foldedSection.enableEdit;
twve.foldedSection.enableEdit = false;
// ------------------------------------------------------------
// The following line of codes causes "too much recursion" error
// when this folded section is within another folded section.
// The solution: temporarily disable this type of wrapper.
var sec = twve.heading.getTitle(prev,true);
// ------------------------------------------------------------
twve.foldedSection.enableEdit = status;
return sec;
},
//}}}
/***
!!!! twve.foldedSection.titleOfWrapper
***/
//{{{
titleOfWrapper : function (w) {
// <<foldHeadings>> folded wrapper.
// The <<foldHeadings>> folded wrapper does not store the tiddler
// and section title. We construct it and store it as an
// attribute named "fullTitle".
var title = w.getAttribute('fullTitle');
if ( ! title ) {
title = twve.foldedSection.titleOfTiddler(w) +
config.textPrimitives.sectionSeparator +
twve.foldedSection.titleOfSection(w);
w.setAttribute('fullTitle',title);
}
return title;
},
//}}}
/***
!!!! twve.foldedSection.wrapperFromTitle
***/
//{{{
wrapperFromTitle : function(title,parent,sec){
// <<foldHeadings>> folded wrappers
// First we collect all foldedHeadings folded wrappers, then
// compare their title and keep those that match the given one.
var w0 = twve.nodeList.querySelectorAll(
parent,
twve.foldedSection.getSelector()
);
if ( ! w0 ) return null;
var w = [];
for (var i=0,len=w0.length; i<len; i++) {
var sp_title = twve.foldedSection.titleOfWrapper(w0[i]);
var sp_sec = twve.text.tiddlerSection(sp_title);
sp_title = twve.text.tiddlerTitle(sp_title);
if ( sp_title != title ) continue;
// This wrapper has the same title we are looking for.
if ( sec ) {
// We are searching wrappers with a definite section.
if ( !sp_sec || sp_sec == sec )
// This wrapper contains the whole tiddler, or
// the same section, keep it.
w[w.length] = w0[i];
} else {
// We are searching a whole tiddler, keep it no matter what.
w[w.length] = w0[i];
}
}
return w;
},
//}}}
/***
!!!! twve.foldedSection.headingCreate
***/
//{{{
preHeadingCreate : null,
headingCreate : function(){
var h=twve.foldedSection.preHeadingCreate.apply(this,arguments);
// removeSelf
var preRemoveSelf = h.removeSelf;
h.removeSelf = function(){
var sp = h.dom.nextSibling;
var foled_selector = twve.foldedSection.getSelector();
if( twve.node.matches(sp,foled_selector) ){
twve.node.show(sp);
var content = sp.childNodes;
var prev = h.dom.previousSibling;
var parent = sp.parentNode;
if ( twve.node.matches(prev,foled_selector) ) {
// There is folded section before this heading,
// merge the content.
var visible = twve.node.isVisible(prev);
twve.node.show(prev);
for ( var i=0,len=content.length; i<len; i++ )
prev.appendChild(content[0]);
if ( ! visible ) twve.node.hide(prev);
} else {
for ( var i=0,len=content.length; i<len; i++ )
parent.insertBefore(content[0],sp);
}
parent.removeChild(sp);
}
return preRemoveSelf.apply(this);
};
return h;
},
//}}}
/***
!!!! twve.foldedSection.inactiveTags
***/
//{{{
preInactiveTags : null,
inactiveTags : function(){
var tags = twve.foldedSection.preInactiveTags.apply(this);
return twve.blockquote
? twve.blockquote.markupTags('inactive').merge(tags)
: tags;
},
//}}}
/***
!!!! twve.foldedSection.hijackInactiveTags
***/
//{{{
/*
hijackInactiveTags : function(){
twve.foldedSection.preInactiveTags = twve.tags.inactiveTags;
twve.tags.inactiveTags = twve.foldedSection.inactiveTags;
},
*/
//}}}
/***
!!!! twve.foldedSection.releaseInactiveTags
***/
//{{{
releaseInactiveTags : function(){
twve.tags.inactiveTags = twve.foldedSection.preInactiveTags;
},
//}}}
/***
!!!! twve.foldedSection.create
***/
//{{{
create : function(src,txt,start,dir){
var section = twve.transcludedElem.create(src,txt,start,dir)
|| twve.wrapper.create();
// clone
section.clone = function(){
// Optional, may save a tiny bit of time.
return twve.foldedSection.create(section);
};
//}}}
/***
!!!!! section.findWrappers
***/
//{{{
//var preFindWrappers = section.findWrappers;
section.findWrappers = function(h){
if ( ! section.wrapper ) {
// If wrapper has not yet been searched, find them.
//if ( ! h ) h = section.getHeading();
if ( (section.wrapper = h.findWrappers()) ) {
// Then remove the direct parents of this folded section.
var parents = twve.node.parents(
section.dom,
twve.tiddler.wrapperSelectors().include
);
if ( parents )
for(var i=0,len=parents.length; i<len; i++){
var p = section.wrapper.indexOf(parents[i]);
if ( p > -1 ) section.wrapper.splice(p,1);
}
} else {
// Wrapper has been removed
console.log('wrapper not found for '+twve.node.info(section.dom)+' wrapper:'+section.wrapper);
}
}
return section.wrapper;
};
//}}}
/***
!!!!! section.box
***/
//{{{
var preBox = section.box;
section.box = function(){
return section.isVisible()
? preBox.apply(this,arguments)
: twve.node.box(section.dom.previousSibling);
};
//}}}
/***
!!!!! section.getHeading
***/
//{{{
section.getHeading = function(elem){
if ( ! elem ) elem = section.dom;
if ( ! elem ) return null;
var prev = elem.previousSibling;
return prev ? twve.heading.create(prev) : null;
};
//}}}
/***
!!!!! section.setElement
***/
//{{{
section.setElement = function(elem){
section.dom = elem;
var h = section.getHeading();
if ( ! h ) return null;
// Find the start: the end of the previous heading
section.wrapper_title = h.wrapper_title;
section.tiddler = h.tiddler;
section.findWrappers(h);
section.start.ndx = h.end.ndx;
section.rIndex = h.rIndex + 1;
if ( ! h.tiddler ) return section;
// Find the end, i.e., the closest of the beginning of
// the followings cases:
// 1. the next heading;
// 2. the <<foldHeadings>> macro command;
// 3. the closing tag of a blockexample, <<<.
var macro = twve.foldedSection.macro();
section.end.ndx = h.tiddler.text.indexOf(
macro, section.start.ndx
);
if ( section.end.ndx > -1 )
// Include the macro command itself.
section.end.ndx += macro.length;
else
// There is no macro command, temporarily set the end of
// section to the end of tiddler.
section.end.ndx = section.tiddler.text.length;
// Check for blockexamples.
if ( twve.node.closest(
elem,twve.blockquote.getSelector()
)) {
// This section is contained within a blockexample,
// compare the position of the blockexample and the
// current end, and choose the closer one.
var bqend = h.tiddler.text.indexOf(
twve.blockquote.blockExampleClose(),
section.start.ndx
);
if ( bqend > -1 && bqend < section.end.ndx )
section.end.ndx = bqend;
}
// Temporarily disables headings within BLOCKQUOTES, because
// blockquotes are considered as single element when gets
// folded, and our real end of section should be after the
// blockquotes.
//twve.foldedSection.hijackInactiveTags();
h.rIndex = -1;
h.dIndex = elem.querySelectorAll(
twve.heading.getSelector()
).length;
h.starts(section.start);
// Determine the end of section.
if ( h.start.ndx > -1 &&
h.start.ndx < section.end.ndx )
// There is a next heading that is closer then the
// current section end, change it to the beginning of
// the heading.
section.end.ndx = h.start.ndx;
// Restore back the previous inactive checking procedure.
//twve.foldedSection.releaseInactiveTags();
return section;
};
//}}}
/***
!!!!! End of section.
***/
//{{{
return section.created(src,txt,start,dir);
}
});
}());
//}}}
[[twve--Example--Headings]]
[[twve--Example--List Items]]
[[twve--Example--Blockquotes]]
[[twve--Example--Styled Text]]
[[twve--Example--Plain Text]]
[[twve--Example--Over Width Inline Elements]]
[[twve--Example--MathJax--Displayed]]
[[twve--Example--Transclusion--Macros]]
[[twve--Example--Folded Sections]]
/***
|editable|k
|''Name:''|twve.extra|
|''Description:''|View mode editing features for block elements (other than tables), in-line elements, and plain text in ~TidfdlyWiki environment|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.extra.min / http://twve.tiddlyspace.com/#twve.extra.min <br>* (regular) http://twve.tiddlyspot.com/#twve.extra / http://twve.tiddlyspace.com/#twve.extra|
|''Type:''|plugin|
|''Version:''|3.2.5|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.6.5|
|''Needs to have:''|twve.core|
!!Options
Look for [[twve.extra Options]] in the Options Panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve&&alert("twve.extra: This plugin needs twve.core to function properly. Please install twve.core before this one.");version.extensions.twve.extra={major:3,minor:2,revision:5,date:new Date("2015/11/06")};
config.macros.twveExtraOptions={init:function(){twve.extra.preKeydownAfter=twve.tiddler.keydownAfter;twve.tiddler.keydownAfter=twve.extra.keydownAfter;twve.extra.preGetPreviewer=twve.tiddler.getPreviewer;twve.tiddler.getPreviewer=twve.extra.getPreviewer;twve.extra.preGetOptionsMenu=twve.tiddler.getOptionsMenu;twve.tiddler.getOptionsMenu=twve.extra.getOptionsMenu;twve.extra.preUpdateText=twve.tiddler.updateText;twve.tiddler.updateText=twve.extra.updateText;twve.MathJax.displayed.preLastIndexOf=twve.text.lastIndexOf;
twve.text.lastIndexOf=twve.MathJax.displayed.lastIndexOf;twve.extra.preInactiveTags=twve.tags.inactiveTags;twve.tags.inactiveTags=twve.extra.inactiveTags;twve.MathJax.preWikify=twve.node.wikify;twve.node.wikify=twve.MathJax.wikify;twve.heading.preWikify=twve.node.wikify;twve.node.wikify=twve.heading.wikify;twve.MathJax.preBox=twve.node.box;twve.node.box=twve.MathJax.box;twve.sliderPanel.preBox=twve.node.box;twve.node.box=twve.sliderPanel.box;twve.foldedSection.preHeadingCreate=twve.heading.create;
twve.heading.create=twve.foldedSection.headingCreate;twve.MathJax.preHeadingClick=twve.heading.click;twve.heading.click=twve.MathJax.headingClick;twve.MathJax.preFold=twve.wrapper.fold;twve.wrapper.fold=twve.MathJax.fold;twve.MathJax.prePrepareElements=twve.wrapper.prepareElements;twve.wrapper.prepareElements=twve.MathJax.prepareElements;twve.MathJax.preRestoreFoldable=twve.wrapper.restoreFoldable;twve.wrapper.restoreFoldable=twve.MathJax.restoreFoldable;twve.extra.preSetOption=setOption;setOption=
twve.extra.setOption;void 0===config.options.chktwveExtraIncludeSubs&&(config.options.chktwveExtraIncludeSubs=void 0===config.options.chkTWtedIncludeSubs?!1:config.options.chkTWtedIncludeSubs);void 0===config.options.chktwveExtraInline&&(config.options.chktwveExtraInline=!0);void 0===config.options.chktwveExtraLocateChar&&(config.options.chktwveExtraLocateChar=!1);void 0===config.options.chktwveExtraNoClick&&(config.options.chktwveExtraNoClick=void 0===config.options.chkTWtedNoClick?!1:config.options.chkTWtedNoClick);
void 0===config.options.txttwveExtraPreviewOpacity&&(config.options.txttwveExtraPreviewOpacity=void 0===config.options.txtTWtedPreviewOpacity?"1.00":config.options.txtTWtedPreviewOpacity);void 0===config.options.chktwveExtraMathAutoNumber&&(config.options.chktwveExtraMathAutoNumber=!1);merge(config.optionsDesc,{chktwveExtraIncludeSubs:"Include sub-elements to edit. For example, sub lists are included in the editbox if this option is set to true.",chktwveExtraInline:"Edit inline elements.",chktwveExtraLocateChar:"(@@color:red;Experimental@@) Character by character editing.",
chktwveExtraNoClick:"Edit element content without clicking it.",txttwveExtraPreviewOpacity:"Opacity of the previewer. Default to 0.9.",chktwveExtraMathAutoNumber:"~MathJax auto numbering."});var a=config.shadowTiddlers.OptionsPanel,b=a.indexOf("[[twve.core Options");0<=b&&(b=a.indexOf("]]\n",b+2)+3,config.shadowTiddlers.OptionsPanel=a.substring(0,b)+"[[twve.extra Options|twve.extra Options]]\n"+a.substring(b));merge(config.shadowTiddlers,{"twve.extra Options":"<<twveExtraOptions>>"});twve.tiddler.registerElement(twve.blockquote);
twve.tiddler.registerElement(twve.cssWrapper);twve.tiddler.registerElement(twve.listitem);twve.tiddler.registerElement(twve.tabset);twve.tiddler.registerElement(twve.heading);twve.tiddler.registerElement(twve.img);twve.tiddler.registerElement(twve.pre);twve.tiddler.registerElement(twve.code);twve.tiddler.registerElement(twve.span);twve.tiddler.registerElement(twve.MathJax.inline);twve.tiddler.registerElement(twve.MathJax.displayed);twve.tiddler.registerElement(twve.styledText.strike);twve.tiddler.registerElement(twve.styledText.under);
twve.tiddler.registerElement(twve.styledText.italic);twve.tiddler.registerElement(twve.styledText.bold);twve.tiddler.registerWrapper(twve.foldedSection)},order:{chktwveExtraIncludeSubs:1,chktwveExtraInline:2,chktwveExtraLocateChar:3,chktwveExtraNoClick:4,txttwveExtraPreviewOpacity:5,chktwveExtraMathAutoNumber:6},handler:function(a){config.macros.twveCoreOptions.showOptionsTable(a,"''twve.extra'' Options","twveExtra",config.macros.twveExtraOptions.order)}};
twve.extra={preSetOption:null,setOption:function(a,b){twve.extra.preSetOption.apply(this,arguments)},preGetPreviewer:null,getPreviewer:function(){var a=twve.extra.preGetPreviewer.apply(this,arguments);a.style.opacity=config.options.txttwveExtraPreviewOpacity;return a},preGetOptionsMenu:null,getOptionsMenu:function(){var a=twve.extra.preGetOptionsMenu.apply(this,arguments);a.addMenu("''twve.extra'' Options",config.macros.twveExtraOptions.handler);return a},preInactiveTags:null,inactiveTags:function(){return twve.extra.preInactiveTags.apply().merge(twve.MathJax.displayed.markupTags()).merge(twve.MathJax.inline.markupTags())},
keydownChar:function(a,b){if(twve.tiddler.keydownCancel(a,b))return!1;var e=twve.tiddler.caret_pos,d=twve.tiddler.cur_editable;switch(a.which){case 46:if(d.offset.end>d.offset.start){twve.tiddler.updateText([b],d,!1,!0);break}var c=d.offset.nextNode;switch(c.nodeValue.length){case 1:d.offset.nextNode=c.nextSibling;c.parentNode.removeChild(c);break;case 0:break;default:d.offset.nextNode=c.splitText(1),c.parentNode.removeChild(c),d.offset.end++,twve.tiddler.updateText([b],d,!0,!0),d.curNode&&d.curNode.end.ndx--,
d.setFocusOffset(b),d.offset.end--}break;case 8:if(a.alterKey)return!1;if(d.offset.end>d.offset.start){twve.tiddler.updateText([b],d,!1,!0);break}e=d.offset.focusNode;c=e.nodeValue.length;switch(c){case 1:d.offset.nextNode=e.nextSibling;d.offset.focusNode=e.previousSibling;e.parentNode.removeChild(e);break;case 0:break;default:e=e.splitText(c-1),e.parentNode.removeChild(e),d.offset.start--,twve.tiddler.updateText([b],d,!0,!0),d.curNode&&d.curNode.end.ndx--,d.setFocusOffset(b,null,-1),d.offset.end--,
twve.tiddler.setCaretPosition(d.offset.end)}break;case 33:case 34:case 35:case 36:break;case 37:if(a.ctrlKey)return!1;twve.tiddler.updateText([b],d,!0,!0);d.setFocusOffset(b,null,-1);d.offset.start=d.offset.end=e.cur-1;twve.tiddler.setCaretPosition(d.offset.end);break;case 38:if(a.ctrlKey)return!1;break;case 39:if(a.ctrlKey)return!1;twve.tiddler.updateText([b],d,!0,!0);d.setFocusOffset(b,null,1);d.offset.start=d.offset.end=e.cur+1;twve.tiddler.setCaretPosition(d.offset.end);break;case 40:if(a.ctrlKey)return!1;
break;default:var f=b.value;b.value="";d.typed+=f;for(var h=twve.tiddler.getPreviewedNodes(f),g=d.nodendx.e0,c=d.offset.nextNode;h.length;)d.inserted[d.inserted.length]=g.insertBefore(h[0],c);f=f.length;d.setFocusOffset(b,null,f,c);twve.tiddler.setCaretPosition(e.cur+f)}return!0},preKeydownAfter:null,keydownAfter:function(a,b){if(config.options.chktwveExtraLocateChar)return twve.extra.keydownChar(a,b);if(!twve.extra.preKeydownAfter.apply(this,arguments))return!1;var e=twve.tiddler.caret_pos,d=twve.tiddler.cur_editable,
c=b.value;switch(a.which){case 37:if(a.ctrlKey)return!1;c?0==e.cur&&0==e.last&&d.editNeighbor("left"):d.editNeighbor("left");break;case 38:if(a.ctrlKey){if(d=d.move("up",1))twve.tiddler.focusElem(d),d.editText();return!1}(!c||0==e.cur&&0==e.last)&&d.editNeighbor("up");break;case 39:if(a.ctrlKey)return!1;c?e.cur==c.length&&e.last==e.cur&&d.editNeighbor("right"):d.editNeighbor("right");break;case 40:if(a.ctrlKey){if(d=d.move("down",1))twve.tiddler.focusElem(d),d.editText();return!1}(!c||e.cur==c.length&&
e.last==e.cur)&&d.editNeighbor("down")}return!0},preUpdateText:null,updateText:function(a,b,e,d){b||(b=twve.tiddler.cur_editable);b&&(config.options.chktwveExtraLocateChar?(b.getElement().normalize(),a||(a=twve.tiddler.getEditBox()),a&&(a[0].value=a[0].defaultValue.substring(0,b.offset.start)+b.typed+a[0].defaultValue.substring(b.offset.end),twve.extra.preUpdateText.call(this,a,b,e,d),d&&(b.curNode&&(b.curNode.end.ndx+=b.typed.length),a[0].defaultValue=a[0].value,b.typed=b.deleted=a[0].value=""))):
twve.extra.preUpdateText.call(this,a,b,e,d));return b}};
merge(twve.tiddler,{editWrappers:function(){return!0},prePrepareFocusBordersExtra:twve.tiddler.prepareFocusBorders,prepareFocusBorders:function(a){var b=a.length,e=twve.tiddler.border_top.length,d=twve.tiddler.border_top[0].parentNode;if(2>b)twve.tiddler.prePrepareFocusBordersExtra.apply(this,arguments);else{var c=twve.node.positionShift();a.top=a[0].top+c.top;a.right=a[0].right+c.left;a.bottom=a[0].bottom+c.top;a.left=a[0].left+c.left;a.height=a[0].height;a.width=a[0].width;twve.tiddler.prePrepareFocusBordersExtra.call(this,
a);for(var f=1;f<b;f++)f>=e&&(twve.tiddler.border_left[f]=twve.tiddler.createFocusBorder(d,"left"),twve.tiddler.border_top[f]=twve.tiddler.createFocusBorder(d,"top"),twve.tiddler.border_bottom[f]=twve.tiddler.createFocusBorder(d,"bottom"),twve.tiddler.border_right[f]=twve.tiddler.createFocusBorder(d,"right")),twve.node.setPosition(twve.tiddler.border_left[f],a[f].left+c.left,a[f].top+c.top),twve.node.setDimension(twve.tiddler.border_left[f],null,a[f].height),twve.node.setPosition(twve.tiddler.border_top[f],
a[f].left+c.left,a[f].top+c.top),twve.node.setDimension(twve.tiddler.border_top[f],a[f].width),twve.node.setPosition(twve.tiddler.border_bottom[f],a[f].left+c.left,a[f].bottom+c.top),twve.node.setDimension(twve.tiddler.border_bottom[f],a[f].width),twve.node.setPosition(twve.tiddler.border_right[f],a[f].right+c.left,a[f].top+c.top),twve.node.setDimension(twve.tiddler.border_right[f],null,a[f].height)}if(b<e){for(f=e-1;f>=b;f--)d.removeChild(twve.tiddler.border_left[f]),d.removeChild(twve.tiddler.border_top[f]),
d.removeChild(twve.tiddler.border_bottom[f]),d.removeChild(twve.tiddler.border_right[f]);e-=b;twve.tiddler.border_left.splice(b,e);twve.tiddler.border_top.splice(b,e);twve.tiddler.border_bottom.splice(b,e);twve.tiddler.border_right.splice(b,e)}return b},editBoxShown:function(a){return 4<a.offsetWidth},prePreviewEditBox:twve.tiddler.previewEditBox,previewEditBox:function(a){return!twve.tiddler.editBoxShown(a)?(twve.node.hide(twve.tiddler.getPreviewer()),null):twve.tiddler.prePreviewEditBox.apply(this,
arguments)},tagsInfo:function(a,b,e,d){var c=twve.object.create();c.start=twve.position.create(e,"");c.start.len=0;c.end=twve.position.create(e,"");c.end.len=0;if(3!=a.nodeType){var f=null;if("HR"==a.nodeName){e=b.charAt(c.start.ndx);if("-"==e)return c.end.ndx+=5,c;if("<"==e&&(c.end.ndx=b.indexOf(">",c.start.ndx)+1,0<c.end.ndx))return c}else if("SPAN"==a.nodeName){if("&"==b.charAt(c.start.ndx))return c.end.ndx=b.indexOf(";",c.start.ndx+1)+1,c;if("--"==b.substring(c.start.ndx,c.start.ndx+2))return c.end.ndx=
c.start.ndx+2,c;if(a.classList.contains("tcalc"))return c.end.ndx=c.start.ndx+a.title.length,c;if(a.classList.contains("sliderPanel"))return c.end.copyFrom(twve.foldedSection.create(a).end),c;f=twve.MathJax.displayed.is(a)?twve.MathJax.displayed.markupTags():twve.MathJax.inline.is(a)?twve.MathJax.inline.markupTags():1==a.classList.length&&"{{"==b.substring(c.start.ndx,c.start.ndx+2)?twve.tags.create("{{"+a.classList.item(0)+"{","}}}"):twve.tiddler.markupTags(a,"parent")}else if("A"==a.nodeName)if(twve.sliderPanel.isButton(a,
!0))f=twve.sliderPanel.markupTags();else if(a.classList.contains("button"))f=twve.tags.create("<<",">>");else return f=twve.link.markupTags(),b.substring(c.start.ndx,c.start.ndx+f.open.length)==f.open?(c.start.matched=f.open,c.start.len=f.open.length,c.end.ndx=c.start.ndx+c.start.len,c.end=twve.text.indexOf(b,f.close,c.end),c.end.len=f.close.length,c.tags=f):c.end.ndx=c.start.ndx+a.textContent.length,c;else{if(twve.node.matches(a,twve.listitem.twveSelector(null,"parent").include))return f=twve.listitem.markupTags(),
c.end=twve.text.consecutiveLinesEnd(b,c.end,!0,twve.listitem.leadingChars(),f),c.end.len=1,c.tags=f,c;if("BR"==a.nodeName)return"\n"==b.charAt(e)?(c.start.ndx=c.end.ndx=e,c.end.len=1):"<br"==b.substring(e,e+3).toLowerCase()?(c.start.ndx=e,c.end.ndx=b.indexOf(">",e+1)+1):e<b.length&&console.log("Internal Error: BR encountered but neither newline nor <br> was found. start="+e+"/"+b.length+" subtxt=["+b.substring(e,e+50)+"]"),c;f=twve.tiddler.markupTags(a,"parent");!f&&"DIV"==a.nodeName&&(f=twve.tiddler.markupTags(a.firstChild,
"parent"))}if(f){c.tags=f;"number"!=typeof d&&(d=0);do if(c.start=f.nextOpenTag(b,c.start),-1==c.start.ndx)return c;while(0<--d);0<=c.start.matched.indexOf("@@")?c.start.len=2+twve.text.skipStyleText(b.substring(c.start.ndx+2)):c.start.len=c.start.matched.length;f=f.exactCloseTag(c.start.matched);c.end.ndx=c.start.ndx+c.start.len;c.end.matched=c.start.matched;c.end=f.matchedCloseTag(b,c.end);-1==c.end.ndx?(c.end.ndx=c.start.ndx,c.end.len=c.start.len):c.end.len="\\end{"==c.end.matched?b.indexOf("}",
c.end.ndx)-c.end.ndx+1:c.end.matched.length}else a.nextSibling?(a=twve.tiddler.tagsInfo(a.nextSibling,b,c.end.ndx),c.end.ndx=a.start.ndx):c.end.ndx=b.length}else c.start.len="~"==b.charAt(c.start.ndx)?1:0,c.end.ndx=c.start.ndx+c.start.len+a.nodeValue.length;return c},selectionOffset:function(a,b,e,d,c,f){var h=f?f.toLowerCase().charAt(0):"";f="e"!=h;for(var h="s"!=h,g="number"==typeof e.start?e.start:e.start.outer,m="number"==typeof e.end?e.end:e.end.outer,p=0,q=b.length;p<q;p++){var l=b[p],k=twve.tiddler.tagsInfo(l,
c,!h?d.start:d.end);if(p<g)f&&(d.start=k.end.ndx+k.end.len),h&&(d.end=d.start);else{if(f&&p==g&&("object"==typeof e.start?(d.start=k.start.ndx+k.start.len,d=twve.tiddler.selectionOffset(a,l.childNodes,{start:e.start.inner,end:e.start.inner},d,c,"start")):d.start=k.start.ndx+("BR"==l.nodeName?0:k.start.len+a.anchorOffset),!h))break;if(h)if(p==m){a.focusNode==a.anchorNode&&a.focusOffset==a.anchorOffset?d.end=d.start:"object"==typeof e.end?(d.end=k.start.ndx+k.start.len,d=twve.tiddler.selectionOffset(a,
l.childNodes,{start:e.end.inner,end:e.end.inner},d,c,"end")):d.end=k.start.ndx+("BR"==l.nodeName?0:k.start.len+a.focusOffset);break}else d.end=k.end.ndx+k.end.len}}return d}});
merge(twve.node,{preBoxExtra:twve.node.box,box:function(a,b,e){twve.node.matches(a,twve.heading.getSpanSelector())&&(a=a.parentNode);var d=twve.node.preBoxExtra.call(this,a,b,e);if(!d)return null;d.containsXShifted=function(b,a,d){d||(d=twve.node.positionShift());return b>=a.left+d.left&&b<=a.right+d.left};var c=d.containsX;d.containsX=function(b,a){if(void 0===a)return c.apply(this,arguments);if("number"==typeof a)return 0<=a&&a<d.length?d.containsXShifted(b,d[a]):!1;if("inner"==a.toLowerCase().substring(0,
5))for(var e=twve.node.positionShift(),f=0;f<d.length;f++)if(d.containsXShifted(b,d[f],e))return d[f];return!1};d.containsYShifted=function(b,a,d){d||(d=twve.node.positionShift());return b>=a.top+d.top&&b<=a.bottom+d.top};var f=d.containsY;d.containsY=function(b,a){if(void 0===a)return f.apply(this,arguments);if("number"==typeof a)return 0<=a&&a<d.length?d.containsYShifted(b,d[a]):null;if("inner"==a.toLowerCase().substring(0,5))for(var c=twve.node.positionShift(),e=0;e<d.length;e++)if(d.containsYShifted(b,
d[e],c))return d[e];return!1};d.containsShifted=function(b,a,c){c||(c=twve.node.positionShift());return b.left?d.containsXShifted(b.left,a,c)&&d.containsXShifted(b.right,a,c)&&d.containsYShifted(b.top,a,c)&&d.containsYShifted(b.bottom,a,c):d.containsXShifted(b.x,a,c)&&d.containsYShifted(b.y,a,c)};var h=d.contains;d.contains=function(b,a){if(void 0===a)return h.apply(this,arguments);if("number"==typeof a)return 0<=a&&a<d.length?d.containsShifted(b,d[a]):!1;if("inner"==a.toLowerCase().substring(0,5))for(var c=
twve.node.positionShift(),e=0;e<d.length;e++)if(d.containsShifted(b,d[e],c))return d[e];return!1};return d}});
merge(twve.editable,{nodeIndex:function(a,b){if(!b)return-1;for(var e=0,d=a.length;e<d;e++){var c=a[e];if(b.nodeType==c.nodeType&&b==c)return e}e=0;for(d=a.length;e<d;e++)if(c=a[e],3!=c.nodeType&&(c=twve.editable.nodeIndex(c.childNodes,b),-1!=c))return ndx=twve.object.create(),ndx.outer=e,ndx.inner=c,ndx;return-1},nodeFromIndex:function(a,b){return b.outer?twve.editable.nodeFromIndex(a[b.outer].childNodes,b.inner):a[b]},preCreateExtra:twve.editable.create,create:function(){var a=twve.editable.preCreateExtra.apply(this,
arguments);a.lineHeight=function(b){return parseInt(a.dom.style.lineHeight||window.getComputedStyle(a.dom).getPropertyValue("line-height"))};var b=a.focusEditBox;a.focusEditBox=function(e,d,c,f){if(d&&config.options.chktwveExtraLocateChar){var h=!a.start.matched?0:a.start.matched.length-("\n"==a.start.matched.charAt(0)?1:0),h=h+twve.text.skipStyleText(d,h),g=getSelection();f||(f=a.getElement());var m=f.length?f[0]:f;a.nodendx=twve.object.create();var p=null;a.curNode&&a.curNode.index&&0<a.curNode.index[0]?
(p=a.curNode.dom,p.nodeType&&(p=[p]),a.nodendx.end=twve.editable.nodeIndex(p,g.focusNode==m?m.childNodes[g.focusOffset]:g.focusNode),0>a.nodendx.end&&(a.nodendx.end=p.length-1),a.nodendx.start=twve.editable.nodeIndex(p,g.anchorNode==m?m.childNodes[g.anchorOffset]:g.anchorNode),0>a.nodendx.start&&(a.nodendx.start=0)):(p=m.childNodes,a.nodendx.end=g.focusNode==m?g.focusOffset:twve.editable.nodeIndex(p,g.focusNode),0>a.nodendx.end&&(a.nodendx.end=p.length-1),g.anchorNode==g.focusNode?a.nodendx.start=
a.nodendx.end:(a.nodendx.start=g.anchorNode==m?g.anchorOffset:twve.editable.nodeIndex(p,g.anchorNode),0>a.nodendx.start&&(a.nodendx.start=0)));a.offset=twve.object.create();a.offset.end=a.offset.start=0;a.offset.focusNode=g.focusNode;a.offset.focusOffset=g.focusOffset;a.offset=twve.tiddler.selectionOffset(g,p,a.nodendx,a.offset,d.substring(h));a.offset.start+=h;a.offset.end+=h;twve.tiddler.caretPosition(e,0,0);twve.tiddler.initCaretPos(a.offset.start);a.typed=a.deleted=e.value="";a.inserted=[];a.nodendx.e0=
m;a.offset.focusNode!=m&&(e.style.borderWidth="0px",e.style.resize="none",e.style.width="1.5px",e.style.height=window.getComputedStyle(e).getPropertyValue("line-height"),a.setFocusOffset(e))}else a.nodendx=a.offset=null,b.apply(this,arguments);return e};a.setFocusOffset=function(b,d,c,f){"number"==typeof d?a.offset.focusOffset=d:"number"==typeof c&&(a.offset.focusOffset+=c);a.offset.nextNode=f?f:twve.editable.nodeFromIndex(a.nodendx.e0.childNodes,a.nodendx.end).splitText(a.offset.focusOffset);d=twve.node.box(a.offset.nextNode);
twve.node.setPosition(b,d.left-1,d.top)};a.boxCopy=function(b,a,c,f){var h=!1;"top"==f?a.top-b.top>=c&&(b.bottom=a.top,h=!0):"bottom"==f?b.bottom-a.bottom>=c&&(b.top=a.bottom,h=!0):(b.top=a.top,b.bottom=a.bottom-a.top<c?b.top+c:a.bottom,"all"==f&&(b.width=a.width,a.left>b.left&&(b.left=a.left),b.right=b.left+b.width),h=!0);b.height=b.bottom-b.top;return h};a.setCurNode=function(b,d,c){a.curNode&&c&&0<=c.toLowerCase().indexOf("end")?(b&&a.curNode.add(b),d&&(a.curNode.end.ndx=d.end.ndx+d.end.len+a.start.ndx)):
(a.curNode=twve.node.create(b),d&&(a.curNode.start=twve.position.create(d.start),a.curNode.end=twve.position.create(d.end),a.curNode.start.ndx+=a.start.ndx,a.curNode.end.ndx+=d.end.len+a.start.ndx));return a};a.collectNode=function(b,d,c,f,h){f&&h&&(a.curNode||(f.top=h.top),f.bottom=h.bottom,f.height=f.bottom-f.top);a.setCurNode(b,c,"end");a.curNode.index||(a.curNode.index=[]);a.curNode.index[a.curNode.index.length]=d;return a};a.skipOpenTag=function(b,d){if(!a.start.matched)return 0;"number"!=typeof d?
d=d&&d.ndx&&0<d.ndx?d.ndx:0:0>d&&(d=0);return a.start.matched.length-("\n"==a.start.matched.charAt(0)&&"\n"!=b.charAt(d))?1:0};a.box=function(b,d){var c=twve.tiddler.mousePosition(d),f=twve.tiddler.focusBox();if(f&&f.contains(c))return f;var h=twve.node.box(a.dom),f=h.clone();if(twve.tiddler.cur_editable&&twve.tiddler.cur_editable.dom==a.dom)return f;a.curNode=null;var g=a.dom.childNodes;if(!g)return f;var m=g.length;if(2>m)return f;if(c.x>=f.left&&c.x<=f.left+4||c.y>=f.top&&c.y<=f.top+4||c.x<=f.right&&
c.x>=f.right-4||c.y<=f.bottom&&c.y>=f.bottom-4)return a.prev=a.next=null,f;for(var p=a.lineHeight(),q=a.getText("content"),l=0,k=twve.tiddler.tagsInfo(g[l],q,a.skipOpenTag(q)),n=null,r=null;!(n=twve.node.box(g[l]));){if(++l==m)return f;k=twve.tiddler.tagsInfo(g[l],q,k.end.ndx+k.end.len)}if(c.y<n.top)return a.boxCopy(f,n,p,"top")&&(a.next||(a.next=twve.element.create()),a.next.start.ndx=k.start.ndx+a.start.ndx),f;do{if(n.containsY(c.y)){if("BR"==g[l].nodeName)return!a.curNode&&(l<m-1&&"EM"==g[l+1].nodeName)&&
-1<q.indexOf("@@display:none;")&&(l++,n=twve.node.box(g[l]),k.start.ndx=q.indexOf("//{{{",k.start.ndx)+6,k.end.ndx=q.indexOf("//}}}",k.start.ndx)-1),a.collectNode(g[l],l,k,f,n),f;if(k.tags&&k.tags.blockElement){if("\\end{"==k.end.matched&&a.curNode)return f;a.curNode=null;a.collectNode(g[l],l,k,f,n);return f}if(k.tags&&k.tags.is(twve.sliderPanel.markupTags())){if(0==k.start.ndx&&"\n"==q.charAt(k.start.ndx-1)&&(k.end.ndx+k.end.len==q.length||"\n"==q.charAt(k.end.ndx+k.end.len+1)))return a.setCurNode(g[l],
k),n;a.collectNode(g[l],l,k,f,n)}else{if(0==n.width&&3!=g[l].nodeType){h=window.getComputedStyle(g[l]);h=twve.node.cssSize(h.getPropertyValue("font-size"))*config.options.txttwveCoreMinEditWidth;a.setCurNode(g[l],k);if(c.x>n.left&&c.x-n.left<=h)return n.width=h,n.right=n.left+n.width,n;a.boxCopy(f,n,p);return f}a.collectNode(g[l],l,k,f,n);if(l==m-1)return f}}else if(a.curNode)if("BR"==g[l].nodeName){if(r&&(r.top>c.y||r.bottom>c.y)||n.top>c.y)return a.collectNode(g[l],l,k,f,r),f;a.curNode=null;twve.node.boxInit(h,
null,f);f.height=f.bottom-f.top}else if(k.tags&&k.tags.blockElement){if(n.top>c.y)return f;a.curNode=null;twve.node.boxInit(h,null,f);f.height=f.bottom-f.top}else a.collectNode(g[l],l,k,f,n);else{if(n.top>c.y)return f;"BR"!=g[l].nodeName&&(!k.tags||!k.tags.blockElement)&&a.collectNode(g[l],l,k,f,n)}twve.node.matches(g[l],"span.MathJax_Preview")&&(a.curNode?(a.collectNode(g[++l],l),a.collectNode(g[++l],l)):l+=2);for(r=n;++l<m;)if(null==(n=twve.node.box(g[l]))&&twve.node.matches(g[l],twve.foldedSection.getSelector()))if(l+
1<m&&twve.node.matches(g[l+1],twve.heading.getSelector()))k=twve.tiddler.tagsInfo(g[l+1],q,k.end.ndx+k.end.len,g[l].querySelectorAll(twve.heading.getSelector()).length),k.end.ndx=k.start.ndx,k.end.matched="",k.end.len=0;else{var s=twve.foldedSection.macro(),t=q.indexOf(s);-1<t?(k.end.ndx=t,k.end.len=s.length):(k.end.ndx=q.length,k.end.len=0)}else{k=twve.tiddler.tagsInfo(g[l],q,k.end.ndx+k.end.len);break}}while(l<m);if(n&&c.y>n.bottom&&a.boxCopy(f,n,p,"bottom"))return a.prev?a.prev.copyFrom(k):a.prev=
twve.object.create(k),a.prev.end.ndx+=a.start.ndx,f;a.prev=a.next=null;return f};a.mousemove=function(b){a.is(twve.tiddler.cur_editable)||twve.tiddler.focus(a,"on",b)};return a}});
merge(twve.element,{preCreateExtra:twve.element.create,create:function(a){var b=twve.element.preCreateExtra.apply(this,arguments);b.scrollIntoView=function(a){var d=b.box("edit"),c;if(a)c=twve.node.box(a);else{a=window;c=twve.node.box(a);var f=a.pageYOffset;c.top+=f;c.bottom+=f;f=scroll.pageXOffset;c.left+=f;c.width+=f}d.right>c.right?a.scrollLeft+=d.right-c.right:d.left<c.left&&(a.scrollLeft+=d.left-c.left);d.bottom>c.bottom?a.scrollTop+=d.bottom-c.bottom:d.top<c.top&&(a.scrollTop+=d.top-c.top);
return b};b.getNeighbor=function(a,d){switch(a){case "left":case "up":d=0>d?d:-d;break;case "right":case "down":d=0<d?d:-d}b.rIndex+=d;var c=b.dIndex;b.dIndex=-1;var f=twve.wrapper.renderedElement(b,null,!0);b.rIndex-=d;b.dIndex=c;return f};b.editNeighbor=function(a,d){twve.tiddler.updateText();twve.tiddler.focus();void 0===d&&(d=1);var c=b.getNeighbor(a,d);c.editText();c.scrollIntoView();return c};b.switchWith=function(a){var d,c;b.start.ndx<a.start.ndx?(d=b,c=a):(d=a,c=b);var f=d.getText(),h=c.getText(),
g=config.options.chktwveCoreManualSave,m=config.options.chktwveCoreManualUpload;config.options.chktwveCoreManualSave=!0;config.options.chktwveCoreManualUpload=!0;c.setText(f);config.options.chktwveCoreManualSave=g;config.options.chktwveCoreManualUpload=m;d.setText(h);d=h.length-f.length;c.start.ndx+=d;c.end.ndx+=d;c=a.dom.innerHTML;a.dom.innerHTML=b.dom.innerHTML;b.dom.innerHTML=c;return b};b.move=function(a,d){var c=b.getNeighbor(a,d);if(!c)return null;twve.tiddler.updateText();twve.tiddler.focusElem(null);
b.switchWith(c);if(1==Math.abs(b.rIndex-c.rIndex))return c;var f;0==c.rIndex?(f=1,a="down"==a?"up":"left"):(f=c.rIndex-1,a="up"==a?"down":"right");for(;b.rIndex!=f;){var h=b.getNeighbor(a,1);b.switchWith(h);b=h}return c};return b}});
merge(twve.leveledElement,{preCreate:twve.leveledElement.create,create:function(a){var b=twve.leveledElement.preCreate.call(this);b.clear=function(){b.sub_elem=null;return b};var e=b.copyFrom;b.copyFrom=function(a){e.apply(this,arguments);b.sub_elem=a.sub_elem;return b};b.getSubElement=function(){return b.dom?b.dom.querySelector(b.dom.nodeName):null};b.subElementEnds=function(a,c){return twve.text.consecutiveLinesEnd(c||b.tiddler.text,a||b.start,!0)};b.dummyLevel=function(a){var b=a.twveSelector(null,
"parent");if(!twve.node.matches(a,b.include))return 0;for(var c=0;;)if(a=a.childNode[0],twve.node.matches(a,b.include))c++;else break;return c};b.firstChildOfDummy=function(a){var b=a&&a.childNodes?a.childNodes[0]:null;return!b||b.nodeName!=a.nodeName?null:b};var d=b.renderedCopy;b.renderedCopy=function(){var a=d.apply(this,arguments);b.sub_elem&&(a&&b!=a)&&(a.sub_elem=a.getSubElement());return a};var c=b.box;b.box=function(){var a=c.apply(this,arguments);if(!config.options.chktwveExtraIncludeSubs){var d=
b.getSubElement();d&&(a.bottom=twve.node.box(d).top,a.height=a.bottom-a.top)}return a};b.removeSelf=function(){if(b.sub_elem){var a=twve.position.create();if(0<b.dIndex){a=twve.position.create(b.start);a.ndx--;var c=b.tiddler.text,a=twve.text.lastIndexOf(c,b.start.matched,a);-1==a.ndx&&1==b.dIndex&&(a.ndx=b.start.ndx-1,a=twve.text.lastIndexOf(c,b.start.matched.substring(1),a));-1<a.ndx&&twve.leveledElement.getLevel(c,a.ndx)==b.getLevel()&&(c=twve.position.create(a),c.ndx++,c=twve.text.indexOf(b.tiddler.text,
"\n",c),c.ndx!=b.start.ndx&&(a.ndx=-1))}-1<a.ndx?(c=b.dom,b.dom=c.previousSibling,c.parentNode.removechild(c),b.dom.appendChild(b.sub_elem),b.rIndex--,b.dIndex--,b.start.matched=a.matched,b.end.ndx=b.start.ndx+1,b.start.ndx=a.ndx):(b.dom.parentNode.replaceChild(b.sub_elem,b.dom),b.dom=b.sub_elem,b.ends())}else b.dom.parentNode.removeChild(b.dom),twve.tiddler.focusElem(null);return b};b.topMostParent=function(){var a=b.dom,c=b.twveSelector(null,"parent");do{var d=twve.node.closest(a.parentNode,c.include);
if(!d)break;a=d}while(1);return a};b.refreshTopLevel=function(a){var c=b.topMostParent();if(b.dom==b.firstChildOfDummy(c))b.dom=c,b.preRefreshSelf.call(this,a);else{var d=b.twveSelector(),f=twve.nodeList.toArray(c.querySelectorAll(d.include)),e=f.length,l=0<e?f.indexOf(b.dom):-1;0<=l&&(b.rIndex-=l+1);var k=config.options.chktwveExtraIncludeSubs;config.options.chktwveExtraIncludeSubs=!0;b.dIndex=-1;b.setElement(c);c=a.charAt(0);f=b.start.matched.charAt(1);if(c!=f){b.end.ndx+=a.length+1;var n=twve.position.create(b.end.ndx+
1);b.end.copyFrom(twve.text.consecutiveLinesEnd(b.tiddler.text,n,!0,f))}f=b.getText();b.preRefreshSelf.call(this,f);b.start.ndx+=f.indexOf(a);b.end.ndx=b.start.ndx+a.length;f=twve.nodeList.toArray(b.dom.querySelectorAll(d.include));a=f.length;c==b.start.matched.charAt(1)?(0<l&&0<a&&(l+=a-e,b.rIndex+=l+1,b.dom=f[l]),twve.tiddler.focus(b)):(e=b.directWrapper(),e.prev=b.clone(),e.prev.dom=f[l-1],e.prev.end.ndx=b.start.ndx-1,e.next=b.clone(),e.next.dom=f[l],e.next.start.ndx=b.end.ndx+1,twve.tiddler.focus(e));
config.options.chktwveExtraIncludeSubs=k}return b};b.refreshThisLevel=function(a){twve.node.wikify(a.substring(b.level),b.dom);return b};b.preRefreshSelf=b.refreshSelf;b.refreshSelf=function(a){-1<a.indexOf("\n")||b.getLevel()!=twve.leveledElement.getLevel(a,0)?b.refreshTopLevel(a):b.refreshThisLevel(a);b.sub_elem&&(b.dom.appendChild(b.sub_elem),b.sub_elem=null);return b};var f=b.ends;b.ends=function(a,c){f.apply(this,arguments);if(0>b.rIndex||0>b.dIndex)return b.end;config.options.chktwveExtraIncludeSubs?
(b.sub_elem=null,b.end.copyFrom(b.subElementEnds(null,c))):b.sub_elem=b.getSubElement();return b.end};return b.created(a)}});
merge(twve.heading,{preWikify:null,wikify:function(a,b,e){if(b&&twve.node.matches(b,"div.board")&&twve.tiddler.cur_editable){var d=twve.foldedSection.macro();twve.node.matches(twve.tiddler.cur_editable.dom,twve.foldedSection.getSelector()+",.foldable")&&-1<a.indexOf("\n!")&&0>a.indexOf(d)&&(a+="\n"+d)}return twve.heading.preWikify.call(this,a,b,e)},preCreateExtra:twve.heading.create,create:function(a){var b=twve.heading.preCreateExtra.call(this);b.subElementEnds=function(a,d,c){if(!c)return b.end};
b.refreshSelf=function(a){var d=null;twve.node.matches(b.dom,".foldable")&&(a+="\n"+twve.foldedSection.macro(),d=b.dom.nextSibling);b.preRefreshSelf.call(this,a);d&&(b.dom.onclick=twve.heading.click,b.dom.parentNode.replaceChild(d,b.dom.nextSibling));return b};return b.created(a)}});
twve.listitem={enableEdit:!0,twveSelector:function(a,b){a||(a=twve.selector.create());b=b?b.toLowerCase():"";return"rendering"==b.substring(0,9)||"parent"==b.substring(0,6)?a.includeSelector("ol,ul"):a.includeSelector("li").excludeSelector("td li,th li,.tagged li,.tidTags li,.listTitle")},leadingChars:function(){return"*#"},markupTags:function(){var a=twve.tags.create(["\n*","\n#"],"\n");a.blockElement=!0;a.clone=function(){return twve.listitem.markupTags()};return a},switchType:function(a){return"*"==
a.charAt(a.length-1)?a.replace(/\*/g,"#"):a.replace(/\#/g,"*")},create:function(a){var b=twve.leveledElement.create();b.index=function(a){a||(a=b.directWrapper().dom.childNodes);return a.indexOf(b.dom.parentNode)};b.markupTags=function(){return twve.listitem.markupTags()};b.clone=function(){return twve.listitem.create(b)};b.twveSelector=function(a,b){return twve.listitem.twveSelector(a,b)};b.getSubElement=function(){return b.dom.querySelectorAll("ol,ul")};var e=b.setElement;b.setElement=function(a){twve.node.matches(a,
"ol,ul")?a.nodeType&&(e.call(this,a.querySelector("li")),b.dom=a):e.call(this,a);return b};b.subElementEnds=function(a,d){var e=b.start.matched,e=e+e.charAt(e.length-1),g=twve.listitem.create(b);g.tags=twve.tags.create([e,twve.listitem.switchType(e)],"\n");a&&g.end.copyFrom(a);d||(d=b.tiddler.text);do if(g.rIndex=-1,g.dIndex=0,g.starts(g.end,d),g.start.ndx==g.end.ndx-1){if(g.ends(null,d),0>g.end.ndx){g.end.ndx=d.length;break}}else break;while(1);return g.end};b.immediatelyFollowingEnds=function(a){a||
(a=b.clone(),a.rIndex=a.dIndex=0);for(;;){a.starts(a.end);if(-1==a.start.ndx||a.start.ndx+1>b.end.ndx)break;b.end.ndx=a.ends().ndx}return b};b.refreshTopLevel=function(a){var d=config.options.chktwveExtraIncludeSubs;config.options.chktwveExtraIncludeSubs=!0;var e=b.topMostParent();b.rIndex-=twve.nodeList.toArray(e.querySelectorAll("li")).indexOf(b.dom);for(var g=b.twveSelector(null,"parent");;){var m=e.previousSibling;if(!twve.node.matches(m,g.include))break;e=m.querySelectorAll("li").length;b.rIndex-=
e;e=m}for(;;){m=e.nextSibling;if(!twve.node.matches(m,g.include))break;m.parentNode.removeChild(m)}b.dIndex=-1;b.setElement(e);g=b.clone();g.rIndex=g.dIndex=0;b.immediatelyFollowingEnds(g);e=a.charAt(0);"*"!=e&&"#"!=e&&(g.end.ndx+=a.length+1,b.end.ndx=g.end.ndx,b.immediatelyFollowingEnds(g));g=b.getText();b.preRefreshSelf.call(this,g);b.start.ndx+=g.indexOf(a);b.end.ndx=b.start.ndx+a.length;g=g.split("\n");a=g.indexOf(a);"*"==e||"#"==e?(b.rIndex+=a,b.start.matched="\n"+e,b.dom=b.dom.querySelectorAll("li")[a],
twve.tiddler.focus(b)):(e=b.directWrapper(),m=e.dom.querySelectorAll("li"),0<a&&(e.prev=twve.listitem.create(),e.prev.dom=m[a-1],e.prev.end.ndx=b.start.ndx-1),a<g.length-1&&(e.next=twve.listitem.create(),e.next.dom=m[a],e.next.start.ndx=b.end.ndx+1),twve.tiddler.focus(e));config.options.chktwveExtraIncludeSubs=d;return b};var d=b.refreshThisLevel;b.refreshThisLevel=function(a){return a.charAt(0)==b.start.matched.charAt(1)?d.call(this,a):b.refreshTopLevel(a)};return b.created(a)}};
twve.blockquote={enableEdit:!0,getSelector:function(){return"blockquote"},twveSelector:function(a,b){a||(a=twve.selector.create());return a.includeSelector(twve.blockquote.getSelector())},blockExampleOpen:function(){return"\n<<<"},blockExampleClose:function(){return"<<<\n"},encloses:function(a,b,e,d){e=twve.position.create(e);d=twve.position.create(d);0>e.ndx&&(e.ndx=0);if(d.ndx<=e.ndx||d.ndx>a.length)d.ndx=a.length;a=a.substring(e.ndx,d.ndx);var c=twve.tags.create(twve.blockquote.blockExampleOpen(),
twve.blockquote.blockExampleClose());for(pos=twve.position.create(e.ndx,c.open);;){pos=c.nextOpenTag(a,pos);if(-1==pos.ndx)break;pos.ndx+=pos.matched.length;if(pos.ndx>b)break;pos.matched=c.close;pos=c.matchedCloseTag(a,pos);if(-1==pos.ndx)return pos.ndx=d.ndx,pos;if(pos.ndx>b)return pos}return null},markupTags:function(a){var b=a&&"inact"==a.toLowerCase().substring(0,5)?twve.tags.create():twve.tags.create(["\n<<<","\n>"],["<<<\n","\n"]);b.blockElement=!0;b.clone=function(){return twve.blockquote.markupTags()};
b.blockExample=function(a){return a.matched==twve.blockquote.blockExampleOpen()};b.notEnclosing=function(){return"string"==typeof b.open&&b.open!=twve.blockquote.blockExampleOpen()};preMatchedCloseTag=b.matchedCloseTag;b.matchedCloseTag=function(a,c){if(b.blockExample(c))return c.ndx++,preMatchedCloseTag.call(this,a,c);var e=twve.text.consecutiveLinesEnd(a,c,!0,">");if(config.options.chktwveExtraIncludeSubs)return e;var h=twve.text.consecutiveLinesEnd(a,c,!1,">");if(e.ndx==h.ndx)return e;var g=twve.leveledElement.getLevel(a,
c.ndx,">");return twve.leveledElement.getLevel(a,h.ndx+1,">")==g?e:h};var e=b.encloses;b.encloses=function(a,c,f,h){var g=e.apply(this,arguments);return g?g:0==c||c>=a.length-1||b.notEnclosing()?null:twve.blockquote.encloses(a,c,f,h)};return b},create:function(a){var b=twve.leveledElement.create();b.markupTags=function(){return twve.blockquote.markupTags()};b.twveSelector=function(a,b){return twve.blockquote.twveSelector(a,b)};var e=b.is;b.is=function(a){if(e.apply(this,arguments))return!0;var d=
b.firstChildOfDummy(a);return d?b.is(d):!1};var d=b.renderingIndex;b.renderingIndex=function(){d.apply(this);for(var a=0;b.sub_elem=b.firstChildOfDummy(b.dom);)a++,b.dom=b.sub_elem;return b.rIndex+=a};var c=b.skipOpenTag;b.skipOpenTag=function(a){var d=c.apply(this,arguments);b.blockExample()&&"\n"==a.charAt(d)&&d++;return d};var f=b.getNeighbor;b.getNeighbor=function(a,b){var d=f.apply(this,arguments);return d&&d.firstChildOfDummy(d.dom)?d.getNeighbor(a,1):d};b.blockExample=function(){return b.tags.blockExample(b.start)};
b.counts=function(a,d){var c=1;if(b.blockExample()){a.remained-=c;a.ndx+=c;if(0==a.remained)return!0;c=twve.position.create(b.start);c.ndx+=c.matched.length;c=twve.text.indexOf(d,"\n>",c);if(-1==c.ndx)return!0;b.start.copyFrom(c)}var e=twve.leveledElement.getLevel(d,0<b.start.ndx?b.start.ndx+1:0,">"),c=e;if(c<a.remained){var f=twve.text.consecutiveLinesEnd(d,b.start.ndx,!0,">"),k=twve.text.consecutiveLinesEnd(d,b.start.ndx,!1,">");do{if(f.ndx==k.ndx)break;var h=twve.leveledElement.getLevel(d,k.ndx+
1,">");if(h>e){c+=h-e;if(c==a.remained){b.start.ndx=k.ndx;break}b.start.ndx=k.ndx;e=h;k=twve.text.consecutiveLinesEnd(d,b.start.ndx,!1,">")}else{e=twve.text.consecutiveLinesEnd(d,k.ndx+1,!1,">");if(e.ndx==f.ndx){b.start.ndx=f.ndx-1;break}b.start.ndx=e.ndx;k=e;e=h}}while(1)}a.remained-=c;a.ndx+=c;return!0};var h=b.subElementEnds;b.subElementEnds=function(){return b.blockExample()?b.end:h.apply(this,arguments)};return b.created(a)}};
twve.tabset={enableEdit:!0,twveSelector:function(a,b){a||(a=twve.selector.create());return a.includeSelector(b&&"parent"==b.toLowerCase().substring(0,6)?"div.tabsetWrapper":"div.tabset")},markupTags:function(){var a=twve.tags.create("<<tabs",">>");a.blockElement=!0;a.clone=function(){return twve.tabset.markupTags()};return a},is:function(a,b){return twve.node.matches(a,twve.tabset.twveSelector(b).include)},includeNextBox:function(a,b){b||(b=twve.node.box(a));if(a.nextSibling){var e=twve.node.box(a.nextSibling);
b.height+=e.height;b.bottom+=e.height}return b},create:function(a){var b=twve.element.create();b.markupTags=function(){return twve.tabset.markupTags()};b.twveSelector=function(){return twve.tabset.twveSelector()};b.box=function(){return twve.tabset.includeNextBox(b.dom)};var e=b.refreshSelf;b.refreshSelf=function(){b.dom.parentNode.removeChild(b.dom.nextSibling);return e.apply(this,arguments)};return b.created(a)}};
twve.span={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("span.marked,span[style*=color],span[style*=font]").excludeSelector(twve.MathJax.getSpanSelector()+","+twve.heading.getSpanSelector())},markupTags:function(){return twve.tags.create("@@","@@")},create:function(a){if(!config.options.chktwveExtraInline)return null;var b=twve.element.create(a);b.markupTags=function(){return twve.span.markupTags()};b.clone=function(){return twve.span.create(b)};b.twveSelector=
function(){return twve.span.twveSelector()};return b}};twve.styledText={preInactiveTags:null,inactiveTags:function(){return twve.styledText.preInactiveTags.apply(this).merge(twve.text.markupTags())},create:function(a){return!config.options.chktwveExtraInline?null:twve.element.create().created(a)}};
twve.styledText.bold={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("strong")},markupTags:function(){var a=twve.tags.create("''","''");a.clone=function(){return twve.styledText.bold.markupTags()};return a},create:function(a){var b=twve.styledText.create();if(!b)return null;b.markupTags=function(){return twve.styledText.bold.markupTags()};b.clone=function(){return twve.styledText.bold.create(b)};b.twveSelector=function(){return twve.styledText.bold.twveSelector()};
return b.created(a)}};
twve.styledText.under={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("u")},markupTags:function(){var a=twve.tags.create("__","__");a.clone=function(){return twve.styledText.under.markupTags()};return a},create:function(a){var b=twve.styledText.create();if(!b)return null;b.markupTags=function(){return twve.styledText.under.markupTags()};b.clone=function(){return twve.styledText.under.create(b)};b.twveSelector=function(){return twve.styledText.under.twveSelector()};return b.created(a)}};
twve.styledText.italic={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("em")},markupTags:function(){var a=twve.tags.create("//","//");a.clone=function(){return twve.styledText.italic.markupTags()};return a},create:function(a){var b=twve.styledText.create();if(!b)return null;b.markupTags=function(){return twve.styledText.italic.markupTags()};b.clone=function(){return twve.styledText.italic.create(b)};b.twveSelector=function(){return twve.styledText.italic.twveSelector()};
var e=b.counts;b.counts=function(a,c){return 5<b.start.ndx&&"http:"==c.substring(b.start.ndx-5,b.start.ndx).toLowerCase()?!1:e.apply(this,arguments)};return b.created(a)}};
twve.styledText.strike={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("strike")},markupTags:function(){var a=twve.tags.create("--","--");a.clone=function(){return twve.styledText.strike.markupTags()};return a},create:function(a){var b=twve.styledText.create();if(!b)return null;b.markupTags=function(){return twve.styledText.strike.markupTags()};b.clone=function(){return twve.styledText.strike.create(b)};b.twveSelector=function(){return twve.styledText.strike.twveSelector()};
return b.created(a)}};twve.img={enableEdit:!0,twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector("img")},markupTags:function(){return twve.tags.create(["[img","[>img","[<img"],"]]")},create:function(a){return twve.element.create().created(a)}};
twve.cssWrapper={enableEdit:!0,is:function(a,b){var e=a&&a.classList&&1==a.classList.length&&("SPAN"==a.nodeName||"DIV"==a.nodeName);e&&b&&(b.include=a.nodeName+"."+a.classList.item(0));return e},markupTags:function(a){var b=a&&1==a.classList.length,e=twve.tags.create("{{"+(b?a.classList.item(0)+"{":""),"}}}");e.blockElement=b&&"DIV"==a.nodeName;e.clone=function(){return twve.cssWrapper.markupTags().merge(e)};return e},create:function(a){a=twve.element.create(a);if(0>a.start.ndx)return null;a.is=
function(a){return twve.cssWrapper.is(a)};return a}};
twve.MathJax={enabled:!0,getSpanSelector:function(){return"span.MathJax_Preview,span[id*=MathJax] span"},nextFormula:function(a,b){var e=twve.MathJax.displayed.markupTags().merge(twve.MathJax.inline.markupTags()),d={};d.start=twve.position.create(b);d.start=e.nextOpenTag(a,d.start);-1<d.start.ndx&&(d.end=twve.position.create(d.start),d.end.ndx+=d.start.matched.length,d.end=e.exactCloseTag(d.start.matched).matchedCloseTag(a,d.end),d.expression=a.substring(d.start.ndx,d.end.ndx+d.end.matched.length));
d.getSource=function(){return"\\begin{"!=d.start.matched?d.expression.substring(d.start.matched.length,d.expression.length-d.end.matched.length):d.expression};return d},preBox:null,box:function(a,b,e){var d=twve.MathJax.displayed.getMathNode(a);d||(d=twve.MathJax.inline.getMathNode(a));return twve.MathJax.preBox.call(this,d||a,b,e)},folded:null,subfoldable:null,subfolded:null,beforeMath:function(a){var b=twve.tiddler.twveFoldableSelector();twve.MathJax.folded=null;if(twve.node.matches(a[1],b.include))twve.node.isVisible(a[1])||
(twve.MathJax.folded=a[1],twve.node.show(a[1]));else{var e=twve.node.parents(a[1],b.include);if(e)for(var d=0,c=e.length;d<c;d++)if(!twve.node.isVisible(e[d])){twve.MathJax.folded=e[d];twve.node.show(e[d]);break}twve.node.closest(a[1],twve.tiddler.displaySelector())&&twve.node.matches(twve.tiddler.wrapperSelectors().include)&&(twve.MathJax.subfoldable=twve.wrapper.findElements(a[1],b),twve.MathJax.subfolded=twve.wrapper.recordFoldable(twve.MathJax.subfoldable),twve.MathJax.subfoldable&&twve.node.show(twve.MathJax.subfoldable))}},
afterMath:function(a){twve.MathJax.subfoldable&&twve.wrapper.restoreFoldable(twve.MathJax.subfoldable,twve.MathJax.subfolded);twve.MathJax.folded&&twve.node.hide(twve.MathJax.folded)},checkReady:function(){return config.extensions.MathJax&&"undefined"!==typeof MathJax?(story.displayTiddler==config.extensions.MathJax.displayTiddler&&(story.displayTiddler=config.extensions.MathJax.displayTiddler_old,MathJax.Hub.Config({"HTML-CSS":{linebreaks:{automatic:!0}},SVG:{linebreaks:{automatic:!0}}}),MathJax.Hub.Register.MessageHook("Begin Process",
twve.MathJax.beforeMath),MathJax.Hub.Register.MessageHook("End Process",twve.MathJax.afterMath)),!0):!1},render:function(a){if(a){var b=a.parentNode?a.parentNode.querySelector(".title"):null;b&&MathJax.Hub.Queue(["Typeset",MathJax.Hub,b]);config.options.chktwveExtraMathAutoNumber?twve.node.matches(a,"div.viewer")&&(MathJax.Hub.Config({TeX:{equationNumbers:{autoNumber:"AMS"}}}),MathJax.Hub.Queue(["resetEquationNumbers",MathJax.InputJax.TeX])):MathJax.Hub.Config({TeX:{equationNumbers:{autoNumber:"none"}}});
MathJax.Hub.Queue(["Typeset",MathJax.Hub,a])}},prePrepareElements:null,prepareElements:function(a){twve.MathJax.checkReady()&&!twve.node.matches(a.dom,"div.board")&&twve.MathJax.render(a.dom);twve.MathJax.prePrepareElements.call(this,a);return a},apply:function(a,b,e){"undefined"!==typeof MathJax?MathJax.Hub.Queue(function(){a.apply(b,e)}):a.apply(b,e)},preFold:null,fold:function(){twve.MathJax.apply(twve.MathJax.preFold,this,arguments)},preHeadingClick:null,headingClick:function(){twve.MathJax.apply(twve.MathJax.preHeadingClick,
this,arguments)},preRestoreFoldable:null,restoreFoldable:function(){twve.MathJax.apply(twve.MathJax.preRestoreFoldable,this,arguments)},preWikify:null,wikify:function(a,b){b=twve.MathJax.preWikify.apply(this,arguments);if(twve.MathJax.enabled&&"undefined"!=typeof MathJax){var e=null;b.nodeType?e=3==b.nodeType?b.parentNode:b:0<b.length&&(e=b[0].parentNode);if(e)try{MathJax.Hub.Queue(["Typeset",MathJax.Hub,e])}catch(d){console.log(d)}}return b},hasID:function(a){return a&&a.id&&"MathJax"==a.id.substring(0,
7)},create:function(a){var b=twve.element.create();b.filter=function(a){if(a.nodeType)return b.getMathNode(a);for(var d=[],c=0,f=a.length;c<f;c++){var h=b.getMathNode(a[c]);h&&(d[d.length]=h,h==a[c]?c++:h==a[c].nextSibling&&(c+=2))}return 0==d.length?null:1<d.length?d:d[0]};return b.created(a)}};
twve.MathJax.displayed={preLastIndexOf:null,lastIndexOf:function(a){for(var b=twve.MathJax.displayed.preLastIndexOf.apply(this,arguments),e=b,d=e;e;)"[["==e.matched&&"\\"==a.charAt(e.ndx-1)?e==b?d=b=e.next:d.next=e.next:d=e,e=e.next;return b},enableEdit:!0,getSelector:function(){return"div.MathJax_Display"},twveSelector:function(a,b){a||(a=twve.selector.create());var e=twve.MathJax.displayed.getSelector();return a.includeSelector(e+(b&&"render"==b.toLowerCase().substring(0,6)?"":","+e+" span"))},
markupTags:function(){var a=twve.tags.create(["\\[","$$","\\begin{"],["\\]","$$","\\end{"]);a.blockElement=!0;a.clone=function(){return twve.MathJax.displayed.markupTags()};var b=a.matchedCloseTag;a.matchedCloseTag=function(e,d,c,f){if("\\end{"!=a.close)return b.apply(this,arguments);var h=e.indexOf("}",d.ndx),g=e.indexOf("{",d.ndx);0>g||g>h?g=d.ndx:g++;for(var m=e.substring(g,h);;){d=b.call(this,e,d,c,f);h=e.indexOf("}",d.ndx);g=d.ndx+5;if(m==e.substring(g,h))return d;d.ndx=h+1}};return a},getMathNode:function(a){var b=
twve.MathJax.displayed.getSelector();switch(a.nodeName){case "SPAN":if(twve.MathJax.hasID(a))return twve.node.closest(a.parentNode,b);var e=a.childNodes;a=e&&2<e.length?e[1]:a.nextSibling;return twve.node.matches(a,b)?a:null;case "DIV":return twve.node.matches(a,b)?a:null;case "SCRIPT":if(twve.MathJax.hasID(a))return a=a.previousSibling,twve.node.matches(a,b)?a:null}return null},is:function(a){return twve.MathJax.displayed.getMathNode(a)},create:function(a){var b=twve.MathJax.create();b.markupTags=
function(){return twve.MathJax.displayed.markupTags()};b.clone=function(){return twve.MathJax.displayed.create(b)};b.twveSelector=function(a,b){return twve.MathJax.displayed.twveSelector(a,b)};b.multiLine=function(){return!0};var e=b.setElement;b.setElement=function(a){a=twve.MathJax.displayed.getMathNode(a);e.call(this,a);0<=b.end.matched.toLowerCase().indexOf("end")&&(a=b.tiddler.text.indexOf("}",b.end.ndx),-1<a&&(a++,b.end.ndx=a));return b};b.getMathNode=function(a){return twve.MathJax.displayed.getMathNode(a)};
b.removeSiblings=function(){if("\\end{"==b.end.matched){var a=b.dom.parentNode;a.removeChild(b.dom.previousSibling);a.removeChild(b.dom.nextSibling);return!0}return!1};var d=b.replaceWith;b.replaceWith=function(a,c){!b.removeSiblings()&&!c&&(c=b.dom.parentNode);return d.call(this,a,c)};var c=b.removeSelf;b.removeSelf=function(){b.removeSiblings()||(b.dom=b.dom.parentNode);return c.apply(this,arguments)};return b.created(a)}};
twve.MathJax.inline={enableEdit:!0,getSelector:function(){return"span.MathJax_Preview"},twveSelector:function(a){a||(a=twve.selector.create());return a.includeSelector(twve.MathJax.inline.getSelector()).excludeSelector(twve.MathJax.displayed.getSelector()+" span")},markupTags:function(){var a=twve.tags.create(["\\(","\\ref{","\\eqref{"],["\\)","}","}"]);a.clone=function(){return twve.MathJax.inline.markupTags()};return a},getMathNode:function(a){switch(a.nodeName){case "SPAN":if(twve.MathJax.hasID(a)){do{if("-Element"==
a.id.substring(7,15))return a;do a=a.parentNode;while(a&&!a.id)}while(a)}else{var b=a.childNodes;a=b&&2<b.length?b[1]:a.nextSibling;return twve.MathJax.hasID(a)?a:null}case "SCRIPT":if(twve.MathJax.hasID(a))return a.previousSibling}return null},is:function(a){return twve.MathJax.inline.getMathNode(a)},create:function(a){var b=twve.MathJax.create();b.markupTags=function(){return twve.MathJax.inline.markupTags()};b.clone=function(){return twve.MathJax.inline.create(b)};b.twveSelector=function(){return twve.MathJax.inline.twveSelector()};
var e=b.setElement;b.setElement=function(a){return e.call(this,twve.MathJax.inline.getMathNode(a))};b.getMathNode=function(a){return twve.MathJax.inline.getMathNode(a)};var d=b.replaceWith;b.replaceWith=function(a,c){return d.call(this,a,c||b.dom.parentNode)};var c=b.removeSelf;b.removeSelf=function(){b.dom=b.dom.parentNode;return c.apply(this,arguments)}}};
merge(twve.tiddlerTitle,{titleOfWrapper:function(a,b){var e="",d=twve.MathJax.inline.twveSelector(),c=twve.MathJax.displayed.twveSelector(),f=null;if(!a.childNodes)return e;for(var h=0,g=a.childNodes.length;h<g;h++){var m=a.childNodes[h];twve.node.matches(m,d.include)?f=twve.MathJax.inline.markupTags():twve.node.matches(m,c.include)?f=twve.MathJax.displayed.markupTags():twve.node.matches(m,"script")&&f?(e+=f.open[b]+m.textContent+f.close[b],f=null):e+=m.textContent||m.nodeValue}return e},preCreateExtra:twve.tiddlerTitle.create,
create:function(a){var b=twve.tiddlerTitle.preCreateExtra.call(this);b.titleOfWrapper=function(){var a=twve.tiddlerTitle.titleOfWrapper(b.dom,0);b.tiddler=twve.tiddler.get(a);b.tiddler||(a=twve.tiddlerTitle.titleOfWrapper(b.dom,1));return a};return b.created(a)}});
merge(twve.wrapper,{preCreateExtra:twve.wrapper.create,create:function(){var a=twve.wrapper.preCreateExtra.apply(this,arguments);a.editNeighbor=function(b,d){return a};var b=a.refreshSelf;a.refreshSelf=function(d){if(a.curNode){var c=twve.tiddler.getPreviewedNodes(d),e=a.curNode.dom;if(e)if(e.nodeType)twve.node.replace(c,e);else{for(var f=1,g=e.length;f<g;f++)a.dom.removeChild(e[f]);twve.node.replace(c,e[0],a.dom)}else if(a.curNode.next){f=0;for(g=c.length;f<g;f++)a.dom.insertBefore(c[0],a.curNode.next)}else{f=
0;for(g=c.length;f<g;f++)a.dom.appendChild(c[0])}return a}if(a.next){c=twve.tiddler.getPreviewedNodes(d);e=a.dom.firstChild;f=0;for(g=c.length;f<g;f++)a.dom.insertBefore(c[0],e);a.next=null;return a}if(a.prev){c=twve.tiddler.getPreviewedNodes(d);f=0;for(g=c.length;f<g;f++)a.dom.appendChild(c[0]);a.prev=null;return a}return b.apply(this,arguments)};var e=a.needsRefresh;a.needsRefresh=function(b,c,d){var f=e.apply(this,arguments);if(f&&b.curNode){var g=a.dom.childNodes;if(c==d)if(1==b.curNode.index.length){var h=
b.curNode.index[0];a.collectNode(g[h],h)}else for(var r=0,s=b.curNode.index.length;r<s;r++)h=b.curNode.index[r],a.collectNode(g[h],h);else{s=b.curNode.index[0];r=b.curNode.index[b.curNode.index.length-1];if(c){if(twve.node.matches(g[b.rIndex],twve.foldedSection.getSelector()))return!1;h=s;f=s+b.rIndex}else{if(void 0===a.rIndex)return f;if(s<a.rIndex||r>a.rIndex+a.dom.childNodes.length)return!1;h=s;f=0}for(;h<=r;h++,f++)a.collectNode(g[f],f);return!0}}return f};a.getOpen=function(){return a.curNode&&
a.curNode.start?a.curNode.start.ndx:a.prev?a.prev.end.ndx:a.start.ndx};a.getClose=function(){return a.curNode&&a.curNode.end?a.curNode.end.ndx:a.next?a.next.start.ndx:a.end.ndx};a.getLeadingBR=function(){var b=a.curNode?a.curNode.dom.length?a.curNode.dom[0]:a.curNode.dom:null;return b&&"BR"==b.nodeName?b:null};a.getEndingBR=function(){var b=a.curNode?a.curNode.dom.length?a.curNode.dom[a.curNode.dom.length-1]:a.curNode.dom:null;return b&&"BR"==b.nodeName?b:null};var d=a.checkNewLines;a.checkNewLines=
function(b,c,e){if(a.curNode)d.apply(this,arguments),a.remove_ending_BR&&(a.newline_close=!1),a.remove_leading_BR&&(a.newline_open=!1);else{if(a.next||a.prev)return d.apply(this,arguments);a.newline_open=!1;a.newline_close=!1}};var c=a.getText;a.getText=function(b){if(b&&0<=b.toLowerCase().indexOf("content"))return c.apply(this,arguments);var d=a.getOpen(),e=a.getClose();return a.getEndingBR()?a.tiddler.text.substring(d,e):c.call(this,d,e)};var f=a.setText;a.setText=function(b){var c=a.getOpen(),
d=a.getClose();!a.curNode&&c>=d&&(c==a.start.ndx?"\n"!=b.charAt(b.length-1)&&(b+="\n"):"\n"!=b.charAt(0)&&(b="\n"+b));return f.call(this,b,c,d)};var h=a.updateText;a.updateText=function(b,c){var d=a.getEndingBR();if(d&&b[0])if("\n"!=b[0].charAt(b[0].length-1)){if(d=d.nextSibling){var e=a.getText("content"),f=twve.tiddler.tagsInfo(d,e,a.curNode.end.ndx);a.curNode.end.ndx=f.end.ndx+f.end.len;b[0]+=e.substring(f.start.ndx,a.curNode.end.ndx);a.remove_ending_BR=!0;"BR"!=d.nodeName&&(a.collectNode(d,a.curNode.index[a.curNode.index.length-
1]+1,f),a.curNode.end.ndx=f.end.ndx+f.end.len)}a.remove_leading_BR="\n"==b[0].charAt(0)&&a.getLeadingBR()}else a.remove_ending_BR=!0;return h.call(this,b,c)};var g=a.editText;a.editText=function(b,d,c){if(a.curNode){var e=twve.tiddler.focusBox();if(e&&0==e[0].width)return twve.tiddler.createEditableWrapper(a.curNode.dom).editText(b)}else a.tiddler||(a.tiddler=store.createTiddler(a.wrapper_title),a.tiddler.tags.push("autoCreate"),a.tiddler.creator=config.options.txtUserName);return g.apply(this,arguments)};
return a}});merge(twve.sliderPanel,{isButton:function(a,b){return(b||"A"==a.nodeName)&&twve.node.matches(a.nextSibling,twve.sliderPanel.getSelector())},preBox:null,box:function(a){var b=twve.sliderPanel.preBox.apply(this,arguments);if(twve.sliderPanel.isButton(a)){var e=twve.sliderPanel.preBox.call(this,a.nextSibling);e&&(b.left>e.left&&(b.left=e.left),b.right<e.right&&(b.right=e.right),b.bottom=e.bottom,b.width=b.right-b.left,b.height=b.bottom-b.top)}return b}});
merge(twve.foldedSection,{titleOfTiddler:function(a){var b=twve.foldedSection.enableEdit;twve.foldedSection.enableEdit=!1;a=twve.node.closest(a.parentNode,twve.tiddler.wrapperSelectors().include);a=twve.tiddler.titleOfWrapper(a);twve.foldedSection.enableEdit=b;return a},titleOfSection:function(a){var b=a.previousSibling;if(!b)return"";a=twve.foldedSection.enableEdit;twve.foldedSection.enableEdit=!1;b=twve.heading.getTitle(b,!0);twve.foldedSection.enableEdit=a;return b},titleOfWrapper:function(a){var b=
a.getAttribute("fullTitle");b||(b=twve.foldedSection.titleOfTiddler(a)+config.textPrimitives.sectionSeparator+twve.foldedSection.titleOfSection(a),a.setAttribute("fullTitle",b));return b},wrapperFromTitle:function(a,b,e){b=twve.nodeList.querySelectorAll(b,twve.foldedSection.getSelector());if(!b)return null;for(var d=[],c=0,f=b.length;c<f;c++){var h=twve.foldedSection.titleOfWrapper(b[c]),g=twve.text.tiddlerSection(h),h=twve.text.tiddlerTitle(h);if(h==a)if(e){if(!g||g==e)d[d.length]=b[c]}else d[d.length]=
b[c]}return d},preHeadingCreate:null,headingCreate:function(a){var b=twve.foldedSection.preHeadingCreate.apply(this,arguments),e=b.removeSelf;b.removeSelf=function(){var a=b.dom.nextSibling,c=twve.foldedSection.getSelector();if(twve.node.matches(a,c)){twve.node.show(a);var f=a.childNodes,h=b.dom.previousSibling,g=a.parentNode;if(twve.node.matches(h,c)){c=twve.node.isVisible(h);twve.node.show(h);for(var m=0,p=f.length;m<p;m++)h.appendChild(f[0]);c||twve.node.hide(h)}else{m=0;for(p=f.length;m<p;m++)g.insertBefore(f[0],
a)}g.removeChild(a)}return e.apply(this)};return b},preInactiveTags:null,inactiveTags:function(){var a=twve.foldedSection.preInactiveTags.apply(this);return twve.blockquote?twve.blockquote.markupTags("inactive").merge(a):a},releaseInactiveTags:function(){twve.tags.inactiveTags=twve.foldedSection.preInactiveTags},create:function(a){var b=twve.transcludedElem.create(a)||twve.wrapper.create();b.clone=function(){return twve.foldedSection.create(b)};b.findWrappers=function(a){if(!b.wrapper)if(b.wrapper=
a.findWrappers()){if(a=twve.node.parents(b.dom,twve.tiddler.wrapperSelectors().include))for(var c=0,e=a.length;c<e;c++){var h=b.wrapper.indexOf(a[c]);-1<h&&b.wrapper.splice(h,1)}}else console.log("wrapper not found for "+twve.node.info(b.dom)+" wrapper:"+b.wrapper);return b.wrapper};var e=b.box;b.box=function(){return b.isVisible()?e.apply(this,arguments):twve.node.box(b.dom.previousSibling)};b.getHeading=function(a){a||(a=b.dom);return!a?null:(a=a.previousSibling)?twve.heading.create(a):null};b.setElement=
function(a){b.dom=a;var c=b.getHeading();if(!c)return null;b.wrapper_title=c.wrapper_title;b.tiddler=c.tiddler;b.findWrappers(c);b.start.ndx=c.end.ndx;b.rIndex=c.rIndex+1;if(!c.tiddler)return b;var e=twve.foldedSection.macro();b.end.ndx=c.tiddler.text.indexOf(e,b.start.ndx);b.end.ndx=-1<b.end.ndx?b.end.ndx+e.length:b.tiddler.text.length;twve.node.closest(a,twve.blockquote.getSelector())&&(e=c.tiddler.text.indexOf(twve.blockquote.blockExampleClose(),b.start.ndx),-1<e&&e<b.end.ndx&&(b.end.ndx=e));c.rIndex=
-1;c.dIndex=a.querySelectorAll(twve.heading.getSelector()).length;c.starts(b.start);-1<c.start.ndx&&c.start.ndx<b.end.ndx&&(b.end.ndx=c.start.ndx);return b};return b.created(a)}});
//}}}
// @@
This tiddler gives detailed information about the ''twve.leveledElement'', an object representing all editable __leveledElement__ in ~TiddlyWiki, including //lists//, //headings//, and //blockquotes//.
This tiddler gives detailed information about the ''twve.selector'', an object representing CSS selectors in ~TiddlyWiki.
/***
|editable|k
|''Name:''|twve.table|
|''Description:''|Table rendering and view mode editing in ~TiddlyWiki environment|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.table.min / http://twve.tiddlyspace.com/#twve.table.min <br>* (regular) http://twve.tiddlyspot.com/#twve.table / http://twve.tiddlyspace.com/#twve.table|
|''Type:''|plugin|
|''Version:''|3.2.6|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core|
!!Features:
* Table rendering and view mode editing features in ~TiddlyWiki environment.
!!Usage:
!!Options
Look for [[twve.table Options]] in the Options Panel.
!!Examples
[[twve.table Examples]]
!!Todo:
!!!Revision history
!!!! 2016/04/30 [3.2.6]
* Saves options in SystemSettings tiddler.
** If you disable autoSave option, you will need to manually save the tiddler after changing a ''twve'' option.
* Starts using strict mode.
* Bug fix
** for table captions.
!!!! 2015/11/06 [3.2.5]
* Avoided handling tables created by Handsontable.
* Bug fix
** for tables with an empty first cell (in the first row);
*** Such a table was correctly located at its beginning but incorrectly at its end.
** for column-wise copy-and-pasting;
** for text copy-and-pasting.
!!!! 2015/06/15 [3.2.4]
* Calls the """.changed()""" event handler after caption change.
* Bug fix
** for previewing a whole table.
!!!! 2015/03/23 [3.2.3]
* Bug fix
** for editing neighbor cell with keyboard navigation.
!!!! 2015/02/13 [3.2.2]
* Replaced regular expression with string operations whenever easy, hoping to save some time.
* Bug fix
** for skipping <noedit> cells with keyboard navigation;
** for working with TableSortingPlugin or SortableGridPlugin.
!!!! 2014/12/22 [3.2.1]
* Bug fix
** for correct row height containing spanned cells in ~TWEdit (iOS).
*** The heights of a spanned row in ~TWEdit are inconsistent with other browsers.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
* Bug fix
** for editing transcluded content in table cells.
!!!! 2014/08/03 [3.1.0]
* Started to remove jQuery dependencies for migrating to TW5.
* Bug fix
** for obtaining the bounding box of elements.
!!!! 2014/06/02 [3.0.3]
* Bug fixes
** for missing cells;
** for missing the table at the very beginning of a tiddler.
!!!! 2014/05/23 [3.0.2]
* Bug fixes
** for focusing behavior;
** for resizing after changed;
** for working with sorting plugins (SortableGridPlugin or TableSortingPlugin);
*** A general error in the code caused the sorting method to work always on the very 1st table in the tiddler.
*** Transposition turns a header row into a TH column, but not vice versa, causing a failure in TableSortingPlugin.
** for leaving shadowed tiddlers alone.
!!!! 2014/05/14 [3.0.1]
* Added ''twve.table'' Options Menu to the table menu (at the top-right corner of the table when activated).
!!!! 2014/05/13 [3.0.0]
* Improved features
** {{{partial refreshing}}} features to cover whole table refreshing;
** handling of missing cells;
*** Previous versions worked for only ''one'' missing cell per row, __this version works for more__.
** visually consistent keyboard navigation with spanned cells.
*** The browser-assigned cell indexes are not consistent with what we see and cause confusion quite easily. The ''twve.table'' fixed that issue and gives a visually consistent experience with keyboard navigation.
* Added a menu (top-right corner) for
** transposition and
*** Works for tables with spanned cells, too.
** recalculation.
* Bug fixes for
** insertion/deletion of rows/columns;
** transposition with spanned cells.
!!!! 2014/01/18
* Collected from [[TWtid|http://twtable.tiddlyspace.com/#TWtid.2.0.10]] and [[TWted|http://twtable.tiddlyspace.com/#TWted.2.0.10]], adapted to the new code structure.
!!!Code
!!!The very beginning
***/
//{{{
(function(){
"use strict";
if ( typeof twve == 'undefined' ) {
alert('twve.table: This plugin needs twve.core to function properly. Please install twve.core before this one.');
}
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve.table = {
major: 3, minor: 2, revision: 6,
date: new Date('2016/04/30')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.twveTableOptions = {
init : function () {
// twve.tiddler methods
twve.table.preReadOnly = twve.tiddler.readOnly;
twve.tiddler.readOnly = twve.table.readOnly;
twve.table.preTextPasted = twve.tiddler.textPasted;
twve.tiddler.textPasted = twve.table.textPasted;
//twve.table.prePreviewEditBox = twve.tiddler.previewEditBox;
//twve.tiddler.previewEditBox = twve.table.previewEditBox;
twve.table.preGetOptionsMenu = twve.tiddler.getOptionsMenu;
twve.tiddler.getOptionsMenu = twve.table.getOptionsMenu;
twve.table.preFocusTarget = twve.tiddler.focusTarget;
twve.tiddler.focusTarget = twve.table.focusTarget;
// twve.node methods
twve.table.preWikify = twve.node.wikify;
twve.node.wikify = twve.table.wikify;
// twve.leveledElement methods
twve.table.preGetLevel = twve.leveledElement.getLevel;
twve.leveledElement.getLevel = twve.table.getLevel;
// twve.wrapper methods
twve.table.prePrepareElements = twve.wrapper.prepareElements;
twve.wrapper.prepareElements = twve.table.prepareElements;
twve.checkOption('chktwveTableEnabled','true');
twve.checkOption(
'chktwveTableEditAll',
(config.options.chkTWtedEditAllTables || false)+''
);
twve.checkOption(
'txttwveTableMinCellWidth',
(config.options.txtTWtidMinCellWidth || '8')
);
twve.checkOption(
'chktwveTableMultiLine',
(config.options.chkTWtidMultiLine || false)+''
);
twve.checkOption('chktwveTableTranspose','false');
twve.checkOption(
'chktwveTableIncludeCSS',
(config.options.chkTWtedIncludeCSS || true)+''
);
merge(config.optionsDesc, {
chktwveTableEnabled:"Enable ''twve.table''.",
chktwveTableEditAll:'Edit all tables. If true, all tables are editable. Otherwise only tables with class name "editable" are editable.',
chktwveTableMultiLine:'Multi-line rendering mode. If true, a table cell can have multi-lined content (with <br /> as line-break). Lists in a table cell is hence possible. Default to false.',
txttwveTableMinCellWidth:'Minimum cell width in characters. Default value is 8.',
chktwveTableTranspose:'Enable transposition. Default to false.',
chktwveTableIncludeCSS:'Include cell-wide CSS statements in the edit box.'
});
// prepare options panel
var optionsTitle = 'twve.table Options';
var txt = config.shadowTiddlers['OptionsPanel'];
var twvecoreopt = '[[twve.core Options';
var p = txt.indexOf(twvecoreopt);
if ( p >= 0 ) {
p = txt.indexOf(']]\n',p+2)+3;
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'[['+optionsTitle+'|'+optionsTitle+']]\n'
+txt.substring(p);
}
merge(config.shadowTiddlers,{
'twve.table Options':'<<twveTableOptions>>'
});
// Register editable
twve.tiddler.registerElement(twve.table);
},
order : {
chktwveTableEnabled:0,
chktwveTableEditAll:1,
chktwveTableMultiLine:2,
txttwveTableMinCellWidth:3,
chktwveTableTranspose:4,
chktwveTableIncludeCSS:5
},
handler : function(place) {
// Collect all the twve.table options for users to play with.
config.macros.twveCoreOptions.showOptionsTable(
place,
"''twve.table'' Options",
'twveTable',
config.macros.twveTableOptions.order
);
}
};
//}}}
/***
!!! Macro <<noedit>>
***/
//{{{
config.macros.noedit={
handler : function(place){
addClass(place,"noedit");
}
};
//}}}
/***
!!! twve.text extra features
***/
//{{{
merge(twve.text,{
//}}}
/***
!!!! twve.text.lineBreakToBr
***/
//{{{
lineBreakToBr : function(txt) {
return txt ? txt.replace(/\n/mg, ' <br>') : txt;
},
//}}}
/***
!!!! twve.text.brToLineBreak
***/
//{{{
brToLineBreak : function(txt) {
return txt
? txt.replace(/ <br>/mg,'\n')
.replace(/<br>/mg, '\n')
.replace(/ <br \/>/mg, '\n')
.replace(/<br \/>/mg, '\n')
: txt;
},
//}}}
/***
!!!! twve.text.nodeWideStyleText
***/
//{{{
nodeWideStyleText : function ( ctxt ) {
// Returns the cell-wide style text, if any.
var p = twve.text.skipStyleText ( ctxt );
return (p>0 ? ctxt.substring(0,p) : '');
}
});
//}}}
/***
!!! twve.node extra features
***/
//{{{
merge(twve.node,{
//}}}
/***
!!!! twve.node.copyMargin
***/
//{{{
copyMargin : function (dest,src,css) {
if ( ! css ) css = window.getComputedStyle(src);
dest.style.marginTop = src.style.marginTop ||
css.getPropertyValue('margin-top');
dest.style.marginRight = src.style.marginRight ||
css.getPropertyValue('margin-right');
dest.style.marginBottom = src.style.marginBottom ||
css.getPropertyValue('margin-bottom');
dest.style.marginLeft = src.style.marginLeft ||
css.getPropertyValue('margin-left');
return dest;
},
//}}}
/***
!!!! twve.node.copyPadding
***/
//{{{
copyPadding : function (dest,src,css) {
if ( ! css ) css = window.getComputedStyle(src);
dest.style.paddingTop = src.style.paddingTop ||
css.getPropertyValue('padding-top');
dest.style.paddingRight = src.style.paddingRight ||
css.getPropertyValue('padding-right');
dest.style.paddingBottom = src.style.paddingBottom ||
css.getPropertyValue('padding-bottom');
dest.style.paddingLeft = src.style.paddingLeft ||
css.getPropertyValue('padding-left');
return dest;
}
});
//}}}
/***
!!! twve.table
***/
//{{{
twve.table = {
//}}}
/***
!!!! twve.leveledElement.getLevel
***/
//{{{
preGetLevel : null,
getLevel : function(txt,start,ch){
if ( ! ch ) ch = txt.charAt(start);
return ch == '|'
? 1 : twve.table.preGetLevel.call(this,txt,start,ch);
},
//}}}
/***
!!!! twve.table.readOnly
***/
//{{{
preReadOnly : null,
readOnly : function(title){
var str = 'twve.table--example';
return (title && title.toLowerCase().substring(0,str.length)==str)
? false
: twve.table.preReadOnly.apply(this,arguments);
},
//}}}
/***
!!!! twve.table.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function (selector,scope) {
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
//var inc = 'table,'+twve.table.wrapper.getSelector();
var inc = 'table';
if ( scope && scope.toLowerCase().indexOf(inc) < 0 )
inc +=
',tr,th,td' + ',' +
//twve.table.wrapper.getSelector('inner') + ',' +
twve.table.wrapper.getSelector('all') + ',' +
twve.table.refBarV.getSelector() + ',' +
twve.table.refBarH.getSelector();
return selector.includeSelector(inc).excludeSelector(
'.syntaxhighlighter table'
).excludeSelector('table.htCore');
},
//}}}
/***
!!!! twve.table.is
***/
//{{{
is : function(node){
return twve.node.matches(
node,'table,'+twve.table.wrapper.getSelector()
);
},
//}}}
/***
!!!! twve.table.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'\n|',
'\n'
);
// blockElement
tags.blockElement = true;
// clone
tags.clone = function(){
// Optional, may save a tiny bit of time.
return twve.table.markupTags();
};
// matchedCloseTag
tags.matchedCloseTag = function (txt, pos) {
// Replacing twve.tags.matchedCloseTag for tables.
// This method is used in twelem.ends method, which
// searches for the ending index of an element in txt.
// We replace it because the general rules implemented
// in twelem.ends do not apply to tables.
// A table contains several consecutive lines of text
// beginning with a vertical bar. This situation can be
// very well handled by twve.text.consecutiveLinesEnd()
// function so we let it do the hard work.
var pend = twve.text.consecutiveLinesEnd(txt,pos,true);
// But we need to take care of one thing after that.
// A valid table row must contain at least two vertical
// bars. If the last line contains only one beginning
// vertical bar, however, twve.text.consecutiveLinesEnd()
// function still considers it as part of the table.
// We check for that case and take care of it here.
while ( true ) {
var pn = txt.lastIndexOf('\n',pend.ndx-1);
var lntxt = txt.substring(pn+1,pend.ndx).split('|');
if ( lntxt.length < 3 ) pend.ndx = pn;
else break;
}
return pos.copyFrom(pend);
};
return tags;
},
//}}}
/***
!!!! twve.table.prepareElements
***/
//{{{
prePrepareElements : null,
prepareElements : function(twwrap){
// Prepare tables in the wrapper. In this method the twve.table
// does multi-line rendering and preparation for sorting.
if ( ! twve.table.prePrepareElements.apply(this,arguments)
|| ! config.options.chktwveTableEnabled
|| ! twwrap.tiddler )
return null;
// Prepare tables
var tables = twwrap.findElements(
twve.table.twveSelector(null,'table')
);
if ( tables ) {
//if ( twve.MathJax ) twve.MathJax.enabled = false;
var txt = twwrap.is('div.board')
? twve.tiddler.getEditInfo().text[0]
: null;
for ( var n=0,len=tables.length; n<len; n++ ){
// Allow TableSortingPlugin to prepare
if ( config.tableSorting
&& config.tableSorting.refresh ) {
config.tableSorting.refresh.call(this,twwrap.dom);
}
// Here we create a new twtable for each table, instead of
// using one twtable variable for all tables, as we did
// before, because sometimes the reference bars don't
// get correct dimensions when there are multiple tables.
var twtable = twve.table.create();
twtable.wrapper_title = twwrap.wrapper_title;
twtable.direct_wrapper = twwrap;
twtable.wrapper = [twtable.direct_wrapper.dom];
twtable.tiddler = txt ? {text: txt} : twwrap.tiddler;
twtable.rIndex = n;
twtable.dIndex = -1;
twtable.setElement(tables[n],txt);
twtable.prepare(txt);
}
//if ( twve.MathJax ) twve.MathJax.enabled = true;
}
return twwrap;
},
//}}}
/***
!!!! twve.table.textPasted
***/
//{{{
preTextPasted : null,
textPasted : function (ta) {
// Replacing the twve.tiddler.textPasted for tables.
// We need to do so because twve.table allows multi-
// lined content in table cells. Conversions between
// line breaks and <br />'s are necessary for correct
// operations.
var text_pasted = preTextPasted.apply(this,arguments);
if ( ! text_pasted || ! twve.tiddler.cur_editable.editNode )
return text_pasted;
// text pasted into a table
var modified = config.options.chktwveTableMultiLine
? twve.text.brToLineBreak(text_pasted)
: twve.text.lineBreakToBr(text_pasted);
if ( modified == text_pasted )
return text_pasted;
ta.value =
twve.tiddler.text_before_paste
? ( twve.tiddler.text_before_paste.substring(
0,
twve.tiddler.selection_at_paste.start
)
+ modified
+ twve.tiddler.text_before_paste.substring(
twve.tiddler.selection_at_paste.end
)
)
: modified;
return modified;
},
//}}}
/***
!!!! twve.table.addOptionsMenu
***/
//{{{
addOptionsMenu : function(menu){
return menu.addMenu(
"''twve.table'' Options",
config.macros.twveTableOptions.handler
);
},
//}}}
/***
!!!! twve.table.getOptionsMenu
***/
//{{{
preGetOptionsMenu : null,
getOptionsMenu : function(){
// Replacing the twve.tiddler.getOptionsMenu for twve.table.
var menu = twve.table.preGetOptionsMenu.apply(this,arguments);
twve.table.addOptionsMenu(menu);
return menu;
},
//}}}
/***
!!!! twve.table.getWrapper
***/
//{{{
getWrapper : function(elem,which) {
// Get the outer-most table wrapper
return twve.node.closest(
elem,twve.table.wrapper.getSelector(which)
);
},
//}}}
/***
!!!! twve.table.findTable
***/
//{{{
findTable : function(elem){
// Returns the table directly related to elem if there is,
// null otherwise.
if ( ! elem ) return null;
var w = twve.table.getWrapper(elem);
return w
? w.querySelector('table')
: twve.node.closest(elem,'table');
},
//}}}
/***
!!!! twve.table.rowRef
***/
//{{{
rowRef : function ( row ) {
// Reference of table rows.
// Other plugins can override this to offer their own
// format of row references.
return row;
},
//}}}
/***
!!!! twve.table.colRef
***/
//{{{
colRef : function ( col ) {
// Reference of table columns.
// Other plugins can override this to offer their own
// format of column references.
return col;
},
//}}}
/***
!!!! twve.table.cellRowSpan
***/
//{{{
cellRowSpan : function(cell){
// Returns the number of rows spanned by this cell.
var span = cell.getAttribute('rowspan');
return !span ? 1 : span*1;
},
//}}}
/***
!!!! twve.table.cellRowSpanMin
***/
//{{{
cellRowSpanMin : function(cells){
// Returns the minimum number of rows spanned by a row of cells.
var min_span = 0;
for ( var i=0, len=cells.length; i<len; i++ ) {
var cspan = twve.table.cellRowSpan(cells[i]);
if ( min_span == 0 || min_span > cspan )
min_span = cspan;
}
return min_span;
},
//}}}
/***
!!!! twve.table.cellColSpan
***/
//{{{
cellColSpan : function(cell){
// Returns the number of columns spanned by this cell.
var span = cell.getAttribute('colspan');
return !span ? 1 : span*1;
},
//}}}
/***
!!!! twve.table.getRowCell
***/
//{{{
getRowCell : function(cells,col){
if ( cells.nodeName == 'TR' )
cells = cells.querySelectorAll('th,td');
var cell = null;
for ( var c=0,len=cells.length; c<len; c++ ) {
cell = cells[c];
var cndx = twve.table.cellColIndex(cell);
if ( cndx == col ) {
// Found the desired cell.
break;
} else if ( cndx > col ) {
// The cell is column spanned and the desired
// cell is included.
var cspan = twve.table.cellColSpan(cell);
if ( cndx-cspan < col ) break;
}
cell = null;
}
return cell;
},
//}}}
/***
!!!! twve.table.columnsInRow
***/
//{{{
columnsInRow : function(tr,cells){
// Find the number of columns in a table row tr (DOM node).
var cols = 0;
if ( ! cells ) cells = tr.querySelectorAll('th,td');
for ( var n=0,len=cells.length; n<len; n++ )
cols += twve.table.cellColSpan(cells[n]);
tr.setAttribute('columns',cols);
tr.setAttribute('cells',cells.length);
return cols;
},
//}}}
/***
!!!! twve.table.rowWithMaxCol
***/
//{{{
rowWithMaxCol : function (table,rows) {
// Find the table row with maximum number of columns.
var trmax = null;
var cols = 0;
var cells = 0;
if ( ! rows ) rows = table.querySelectorAll('tr');
for ( var n=0,len=rows.length; n<len; n++ ){
var tr = rows[n];
var trcols = twve.table.columnsInRow(tr);
if ( trcols > cols ) {
cols = trcols;
}
var trcells = tr.getAttribute('cells')*1;
if ( trcells > cells ) {
trmax = tr;
cells = trcells;
}
}
return trmax;
},
//}}}
/***
!!!! twve.table.maxColumns
***/
//{{{
maxColumns : function(table,rows){
// Find the maximum number of columns in table.
return twve.table.rowWithMaxCol(table,rows)
.getAttribute('columns')*1;
},
//}}}
/***
!!!! twve.table.rowStarts
***/
//{{{
rowStarts : function(txt,rstart,end,r) {
// Find the index of the first character of the text that
// defines the rth row of the table. Start searching from
// index start.
// Arguments:
// txt: The wikitext that defines the tiddler containing
// the table.
// rstart: index of the first character to start searching.
// end: index of the last character to stop searching.
// r: The row index within the table, starting from 0.
// Returns:
// If found, returns the index of the first character, (|),
// of the rth table row. Otherwise -1.
var row = -1;
var rend;
while ( true ) {
rend = txt.indexOf('\n',rstart+1);
if ( rend > end ) return -1;
else if ( rend == -1 ) rend = end;
var ch = txt.charAt(rend-1).toLowerCase();
if ( ch=='h' || ch=='f' || ch=='|' )
if ( ++row == r )
// The index rstart is pointing at a '\n', but we want to
// return the index of '|'.
return rstart + (txt.charAt(rstart)=='\n'?1:0);
rstart = txt.indexOf('\n|',rend);
if ( rstart < 0 || rstart >= end )
return -1;
}
},
//}}}
/***
!!!! twve.table.rowEnds
***/
//{{{
rowEnds : function(txt,rstart) {
// A valid table row must contain at least two vertical bars.
var pn = txt.indexOf('\n',rstart);
if ( pn == -1 ) pn = txt.length;
return txt.substring(rstart,pn).split('|').length < 3
? (rstart-1)
: pn;
},
//}}}
/***
!!!! twve.table.locateCell
***/
//{{{
locateCell : function (txt,rstart,rend,col){
// Locate a table cell. If the cell is defined, returns the index
// of its first character and one after the last. If, however, the
// cell is missing, returns the index of the '|' immediately before
// the place where it should be inserted.
// Arguments:
// txt: tiddler text containing the table cell
// rstart: starting position of the table row containing
// this table cell
// rend: ending position of the table row
// col: column index of the table cell, returned from
// twve.table.cellColIndex(cell)
// Return value:
// An object containing the start and end positions of the
// table cell.
// {
// open: first character,
// close: last character of the table cell
// }
var c = -1;
var cpos = twve.object.create();
cpos.open = rstart;
while (true) {
cpos.close =
twve.text.skipToActive(txt,'|',cpos.open+1,rend).ndx;
if ( cpos.close == -1 || cpos.close > rend ) {
cpos.close = rend-1;
break;
}
if ( ++c == col ) break;
cpos.open = cpos.close;
}
if ( cpos.open < cpos.close ) cpos.open++;
return cpos;
},
//}}}
/***
!!!! twve.table.cellPos
***/
//{{{
cellPos : function (txt,start,end,row,col,span){
// Find the indexes of the first and last characters of
// a spanned table cell.
// Arguments:
// txt: tiddler text containing the table
// start: position in txt to start searching
// end: position in txt to end searching
// row: row index of the table cell
// col: column index of the table cell, returned from
// twve.table.cellColIndex(cell).
// span: object containing the number of spanned
// rows/cols, returned from
// twve.table.cellSpans(cell)
// {
// row: number of spanned rows,
// col: number of spanned columns
// }
// Return value:
// If span.row == 1 and span.col == 1, returns an object
// containing the start and end positions of the table
// cell. See locateCell above for the position object
// structure. If either span.row > 1 or span.col > 1,
// returns an array of cell positions (see above for
// position object structure). Otherwise returns an empty
// array (not supported yet).
var rstart = twve.table.rowStarts(txt,start,end,row);
var rend = twve.table.rowEnds(txt,rstart);
if ( span.row == 1 && span.col == 1 ) {
return twve.table.locateCell(txt,rstart,rend,col);
}
var pos = [];
// row spanned cells
for ( var r = 0; r < span.row; r++ ) {
pos[r] = [];
var cpos = twve.table.locateCell(
txt,rstart,rend,col-span.col+1
);
for ( var c = 0; c < span.col-1; c++ ) {
pos[r][pos[r].length] = {
open:cpos.open,
close:cpos.close
};
cpos.open += 2; cpos.close += 2;
}
pos[r][pos[r].length] =
twve.table.locateCell(txt,cpos.open-1,rend,0);
rstart = twve.table.rowStarts(txt,rend,end,0);
rend = twve.table.rowEnds(txt,rstart);
}
return pos;
},
//}}}
/***
!!!! twve.table.rowCells
***/
//{{{
rowCells : function (txt,rstart,rend){
// Find the indexes of the first and last characters of
// all cells in one table row.
// Arguments:
// txt: text containing the table
// rstart: starting position of the table row
// rend: ending position of the table row
// Return value:
// Returns an array of objects containing the indexes
// of the first and last characters of all cells in the
// table row. The indexes for the cth cell can be found
// at the [c]th element of the returned array.
var rpos = [];
var cpos = twve.object.create();
cpos.open = rstart;
cpos.close = rstart;
do {
cpos = twve.table.locateCell(txt,cpos.close,rend,0);
var ctxt = txt.substring(cpos.open,cpos.close);
if ( cpos.close == rend-1 ) {
var ch = txt.charAt(cpos.close).toLowerCase();
if ( ! (ch=='h' || ch=='f') )
rpos[rpos.length] = cpos;
return rpos;
}
rpos[rpos.length] = cpos;
} while ( true );
},
//}}}
/***
!!!! twve.table.allCells
***/
//{{{
allCells : function (txt,start,end){
// Find the indexes of the first and last characters of
// all table cells.
// Arguments:
// txt: text containing the table
// start: starting position of the table in txt
// end: ending position of the table in txt
// Return value:
// Returns an array of array of objects containing the
// indexes of the first and last characters of all table
// cells. The indexes for the cell (r, c) can be found
// at the [r][c]th element of the returned array.
var pos = [];
var rend = start;
var rstart;
do {
rstart = twve.table.rowStarts(txt,rend,end,0);
if ( rstart < 0 || rstart >= end ) break;
rend = twve.table.rowEnds(txt,rstart);
if (rend < rstart || rend > end) break;
pos[pos.length] = twve.table.rowCells(txt,rstart,rend);
if ( rend >= end ) break;
} while (true);
return pos;
},
//}}}
/***
!!!! twve.table copy/paste information
***/
//{{{
copied_rndx : -1,
copiedRowIndex : function(row){
if ( typeof row == 'number' )
twve.table.copied_rndx = row;
return twve.table.copied_rndx;
},
copied_rtxt : null,
copiedRowText : function(txt){
if ( txt === null || typeof txt == 'string' )
twve.table.copied_rtxt = txt;
return twve.table.copied_rtxt;
},
copied_cndx : -1,
copiedColumnIndex : function(col){
if ( typeof col == 'number' )
twve.table.copied_cndx = col;
return twve.table.copied_cndx;
},
copied_ctxt : null,
copiedColumnText : function(txt){
if ( txt === null || (typeof txt == 'object' && txt.length) )
twve.table.copied_ctxt = txt;
return twve.table.copied_ctxt;
},
//}}}
/***
!!!! twve.table sorting functions
***/
//{{{
sortTable : null, // SortableGridPlugin
tsResortTable : null, // TableSortingPlugin
//}}}
/***
!!!! twve.table.sortingEnabled
***/
//{{{
sortingEnabled : function() {
return !! (twve.table.sortTable
|| twve.table.tsResortTable);
},
//}}}
/***
!!!! twve.table.inEditArea
***/
//{{{
inEditArea : function(cell,ev){
if ( ! ev ) return true;
// Test if the mouse is cell in the first row while sorting
// feature is enabled. Check whether the user clicks
// on a place close to the content of this cell. If so,
// prepare to edit, otherwise leave it for sorting.
var ebox = twve.node.box(cell);
var pos = twve.tiddler.mousePosition(ev);
var x = (pos.x-ebox.left)/ebox.width;
//var y = (pos.y-ebox.top)/ebox.height;
// Define edge of edit area: 0.25 for SortableGridPlugin and 0.2
// for TableSortingPlugin.
// Note: SortableGridPlugin activates only when the user
// clicks on the text of the cell, while
// TableSortingPlugin activates whenever the user clicks
// within the cell.
// Here we do in a consistent way: Let the sorting plugin do its
// job when the user clicks in the central area (cursor would
// show a pointer), and take over to do editing when clicks
// closer to the left and right borders of the cell (cursor
// show a vertical bar as usually seen in a text box).
var edge = 0.2;
var central = true;
var talign = cell.style.textAlign ||
window.getComputedStyle(cell)
.getPropertyValue('text-align');
talign = talign.toLowerCase();
if ( talign.indexOf('right') >= 0 ) {
if ( x < edge ) return central;
} else if ( talign.indexOf('center') >= 0 ) {
if ( x < edge || x > (1-edge) ) return central;
} else if ( talign.indexOf('left') >= 0 ) {
if ( x > (1-edge) ) return central;
}
return ! central;
},
//}}}
/***
!!!! twve.table.wikify
***/
//{{{
preWikify : null,
wikify : function (txt,elem,children) {
// Replacing the twve.node.wikify for tables.
// We need to do so because table cells have their
// own rules for alignments, and, unfortunately,
// these rules are ignored in TiddlyWiki's wikify()
// function. We need to take care of the alignment
// settings here.
//
// Note: If I had had understood the formatters better,
// I could have had rewritten the one for tables.
// Unfortunately I just don't understand them.
if ( twve.node.matches(elem,'th,td') ) {
var style = twve.text.nodeWideStyleText(txt);
if ( style )
txt = txt.substring(style.length);
// Make sure the right cell type
var expected = txt.charAt(0) == '!'
? 'TH' : 'TD';
if ( elem.nodeName != expected ) {
var newcell = createTiddlyElement(null,expected);
elem.parentNode.replaceChild(newcell,elem);
elem = newcell;
if ( expected == 'TH' )
txt = txt.substring(1);
}
// Adjust text alignment
var leftsp = txt.charAt(0) == ' ';
var len = txt.length;
var rightsp = txt.charAt(len-1) == ' ';
if ( leftsp ) {
if ( rightsp ) {
elem.style.textAlign = 'center';
txt = txt.substring(1,len-1);
} else {
elem.style.textAlign = 'right';
txt = txt.substring(1);
}
} else {
elem.style.textAlign = 'left';
if ( rightsp )
txt = txt.substring(0,len-1);
}
if ( style ) {
// Apply the style text
style = style.match(/[\w-]+[:]{1}[\w-]+[;]{1}/g);
var css = [];
for ( var i = 0; i < style.length; i++ ) {
var si = style[i].match(/[\w-]+/g);
css[css.length]={'style':si[0],'value':si[1]};
}
config.formatterHelpers.applyCssHelper(elem,css);
}
}
return twve.table.preWikify.call(this,txt,elem,children);
},
//}}}
/***
!!!! twve.table.box
***/
//{{{
/*
preBox : null,
box : function(node,outerw,outerh){
if ( node.nodeName == 'TABLE' ) {
node = twve.table.getWrapper(node) || node;
}
//return twve.table.preBox.call(this,node,outerw,outerh);
return twve.table.preBox.apply(this,arguments);
},
*/
//}}}
/***
!!!! twve.table.getTableMenu
***/
//{{{
tableMenu : null,
getTableMenu : function(){
var label = 'Transpose';
var item = null;
if ( ! twve.table.tableMenu ) {
twve.table.tableMenu = twve.menu.create(
twve.button.create(
null,
String.fromCharCode(8801),
"table menu"
)
);
twve.table.addOptionsMenu(twve.table.tableMenu);
item = twve.table.tableMenu.addItem(
label,'Transpose this table'
);
item.click = function(){
if ( twve.button.isActive(this) )
twve.tiddler.focusElem().transpose();
};
}
if ( ! item )
item = twve.table.tableMenu.findItem(label);
item.activate(config.options.chktwveTableTranspose);
return twve.table.tableMenu;
},
//}}}
/***
!!!! twve.table.sort
***/
//{{{
sort : function(twtable,o,rev){
if ( ! twtable.startSorting() ) return;
var table = twtable.dom;
var cell = twtable.sortingCell(o);
// Make a copy of the rows before sorting
var rows_org = [];
var rows = table.querySelectorAll('tr');
for ( var r=0,len=rows.length; r<len; r++ )
rows_org[r] = rows[r];
// Save the height of wrappers
// For reasons unknown to me, the height of tbref and
// all in twtable.wrappers got changed after sorting.
// Do the sorting and keep the sorting info
try {
if ( config.tableSorting ) {
twve.table.sortTable.call(this,o,rev);
} else if ( config.macros.sortableGridPlugin ) {
twve.table.tsResortTable.call(this,o,rev);
}
} catch(e) {
console.log(e);
var msg = e.toString();
if ( msg.indexOf('undefined') > -1 )
console.log (
'There are probably spanned cells and/or missing cells in this table.'
);
return twtable;
}
// Retrieve the sorted rows, update the row index and
// even/odd classes.
var rows_sorted = [];
rows = table.querySelectorAll('tr');
for ( var r=0,len=rows.length; r<len; r++ ){
var tr = rows[r];
rows_sorted[r] = tr;
tr.setAttribute('row', r);
var cl = tr.classList;
cl.remove('oddRow');
cl.remove('evenRow');
cl.add(r%2?'oddRow':'evenRow');
}
// Reorder the underlying wikitext accordingly.
var txt = twtable.getText();
var sortedtxt = txt;
var sortedndx = new Array (rows_sorted.length);
// Record change of row indexes after sorting
// --------------------------------------------------
// This can be the most time-consuming part
// in sorting. Should figure a way to speed
// up if possible.
// --------------------------------------------------
for ( var r=0,len=rows_org.length; r < len; r++ ) {
for ( var rs=0,slen=rows_sorted.length; rs<slen; rs++ ) {
if ( rows_sorted[rs] != rows_org[r] )
continue;
sortedndx[rs] = r;
if ( rs != r )
txt = twtable.changed(
{
what: 'ROW MOVED',
from: r,
to: rs,
text: txt
}
);
break;
}
}
// Update the sorted text
var end = txt.length;
var send = sortedtxt.length;
var rstart, rsend = 0, rsstart;
for ( var rs=0; rs<rows_sorted.length; rs++ ) {
rsstart = twve.table.rowStarts(sortedtxt,rsend,send,0);
rsend = twve.table.rowEnds(sortedtxt,rsstart);
rstart = twve.table.rowStarts(txt,0,end,sortedndx[rs]);
rend = twve.table.rowEnds(txt,rstart);
sortedtxt = sortedtxt.substring(0,rsstart)
+ txt.substring(rstart,rend)
+ sortedtxt.substring(rsend);
var dlen = ((rend-rstart)-(rsend-rsstart));
send += dlen;
rsend += dlen;
}
sortedtxt = twtable.changed(
{
what:'SORTED',
text:sortedtxt
}
);
// Column sorting (moving rows) does not change the
// size of the table text.
//twve.tiddler.saveText(twtable,sortedtxt);
twtable.setText(sortedtxt);
twtable.showRefBars();
twtable.drawFocusBorders();
return twtable;
},
//}}}
/***
!!!! twve.table.focusTarget
***/
//{{{
preFocusTarget : null,
focusTarget : function(ev){
return twve.table.findTable(ev.target) ||
twve.table.preFocusTarget.apply(this,arguments);
},
//}}}
/***
!!!! twve.table.hasHeader
***/
//{{{
hasHeader : function(table,rows){
// Tests whether a table has a header.
if ( table.dom ) {
// If table is twve.table object, look for its header
// signature.
if ( table.headPos().open > -1 ) return true;
table = table.dom;
}
// If table is a DOM object, check for tHead.
if ( table.tHead ) return true;
// Otherwise check if the first row contains all TH's.
//
// Q: Do I really need to do this? 2015/02/11
//
if ( ! rows ) rows = table.querySelectorAll('tr');
//for ( var r=0, rlen=rows.length; r<rlen; r++ ) {
//var cells = rows[r].querySelectorAll('th,td');
var cells = rows[0].querySelectorAll('th,td');
var thcount = 0;
for ( var c=0, clen=cells.length; c<clen; c++ ) {
if ( twve.node.matches(cells[c],'th') )
thcount++;
}
if ( thcount == cells.length ) return true;
//}
return false;
},
//}}}
/***
!!!! twve.table.rowIndex
***/
//{{{
rowIndex : function(tr,ndx){
if ( ndx === undefined ) {
var row = tr.getAttribute('row');
return row ? (row * 1) : tr.rowIndex;
} else {
tr.setAttribute('row',ndx);
return ndx;
}
},
//}}}
/***
!!!! twve.table.cellRowIndex
***/
//{{{
cellRowIndex : function (cell,ndx) {
return twve.table.rowIndex(cell.parentNode,ndx);
},
//}}}
/***
!!!! twve.table.cellColIndex
***/
//{{{
cellColIndex : function (cell,ndx) {
if ( ndx === undefined ) {
var col = cell.getAttribute('col');
return col ? (col * 1) : cell.cellIndex;
} else {
cell.setAttribute('col',ndx);
}
},
//}}}
/***
!!!! twve.table.cellSpans
***/
//{{{
cellSpans : function(cell){
// Returns the number of rows and columns spanned by
// this cell.
var span = twve.object.create();
span.row = twve.table.cellRowSpan(cell);
span.col = twve.table.cellColSpan(cell);
return span;
},
//}}}
/***
!!!! twve.table.create
***/
//{{{
create : function(table,txt,start,dir){
// Creates a twve.table object
var twtable = twve.element.create();
// clear
//var preClear = twtable.clear;
twtable.clear = function(){
//preClear.apply(this);
twtable.wrappers = null;
twtable.refbarV = null;
twtable.refbarH = null;
twtable.editNode = null;
twtable.differentBox = true;
return twtable;
};
// copyFrom
var preCopyFrom = twtable.copyFrom;
twtable.copyFrom = function(table){
preCopyFrom.apply(this,arguments);
twtable.wrappers = table.wrappers;
twtable.refbarV = table.refbarV;
twtable.refbarH = table.refbarH;
twtable.editNode = table.editNode;
return twtable;
};
// markupTags
twtable.markupTags = function(){
return twve.table.markupTags();
};
// twveSelector
twtable.twveSelector = function(){
return twve.table.twveSelector(null,'table');
};
// is
var preIs = twtable.is;
twtable.is = function(elem){
if ( ! elem ) return false;
if ( preIs.apply(this,arguments) ) return true;
return twve.node.matches(
elem,twve.table.twveSelector().include
) || twve.node.closest(elem,'th,td');
};
// is editable
twtable.isEditable = function(elem){
if ( twtable.wrappers ) {
if ( twtable.wrappers.wrap == elem ) {
twtable.editNode = null;
return true;
}
if ( ! twve.node.contains(twtable.wrappers.wrap,elem) )
return false;
}
var cell = null;
if ( twve.node.matches(elem,'th,td') )
cell = elem;
else
cell = twve.node.closest(elem,'th,td');
if ( cell ) {
// A table cell or elements within a table cell.
twtable.editNode = cell;
return true;
}
var selector_cap = twve.table.wrapper.getSelector('cap');
if ( twve.node.matches(elem,selector_cap) ) {
// Table caption.
twtable.editNode = elem;
return true;
}
if ( twve.node.matches(elem,'table,tr') ) {
// Missing cells. FireFox uses TR, while Chrome, Opera
// and IE use TABLE to cover them. I don't know about
// Safari.
twtable.editNode = elem;
return true;
}
if ( twve.node.matches (
elem,
twve.table.wrapper.getSelector('all')
) ) {
// Whole table
twtable.editNode = null;
return true;
}
return false;
};
// clone
twtable.clone = function(){
// Optional, may save a tiny bit of time.
return twve.table.create(twtable);
};
// disabled
twtable.disabled = function(){
return (twtable.end.ndx == -1
|| ! config.options.chktwveTableEnabled
|| twtable.isSystemShadow()
|| ! (config.options.chktwveTableEditAll
|| twtable.hasClass('editable')
|| twtable.hasClass('|editable')));
};
// mouseenter
twtable.mouseenter = function(ev){
if ( twtable.disabled() ) return false;
if ( ! twtable.refbarV.isVisible() ) {
twtable.showRefBars();
}
return true;
};
// cellAtMouse
twtable.cellAtMouse = function(ev){
return ev.target;
};
// mouesmove
twtable.mousemove = function(ev){
//if ( /^t[hd]$/i.test(ev.target.nodeName) ) {
if ( twve.node.box(twtable.dom)
.contains(twve.tiddler.mousePosition(ev)) ) {
twtable.refbarV.hideMenu();
twtable.refbarH.hideMenu();
twve.table.getTableMenu().hide();
if ( twve.table.sortingEnabled()
&& twve.table.hasHeader(twtable) ) {
// There is table sorting plugin installed, and
// this table has a header (sortable).
var cell = twtable.cellAtMouse(ev);
if ( twve.table.cellRowIndex(cell)==0 ) {
// This cell is in the header row.
cell.style.cursor =
twve.table.inEditArea(cell,ev)
? 'text'
: 'pointer';
}
}
}
};
// mouseleave
twtable.mouseleave = function(ev){
if ( twve.tiddler.getEditBox() &&
twve.tiddler.cur_editable.is(twtable) )
return false;
return ! twve.node.box(twtable.wrappers.wrap)
.contains(twve.tiddler.mousePosition(ev));
};
// blur
twtable.blur = function(){
if ( twtable.refbarV.isVisible() ) {
twtable.hideRefBars();
twve.table.getTableMenu().hide(true);
if ( twtable.beingEdited() )
twve.tiddler.updateText();
}
};
// getCurElem
twtable.getLastCurElem = function(){
return twtable.editNode.length
? twtable.editNode[twtable.editNode.length-1]
: twtable.editNode;
};
// box
twtable.box = function(action,ev,elem){
action = action.toLowerCase();
if ( twtable.editNode && action.indexOf('edit')>=0 ) {
if ( ! elem ) elem = twtable.getLastCurElem();
var eb = twve.node.box(elem);
if ( (config.browser.firefoxDate
&& config.browser.isLinux)
&& twve.node.matches(elem,'th,td') ){
var delta = twve.node.cssSize(
elem.style.paddingLeft
);
eb.left -= delta / 2;
eb.width -= delta;
delta = twve.node.cssSize(
elem.style.paddingTop
);
eb.top -= delta / 2;
eb.height -= delta;
}
return eb;
}
var eb = twve.node.box(
twtable.wrappers
? twtable.wrappers.wrap
: twtable.dom
);
/*
if ( /^focus/i.test(action) ) {
var pos = twve.object.create();
pos.left = eb.right;
pos.top = eb.top;
twve.table.getTableMenu().showRoot(pos,'left');
}
*/
return eb;
};
// prepare
twtable.prepare = function (txt) {
if ( twtable.disabled() ) return false;
// Prepare the table rows for editing.
twtable.initRows(txt);
// Prepare the sorting plugin.
if ( twve.table.hasHeader(twtable) ) {
if ( config.tableSorting ) {
// The TableSortingPlugin is installed.
if ( ! twve.table.sortTable ) {
twve.table.sortTable =
config.tableSorting.sortTable;
config.tableSorting.sortTable=function(o,rev){
twve.table.sort(
twve.tiddler.focusElem(),o,rev
);
};
}
} else if ( config.macros.sortableGridPlugin ) {
// The SortableGridPlugin is installed.
if ( ! twve.table.tsResortTable ) {
twve.table.tsResortTable =
config.macros.sortableGridPlugin
.ts_resortTable;
config.macros.sortableGridPlugin.ts_resortTable =
function(lnk){
twve.table.sort(
twve.tiddler.focusElem(),lnk
);
};
}
}
}
// Call the resize handler of twtable.
/*
var foldedParents = twve.node.parents(
twtable.wrappers.wrap,
//twve.foldedSection.getSelector()
twve.tiddler.twveFoldableSelector().include,
function(){
return ! twve.node.isVisible(this);
}
);
if ( foldedParents ) twve.node.show(foldedParents);
*/
twtable.adjustWidth();
twtable.resize();
//if ( foldedParents ) twve.node.hide(foldedParents);
return true;
};
// Find the table row with maximum number of cells.
twtable.rowWithMaxCol = function (rows) {
return twve.table.rowWithMaxCol(twtable.dom,rows);
};
// counts
twtable.counts = function(searchInfo,txt){
// Check if the currently found element counts as a valid
// one. If so,
// 1. update the ndx and remained properties of
// searchInfo,
// 2. return true.
// Otherwise,
// 1. return false.
var pn = twve.table.rowEnds(
txt,
twtable.start.ndx+(txt.charAt(twtable.start.ndx)=='\n'?1:0)
);
if ( pn <= twtable.start.ndx ) {
// Not a valid table. Don't count.
return false;
} else {
// A table for sure. Update the index and remained.
--searchInfo.remained;
++searchInfo.ndx;
return true;
}
};
// starts
var preStarts = twtable.starts;
twtable.starts = function(start,txt,dir){
if ( dir && dir < 0 ){
// Going backwards,
preStarts.call(this,start-1,txt,dir);
var tags = twtable.tags.exactCloseTag(
twtable.start.matched
);
if ( ! txt ) txt = twtable.tiddler.text;
// Locate the end of this table.
twtable.end.ndx = txt.indexOf(
tags.close,twtable.start.ndx+1
)+tags.close.length;
twtable.end.matched = tags.close;
// Find the begining of this table.
twtable.start = twve.text.consecutiveLinesEnd(
txt,twtable.start,true,'|',tags,-1
);
if(twtable.start.ndx > 0) twtable.start.ndx++;
twtable.found = true;
return twtable.start;
}
return preStarts.apply(this,arguments);
};
// ends
var preEnds = twtable.ends;
twtable.ends = function(){
return twtable.found
? twtable.end
: preEnds.apply(this,arguments);
};
// get element
twtable.getElement = function(){
return twtable.editNode
? twtable.getLastCurElem()
: twtable.dom;
};
// createWrapperRefBars
twtable.createWrapperRefBars = function(){
//if(Handsontable && twtable.hasClass('handson')){
// config.extensions.Handsontable.create(twtable);
//} else {
twtable.wrappers = twve.table.wrapper.create(twtable);
twtable.refbarV = twve.table.refBarV.create(twtable);
twtable.refbarH = twve.table.refBarH.create(twtable);
//}
return twtable;
};
// setElement
var preSetElement = twtable.setElement;
twtable.setElement = function(elem,txt,start,dir){
if(twtable.isEditable(elem)){
elem = twve.table.findTable(elem);
} else {
twtable.editNode = null;
if ( ! twve.node.matches(elem,'table') )
elem = twve.table.findTable(elem);
}
preSetElement.call(this,elem,txt,start,dir);
if ( dir < 0 ){
// Going backwards, look for the very begining of
// this table.
twtable.start.copyFrom(
twve.text.consecutiveLinesEnd(
txt, twtable.start, true,
'|', twtable.tags, dir
)
);
}
if ( ! twtable.disabled() )
// create wrappers for scrolling, etc.
twtable.createWrapperRefBars();
return twtable;
};
// setTable (alternative to setElement)
twtable.setTable = function(table,txt,start,dir){
return twtable.setElement(table,txt,start,dir);
};
// putRefBars
twtable.putRefBars = function(left,top){
var css = window.getComputedStyle(twtable.dom);
twve.node.setPosition(
twtable.refbarH.dom,
left+parseInt(css.getPropertyValue('margin-left'))
);
twve.node.setPosition(
twtable.refbarV.dom,
null,
top+parseInt(css.getPropertyValue('margin-top'))
);
return twtable;
};
// show reference bars
twtable.showRefBars = function(){
twtable.refbarV.show();
twtable.refbarH.show();
twtable.adjustRefBars();
var dw = twtable.refbarV.getSize();
var dh = twtable.refbarH.getSize();
var tbw = twtable.wrappers;
tbw.resize(dw,dh);
twve.node.setPosition(tbw.cap,dw);
twve.node.setPosition(tbw.tb,dw,dh);
twtable.putRefBars(dw,dh);
return twtable;
};
// hide reference bars
twtable.hideRefBars = function(){
if ( ! twtable.refbarV.isVisible() ) return twtable;
twtable.refbarV.hide();
twtable.refbarH.hide();
var tbw = twtable.wrappers;
twve.node.setPosition(tbw.cap,0);
twve.node.setPosition(tbw.tb,0,0);
tbw.resize();
return twtable;
};
// maxColumns
twtable.maxColumns = function(rows){
return twve.table.maxColumns(twtable.dom,rows);
};
// renderedCopy
var preRenderedCopy = twtable.renderedCopy;
twtable.renderedCopy = function(){
var the_copy = preRenderedCopy.apply(this,arguments);
if ( the_copy && twtable != the_copy ) {
// Create wrappers for the_copy
the_copy.createWrapperRefBars();
if ( twtable.editNode ) {
// There is editNode, and the_copy is not twtable
// itself. Find the corresponding editNode in
// the_copy.
var elem = twtable.editNode;
if ( twve.node.matches(elem,'div') ) {
// table caption
the_copy.editNode = the_copy.wrappers.cap;
} else if (twve.node.matches(elem,'td,th')) {
// table cell
var tr = the_copy.dom.querySelectorAll('tr')[
twve.table.cellRowIndex(elem)
];
if ( twve.node.matches(elem,'[missed]') ) {
// Missing cell(s), create new one(s) and
// append to the end of tr.
the_copy.editNode =
the_copy.appendCell(
tr, elem.length || 1
);
} else {
the_copy.editNode = twve.table.getRowCell(
tr,twve.table.cellColIndex(elem)
);
}
}
}
}
return the_copy;
};
// getCell
twtable.getCell = function (row,col,rows) {
// Get the cell indexed by (index.row, index.col).
// If there are row- or col-spanned cells, the indexes of
// table cells assigned by the browsers are not consistent
// to what we see, making it confusing with keyboard
// navigation. To fix this, we need to search from the
// first row, and from the first cell in each row, to
// achieve visually consistent indexes for table cells.
if ( ! rows ) rows = twtable.dom.querySelectorAll('tr');
var cell = null;
for ( var r=0,len=rows.length; r<len; r++ ){
cell = twve.table.getRowCell(rows[r],col);
if ( ! cell ) {
// There is no cell with column index col in this
// row.
if ( r == row )
// If this is the row we are expecting, then we
// are looking for a missing cell, stop
// searching and return.
return null;
// Otherwise go to the next row.
continue;
}
// There is a cell with column index col in this row.
// Check its row spanning status.
var rspan = twve.table.cellRowSpan(cell);
if ( (rspan == 1 && r == row)
// If this cell is not row-spanned,
|| (r+rspan-1 >= row) )
// or it is row-spanned and includes the target row,
// stop searching and return.
break;
// Otherwise go to the next row.
cell = null;
}
return cell;
};
// appendCell
twtable.appendCell = function(tr, n, cndx){
// Append n cells to the end of tr.
// Returns an array of DOM object representing the n cells
// just appended.
if ( typeof n != 'number' || n <= 0 ) n = 1;
if ( typeof cndx != 'number' ) {
var cells = tr.querySelectorAll('th,td');
cndx = twve.table.cellColIndex(cells[cells.length-1]);
}
var cell = [];
for ( var i = 0; i < n; i++ ) {
var c = document.createElement('td');
c.setAttribute('missed',true);
c.setAttribute('col',(++cndx));
tr.appendChild(c);
cell[cell.length] = c;
}
return cell;
};
// editNextCell
twtable.editNextCell = function(which,delta,dr,dc){
// Edit the neighboring cell indexed separated by delta, in
// the direction specified by which.
if ( dr === undefined ) dr = 0;
if ( dc === undefined ) dc = 0;
var cell = twtable.getLastCurElem();
var row = twve.table.cellRowIndex(cell)+dr;
var col = twve.table.cellColIndex(cell)+dc;
switch ( which ) {
case 'left' :
col -= delta;
break;
case 'up' :
row -= delta;
break;
case 'right' :
col += delta;
break;
case 'down' :
row += delta;
break;
}
// Save changes to this one.
twve.tiddler.updateText();
// Check validity of indexes.
var rows = twtable.dom.querySelectorAll('tr');
if ( row < 0 ) row = rows.length-1;
else if ( row >= rows.length ) row = 0;
var maxcols = twtable.maxColumns(rows);
if ( col < 0 ) col = maxcols - 1;
else if ( col >= maxcols ) col = 0;
// Get the neighboring cell.
twtable.editNode = twtable.getCell(row,col,rows);
if ( ! twtable.editNode ) {
// Missing cell. Get the corresponding row,col indexes
// from the last cell in the row.
var cells = rows[row].querySelectorAll('th,td');
var cndx = twve.table.cellColIndex(cells[cells.length-1]);
// Then create new cells accordingly, and get the
// desired cell.
twtable.editNode = twtable.appendCell(
rows[row], col-cndx, cndx
)[col-cndx-1];
} else if ( twtable.editNode.classList.contains('noedit') ) {
return twtable.editNextCell(which,delta,dr,dc);
}
// Edit the next cell.
twtable.editText(null,row,col);
return twtable;
};
// editNeighbor
var preEditNeighbor = twtable.editNeighbor
twtable.editNeighbor = function (which,delta) {
// Edit the neighboring cell or table of twtable. The
// direction is specified in the second argument which:
// 'left', 'right', 'up', or 'down'.
if ( ! twtable.editNode ||
twve.node.matches(twtable.editNode,'div') )
// Table or caption
return preEditNeighbor
? preEditNeighbor.apply(this,arguments)
: null;
// Table cell
if ( delta === undefined ) delta = 1;
var tas = twve.tiddler.getEditBox();
var talen = tas.length;
if ( talen == 1 ) {
tas = tas[0];
talen = tas.length;
}
if ( ! (talen > 1) ) {
// The editbox has only one textarea, edit its next cell.
return twtable.editNextCell(which,delta);
}
// If the editbox has multiple textareas, check the row and
// column spans and decide what to do.
var ndx = 0;
for ( ; ndx<talen; ndx++ )
if ( twve.node.matches(tas[ndx],'textarea:focus') )
break;
var span = twve.table.cellSpans(twtable.editNode);
if ( span.col > 1 ) {
// Column spanned cell
var dc = span.col-ndx-1;
switch ( which ) {
case 'left' :
ndx -= delta;
break;
case 'right' :
ndx += delta;
break;
case 'up' :
case 'down' :
return twtable.editNextCell(which,delta,0,-dc);
}
if ( ndx < 0 || ndx >= talen )
// If we are moving out of the multiple textareas,
// go to the next cell.
return twtable.editNextCell(which,delta,0,-dc);
} else if ( span.row > 1 ) {
// Row spanned cell
var dr = ndx;
switch ( which ) {
case 'left' :
case 'right' :
return twtable.editNextCell(which,delta,dr,0);
case 'up' :
ndx -= delta;
break;
case 'down' :
ndx += delta;
break;
}
if ( ndx < 0 || ndx >= talen )
// If we are moving out of the multiple textareas,
// go to the next cell.
return twtable.editNextCell(which,delta,dr,0);
}
// Otherwise move to the next textarea.
var ta = tas[ndx].nodeType ? tas[ndx] : tas[ndx][0];
ta.focus();
twve.tiddler.previewEditBox(ta);
return twtable;
};
// getCellText
var cell_pos = null;
var cell_style = '';
twtable.getCellText = function(pos,txt){
if ( ! txt ) txt = twtable.getText();
cell_style = '';
var wikitxt = '';
if ( pos.length ) {
// spanned cell
wikitxt = [];
for ( var r = 0; r < pos.length; r++ ) {
wikitxt[r] = [];
for ( var c = 0; c < pos[r].length; c++ )
wikitxt[r][c] = txt.substring(
pos[r][c].open,pos[r][c].close
);
}
if ( ! config.options.chktwveTableIncludeCSS ) {
cell_style = twve.text.nodeWideStyleText(
wikitxt[0][pos[0].length-1]
);
wikitxt[0][pos[0].length-1] =
wikitxt[0][pos[0].length-1].substring(
cell_style.length
);
}
if ( config.options.chktwveTableMultiLine )
wikitxt[0][pos[0].length-1] =
twve.text.brToLineBreak(
wikitxt[0][pos[0].length-1]
);
} else {
// single cell
wikitxt = txt.substring(pos.open,pos.close);
if ( ! config.options.chktwveTableIncludeCSS ) {
cell_style = twve.text.nodeWideStyleText(wikitxt);
wikitxt = wikitxt.substring(cell_style.length);
}
if ( config.options.chktwveTableMultiLine )
wikitxt = twve.text.brToLineBreak(wikitxt);
}
cell_pos = pos;
return wikitxt;
};
// set cell text
twtable.setCellText = function(wikitxt,pos){
// Set cell text. The first argument wikitxt
// is always an array of strings. If it is to update a
// single cell, wikitxt is a simple array of length 1.
// If, however, we are to update a spanned cell, the
// wikitxt shall be a double array: wikitxt.length
// should correspond to the number of rows spanned by
// the cell, while each of the element wikitxt[n] is
// an array with a length corresponding to the number
// of columns spanned by the cell.
//
// The 2nd argument pos should be the return value of
// twve.table.cellPos(...) method.
if ( ! pos ) pos = cell_pos;
if ( ! pos ) {
displayMessage(
'Internal error: twtable.setCellText().'+
' Trying to set text without a specific position.'
);
return null;
}
var start = twtable.start.ndx;
if ( pos.length ) {
// spanned cell
if ( cell_style )
wikitxt[0][pos[0].length-1]=
cell_style+wikitxt[0][pos[0].length-1];
for(var r=pos.length-1; r>=0; r--){
for(var c=pos[r].length-1;c>=0;c--){
// convert \n to <br /> if necessary
if(config.options.chktwveTableMultiLine
&& wikitxt[r][c].indexOf('\n')>=0)
wikitxt[r][c] =
twve.text.lineBreakToBr(wikitxt[r][c]);
// update one cell
twtable.setText(
wikitxt[r][c],
pos[r][c].open+start,
pos[r][c].close+start
);
}
}
} else {
// single cell
// convert \n to <br /> if necessary
if ( config.options.chktwveTableMultiLine
&& wikitxt[0].indexOf('\n')>=0 )
wikitxt[0] = twve.text.lineBreakToBr(wikitxt[0]);
if ( cell_style ) {
wikitxt[0] = cell_style+wikitxt[0];
}
// update cell text
twtable.setText(
wikitxt[0],pos.open+start,pos.close+start
);
}
return twtable;
};
// update cell
twtable.updateCell = function(txt){
// Update cell text. The argument txt is always an array of
// strings. If it is to update a single cell, txt is a
// simple array of length 1. If, however, we are to update a
// spanned cell, the txt shall be a double array: an array of
// array of strings. The txt.length corresponds to the
// number of spanned rows, while txt[n].length is the number
// of spanned column in that row.
var txt0 = txt[0];
var cell = twtable.editNode.length
? twtable.editNode
: [twtable.editNode];
for ( var n=cell.length-1; n>=0; n-- )
if(twve.node.matches(cell[n],'[missed]'))
txt[0] = '|'+txt[0];
if ( ! twtable.setCellText(txt) )
return false;
// Decide how to refresh
if ( // spanned cell before
cell_pos.length
// cell wide style text before
|| cell_style
// cell wide style text now
|| twve.text.nodeWideStyleText(txt0)
// spanned cell now
|| (txt0=='~' || txt0=='>') ) {
// Any of the above situations, we refresh the
// whole table.
//twtable.editNode = null;
twtable.refreshTable(txt);
} else {
// Otherwise refresh only the cell just changed
// and all its copies.
// First we remove the 'missed' attribute.
for ( var n=cell.length-1; n>=0; n-- )
cell[n].removeAttribute('missed');
// Then we refresh the cell just changed.
cell = cell[cell.length-1];
var param = twve.object.create();
param.what = 'MODIFIED';
param.row = twve.table.cellRowIndex(cell);
param.col = twve.table.cellColIndex(cell);
param.text = twtable.getText();
// Refresh the cell.
twtable.refreshAll(txt0,param);
twtable.editNode = null;
}
cell_style = '';
cell_pos = null;
return true;
};
//}}}
/***
!!!!! twtable.rowAt
***/
//{{{
twtable.rowAt = function(pos,rows){
// Locate the table row, within rows, at position pos.
if ( ! rows ) rows = twtable.dom.querySelectorAll('tr');
for ( var r=0,len=rows.length; r<len; r++ ){
if ( twve.node.box(rows[r]).containsY(pos.y) )
return rows[r];
}
return null;
};
//}}}
/***
!!!!! twtable.rowIndexAt
***/
//{{{
twtable.rowIndexAt = function(pos,rows){
// Find the index of a table row that is supposed to be at
// position pos.
var tr = twtable.rowAt(pos,rows);
return tr ? twve.table.rowIndex(tr) : -1;
};
//}}}
/***
!!!!! twtable.colIndexAt
***/
//{{{
twtable.colIndexAt = function(pos){
// Find the index of a table cell that is supposed to be at
// position pos.
var csp = twtable.refbarH.dom.querySelectorAll('span');
var cndx = csp.length-1;
for ( ; cndx >= 0; cndx-- )
if (twve.node.box(csp[cndx]).containsX(pos.x))
break;
return cndx;
};
//}}}
/***
!!!!! twtable.previewEditBox
***/
//{{{
var prePreviewEditBox = twtable.previewEditBox;
twtable.previewEditBox = function(ta,elem){
var preview = prePreviewEditBox.apply(this,arguments);
if ( elem.nodeName == 'TABLE' ) {
var twwrap = twtable.direct_wrapper.clone();
twwrap.dom = preview.querySelector('div.board');
twwrap.waitAndPrepare();
}
return preview;
};
//}}}
/***
!!!!! twtable.editCell
***/
//{{{
twtable.editCell = function(ev,rowndx,colndx){
var table = twtable.dom;
var cell = twtable.getLastCurElem();
// The 2nd argument, rowndx, was determined using mouse
// position, is the same as the following variable, row,
// only for single-row cells. The two are different when
// the cell is row-spanned (occupying space of multiple rows).
var row = twve.table.cellRowIndex(cell);
var pos;
var rows = table.querySelectorAll('tr');
// Determine whether to sort or edit if cell is a
// header cell.
if ( row == 0 && twve.table.hasHeader(table,rows)
&& twve.table.sortingEnabled()
&& ! twve.table.inEditArea(cell,ev) ) {
return null;
}
if(twve.node.matches(cell,'.noedit')) return null;
if ( ev ) {
// Check if there is transcluded content inside this
// cell.
var twwrap =
twve.tiddler.createEditableWrapper(ev.target);
if ( twwrap ) {
// We are clicking on the transcluded content.
return twwrap.editText(ev);
}
}
// Now do the editing for this cell.
// The 3rd argument, colndx, was determined using mouse
// position, is the same as the following variable, col,
// only for single-column cells. The two are different when
// the cell is column-spanned (occupying space of multiple
// columns).
var col = twve.table.cellColIndex(cell);
var span = twve.table.cellSpans(cell);
var txt = twtable.getText();
pos = twve.table.cellPos(
txt,0,txt.length,row,col,span
);
var wikitxt = twtable.getCellText(pos,txt);
if ( ! pos.length )
return preEditText.call(this,ev,wikitxt,cell);
// spanned cell
twve.tiddler.cur_editable = twtable;
var ta0;
var sp_h = twtable.refbarH.dom.querySelectorAll('span');
var css = window.getComputedStyle(cell);
var fs = twve.node.cssSize(
css.getPropertyValue('font-size')
);
var talign = css.getPropertyValue('text-align');
var lh = twve.node.cssSize(
css.getPropertyValue('line-height')
);
var display = twve.node.closest(
table, twve.tiddler.displaySelector()
);
var edit_box = [];
var minw = fs*config.options.txttwveCoreMinEditWidth;
for ( var r=0,rlen=wikitxt.length; r<rlen; r++ ){
var tar = [];
var tr = rows[row+r];
var trbox = twve.node.box(tr);
if ( trbox.height < lh ) trbox.height = lh;
for ( var c=0,clen=wikitxt[r].length; c<clen; c++ ){
var cndx = col-span.col+1+c;
var eb = twve.node.box(sp_h[cndx]);
eb.top = trbox.top;
eb.height = trbox.height;
var tarc = document.createElement('textarea');
var ctxt = wikitxt[r][c];
tarc.value = ctxt;
tarc.defaultValue = ctxt;
display.appendChild(tarc);
twtable.prepareEditBox(tarc,ctxt,eb,talign,fs);
if ( eb.width < minw )
twve.node.setDimension(tarc,minw);
tar[c] = tarc;
if ( r+row == rowndx && cndx == colndx ) {
txt = ctxt;
ta0 = tarc;
}
}
edit_box[r] = tar;
}
twve.tiddler.setEditBox(edit_box);
twtable.focusEditBox(ta0,txt,ev,cell);
twtable.previewEditBox(ta0,cell,display);
return ta0;
};
//}}}
/***
!!!!! twtable.signaturePosByTail
***/
//{{{
twtable.signaturePosByTail = function(tail,txt){
// Find the position of a signature by its tail, such as
// 1. a class string that ends with |k, such as |className|k,
// 2. a caption that ends with |c, such as |caption|c,
// 3. a table head that ends with |h, or
// 4. a footage that ends with |f.
// A signature tail MUST be either
// 1. at the end of a line of text, or
// 2. at the end of the tiddler text.
//
// The argument "tail" MUST NOT contain an ending \n.
//
var pos = twve.object.create();
pos.open = -1;
pos.close = -1;
var start, end;
if ( ! txt ) txt = twtable.getText();
start = 0; end = txt.length;
pos.close = txt.indexOf(tail.toLowerCase(),start);
if ( pos.close == -1 )
pos.close = txt.indexOf(tail.toUpperCase());
if(pos.close>-1 && pos.close<end &&
(pos.close+tail.length==end ||
txt.charAt(pos.close+tail.length)=='\n')){
// a signature tail is found
pos.open = pos.close;
do {
pos.open = txt.lastIndexOf(
'|',pos.open-1
);
}while(pos.open>0 && txt.charAt(pos.open-1)!='\n');
pos.open++;
} else {
// No signature tail is found.
pos.close = -1;
}
return pos;
};
//}}}
/***
!!!!! twtable.headPos
***/
//{{{
twtable.headPos = function(txt){
// Find the position of a table head |....|h\n.
return twtable.signaturePosByTail('|h',txt);
};
//}}}
/***
!!!!! twtable.getHeader
***/
//{{{
twtable.getHeader = function(txt){
if ( ! txt ) txt = twtable.getText();
var pos = twtable.headPos(txt);
return pos.open != -1
? txt.substring(pos.open,pos.close)
: '';
};
//}}}
/***
!!!!! twtable.footPos
***/
//{{{
twtable.footPos = function(txt){
// Find the position of a footage |....|f\n.
return twtable.signaturePosByTail('|f',txt);
};
//}}}
/***
!!!!! twtable.getFootNote
***/
//{{{
twtable.getFootNote = function(txt){
if ( ! txt ) txt = twtable.getText();
var pos = twtable.footPos(txt);
return pos.open != -1
? txt.substring(pos.open,pos.close)
: '';
};
//}}}
/***
!!!!! twtable.classPos
***/
//{{{
twtable.classPos = function(txt){
// Find the position of a class string |className|k\n.
return twtable.signaturePosByTail('|k',txt);
};
//}}}
/***
!!!!! twtable.getClass
***/
//{{{
twtable.getClass = function(txt){
if ( ! txt ) txt = twtable.getText();
var pos = twtable.classPos(txt);
return pos.open != -1
? txt.substring(pos.open,pos.close)
: '';
};
//}}}
/***
!!!!! twtable.captionPos
***/
//{{{
twtable.captionPos = function(txt){
// Find the position of a caption string |className|c\n.
return twtable.signaturePosByTail('|c',txt);
};
//}}}
/***
!!!!! twtable.getCaption
***/
//{{{
twtable.getCaption = function(txt){
if ( ! txt ) txt = twtable.getText();
var pos = twtable.captionPos(txt);
return pos.open != -1
? txt.substring(pos.open,pos.close)
: '';
};
//}}}
/***
!!!!! twtable.setCaption
***/
//{{{
twtable.setCaption = function (newtxt) {
// For some reason unknown to me, there are two focusout
// events fired for caption update, and in the 2nd time
// the tiddler is null and shall be ignored.
// Locate caption text.
var pos = twtable.captionPos();
var start = twtable.start.ndx;
if ( pos.open != -1 ) {
// There is caption currently and the user wants
// to change it.
if ( ! newtxt ) {
// If the user wants to remove the caption,
// we remove the whole caption line.
if ( pos.open > 0 ) pos.open--;
pos.close += 3;
}
twtable.setText(newtxt,pos.open+start,pos.close+start);
} else if ( newtxt ) {
// There is no caption at this moment, and
// the user is setting a new one.
// Look for class position.
pos = twtable.classPos();
if ( pos.open > -1 ) {
// There is class string in the defining text, put
// the caption after it.
pos.open = (pos.close+=3);
twtable.setText(
'|'+newtxt+'|c',pos.open+start,pos.close+start
);
} else {
// Otherwise add the caption to the beginning.
pos.open = pos.close = 0; //twtable.start.ndx;
twtable.setText('|'+newtxt+'|c\n',start,start);
}
}
return true;
};
//}}}
/***
!!!!! twtable.editText
***/
//{{{
var preEditText = twtable.editText;
twtable.editText = function(ev,rowndx,colndx){
// Replacing the twve.editable.editText for tables.
twve.tiddler.clearFocusBox();
if ( twtable.editNode ) {
var pos = null;
switch ( twtable.editNode.nodeName ) {
case 'TABLE' :
// Clicking on a missing cell.
// Need to find the corresponding row index.
pos = twve.tiddler.mousePosition(ev);
twtable.editNode = twtable.rowAt(
pos,
twtable.editNode.querySelectorAll('tr')
);
if ( ! twtable.editNode ) return null;
case 'TR' :
// Clicking on a missing cell. Determine how
// many cells away from the last one in this row.
if ( ! pos )
pos = twve.tiddler.mousePosition(ev);
colndx = twtable.colIndexAt(pos);
rowndx = twve.table.rowIndex(twtable.editNode);
var cells = twtable.editNode
.querySelectorAll('th,td');
var lastndx = twve.table.cellColIndex(
cells[cells.length-1]
);
twtable.editNode = twtable.appendCell(
twtable.editNode,
colndx-lastndx,
lastndx
);
case 'TH' :
case 'TD' :
// table cell
if ( ! pos && ev ) {
pos = twve.tiddler.mousePosition(ev);
colndx = twtable.colIndexAt(pos);
rowndx = twtable.rowIndexAt(pos);
}
return (rowndx > -1 && colndx > -1)
? twtable.editCell(ev,rowndx,colndx)
: null;
case 'DIV' :
// table caption
return preEditText.call(
this,ev,
twtable.getCaption(),
twtable.editNode
);
}
}
return preEditText.apply(this,arguments);
};
//}}}
/***
!!!!! twtable.reInitializeTable
***/
//{{{
twtable.reInitializeTable = function(txt){
var nodes = twve.tiddler.getPreviewedNodes(txt);
var table = twtable.filter(nodes,'table');
twtable.replaceWith(nodes, twtable.wrappers.wrap);
twtable.dom = table;
twtable.createWrapperRefBars();
};
//}}}
/***
!!!!! twtable.refreshSelf
***/
//{{{
twtable.refreshSelf = function(txt){
// Refresh the table represented by this twve.table object.
// The direct wrapper of this table is assumed open.
var elem = twtable.getElement();
switch ( elem.nodeName ) {
case 'DIV' :
// Table caption
if ( ! txt ) txt = ' ';
twve.node.wikify(txt,elem);
break;
case 'TH' :
case 'TD' :
// refresh the cell
// Must find the corresponding cell here if there are
// multiple transcluded copies.
twve.node.wikify(txt,elem);
break;
default :
// refresh the whole table
twtable.reInitializeTable(txt);
twtable.prepare(txt);
break;
}
return twtable;
};
//}}}
/***
!!!!! twtable.removeSelf
***/
//{{{
var preRemoveSelf = twtable.removeSelf;
twtable.removeSelf = function(param){
if ( ! twtable.editNode ) {
twtable.wrappers.wrap.parentNode.removeChild(
twtable.wrappers.wrap
);
return preRemoveSelf.apply(this,arguments);
}
twtable.refreshSelf('');
if ( param && param.what ) twtable.changed(param);
return twtable;
};
//}}}
/***
!!!!! twtable.adjustRefBars
***/
//{{{
twtable.adjustRefBars = function(){
twtable.refbarV.adjust(twtable);
twtable.refbarH.adjust(twtable);
return twtable;
};
//}}}
/***
!!!!! twtable.refreshTable
***/
//{{{
twtable.refreshTable = function(txt){
// Refresh the whole table
twtable.editNode = null;
twtable.hideRefBars();
twtable.refreshAll(txt);
twtable.showRefBars();
twtable.drawFocusBorders();
return twtable;
};
//}}}
/***
!!!!! twtable.updateText
***/
//{{{
//var preUpdateText = twtable.updateText;
twtable.updateText = function(txt){
var elem = twtable.getElement();
switch ( elem.nodeName ) {
case 'TH' :
case 'TD' :
return twtable.updateCell(txt);
case 'DIV' :
// Table caption
twtable.setCaption(txt[0]);
var param = twve.object.create();
param.what = 'CAPTION MODIFIED';
param.text = twtable.getText();
// Refresh the caption and call the .changed() event
// handler.
return twtable.refreshAll(txt[0],param);
break;
default :
twtable.setText(txt[0]);
break;
}
return twtable.refreshAll(txt[0]);
};
//}}}
/***
!!!!! twtable.drawFocusBorders
***/
//{{{
twtable.drawFocusBorders = function(eb){
if ( ! eb ) eb = twve.node.box(twtable.wrappers.wrap);
twve.tiddler.drawFocusBorders(eb);
var pos = twve.object.create();
pos.left = eb.right;
pos.top = eb.top;
var menu = twve.table.getTableMenu();
menu.showRoot(pos,'left');
if ( menu.isVisible() ) {
pos.left = menu.root.dom.offsetLeft;
menu.show(pos,'left');
}
return twtable;
};
//}}}
/***
!!!!! twtable.changed
***/
//{{{
var preChanged = twtable.changed;
twtable.changed = function (param) {
if ( param )
param.text = preChanged.apply(this,arguments);
twtable.resize();
twtable.adjustRefBars();
return param ? param.text : '';
};
//}}}
/***
!!!!! twtable.insertRow
***/
//{{{
twtable.insertRow = function ( row, which ) {
// Inserts one table row, above or below the one with index row.
// Whether 'above' or 'below' is given in the 2nd argument 'which'.
var txt = twtable.getText();
var end = txt.length;
var rstart, rend, cpos;
// Locate the table row. If we are inserting above, then
// we need to locate the end of the previous row. If
// we are inserting below, we need to locate the end
// of this row.
if ( which.toLowerCase().substring(0,5)=='below' ) row++;
if ( row == 0 ) {
rstart = twve.table.rowStarts(txt,0,end,0);
rend = rstart;
} else {
rstart = twve.table.rowStarts(txt,0,end,row-1);
rend = twve.table.rowEnds(txt,rstart);
}
// Prepare one empty row to insert
var newline = '|';
cpos = twve.table.rowCells(txt,rstart,rend);
for ( var cols = cpos.length,c = 0; c < cols; c++ )
newline += '|';
// Insert an empty row the same length as its neighbor.
txt = txt.substring(0,rend)
+ (row>0?('\n'+newline):(newline+'\n'))
+ (rend<end?txt.substring(rend):'');
// Update the twve.table.copiedRowIndex if necessary.
var rndx = twve.table.copiedRowIndex();
if ( row <= rndx )
twve.table.copiedRowIndex(++rndx);
// Call the event handler.
txt = twtable.changed ({
what:'ROW INSERTED',
where:row,
text:txt
});
// Save the tiddler text and refresh all copies of this
// table.
//twve.tiddler.saveText(twtable,txt);
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.deleteRow
***/
//{{{
twtable.deleteRow = function ( row, cf ) {
// Deletes the table row indexed by the 1st argument row.
var txt = twtable.getText();
var end = txt.length, rstart, rend, cpos;
// Confirm to delete if required.
if ( (cf !== false) &&
config.options.chktwveCoreConfirmToDelete )
if(!confirm('Delete row ' + row))
return twtable;
// Locate the row to delete.
rstart = twve.table.rowStarts(txt,0,end,row);
rend = twve.table.rowEnds(txt,rstart);
var dlen = 0;
if ( rend < end ) {
// Not the last line of text, look for row-spanned
// cells.
// If one of the cells contains the content of a
// row-spanned cell, clear the cell below it as well.
cpos = twve.table.rowCells(txt,rstart,rend);
var nrstart = twve.table.rowStarts(txt,rend,end,0);
var nrend = twve.table.rowEnds(txt,nrstart);
var ncpos = twve.table.rowCells(txt,nrstart,nrend);
for ( var c = 1; c < cpos.length; c++ ) {
if ( !cpos[c] || !ncpos[c] ) continue;
// Cell in this row.
var ctxt = txt.substring(cpos[c].open,cpos[c].close);
// Cell below.
var nctxt = txt.substring(ncpos[c].open,ncpos[c].close);
if ( nctxt == '~' && ctxt != '~' ) {
// This cell contains a row-spanned content.
// Clear the cell below it.
txt = txt.substring(0,ncpos[c].open) +
txt.substring(ncpos[c].close);
end--;
dlen++;
}
}
}
// Delete that row
txt = (rstart>0?txt.substring(0,rstart-1):'')
+(rend<end?txt.substring(rend):'');
// Update the twve.table.copiedRowIndex if necessary.
var rndx = twve.table.copiedRowIndex();
if ( row < rndx )
twve.table.copiedRowIndex(--rndx);
/*
else if ( row == rndx ) {
twve.table.copiedRowIndex(-1);
twve.table.copiedRowText(null);
}
*/
// Call the event handler
txt = twtable.changed (
{
what:'ROW DELETED',
where:row,
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
//twve.tiddler.saveText(twtable,txt);
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.moveRow
***/
//{{{
twtable.moveRow = function ( row, dr ) {
// Move the table row, indexed by the 1st argument, up or
// down by dr.
var txt = twtable.getText();
var end = txt.length;
var rstart = twve.table.rowStarts(txt,0,end,row);
var rend = twve.table.rowEnds(txt,rstart);
// Extract the text of this row and find its final
// destination.
var rtxt = txt.substring(rstart,rend);
var dest = row + dr;
var dest_start = twve.table.rowStarts(txt,0,end,dest);
var dest_end = twve.table.rowEnds(txt,dest_start);
if ( dr > 0 ) {
// Move down, we do
// 1. empty this row,
txt = txt.substring(0,rstart) +
txt.substring(rend+1,dest_end) +
// The rend+1 above is to remove the '\n' at the end
// of this row.
// 2. and insert it to the destination.
'\n' + rtxt +
txt.substring(dest_end);
} else {
// Move up, we do
// 1. insert it to the destination,
txt = txt.substring(0,dest_start) +
rtxt + '\n' +
txt.substring(dest_start,rstart-1) +
// The rstart-1 above is to remove the '\n' at the
// beginning of this row.
// 2. empty this row.
txt.substring(rend);
}
// Update twve.table.copiedRowIndex /
// twve.table.copiedRowText
// if necessary.
var rndx = twve.table.copiedRowIndex();
if ( row == rndx )
twve.table.copiedRowIndex(rndx+dr);
//else if ( dest == rndx )
// twve.table.copiedRowText(rtxt);
// Call the event handler
txt = twtable.changed (
{
what:'ROW EXCHANGED',
from:row,
to:dest,
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.copyRow
***/
//{{{
twtable.copyRow = function ( row ) {
// Copy the table row indexed by the argument.
var txt = twtable.getText();
var rstart = twve.table.rowStarts(txt,0,txt.length,row);
var rend = twve.table.rowEnds(txt,rstart);
twve.table.copiedRowText(txt.substring(rstart,rend));
twve.table.copiedRowIndex(row);
twve.table.copiedColumnText(null);
twve.table.copiedColumnIndex(-1);
return twtable;
};
//}}}
/***
!!!!! twtable.pasteRow
***/
//{{{
twtable.pasteRow = function ( row ) {
// Paste the previously copied row text into the table row
// indexed by the argument. The existing text will be
// replaced with the twve.table.copiedRowText().
var txt = twtable.getText();
var end = txt.length;
var rstart = twve.table.rowStarts(txt,0,end,row);
var rend = twve.table.rowEnds(txt,rstart);
// Replace the existing text with the
// twve.table.copiedRowText().
var rtxt = twve.table.copiedRowText();
txt = txt.substring(0,rstart) +
rtxt +
(rend<end?txt.substring(rend):'');
// Call the event handler
txt = twtable.changed (
{
what:'ROW PASTED',
where:row,
from:twve.table.copiedRowIndex(),
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.insertColumn
***/
//{{{
twtable.insertColumn = function ( col, which ) {
// Inserts one table column, either to the left or right of,
// the one with index col. Whether 'left' or 'right' is
// given in the 2nd argument 'which'.
var txt = twtable.getText();
// Positions of all cells.
var cpos = twve.table.allCells(txt,0,txt.length);
// If we are inserting to the right, we can use the end of
// this cell. If, on the other hand, we are inserting to
// the left, we can use the beginning of this cell.
var left = which.toLowerCase().substring(0,4)=='left';
var dlen = 0;
// We go from bottom to top.
for ( var r = cpos.length-1; r >= 0; r-- ) {
var clast = cpos[r].length - 1;
if ( col <= clast ) {
// This cell is regular.
if ( left )
txt = txt.substring(0,cpos[r][col].open) +
'|' +
txt.substring(cpos[r][col].open);
else
txt = txt.substring(0,cpos[r][col].close+1) +
'|' +
txt.substring(cpos[r][col].close+1);
} else {
// This cell is missing. Prepare empty cells to
// append. If we are inserting to the left, we
// append the empty cells and stop. If to the right,
// we append the empty cells to the left, one more
// at this cell, and one more to the right.
var nctxt = '';
for(var c=clast; c<col; c++) nctxt += '|';
// One more empty cell if inserting to the right.
if ( ! left ) nctxt += '|';
// Append the empty cells.
txt = txt.substring(0,cpos[r][clast].close+1) +
nctxt +
txt.substring(cpos[r][clast].close+1);
dlen += nctxt.length;
}
}
// Update the ending position of the table.
//twtable.end.ndx += cpos.length+dlen;
if ( ! left ) col++;
// Update the twve.table.copiedColumnIndex if necessary.
var cndx = twve.table.copiedColumnIndex();
if ( col <= cndx )
twve.table.copiedColumnIndex(++cndx);
// Call the event handler.
txt = twtable.changed (
{
what:'COL INSERTED',
where:col,
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.deleteColumn
***/
//{{{
twtable.deleteColumn = function ( col, cf ) {
// Deletes the table column indexed by the 1st argument row.
var txt = twtable.getText();
var cpos = twve.table.allCells(txt,0,txt.length);
// Confirm deletion if required.
if ( (cf !== false) &&
config.options.chktwveCoreConfirmToDelete )
if(!confirm('Delete column '+twve.table.colRef(col)))
return -1;
// Go on to delete the column.
var dlen = 0;
// We go from bottom to top.
for ( var r = cpos.length-1; r >= 0; r-- ) {
if ( col >= cpos[r].length )
// This is a missing cell. Do nothing.
continue;
// To delete a cell in a row, we need to check whether
// that cell contains column-spanned content. If it
// does, we also need to clear the left cell after
// deleting this one.
var ctxt = txt.substring(
cpos[r][col].open,cpos[r][col].close
);
var ltxt = col > 0
? txt.substring(
cpos[r][col-1].open,cpos[r][col-1].close
)
: '';
// Delete this cell.
txt = txt.substring(0,cpos[r][col].open-1) +
txt.substring(cpos[r][col].close);
// Record length change.
dlen += ctxt.length+1;
// Check for column-spanned content.
if ( ctxt != '>' && ltxt == '>' ) {
// Yes this cell contains the column-spanned
// content. Clear the left cell, too.
txt = txt.substring(0,cpos[r][col-1].open-1) +
txt.substring(cpos[r][col-1].close);
// Record length change.
dlen += ltxt.length+1;
}
}
// Update the ending positions of the table.
//twtable.end.ndx -= dlen;
// Update twve.table.copiedColumnIndex /
// twve.table.copiedColumnText if necessary.
var cndx = twve.table.copiedColumnIndex();
if ( col < cndx )
twve.table.copiedColumnIndex(--cndx);
/*
else if ( col == cndx ) {
twve.table.copiedColumnIndex(-1);
twve.table.copiedColumnText(null);
}
*/
// Call the event handler.
txt = twtable.changed (
{
what:'COL DELETED',
where:col,
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.moveColumn
***/
//{{{
twtable.moveColumn = function ( col, dc ) {
// Move the table column indexed by col to the left or
// right by dc.
var txt = twtable.getText();
var cpos = twve.table.allCells(txt,0,txt.length);
// Destination
var dest = col + dc;
var cndx = twve.table.copiedColumnIndex();
var ctxtary = (dest == cndx) ? [] : null;
var dlen = 0;
// We go from bottom to top.
for ( var r = cpos.length-1; r >= 0; r-- ) {
var clast = cpos[r].length - 1;
var ctxt = col <= clast
? txt.substring(
cpos[r][col].open,cpos[r][col].close
)
: '';
// Save this cell if necessary.
if (ctxtary) ctxtary.unshift(ctxt);
// Now go on to move it.
if ( col <= clast ) {
// This cell is regular. If moving left then the
// destination must also be regular. Only when
// moving right we may encounter the case where
// destination is missing.
if ( dc > 0 ) {
// Move right
if ( dest <= clast ) {
// The destination is also regular. We do
// 1. empty the cell,
txt=txt.substring(0,cpos[r][col].open-1)+
txt.substring(
cpos[r][col].close,
cpos[r][dest].close
)+
// 2. insert it to destination.
'|' + ctxt +
txt.substring(cpos[r][dest].close);
} else {
// The destination is missing. We do
// 1. prepare empty cells to append before
// destination,
var nctxt = '';
for(var c=clast; c<dest; c++) nctxt += '|';
// 2. empty the cell,
txt=txt.substring(0,cpos[r][col].open)+
// 3. append empty cells before destination
nctxt +
// 4. insert this cell and end this table
// row.
ctxt +
txt.substring(cpos[r][clast].close);
dlen += nctxt.length;
}
} else {
// If move left, we do
// 1. insert this cell at destination,
txt=txt.substring(0,cpos[r][dest].open)+
ctxt + '|' +
txt.substring(
cpos[r][dest].open,
cpos[r][col].open-1
)+
// 2. empty this cell.
txt.substring(cpos[r][col].close);
}
} else {
// This cell is missing. If both are missing, we
// don't do anything. This is for sure if we are
// moving right. If, however, we are moving left,
// we can encounter the case where destination is
// regular, and do the moving.
if ( dest <= clast ) {
// Must be moving left. We do
// 1. insert an empty cell at destination,
txt=txt.substring(0,cpos[r][dest].open)+
'|' +
txt.substring(cpos[r][dest].open);
// 2. and done.
dlen += 1;
}
}
}
// Update the end index of this table.
//twtable.end.ndx += dlen;
// Update the twve.table.copiedColumnIndex /
// twve.table.copiedColumnText if necessary.
if ( col == cndx )
twve.table.copiedColumnIndex(cndx+dc);
//else if ( dest == cndx )
// twve.table.copiedColumnText(ctxtary);
// Call the event handler
txt = twtable.changed (
{
what:'COL EXCHANGED',
from:col,
to:dest,
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.copyColumn
***/
//{{{
twtable.copyColumn = function ( col ) {
// Copy the table column indexed by col, and store it as
// an array for later use.
var txt = twtable.getText();
var cpos = twve.table.allCells(txt,0,txt.length);
var ctxt = [];
for ( var r = 0; r < cpos.length; r++ ) {
ctxt[ctxt.length] =
col < cpos[r].length
? txt.substring(
cpos[r][col].open,cpos[r][col].close
)
: '';
}
twve.table.copiedColumnText(ctxt);
twve.table.copiedColumnIndex(col);
twve.table.copiedRowText(null);
twve.table.copiedRowIndex(-1);
return twtable;
};
//}}}
/***
!!!!! twtable.pasteColumn
***/
//{{{
twtable.pasteColumn = function ( col ) {
// Paste the previously copied column into the one indexed
// by col (replace the current content).
var txt = twtable.getText();
var end = txt.length;
var cpos = twve.table.allCells(txt,0,end);
// If this table has more rows than the copied column, we
// replace content up to that of the copied column and
// leave the rest in this table untouched. If, however, this
// table has less rows than the copied column, we replace
// content up to that of this table, then append new rows
// to hold the rest in the copied column.
var ctxt = twve.table.copiedColumnText();
var clen = ctxt.length;
var rlast = Math.min(cpos.length, clen);
var plen = 0;
// We go from bottom to top.
for ( var r = rlast-1; r >= 0; r-- ) {
var clast = cpos[r].length - 1;
if ( col <= clast ) {
// This cell is regular, replace it.
txt = txt.substring(0,cpos[r][col].open) +
ctxt[r] +
txt.substring(cpos[r][col].close);
plen += ctxt[r].length
-(cpos[r][col].close-cpos[r][col].open);
} else {
// This cell is missing. We do
// 1. prepare empty cells to append to the left
// of this cell,
var nctxt = '';
for(var c=clast; c<col; c++) nctxt += '|';
// 2. append the empty cells,
txt = txt.substring(0,cpos[r][clast].close+1) +
nctxt +
// 3. insert content of this cell (from the copied
// column) and end this row.
ctxt[r] + '|\n' +
txt.substring(0,cpos[r][clast].close+1);
plen += nctxt.length + ctxt[r].length + 2;
}
}
// Update the closing position of the table
//twtable.end.ndx += plen;
// Append extra rows if necessary.
for(var r=rlast; r<clen; r++){
// Prepare empty cells to append to the left.
var nctxt = '\n';
for (var c=0; c < col; c++) nctxt += '|';
// Append the empty cells.
txt += nctxt +
// Insert content of this cell (from the copied column)
// and end this row.
ctxt[r] + '|';
// Update the closing position of the table
//twtable.end.ndx += nctxt.length+ctxt[r].length+1;
}
// Call the event handler
txt = twtable.changed (
{
what:'COL PASTED',
where:col,
from:twve.table.copiedColumnIndex(),
text:txt
}
);
// Save the tiddler text and refresh all copies of this
// table.
twtable.setText(txt);
return twtable.refreshTable(txt);
};
//}}}
/***
!!!!! twtable.resize
***/
//{{{
twtable.resize = function(){
if ( ! twtable.wrappers ) return twtable;
var focused = twve.tiddler.focusElem();
if ( focused ) focused = focused.is(twtable);
var dw = 0, dh = 0;
if ( focused ) {
dw = twtable.refbarV.getSize();
dh = twtable.refbarH.getSize();
}
twtable.wrappers.resize(dw,dh);
if ( focused ) {
twtable.drawFocusBorders();
}
return twtable;
};
//}}}
/***
!!!!! twtable.adjustWidth
***/
//{{{
twtable.adjustWidth = function() {
var tw = twve.node.width(twtable.dom,'outer',true);
var minch = config.options.txttwveTableMinCellWidth*1;
var w = 0;
var cells = twtable.rowWithMaxCol()
.querySelectorAll('th,td');
for ( var n=0,len=cells.length; n<len; n++ ){
var c = cells[n];
var cw = twve.node.width(c,'outer',true);
var minw = twve.node.cssSize(
window.getComputedStyle(c)
.getPropertyValue('font-size')
) * minch;
var dw = version.major==2 && version.minor<7
? (twve.node.width(c)-cw)/2
: 0;
if ( cw < minw ) {
twve.node.setDimension(c,minw+dw);
cw = twve.node.width(c,'outer',true);
} else if ( dw > 0 ) {
twve.node.setDimension(c,cw+dw);
cw = twve.node.width(c,'outer',true);
}
w += cw;
}
if ( w < tw ) w = tw;
else w += 1;
// It is good to set the table width here, because
// otherwise the table would become slightly narrower
// and the cell width changed after setting the wrapper
// width.
twve.node.setDimension(
twtable.dom,
w + (config.tableSorting ||
config.macros.sortableGridPlugin
? 1 : 0)
);
return twtable;
};
//}}}
/***
!!!!! twtable.initRows
***/
//{{{
twtable.initRows = function (txt) {
// Initialize table rows.
// First we store the CORRECT row index as an attribute
// of the table row, because
// 1. tr.rowIndex is not consistent across browsers;
// 2. I don't want to use
// table.querySelectorAll('tr') every time needed.
// Store row index as an attribute. This is necessary
// because different browsers give different index
// values in tr.rowIndex.
var table = twtable.dom;
var rows = table.querySelectorAll('tr');
for ( var r=0,len=rows.length; r<len; r++ ) {
if (rows[r].getAttribute('row')) return false;
rows[r].setAttribute('row', r);
}
if ( ! txt ) txt = twtable.getText();
var end = txt.length;
var rstart, rend = 0;
// Temporarily disable the IncludeCSS option
var includeCSS = config.options.chktwveTableIncludeCSS;
config.options.chktwveTableIncludeCSS = false;
// Start the initialization
for ( var r=0,rlen=rows.length; r<rlen; r++ ) {
rstart = twve.table.rowStarts(txt,rend,end,0);
rend = twve.table.rowEnds(txt,rstart);
var col = 0, cpos;
var cells = rows[r].querySelectorAll('th,td');
for ( var c=0,clen=cells.length; c<clen; c++ ){
var cell = cells[c];
while ( rstart < rend ) {
// The reason to use a loop here is to take
// care of the spanned cells. It is necessary
// because a spanned cell consists of multiple
// cells in the defining wiki text but only
// one in the resulting rendered page.
cpos = twve.table.locateCell(
txt,rstart,rend,0
);
rstart = cpos.close;
var ctxt = twtable.getCellText(cpos,txt);
if ( ! ctxt ) {
cell.setAttribute('col',col++);
break;
}
if ( ctxt=='~' || ctxt=='>' ) {
// A spanned cell. Loop through to the
// content.
col++; continue;
}
if ( config.options.chktwveTableMultiLine ) {
var ch0 = ctxt.charAt(0);
if(ch0=='*' || ch0=='#' || ctxt.indexOf('\n')>-1)
twve.node.wikify(ctxt,cell);
}
cell.setAttribute('col',col++);
break;
}
}
}
// End of initialization.
// Restore the IncludeCSS option.
config.options.chktwveTableIncludeCSS = includeCSS;
return true;
};
//}}}
/***
!!!!! twtable.multiLine
***/
//{{{
var preMultiLine = twtable.multiLine;
twtable.multiLine = function () {
// Replacing the twve.element.multiLine for tables.
// We need to do so because twve.table allows multi-lined
// content in a table cell.
return twtable.editNode
? (twve.node.matches(twtable.editNode,'th,td')
? config.options.chktwveTableMultiLine
: false)
: preMultiLine.apply(this,arguments);
};
//}}}
/***
!!!!! twtable.misc
***/
//{{{
var preHasClass = twtable.hasClass;
twtable.hasClass = function ( cstr ) {
// The dom in this twve.element object does not have the
// specified class. If it is a table, then its class could
// be removed by SortableGridPlugin. Check the wikitext that
// defines this table.
if ( config.macros.sortableGridPlugin ) {
// SortableGridPlugin is installed, and this element is
// a table. Find the wikitext and look for table classes.
var pos = twtable.classPos();
if ( pos.open > -1 ) {
var p = twtable.getText().indexOf(cstr,pos.open);
if ( p > -1 && p < pos.close ) return true;
}
return false;
}
return preHasClass.apply(this,arguments);
};
//}}}
/***
!!!!! twtable.sorting
***/
//{{{
// startSorting
twtable.startSorting = function() {
return ! twve.tiddler.getEditBox();
};
// sortingCell
twtable.sortingCell = function(o){
// Find the header cell for sorting
twtable.editNode = o.nodeName=='A'
? o.parentNode // SortableGridPlugin
: o; // TableSortingPlugin
return twtable.editNode;
};
// sort
twtable.sort = function (o, rev) {
return twve.table.sort(twtable,o,rev);
};
//}}}
/***
!!!!! twtable.transpose
***/
//{{{
twtable.transpose = function () {
// Transpose this table, that is, move cell content from
// (r, c) to (c, r).
// For tables without spanned cells, we simply start from
// the bottom-right corner and go in the bottom-up and
// right-to-left manner. When we reach the top-left corner
// the job is done. For tables with spanned cells, however,
// we need to reverse the spanned cells before transposing
// the table. For more details please see
// http://twve.tiddlyspace.com/#%5B%5BTransposing%20a%20table%20--%20backward%20thinking%5D%5D
var txt = twtable.getText();
// Reverse the column-spanned cells
var p1 = 0;
while ( true ) {
// Look for the signature of a column-spanned cell.
p1 = txt.indexOf('|>|',p1);
if ( p1 == -1 ) break;
// There is a column spanned cell, search for the
// content before end of line or end of table.
var p2 = p1 + 3; p1++;
var pend = txt.indexOf('\n',p2);
if ( pend == -1 ) pend = txt.length;
while ( p2 < pend && txt.substring(p2,p2+2) == '>|' )
p2 += 2;
if ( p2 < pend ) {
// Now p2 shall be pointing at the beginning of the
// content. Look for its end (p3).
//var p3 = text.indexOf('|',p2);
var p3=twve.text.skipToActive(txt,'|',p2,pend).ndx;
if ( p3 == -1 ) break;
if ( p3 < pend ) {
// Now p3 should be at the end of the content.
// Switch the content and the first '>'.
txt = txt.substring(0,p1)+ // before first '>'
txt.substring(p2,p3)+ // the content
txt.substring(p1+1,p2)+ // all the rest '>'
'>'+
txt.substring(p3); // after content
}
p1 = p3;
} else p1 = p2;
}
// Get positions of all cells.
var cpos = twve.table.allCells(txt,0,txt.length);
var rows = cpos.length;
var cols = 0, ctxt;
var r, c, thead, h;
// Find the maximum number of columns
for ( r = 0; r < rows; r++ ) {
if ( cpos[r].length > cols ) cols = cpos[r].length;
}
// Reverse the row spanned cells
for ( c = cols-1; c >= 0; c-- ) {
var r0 = -1;
for ( r = rows-1; r >= 0; r-- ) {
// Skip missing cells
if ( c >= cpos[r].length ) continue;
// Get the cell text.
ctxt = txt.substring(
cpos[r][c].open,cpos[r][c].close
);
if ( ctxt == '~' ) {
// There is a row-spanned cell, mark the row
// index if not yet (for later use).
if ( r0 == -1 ) r0 = r;
} else if ( r0 != -1 ) {
// ctxt is now the content of a row-spanned cell,
// switch it with the last '~' in row r0.
txt = txt.substring(0,cpos[r][c].open)+
'~'+
txt.substring(
cpos[r][c].close,cpos[r0][c].open
)+
ctxt+
txt.substring(cpos[r0][c].close);
// Re-obtain the correct positions of cells.
cpos = twve.table.allCells(txt,0,txt.length);
r0 = -1;
}
}
}
// Prepare an empty table of dimensions (cols, rows).
var newtxt = '';
for ( c = 0; c < cols; c++ ) {
newtxt += '|';
for ( r = 0; r < rows; r++ )
newtxt += '|';
if ( c < cols-1 ) newtxt += '\n';
}
// Get all of its cell positions.
var newpos = twve.table.allCells(newtxt,0,newtxt.length);
// Start transposition.
var reghead = /^\s*\!/;
var regtxt = /\S/;
for ( c = cols-1; c >= 0; c-- ) {
for ( r = rows-1; r >= 0; r-- ) {
// Skip missing cells
if ( c >= cpos[r].length ) continue;
// Check for header row.
var ch=txt.charAt(cpos[r][cpos[r].length-1].close+1);
thead = (ch=='h' || ch=='H');
// Get text of the (r, c) cell.
ctxt = txt.substring(
cpos[r][c].open,cpos[r][c].close
);
if ( ctxt == '~' ) {
// Row-spanned cells. Transpose to column-
// spanned cells.
ctxt = '>';
} else if ( ctxt == '>' ) {
// Column-spanned cells. Transpose to row-
// spanned cells.
ctxt = '~';
} else if (thead && ! reghead.test(ctxt)) {
// Header row cell, make it a TH in the
// transposed table.
if ( (h=ctxt.search(regtxt)) > 0 )
ctxt = ctxt.substring(0,h)+'!'
+ ctxt.substring(h);
else ctxt = '!'+ctxt;
}
// Put it to the (c, r) cell in the transposed table.
newtxt = newtxt.substring(0,newpos[c][r].open)+
ctxt+
newtxt.substring(newpos[c][r].close);
}
}
// Make the first row that contains all TH's a header row.
cpos = twve.table.allCells(newtxt,0,newtxt.length);
for ( r = 0; r < cpos.length; r++ ) {
thead = false;
for ( c = cpos[r].length-1; c >= 0; c-- ) {
ctxt = newtxt.substring(
cpos[r][c].open,cpos[r][c].close
);
if ( ! reghead.test(ctxt) ) break;
thead = (c==0);
}
if ( thead ) {
c = cpos[r].length-1;
newtxt = newtxt.substring(0,cpos[r][c].close+1)+
'h'+
newtxt.substring(cpos[r][c].close+1);
// Remove the leading '!' in the cell text.
for ( ; c >=0; c-- ) {
ctxt = newtxt.substring(
cpos[r][c].open,cpos[r][c].close
);
var p = ctxt.indexOf('!');
ctxt = ctxt.substring(0,p)+ctxt.substring(p+1);
newtxt = newtxt.substring(0,cpos[r][c].open)+
ctxt+
newtxt.substring(cpos[r][c].close);
}
break;
}
}
// Copy the foot note row if there is one.
var tmp = twtable.getFootNote(txt);
if ( tmp ) newtxt='|'+tmp+'|f\n'+newtxt;
// Copy the caption row if there is one.
tmp = twtable.getCaption(txt);
if ( tmp ) newtxt='|'+tmp+'|c\n'+newtxt;
// Copy the class row if there is one.
tmp = twtable.getClass(txt);
if ( tmp ) newtxt='|'+tmp+'|k\n'+newtxt;
// Done transposition. Set it back. Note that the length
// of the definition text may change so we call
// twtable.setText() to take care of that.
//twtable.setText(newtxt);
// Call the event handler.
newtxt = twtable.changed(
{
what : 'TRANSPOSED',
text : newtxt //twtable.tiddler.text
}
);
twtable.setText(newtxt);
return twtable.refreshTable(newtxt);
};
//}}}
/***
!!!!! end of twtable
***/
//{{{
return twtable.created(table,txt,start,dir);
}
};
//}}}
/***
!!! twve.table.wrapper
***/
//{{{
twve.table.wrapper = {
//}}}
/***
!!!! twve.table.wrapper classes
***/
//{{{
class_wrap : 'twveTable',
class_tbref : 'twveTBRef',
class_tb : 'twveTB',
class_cap : 'twveTCap',
//}}}
/***
!!!! twve.table.wrapper.getSelector
***/
//{{{
getSelector : function(which){
var pref = 'div.';
if ( ! which )
return pref+twve.table.wrapper.class_wrap;
which = which.toLowerCase();
if ( which == 'cap' )
return pref+twve.table.wrapper.class_cap;
if ( which == 'tbref' )
return pref+twve.table.wrapper.class_tbref;
if ( which == 'tb' )
return pref+twve.table.wrapper.class_tb;
var inner =
pref+twve.table.wrapper.class_cap+','+
pref+twve.table.wrapper.class_tbref+','+
pref+twve.table.wrapper.class_tb;
return ( which == 'inner' )
? inner
: pref+twve.table.wrapper.class_wrap+','+inner;
},
//}}}
/***
!!!! twve.table.wrapper.create
***/
//{{{
create : function (twtable) {
// Create wrappers to contain a table.
// In twve tables are wrapped with more than one <div>
// elements, namely tb, tbref, cap, and all, for
// scrolling, reference bars, floating menus, etc.
// A table is first wrapped with tb, with overflow:auto css
// style assignment, so oversized tables are scrollable.
// Then wrapped again with tbref, with overflow:hidden and
// position:relative styles, so reference bars and floating
// menus can be added here.
// The caption, if any, is detached from the table and put
// above it into cap.
// Finally, the whole combination is wrapped within all,
// so the whole things can be considered as one object in
// the page flow.
var tbw = twve.object.create();
tbw.clear = function() {
tbw.wrap = null;
tbw.tbref = null;
tbw.tb = null;
tbw.cap = null;
return tbw;
};
tbw.captionHeight = function(){
return tbw.cap
? twve.node.height(tbw.cap,'outer',true)
: 0;
};
// set table
tbw.setTable = function(twtable){
var table = twtable.dom;
if ( ! table ) return tbw;
tbw.wrap = twve.table.getWrapper(table);
if ( tbw.wrap ) {
// This table already has wrappers.
tbw.tbref = tbw.wrap.querySelector(
twve.table.wrapper.getSelector('tbref')
);
tbw.tb = tbw.wrap.querySelector(
twve.table.wrapper.getSelector('tb')
);
tbw.cap = tbw.wrap.querySelector(
twve.table.wrapper.getSelector('cap')
);
return tbw;
}
// Create some <div> wrappers for a table.
tbw.wrap = document.createElement('div');
tbw.tbref = document.createElement('div');
tbw.tb = document.createElement('div');
tbw.cap = document.createElement('div');
var cap = table.caption
? table.childNodes[0]
: null;
if ( cap ) {
table.removeChild(cap);
tbw.cap.innerHTML = cap.innerHTML;
} else {
tbw.cap.innerHTML = ' ';
}
twve.node.copyMargin(tbw.wrap,table);
table.style.marginLeft = '4px';
table.parentNode.replaceChild(tbw.wrap,table);
tbw.tb.appendChild(table);
tbw.wrap.style.position = 'relative';
//tbw.wrap.style.marginTop = '2px';
tbw.wrap.style.paddingTop = '8px';
tbw.wrap.classList.add(twve.table.wrapper.class_wrap);
table.style.marginTop = '4px';
// cap
tbw.cap.style.position = 'relative';
tbw.cap.style.textAlign = 'center';
tbw.cap.style.overflow = 'hidden';
tbw.cap.classList.add(twve.table.wrapper.class_cap);
tbw.wrap.appendChild(tbw.cap);
// tbref
//tbw.tbref.style.border = '1px solid';
tbw.tbref.style.border = 0;
tbw.tbref.style.overflow = 'hidden';
tbw.tbref.style.position = 'relative';
tbw.tbref.classList.add(
twve.table.wrapper.class_tbref
);
tbw.wrap.appendChild(tbw.tbref);
// tb
//tbw.tb.style.border = '1px solid';
tbw.tb.style.border = 0;
tbw.tb.style.overflow = 'auto';
tbw.tb.style.position = 'relative';
tbw.tb.classList.add(
twve.table.wrapper.class_tb
);
tbw.tbref.appendChild(tbw.tb);
return tbw;
};
// resize
tbw.resize = function(dw,dh){
var table = tbw.tb.childNodes[0];
tbw.width = twve.node.width(table,'outer',true)+(dw || 0);
tbw.height = twve.node.height(table,'outer',true)+(dh || 1);
twve.node.setDimension(tbw.cap,tbw.width-dw);
twve.node.setDimension(tbw.tb,tbw.width,tbw.height);
twve.node.setDimension(tbw.tbref,tbw.width,tbw.height);
twve.node.setDimension(
tbw.wrap,tbw.width,
tbw.height+tbw.captionHeight()
);
return tbw;
};
tbw.clear();
return twtable
? tbw.setTable(twtable)
: tbw;
}
};
//}}}
/***
!!! twve.table.refBar
***/
//{{{
twve.table.refBar = {
//}}}
/***
!!!! twve.table.refBar.create
***/
//{{{
create : function () {
var bar = twve.node.create();
// getSelector
bar.getSelector = function(){
return 'div.'+bar.getClass();
};
// getMenuSelector
bar.getMenuSelector = function(){
return 'div.'+bar.getMenuClass();
};
// getMenu
bar.getMenu = function(){
return twve.tiddler.getDisplay().querySelector(
bar.getMenuSelector()
);
};
// hideMenu
bar.hideMenu = function(){
var menu = bar.getMenu();
if ( menu ) twve.node.hide(menu);
return bar;
};
// menuButtonStyle
bar.menuButtonStyle = function(btn){
btn.dom.style.position = 'absolute';
btn.dom.style.backgroundColor = '#fff8dc';
btn.dom.style.color = '#000000';
};
// show
bar.show = function(twtable){
twve.node.show(bar.dom);
return bar;
};
// hide
bar.hide = function(){
twve.node.hide(bar.dom);
bar.hideMenu();
return bar;
};
// setTable
bar.setTable = function(twtable){
// Associate this reference bar to a table (twve.table
// object).
// If the table has already a reference bar associated
// with it, retrieve it to this bar and return this bar.
// Otherwise return null.
// Objects derived from twve.table.bar should check the
// return value from this method. If it is not null, no
// need to do anything. If, however, it is null, should
// call bar.create() to create a reference bar in the
// derived object's setTable method. See the setTable
// method in twve.table.refBarV and twve.table.refBarH
// below for examples.
bar.dom = twtable.wrappers.tbref.querySelector(
bar.getSelector()
);
if ( bar.dom ) {
// reference bar already created
return bar;
}
return null;
};
// create
bar.create = function(twtable){
// Creates a DIV as the base of this reference bar,
// and inserts it to twtable (twve.table object) if given.
// Returns this reference bar (not the created DIV).
bar.dom = document.createElement('div');
bar.dom.style.position = 'absolute';
twve.node.hide(bar.dom);
if ( twtable )
twtable.wrappers.tb.parentNode.insertBefore(
bar.dom,twtable.wrappers.tb
);
return bar;
};
return bar.clear();
}
};
//}}}
/***
!!! twve.table.refBarV
***/
//{{{
twve.table.refBarV = {
//}}}
/***
!!!! twve.table.refBarV.getClass
***/
//{{{
getClass : function(){
return 'twveTRefBarV';
},
//}}}
/***
!!!! twve.table.refBarV.getSelector
***/
//{{{
getSelector : function(){
var sel = 'div.'+twve.table.refBarV.getClass();
return sel+','+sel+' span';
},
//}}}
/***
!!!! twve.table.refBarV.create
***/
//{{{
create : function (twtable) {
var bar = twve.table.refBar.create();
// getClass
bar.getClass = function(){
return twve.table.refBarV.getClass();
};
// getMenuClass
bar.getMenuClass = function(){
return 'twveTMenuV';
};
// getSize
bar.getSize = function(){
return twve.node.width(bar.dom,'outer',true);
};
// getMenu
var preGetMenu = bar.getMenu;
bar.getMenu = function(){
var menu = preGetMenu.apply(this);
if ( ! menu ) {
var menu_root = twve.tiddler.getOptionsMenu().root;
var bw = menu_root.width()*1.1;
var bh = menu_root.height()*0.9;
var delta = 0.6;
var mw = bw*(3+delta*2);
var mh = bh*(3+delta*2);
var x0 = 0;
menu = document.createElement('div');
twve.tiddler.getDisplay().appendChild(menu);
menu.style.position = 'absolute';
//menu.style.border = '1px solid red';
menu.style.border = 0;
twve.node.setDimension(menu,mw,mh);
var btn = twve.button.create(
null,
//String.fromCharCode(8743),
String.fromCharCode(5169),
"Insert row above",
function(){
//if(twve.button.isActive(this))
twve.tiddler.focusElem().insertRow(
this.parentNode.getAttribute('row')*1,
'above'
);
},
'rU'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta)+x0+2,bh*(0+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
//String.fromCharCode(8744),
String.fromCharCode(5167),
"Insert row below",
function() {
//if(twve.button.isActive(this))
twve.tiddler.focusElem().insertRow(
this.parentNode.getAttribute('row')*1,
'below'
);
},
'rD'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta)+x0+2,bh*(2+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.createDelete(
"Delete this row",
function(){
twve.tiddler.focusElem().deleteRow(
this.parentNode.getAttribute('row')*1
);
},
'xR'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta*2)+x0,bh*(1+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'C',
"Copy this row",
function() {
var menu = this.parentNode;
twve.tiddler.focusElem().copyRow(
menu.getAttribute('row')*1
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnrP]'),
true
);
},
'rC'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(0+delta)+x0,bh*(0+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'X',
"Cut this row",
function() {
var menu = this.parentNode;
var row = menu.getAttribute('row')*1;
var twtable = twve.tiddler.focusElem();
twtable.copyRow(row);
twtable.deleteRow(row,false);
twve.button.activate(
menu.querySelector('[id^=twveTbtnrP]'),
true
);
},
'rX'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,0+x0,bh*(1+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'P',
"Paste to this row",
function() {
if(twve.button.isActive(this))
twve.tiddler.focusElem().pasteRow(
this.parentNode.getAttribute('row')*1
);
},
'rP'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(0+delta)+x0,bh*(2+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
//String.fromCharCode(8593),
String.fromCharCode(8657),
"Exchange with above",
function(){
if(twve.button.isActive(this))
twve.tiddler.focusElem().moveRow(
this.parentNode.getAttribute('row')*1,
-1
);
},
'mU'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(1+delta)+x0,0
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
//String.fromCharCode(8595),
String.fromCharCode(8659),
"Exchange with below",
function() {
if(twve.button.isActive(this))
twve.tiddler.focusElem().moveRow(
this.parentNode.getAttribute('row')*1,
1
);
},
'mD'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(1+delta)+x0,bh*(2+delta*2)
);
menu.appendChild(btn.dom);
menu.classList.add(bar.getMenuClass());
twve.node.hide(menu);
}
return menu;
};
// adjust
bar.adjust = function(twtable){
var tbw = twtable.wrappers;
var lh = twve.node.cssSize(
window.getComputedStyle(tbw.tb)
.getPropertyValue('line-height')
);
var sp = bar.dom.querySelectorAll('span');
var rows = twtable.dom.querySelectorAll('tr');
if ( config.browser.isiOS )
// TWEdit returns wrong height for completely spanned rows,
// we need to take care of it ourselves.
// A completely spanned row is a row that contains ALL its
// cells spanning over the same number of rows.
for ( var r=0,rlen=rows.length; r<rlen; r++ ){
var tr = rows[r];
var trh = twve.node.height(tr,'outer',true);
var cells = tr.cells;
var rspan = twve.table.cellRowSpan(cells[0]);
if ( rspan > 1 ) {
rspan = twve.table.cellRowSpanMin(cells);
trh /= rspan;
for ( var j=0; j<rspan; j++ ) {
twve.node.setPosition(
sp[r+j],null,tr.offsetTop+(j*trh)+(trh-lh)*0.5
);
}
r += rspan-1;
} else {
twve.node.setPosition(
sp[r],null,tr.offsetTop+(trh-lh)*0.5
);
}
}
else
// Other browsers are fine.
for ( var r=0,rlen=rows.length; r<rlen; r++ ){
var tr = rows[r];
var trh = twve.node.height(tr,'outer',true);
twve.node.setPosition(
sp[r],null,tr.offsetTop+(trh-lh)*0.5
);
}
return bar;
};
// showMenu
bar.showMenu = function(sp){
var menu = bar.getMenu();
var row = twve.node.index(sp);
var spbox = twve.node.box(sp);
var table = twve.tiddler.focusElem().dom;
twve.node.show(menu);
//menu.style.border = '1px solid red';
twve.node.setPosition(
menu,
spbox.left-
(twve.node.width(menu,'outer',true)-spbox.width)/2-
(config.browser.isLinux?1:0),
spbox.top-
(twve.node.height(menu,'outer',true)-
spbox.height)/2-4
);
menu.setAttribute('row',row);
twve.button.activate(
menu.querySelector('[id^=twveTbtnrP]'),
twve.table.copiedRowIndex()>=0
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnrU]'),
!(twve.table.hasHeader(table) && row==0)
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnmU]'),
!(row==0
|| (twve.table.hasHeader(table) && row==1)
|| (table.tFoot
&& row==table.rows.length-1))
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnrD]'),
!(table.tFoot
&& row==table.rows.length-1)
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnmD]'),
!(row==table.rows.length-1
|| (twve.table.hasHeader(table) && row==0)
|| (table.tFoot
&& row==table.rows.length-2))
);
return bar;
};
// getWidth
bar.getWidth = function(table){
var rows = table.rows.length;
var fs = twve.node.cssSize(
window.getComputedStyle(table)
.getPropertyValue('font-size')
);
var w = fs;
while ( (rows/=10) > 1 ) w += fs;
if ( w == fs ) w = fs;
return w;
};
// setTable
var preSetTable = bar.setTable;
bar.setTable = function(twtable){
if ( preSetTable.apply(this,arguments) ) {
// If twtable already has a reference bar, do nothing
// and return.
return bar;
}
// Otherwise create the reference bar here.
var tbw = twtable.wrappers;
//var scrtop = tbw.tb.scrollTop;
var lh = twve.node.cssSize(
window.getComputedStyle(tbw.tb)
.getPropertyValue('line-height')
);
var dw = bar.getWidth(twtable.dom);
bar.create(twtable);
//bar.dom.style.left = 0;
//twve.node.setPosition(
// bar.dom,null,twve.node.box(tbw.tb).top
//);
twve.node.setDimension(bar.dom,dw);
bar.dom.classList.add(bar.getClass());
// create cell panels
var rows = twtable.dom.querySelectorAll('tr');
for ( var n=0,len=rows.length; n<len; n++ ){
var tr = rows[n];
//var trbox = twve.node.box(tr);
//var trh = twve.node.height(tr,'outer',true) ||
// twve.node.height(tr.firstChild,'outer',true) || lh;
var sp = document.createElement('span');
sp.style.border = 0;
//sp.style.border = '1px solid red';
sp.style.position = 'absolute';
sp.style.padding = 0;
sp.style.margin = 0;
sp.style.textAlign = 'center';
twve.node.setDimension(sp,dw,lh);
//twve.node.setPosition(
//sp,null,(tr.offsetTop+scrtop)+(trh-lh)*0.5
//);
sp.setAttribute('row',n);
sp.addEventListener(
'click',
function(ev){
var sp = this;
var row = sp.getAttribute('row')*1;
var barV = sp.parentNode;
var table = barV.parentNode
.querySelector('table');
var rows = table.querySelectorAll('tr');
var last_row = barV.getAttribute('last-row');
last_row = last_row ? last_row * 1 : -1;
if ( last_row > -1 && rows[last_row] )
rows[last_row].style.opacity = 0;
if ( row != last_row && rows[row] ) {
rows[row].style.opacity = 0;
barV.setAttribute('last-row',row);
} else barV.removeAttribute('last-row');
}
);
sp.addEventListener(
'mouseenter',
function(ev){
bar.showMenu(this);
}
);
sp.textContent = twve.table.rowRef(n);
bar.dom.appendChild(sp);
}
return bar;
};
return twtable ? bar.setTable(twtable) : bar;
}
};
//}}}
/***
!!! twve.table.refBarH
***/
//{{{
twve.table.refBarH = {
//}}}
/***
!!!! twve.table.refBarH.getClass
***/
//{{{
getClass : function(){
return 'twveTRefBarH';
},
//}}}
/***
!!!! twve.table.refBarH.getSelector
***/
//{{{
getSelector : function(){
var sel = 'div.'+twve.table.refBarH.getClass();
return sel+','+sel+' span';
},
//}}}
/***
!!!! twve.table.refBarH.create
***/
//{{{
create : function (twtable) {
var bar = twve.table.refBar.create();
// getClass
bar.getClass = function(){
return twve.table.refBarH.getClass();
};
// getMenuClass
bar.getMenuClass = function(){
return 'twveTMenuH';
};
// getSize
bar.getSize = function(){
return twve.node.height(bar.dom,'outer',true);
};
// getMenu
var preGetMenu = bar.getMenu;
bar.getMenu = function(){
var menu = preGetMenu.apply(this);
if ( ! menu ) {
var menu_root = twve.tiddler.getOptionsMenu().root;
var bw = menu_root.width()*1.2;
var bh = menu_root.height()*0.8;
var delta = 0.6;
var mw = bw*(3+delta*2);
var mh = bh*(3+delta*2);
menu = document.createElement('div');
twve.tiddler.getDisplay().appendChild(menu);
menu.style.position = 'absolute';
twve.node.setDimension(menu,mw,mh);
var btn = twve.button.create(
null,
String.fromCharCode(8658),
//String.fromCharCode(187),
"Exchange with right",
function() {
if(twve.button.isActive(this))
twve.tiddler.focusElem().moveColumn(
this.parentNode.getAttribute('col')*1,
1
);
},'mR'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta*2),bh*(1+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
String.fromCharCode(8656),
//String.fromCharCode(171),
"Exchange with left",
function() {
if(twve.button.isActive(this))
twve.tiddler.focusElem().moveColumn(
this.parentNode.getAttribute('col')*1,
-1
);
},'mL'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,0,bh*(1+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
//String.fromCharCode(60),
String.fromCharCode(5176),
"Insert column left",
function(){
//if(twve.button.isActive(this))
twve.tiddler.focusElem().insertColumn(
this.parentNode.getAttribute('col')*1,
'left'
);
},'cL'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(0+delta),bh*(2+delta)+2
);
menu.appendChild(btn.dom);
btn = twve.button.createDelete(
"Delete this column",
function() {
twve.tiddler.focusElem().deleteColumn(
this.parentNode.getAttribute('col')*1
);
},'xC'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(1+delta),bh*(2+delta*2)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
//String.fromCharCode(62),
String.fromCharCode(5171),
"Insert column right",
function(){
//if(twve.button.isActive(this))
twve.tiddler.focusElem().insertColumn(
this.parentNode.getAttribute('col')*1,
'right'
);
},'cR'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta),bh*(2+delta)+2
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'C',
"Copy this column",
function() {
var menu = this.parentNode;
twve.tiddler.focusElem().copyColumn(
menu.getAttribute('col')*1
);
twve.button.activate(
menu.querySelector('[id^=twveTbtncP]'),
true
);
},'cC'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(0+delta),bh*(0+delta)
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'X',
"Cut this column",
function() {
var menu = this.parentNode;
var col = menu.getAttribute('col')*1;
var twtable = twve.tiddler.focusElem();
twtable.copyColumn(col);
twtable.deleteColumn(col,false);
twve.button.activate(
menu.querySelector('[id^=twveTbtncP]'),
true
);
},'cX'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(1+delta),0
);
menu.appendChild(btn.dom);
btn = twve.button.create(
null,
'P',
"Paste to this column",
function() {
if(twve.button.isActive(this))
twve.tiddler.focusElem().pasteColumn(
this.parentNode.getAttribute('col')*1
);
},'cP'
);
bar.menuButtonStyle(btn);
twve.node.setPosition(
btn.dom,bw*(2+delta),bh*(0+delta)
);
menu.appendChild(btn.dom);
menu.classList.add(bar.getMenuClass())
twve.node.hide(menu);
}
return menu;
};
// adjust
bar.adjust = function(twtable){
var tbw = twtable.wrappers;
var sp = bar.dom.querySelectorAll('span');
var cells=twtable.rowWithMaxCol().querySelectorAll('th,td');
for ( var n=0,len=cells.length; n<len; n++ ){
var c = cells[n];
twve.node.setDimension(
sp[n],twve.node.width(c,'inner')
);
twve.node.setPosition(sp[n],c.offsetLeft);
}
return bar;
};
// showMenu
bar.showMenu = function(sp){
var menu = bar.getMenu();
var table = twve.tiddler.focusElem().dom;
var col = sp.getAttribute('col')*1;
var spbox = twve.node.box(sp);
twve.node.show(menu);
twve.node.setPosition(
menu,
spbox.left+
(spbox.width-
twve.node.width(menu,'outer',true))/2-2,
spbox.top-
(twve.node.height(menu,'outer',true)-
spbox.height)/2-4
);
menu.setAttribute('col',col); //twve.node.index(sp));
twve.button.activate(
menu.querySelector('[id^=twveTbtncP]'),
twve.table.copiedColumnIndex()>=0
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnmL]'),
(col>0)
);
twve.button.activate(
menu.querySelector('[id^=twveTbtnmR]'),
(col<sp.parentNode.querySelectorAll('span').length-1)
);
};
// setTable
var preSetTable = bar.setTable;
bar.setTable = function(twtable){
if ( preSetTable.apply(this,arguments) )
// If twtable already has a reference bar, do nothing
// and return.
return bar;
// Otherwise create the reference bar here.
var tbw = twtable.wrappers;
//var tbbox = twve.node.box(tbw.tb);
var scrleft = tbw.tb.scrollLeft;
var table = twtable.dom;
//var thead = table.querySelectorAll('tr')[0];
var css = window.getComputedStyle(tbw.tb);
var lh = twve.node.cssSize(
css.getPropertyValue('line-height')
);
css = window.getComputedStyle(tbw.tbref);
var bc = css.getPropertyValue('background-color');
var fc = css.getPropertyValue('color');
bar.create(twtable);
bar.dom.style.border = 0; //'1px solid black';
bar.dom.style.paddingBottom = '4px';
//twve.node.setPosition(bar.dom,0,tbbox.top);
twve.node.setDimension(
bar.dom,twve.node.width(tbw.tbref),lh
);
bar.dom.classList.add(bar.getClass());
// create cell spans
var cells=twtable.rowWithMaxCol().querySelectorAll('th,td');
for (var n=0,len=cells.length; n<len; n++){
var c = cells[n];
var sp = document.createElement('span');
css = window.getComputedStyle(c);
var bw = twve.node.cssSize(
css.getPropertyValue('border-left-width')
) + twve.node.cssSize(
css.getPropertyValue('border-right-width')
);
twve.node.copyMargin(sp,c);
twve.node.copyPadding(sp,c);
sp.setAttribute('col',n);
sp.style.backgroundColor = bc;
sp.style.color = fc;
sp.style.position = 'absolute';
sp.style.border = 0;
sp.style.textAlign = 'center';
twve.node.setDimension(
sp,twve.node.width(c)+bw
);
bar.dom.appendChild(sp);
sp.addEventListener(
'click',
function(ev){
var sp = this;
var col = sp.getAttribute('col')*1;
var h0 = sp.parentNode;
var table = h0.parentNode.querySelector('table');
var last_col = h0.getAttribute('last-col');
last_col = last_col ? last_col * 1 : -1;
var rows = table.querySelectorAll('tr');
for (var r=0,rlen=rows.length;r<rlen;r++){
var cols = rows[r].querySelectorAll('th,td');
if ( last_col > -1 && cols[last_col] )
cols[last_col].style.opacity=1;
if ( col != last_col && cols[col] )
cols[col].style.opacity=0;
}
h0.setAttribute(
'last-col',(col!=last_col?col:'')
);
}
);
sp.addEventListener(
'mouseenter',
function(ev){
bar.showMenu(this);
}
);
sp.textContent = twve.table.colRef(n);
}
return bar;
};
return twtable ? bar.setTable(twtable) : bar;
}
};
}());
//}}}
[[twve.table--Example--A Simple Table]]
[[twve.table--Example--Multi-lined Content]]
[[twve.table--Example--Missing Cells and Keyboard Navigation]]
[[twve.table--Example--Spanned Cells and Keyboard Navigation]]
[[twve.table--Example--Insertion, Deletion, and Exchange]]
[[twve.table--Example--Copy and Paste]]
[[twve.table--Example--Large Table]]
[[twve.table--Example--Transclusion]]
[[twve.table--Example--Transposition]]
[[twve.table--Example--Sorting]]
<<<
This tiddler shows the very basic of editing table content in the view mode.
# Hover the mouse cursor over a table to activate it.
# Click on a table cell to edit its content.
## Make changes you want or __navigate to another cell using the arrow keys__.
## Click away or press {{{Enter}}} key (single lined) or {{{Ctrl-Enter}}} key combination (multi-lined), depending on the __<<option chktwveTableMultiLine>> multi-lined option setting__ of the ''twve.table'', to accept changes.
## Press {{{Esc}}} key to cancel changes.
# Click on the caption area to edit the table caption.
# Click on the empty area between the table borders and the focusing borders to edit the whole table.
<<<
!!! Example
|editable|k
|A simple table|c
| A0 | A1 | A2 | A3 |
| B0 | B1 | B2 | B3 |
| C0 | C1 | C2 | C3 |
<<<
The ''twve.table'' supports copy-and-pasting for __rows and columns__, as well as for individual cell content.
<<<
To perform the copy-and-pasting operations, you can
# hover the mouse over the table to activate it;
** You will see the table shifted a bit lower-right for the two reference bars, one horizontal and the other vertical.
** The vertical bar shows the row references.
** The horizontal bar shows the column references.
# To ''copy-and-paste individual cell'' content,
** click on the cell you want to copy from, an editbox with its content will show up, just copy it as you would with a normal text editor;
** click on the cell you want to paste the content in, an editbox with its current content would show up, just paste it as you would with a normal text editor;
** click away or press {{{Enter}}} or {{{Ctrl-Enter}}} to accept the change.
# To ''copy-and-paste a table row or column'', you can
** hover the mouse over the place showing the reference of the row/column you want to copy from, a floating menu with several buttons would show up;
*** The tooltip text of each button tells you what it does when you click it.
** click the {{{Copy}}} button to copy that row/column;
** hover the mouse over the place showing the reference of the row/column you want to paste in;
** click the {{{Paste}}} button to paste the copied row/column.
** @@color:blue;//Note: The {{{Paste}}} button is deactivated (dimmed) when no rows/columns had been copied.//@@
Try them with the following table.
|editable|k
|Insertion, Deletion, and Exchange|c
| A0 | A1 | A2 | A3 |
| B0 | B1 | B2 | B3 |
| C0 | C1 | C2 | C3 |
<<<
The ''twve.table'' supports //insertion//, //deletion//, and //exchange// for __rows and columns__, ''not'' for individual cells.
<<<
With ''twve'', you can __insert an empty row/column__, __delete an existing row/column__, or __exchange an existing row/column with its neighbor__. To perform these operations, you can
# hover the mouse over the table to activate it;
** You will see the table shifted a bit lower-right for the two reference bars, one horizontal and the other vertical.
** The vertical bar shows the row references.
** The horizontal bar shows the column references.
# Hover the mouse over one of the reference bars, a floating menu with several buttons would show up.
** The tooltip text of each button tells you what it does when you click it.
# To ''insert an empty row/column'',
** hover the mouse over the place showing the reference of the row/column besides which you want to insert an empty row/column;
** click one of the {{{Insert}}} buttons in the floating menu.
# To ''delete an existing row/column'',
** hover the mouse over the place showing the reference of the row/column you want to delete;
** click the {{{Delete}}} button in the floating menu.
# To ''exchange an existing row/column with its neighbor'',
** hover the mouse over the place showing the reference of the row/column you want to exchange with its neighbor;
** click one of the {{{Exchange}}} buttons in the floating menu.
** @@color:blue;//Note: The {{{Exchange}}} button is deactivated (dimmed) when exchange with neighbor is not possible or inappropriate.//@@
Try them with the following table.
|editable|k
|Insertion, Deletion, and Exchange|c
| A0 | A1 | A2 | A3 |
| B0 | B1 | B2 | B3 |
| C0 | C1 | C2 | C3 |
<<<
This tiddler shows the supported features for large tables:
# scrollable table body,
# auto scroll-into-view with keyboard navigation,
# @@color:red;(unstable, deactivated for now)@@ fixed first few rows/columns.
<<<
!!! Width is over
|editable|k
|Width is too large|c
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
!!! Height is over
|editable|k
|Height is too large|c
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
!!! Both are over
|editable|k
|Both width and height are too large|c
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
| =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() | =Cola()Row() |
<<<
This tiddler shows the supported features for tables with missing cells. One can edit the missing cells
# by clicking in the //supposed to be// area, or
# with keyboard navigation.
** You can navigate through missing cells with arrow keys, even when there are more than one in a table row.
@@color:red;''Note:'' You need to install ''twve.extra'' to enable the keyboard navigation feature.@@
<<<
|editable|k
|A Table With Missing Cells|c
|cell1|cell2|cell3|cell4|h
|One|missing cell|in this row|
|Two|missing cells in this row|
|Three|
|They|seem|all|work!|
Some text after the table.
<<<
This tiddler shows the //multi-lined content// feature for tables:
# multi-lined text in a table cell (simply hit the <enter> key to add a new line),
# lists in a table cell,
# other multi-lined elements are possible.
<<<
@@color:blue;To enable multi-lined feature, check the option <<option chktwveTableMultiLine>> {{{chktwveTableMultiLine}}} and __reload tiddler__.@@
|editable|k
|Multi-lined content|c
|Multi-lined text <br>can be created easily. <br>Just press {{{Enter}}} key <br>to add a new line.|* An unordered list <br>** can be created <br>*** here. <br># An ordered list <br>## is fine, <br>### too <br>|
|>blockquotes <br>>>work <br>>as well|A table here? <br>Well, maybe not.|
<<<
@@color:red;''Note:'' For some reason unknown to me, sorting works on local files but ''not'' on ~TiddlySpace.@@
The ''twve.table'' supports table sorting through TableSortingPlugin or SortableGridPlugin. There is ''no'' built-in sorting mechanism implemented in ''twve.table''.
<<<
* To ''sort by a column'', click on the __central area__ (mouse cursor shows a //finger//) of its header cell (top-most one).
* To ''edit a header cell'', click on the __left (right aligned) or right (left aligned) side__ (mouse cursor shows an I) of that cell.
!!! The example from TableSortingPlugin
|sortable editable|k
|Name |Salary |Extension |Performance |File Size |Start date |h
|ZBloggs, Fred |$12000.00 |1353 |+1.2 |74.2Kb |Aug 19, 2003 21:34:00 |
|ABloggs, Fred |$12000.00 |1353 |1.2 |3350b |09/18/2003 |
|CBloggs, Fred |$12000 |1353 |1.200 |55.2Kb |August 18, 2003 |
|DBloggs, Fred |$12000.00 |1353 |1.2 |2100b |07/18/2003 |
|Bloggs, Fred |$12000.00 |1353 |01.20 |6.156Mb |08/17/2003 05:43 |
|Turvey, Kevin |$191200.00 |2342 |-33 |1b |02/05/1979 |
|Mbogo, Arnold |$32010.12 |2755 |-21.673 |1.2Gb |09/08/1998 |
|Shakespeare, Bill |ã122000.00|3211 |6 |33.22Gb |12/11/1961 |
|Shakespeare, Hamlet |ã9000 |9005 |-8 |3Gb |01/01/2002 |
|Fitz, Marvin |â¬3300.30 |5554 |+5 |4Kb |05/22/1995 |
<<<
This tiddler shows the supported features for tables with spanned cells, including
# edit the //otherwise-would-be-there// cells in a spanned cell,
# ''visually consistent keyboard navigation''.
** The browser-assigned cell indexes are not consistent with what we see and cause confusion quite easily. The ''twve.table'' fixed that issue and gives a visually consistent experience with keyboard navigation.
# correct transposition -- from a row-spanned to a column-spanned cell, and vice versa,
** Transposition command available in the floating menu located at the top-right corner of the focusing borders.
@@color:red;''Note:'' You need to install ''twve.extra'' to enable the keyboard navigation feature.@@
<<<
|editable|k
|A Table With Spanned Cells|c
|This cell spanned 2 rows|Normal cell|Normal cell|Normal cell|
|~|>|>|This cell spanned 3 columns|
|Normal cell|Normal cell|Normal cell|Normal cell|
<<<
This tiddler shows the //transclusion// related features for tables:
# transclusion synchronization,
# scrolling synchronization (for large tables).
** @@color:blue;Note: Scrolling synchronization doesn't seem to work with the mobile Firefox. Not tested with other mobile browsers yet.@@
<<<
!!! With {{{<<tiddler>>}}} macro
<<tiddler "twve.table--Example--Large Table##Width is over">>
!!! With {{{<<slider>>}}} macro
<<slider "" "twve.table--Example--Large Table##Width is over" "twve.table--Example--Large Table">>
<<<
Table transposition is supported in the ''twve'' for __simple tables__, __tables with row- or column-spanned cells__, and for __tables with missing cells__.
@@color:blue;//Note: Missing cells are transposed into and remain as empty cells.//@@
<<<
__To enable transposition operation__, check the option <<option chktwveTableTranspose>> {{{chktwveTableTranspose}}}.
To transpose a table, you can
# hover the mouse over a table to activate it;
# hover the mouse over the menu at the top-right corner;
# click the {{{Transpose}}} command.
Try the following examples to see.
!!! A simple table
|editable|k
|A simple table|c
| A0 | A1 | A2 | A3 |
| B0 | B1 | B2 | B3 |
| C0 | C1 | C2 | C3 |
!!! A table with spanned cells
@@color:blue;Column-spanned cells are correctly transposed into row-spanned cells, and vice versa.@@
|editable|k
|A Table With Spanned Cells|c
|This cell spanned 2 rows|Normal cell|Normal cell|Normal cell|
|~|>|>|This cell spanned 3 columns|
|Normal cell|Normal cell|Normal cell|Normal cell|
!!! A table with missing cells
@@color:blue;''Note: A missing cell is transposed into an empty cell, and becomes no longer missing afterwards.''@@
|editable|k
|A Table With Missing Cells|c
|cell1|cell2|cell3|cell4|h
|One|missing cell|in this row|
|Two|missing cells in this row|
|Three|
|They|seem|all|work!|
If you are interested in more details, please see [[Transposing a table -- backward thinking]].
/***
|editable|k
|''Name:''|twve.table|
|''Description:''|Table rendering and view mode editing in ~TiddlyWiki environment|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.table.min / http://twve.tiddlyspace.com/#twve.table.min <br>* (regular) http://twve.tiddlyspot.com/#twve.table / http://twve.tiddlyspace.com/#twve.table|
|''Type:''|plugin|
|''Version:''|3.2.5|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2014/01/18 collected from TWElement, TWtid and TWted|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core|
!!Options
Look for [[twve.table Options]] in the system Options panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve&&alert("twve.table: This plugin needs twve.core to function properly. Please install twve.core before this one.");version.extensions.twve.table={major:3,minor:2,revision:5,date:new Date("2015/11/06")};
config.macros.twveTableOptions={init:function(){twve.table.preReadOnly=twve.tiddler.readOnly;twve.tiddler.readOnly=twve.table.readOnly;twve.table.preTextPasted=twve.tiddler.textPasted;twve.tiddler.textPasted=twve.table.textPasted;twve.table.preGetOptionsMenu=twve.tiddler.getOptionsMenu;twve.tiddler.getOptionsMenu=twve.table.getOptionsMenu;twve.table.preFocusTarget=twve.tiddler.focusTarget;twve.tiddler.focusTarget=twve.table.focusTarget;twve.table.preWikify=twve.node.wikify;twve.node.wikify=twve.table.wikify;
twve.table.preGetLevel=twve.leveledElement.getLevel;twve.leveledElement.getLevel=twve.table.getLevel;twve.table.prePrepareElements=twve.wrapper.prepareElements;twve.wrapper.prepareElements=twve.table.prepareElements;void 0===config.options.chktwveTableEnabled&&(config.options.chktwveTableEnabled=!0);void 0===config.options.chktwveTableEditAll&&(config.options.chktwveTableEditAll=void 0===config.options.chkTWtedEditAllTables?!1:config.options.chkTWtedEditAllTables);void 0===config.options.txttwveTableMinCellWidth&&
(config.options.txttwveTableMinCellWidth=void 0===config.options.txtTWtidMinCellWidth?"8":config.options.txtTWtidMinCellWidth);void 0===config.options.chktwveTableMultiLine&&(config.options.chktwveTableMultiLine=void 0===config.options.chkTWtidMultiLine?!1:config.options.chkTWtidMultiLine);void 0===config.options.chktwveTableTranspose&&(config.options.chktwveTableTranspose=!1);void 0===config.options.chktwveTableIncludeCSS&&(config.options.chktwveTableIncludeCSS=void 0===config.options.chkTWtedIncludeCSS?
!0:config.options.chkTWtedIncludeCSS);merge(config.optionsDesc,{chktwveTableEnabled:"Enable ''twve.table''.",chktwveTableEditAll:'Edit all tables. If true, all tables are editable. Otherwise only tables with class name "editable" are editable.',chktwveTableMultiLine:"Multi-line rendering mode. If true, a table cell can have multi-lined content (with <br /> as line-break). Lists in a table cell is hence possible. Default to false.",txttwveTableMinCellWidth:"Minimum cell width in characters. Default value is 8.",
chktwveTableTranspose:"Enable transposition. Default to false.",chktwveTableIncludeCSS:"Include cell-wide CSS statements in the edit box."});var b=config.shadowTiddlers.OptionsPanel,a=b.indexOf("[[twve.core Options");0<=a&&(a=b.indexOf("]]\n",a+2)+3,config.shadowTiddlers.OptionsPanel=b.substring(0,a)+"[[twve.table Options|twve.table Options]]\n"+b.substring(a));merge(config.shadowTiddlers,{"twve.table Options":"<<twveTableOptions>>"});twve.tiddler.registerElement(twve.table)},order:{chktwveTableEnabled:0,
chktwveTableEditAll:1,chktwveTableMultiLine:2,txttwveTableMinCellWidth:3,chktwveTableTranspose:4,chktwveTableIncludeCSS:5},handler:function(b){config.macros.twveCoreOptions.showOptionsTable(b,"''twve.table'' Options","twveTable",config.macros.twveTableOptions.order)}};config.macros.noedit={handler:function(b){addClass(b,"noedit")}};
merge(twve.text,{lineBreakToBr:function(b){return b?b.replace(/\n/mg," <br>"):b},brToLineBreak:function(b){return b?b.replace(/ <br>/mg,"\n").replace(/<br>/mg,"\n").replace(/ <br \/>/mg,"\n").replace(/<br \/>/mg,"\n"):b},nodeWideStyleText:function(b){var a=twve.text.skipStyleText(b);return 0<a?b.substring(0,a):""}});
merge(twve.node,{copyMargin:function(b,a,h){h||(h=window.getComputedStyle(a));b.style.marginTop=a.style.marginTop||h.getPropertyValue("margin-top");b.style.marginRight=a.style.marginRight||h.getPropertyValue("margin-right");b.style.marginBottom=a.style.marginBottom||h.getPropertyValue("margin-bottom");b.style.marginLeft=a.style.marginLeft||h.getPropertyValue("margin-left");return b},copyPadding:function(b,a,h){h||(h=window.getComputedStyle(a));b.style.paddingTop=a.style.paddingTop||h.getPropertyValue("padding-top");
b.style.paddingRight=a.style.paddingRight||h.getPropertyValue("padding-right");b.style.paddingBottom=a.style.paddingBottom||h.getPropertyValue("padding-bottom");b.style.paddingLeft=a.style.paddingLeft||h.getPropertyValue("padding-left");return b}});
twve.table={preGetLevel:null,getLevel:function(b,a,h){h||(h=b.charAt(a));return"|"==h?1:twve.table.preGetLevel.call(this,b,a,h)},preReadOnly:null,readOnly:function(b){return b&&"twve.table--example"==b.toLowerCase().substring(0,19)?!1:twve.table.preReadOnly.apply(this,arguments)},enableEdit:!0,twveSelector:function(b,a){b||(b=twve.selector.create());var h="table";a&&0>a.toLowerCase().indexOf(h)&&(h+=",tr,th,td,"+twve.table.wrapper.getSelector("all")+","+twve.table.refBarV.getSelector()+","+twve.table.refBarH.getSelector());
return b.includeSelector(h).excludeSelector(".syntaxhighlighter table").excludeSelector("table.htCore")},markupTags:function(){var b=twve.tags.create("\n|","\n");b.blockElement=!0;b.clone=function(){return twve.table.markupTags()};b.matchedCloseTag=function(a,b){for(var k=twve.text.consecutiveLinesEnd(a,b,!0);;){var c=a.lastIndexOf("\n",k.ndx-1);if(3>a.substring(c+1,k.ndx).split("|").length)k.ndx=c;else break}return b.copyFrom(k)};return b},prePrepareElements:null,prepareElements:function(b){if(!twve.table.prePrepareElements.apply(this,
arguments)||!config.options.chktwveTableEnabled||!b.tiddler)return null;var a=b.findElements(twve.table.twveSelector(null,"table"));if(a)for(var h=b.is("div.board")?twve.tiddler.getEditInfo().text[0]:null,k=0,c=a.length;k<c;k++){config.tableSorting&&config.tableSorting.refresh&&config.tableSorting.refresh.call(this,b.dom);var f=twve.table.create();f.wrapper_title=b.wrapper_title;f.direct_wrapper=b;f.wrapper=[f.direct_wrapper.dom];f.tiddler=h?{text:h}:b.tiddler;f.rIndex=k;f.dIndex=-1;f.setElement(a[k],
h);f.prepare()}return b},preTextPasted:null,textPasted:function(b){var a=preTextPasted.apply(this,arguments);if(!a||!twve.tiddler.cur_editable.curNode)return a;var h=config.options.chktwveTableMultiLine?twve.text.brToLineBreak(a):twve.text.lineBreakToBr(a);if(h==a)return a;b.value=twve.tiddler.text_before_paste?twve.tiddler.text_before_paste.substring(0,twve.tiddler.selection_at_paste.start)+h+twve.tiddler.text_before_paste.substring(twve.tiddler.selection_at_paste.end):h;return h},addOptionsMenu:function(b){return b.addMenu("''twve.table'' Options",
config.macros.twveTableOptions.handler)},preGetOptionsMenu:null,getOptionsMenu:function(){var b=twve.table.preGetOptionsMenu.apply(this,arguments);twve.table.addOptionsMenu(b);return b},getWrapper:function(b,a){return twve.node.closest(b,twve.table.wrapper.getSelector(a))},findTable:function(b){if(!b)return null;var a=twve.table.getWrapper(b);return a?a.querySelector("table"):twve.node.closest(b,"table")},rowRef:function(b){return b},colRef:function(b){return b},cellRowSpan:function(b){b=b.getAttribute("rowspan");
return!b?1:1*b},cellRowSpanMin:function(b){for(var a=0,h=0,k=b.length;h<k;h++){var c=twve.table.cellRowSpan(b[h]);if(0==a||a>c)a=c}return a},cellColSpan:function(b){b=b.getAttribute("colspan");return!b?1:1*b},getRowCell:function(b,a){"TR"==b.nodeName&&(b=b.querySelectorAll("th,td"));for(var h=null,k=0,c=b.length;k<c;k++){var h=b[k],f=twve.table.cellColIndex(h);if(f==a)break;else if(f>a){var g=twve.table.cellColSpan(h);if(f-g<a)break}h=null}return h},columnsInRow:function(b,a){var h=0;a||(a=b.querySelectorAll("th,td"));
for(var k=0,c=a.length;k<c;k++)h+=twve.table.cellColSpan(a[k]);b.setAttribute("columns",h);b.setAttribute("cells",a.length);return h},rowWithMaxCol:function(b,a){var h=null,k=0,c=0;a||(a=b.querySelectorAll("tr"));for(var f=0,g=a.length;f<g;f++){var e=a[f],q=twve.table.columnsInRow(e);q>k&&(k=q);q=1*e.getAttribute("cells");q>c&&(h=e,c=q)}return h},maxColumns:function(b,a){return 1*twve.table.rowWithMaxCol(b,a).getAttribute("columns")},rowStarts:function(b,a,h,k,c){var f=-1;c||(c=a.ndx);for(var g;;){g=
b.indexOf("\n",c+1);if(g>h.ndx)return-1;-1==g&&(g=h.ndx);var e=b.charAt(g-1).toLowerCase();if(("h"==e||"f"==e||"|"==e)&&++f==k)return c+("\n"==b.charAt(c)?1:0);c=b.indexOf("\n|",g);if(c<a.ndx||c>=h.ndx)return-1}},rowEnds:function(b,a){var h=b.indexOf("\n",a);-1==h&&(h=b.length);return 3>b.substring(a,h).split("|").length?a-1:h},locateCell:function(b,a,h,k){var c=-1,f=twve.object.create();for(f.open=a;;){f.close=twve.text.skipToActive(b,"|",f.open+1,h).ndx;if(-1==f.close||f.close>h){f.close=h-1;break}if(++c==
k)break;f.open=f.close}f.open<f.close&&f.open++;return f},cellPos:function(b,a,h,k,c,f){var g=twve.table.rowStarts(b,a,h,k);k=twve.table.rowEnds(b,g);if(1==f.row&&1==f.col)return twve.table.locateCell(b,g,k,c);for(var e=[],q=0;q<f.row;q++){e[q]=[];for(var g=twve.table.locateCell(b,g,k,c-f.col+1),p=0;p<f.col-1;p++)e[q][e[q].length]={open:g.open,close:g.close},g.open+=2,g.close+=2;e[q][e[q].length]=twve.table.locateCell(b,g.open-1,k,0);g=twve.table.rowStarts(b,a,h,0,k);k=twve.table.rowEnds(b,g)}return e},
rowCells:function(b,a,h){var k=[],c=twve.object.create();c.open=a;c.close=a;do{c=twve.table.locateCell(b,c.close,h,0);b.substring(c.open,c.close);if(c.close==h-1)return b=b.charAt(c.close).toLowerCase(),"h"==b||"f"==b||(k[k.length]=c),k;k[k.length]=c}while(1)},allCells:function(b,a,h){var k=[],c=a.ndx,f;do{f=twve.table.rowStarts(b,a,h,0,c);if(0>f||f>=h.ndx)break;c=twve.table.rowEnds(b,f);if(c<f||c>h.ndx)break;k[k.length]=twve.table.rowCells(b,f,c);if(c>=h.ndx)break}while(1);return k},copied_rndx:-1,
copiedRowIndex:function(b){"number"==typeof b&&(twve.table.copied_rndx=b);return twve.table.copied_rndx},copied_rtxt:null,copiedRowText:function(b){if(null===b||"string"==typeof b)twve.table.copied_rtxt=b;return twve.table.copied_rtxt},copied_cndx:-1,copiedColumnIndex:function(b){"number"==typeof b&&(twve.table.copied_cndx=b);return twve.table.copied_cndx},copied_ctxt:null,copiedColumnText:function(b){if(null===b||"object"==typeof b&&b.length)twve.table.copied_ctxt=b;return twve.table.copied_ctxt},
sortTable:null,tsResortTable:null,sortingEnabled:function(){return!(!twve.table.sortTable&&!twve.table.tsResortTable)},inEditArea:function(b,a){if(!a)return!0;var h=twve.node.box(b),h=(twve.tiddler.mousePosition(a).x-h.left)/h.width,k=b.style.textAlign||window.getComputedStyle(b).getPropertyValue("text-align"),k=k.toLowerCase();if(0<=k.indexOf("right")){if(0.2>h)return!0}else if(0<=k.indexOf("center")){if(0.2>h||0.8<h)return!0}else if(0<=k.indexOf("left")&&0.8<h)return!0;return!1},preWikify:null,
wikify:function(b,a,h){if(twve.node.matches(a,"th,td")){var k=twve.text.nodeWideStyleText(b);k&&(b=b.substring(k.length));var c="!"==b.charAt(0)?"TH":"TD";if(a.nodeName!=c){var f=createTiddlyElement(null,c);a.parentNode.replaceChild(f,a);a=f;"TH"==c&&(b=b.substring(1))}var c=" "==b.charAt(0),f=b.length,g=" "==b.charAt(f-1);c?g?(a.style.textAlign="center",b=b.substring(1,f-1)):(a.style.textAlign="right",b=b.substring(1)):(a.style.textAlign="left",g&&(b=b.substring(0,f-1)));if(k){k=k.match(/[\w-]+[:]{1}[\w-]+[;]{1}/g);
c=[];for(f=0;f<k.length;f++)g=k[f].match(/[\w-]+/g),c[c.length]={style:g[0],value:g[1]};config.formatterHelpers.applyCssHelper(a,c)}}return twve.table.preWikify.call(this,b,a,h)},tableMenu:null,getTableMenu:function(){var b=null;twve.table.tableMenu||(twve.table.tableMenu=twve.menu.create(twve.button.create(null,String.fromCharCode(8801),"table menu")),twve.table.addOptionsMenu(twve.table.tableMenu),b=twve.table.tableMenu.addItem("Transpose","Transpose this table"),b.click=function(){twve.button.isActive(this)&&
twve.tiddler.focusElem().transpose()});b||(b=twve.table.tableMenu.findItem("Transpose"));b.activate(config.options.chktwveTableTranspose);return twve.table.tableMenu},sort:function(b,a,h){if(b.startSorting()){var k=b.dom;b.sortingCell(a);for(var c=[],f=k.querySelectorAll("tr"),g=0,e=f.length;g<e;g++)c[g]=f[g];try{config.tableSorting?twve.table.sortTable.call(this,a,h):config.macros.sortableGridPlugin&&twve.table.tsResortTable.call(this,a,h)}catch(q){return console.log(q),-1<q.toString().indexOf("undefined")&&
console.log("There are probably spanned cells and/or missing cells in this table."),b}a=[];f=k.querySelectorAll("tr");g=0;for(e=f.length;g<e;g++)k=f[g],a[g]=k,k.setAttribute("row",g),k=k.classList,k.remove("oddRow"),k.remove("evenRow"),k.add(g%2?"oddRow":"evenRow");e=f=b.tiddler.text;k=Array(a.length);h=twve.position.create(b.start);for(var p=twve.position.create(b.end),r,t,g=0;g<c.length;g++)for(r=0;r<a.length;r++)if(a[r]==c[g]){k[r]=g;r!=g&&(f=b.changed({what:"ROW MOVED",from:g,to:r,text:f}));break}c=
h.ndx;for(r=0;r<a.length;r++)t=twve.table.rowStarts(e,h,p,0,c),c=twve.table.rowEnds(e,t),g=twve.table.rowStarts(f,b.start,b.end,k[r]),rend=twve.table.rowEnds(f,g),e=e.substring(0,t)+f.substring(g,rend)+e.substring(c),g=rend-g-(c-t),p.ndx+=g,c+=g;e=b.changed({what:"SORTED",text:e});twve.tiddler.saveText(b,e);b.showRefBars();b.drawFocusBorders();return b}},preFocusTarget:null,focusTarget:function(b){return twve.table.findTable(b.target)||twve.table.preFocusTarget.apply(this,arguments)},hasHeader:function(b,
a){if(b.dom){if(-1<b.headPos().open)return!0;b=b.dom}if(b.tHead)return!0;a||(a=b.querySelectorAll("tr"));for(var h=a[0].querySelectorAll("th,td"),k=0,c=0,f=h.length;c<f;c++)twve.node.matches(h[c],"th")&&k++;return k==h.length?!0:!1},rowIndex:function(b,a){if(void 0===a){var h=b.getAttribute("row");return h?1*h:b.rowIndex}b.setAttribute("row",a);return a},cellRowIndex:function(b,a){return twve.table.rowIndex(b.parentNode,a)},cellColIndex:function(b,a){if(void 0===a){var h=b.getAttribute("col");return h?
1*h:b.cellIndex}b.setAttribute("col",a)},cellSpans:function(b){var a=twve.object.create();a.row=twve.table.cellRowSpan(b);a.col=twve.table.cellColSpan(b);return a},create:function(b){var a=twve.element.create();a.clear=function(){a.wrappers=null;a.refbarV=null;a.refbarH=null;a.curNode=null;a.differentBox=!0;return a};var h=a.copyFrom;a.copyFrom=function(m){h.apply(this,arguments);a.wrappers=m.wrappers;a.refbarV=m.refbarV;a.refbarH=m.refbarH;a.curNode=m.curNode;return a};a.markupTags=function(){return twve.table.markupTags()};
a.twveSelector=function(){return twve.table.twveSelector(null,"table")};var k=a.is;a.is=function(a){return!a?!1:k.apply(this,arguments)?!0:twve.node.matches(a,twve.table.twveSelector().include)||twve.node.closest(a,"th,td")};a.isEditable=function(m){if(a.wrappers){if(a.wrappers.all==m)return a.curNode=null,!0;if(!twve.node.contains(a.wrappers.all,m))return!1}var b=null;if(b=twve.node.matches(m,"th,td")?m:twve.node.closest(m,"th,td"))return a.curNode=b,!0;b=twve.table.wrapper.getSelector("cap");return twve.node.matches(m,
b)||twve.node.matches(m,"table,tr")?(a.curNode=m,!0):twve.node.matches(m,twve.table.wrapper.getSelector("all"))?(a.curNode=null,!0):!1};a.clone=function(){return twve.table.create(a)};a.disabled=function(){return-1==a.end.ndx||!config.options.chktwveTableEnabled||a.isSystemShadow()||!(config.options.chktwveTableEditAll||a.hasClass("editable")||a.hasClass("|editable"))};a.mouseenter=function(m){if(a.disabled())return!1;a.refbarV.isVisible()||a.showRefBars();return!0};a.cellAtMouse=function(a){return a.target};
a.mousemove=function(m){if(twve.node.box(a.dom).contains(twve.tiddler.mousePosition(m))&&(a.refbarV.hideMenu(),a.refbarH.hideMenu(),twve.table.getTableMenu().hide(),twve.table.sortingEnabled()&&twve.table.hasHeader(a))){var b=a.cellAtMouse(m);0==twve.table.cellRowIndex(b)&&(b.style.cursor=twve.table.inEditArea(b,m)?"text":"pointer")}};a.mouseleave=function(m){return twve.tiddler.getEditBox()&&twve.tiddler.cur_editable.is(a)?!1:!twve.node.box(a.wrappers.all).contains(twve.tiddler.mousePosition(m))};
a.blur=function(){a.refbarV.isVisible()&&(a.hideRefBars(),twve.table.getTableMenu().hide(!0),a.beingEdited()&&twve.tiddler.updateText())};a.getLastCurElem=function(){return a.curNode.length?a.curNode[a.curNode.length-1]:a.curNode};a.box=function(m,b,d){m=m.toLowerCase();return a.curNode&&0<=m.indexOf("edit")?(d||(d=a.getLastCurElem()),m=twve.node.box(d),config.browser.firefoxDate&&config.browser.isLinux&&twve.node.matches(d,"th,td")&&(b=twve.node.cssSize(d.style.paddingLeft),m.left-=b/2,m.width-=
b,b=twve.node.cssSize(d.style.paddingTop),m.top-=b/2,m.height-=b),m):m=twve.node.box(a.wrappers?a.wrappers.all:a.dom)};a.prepare=function(){if(a.disabled())return!1;a.initRows();twve.table.hasHeader(a)&&(config.tableSorting?twve.table.sortTable||(twve.table.sortTable=config.tableSorting.sortTable,config.tableSorting.sortTable=function(a,b){twve.table.sort(twve.tiddler.focusElem(),a,b)}):config.macros.sortableGridPlugin&&!twve.table.tsResortTable&&(twve.table.tsResortTable=config.macros.sortableGridPlugin.ts_resortTable,
config.macros.sortableGridPlugin.ts_resortTable=function(a){twve.table.sort(twve.tiddler.focusElem(),a)}));a.adjustWidth();a.resize();return!0};a.rowWithMaxCol=function(m){return twve.table.rowWithMaxCol(a.dom,m)};a.counts=function(m,b){if(twve.table.rowEnds(b,a.start.ndx+("\n"==b.charAt(a.start.ndx)?1:0))<=a.start.ndx)return!1;--m.remained;++m.ndx;return!0};a.getElement=function(){return a.curNode?a.getLastCurElem():a.dom};a.createWrapperRefBars=function(){a.wrappers=twve.table.wrapper.create(a);
a.refbarV=twve.table.refBarV.create(a);a.refbarH=twve.table.refBarH.create(a);return a};var c=a.setElement;a.setElement=function(m,b){a.isEditable(m)?m=twve.table.findTable(m):(a.curNode=null,twve.node.matches(m,"table")||(m=twve.table.findTable(m)));c.call(this,m,b);a.createWrapperRefBars();return a};a.setTable=function(m,b){return a.setElement(m,b)};a.putRefBars=function(m,b){var d=window.getComputedStyle(a.dom);twve.node.setPosition(a.refbarH.dom,m+parseInt(d.getPropertyValue("margin-left")));
twve.node.setPosition(a.refbarV.dom,null,b+parseInt(d.getPropertyValue("margin-top")));return a};a.showRefBars=function(){a.refbarV.show();a.refbarH.show();a.adjustRefBars();var m=a.refbarV.getSize(),b=a.refbarH.getSize(),d=a.wrappers;d.resize(m,b);twve.node.setPosition(d.cap,m);twve.node.setPosition(d.tb,m,b);a.putRefBars(m,b);return a};a.hideRefBars=function(){if(!a.refbarV.isVisible())return a;a.refbarV.hide();a.refbarH.hide();var m=a.wrappers;twve.node.setPosition(m.cap,0);twve.node.setPosition(m.tb,
0,0);m.resize();return a};a.maxColumns=function(m){return twve.table.maxColumns(a.dom,m)};var f=a.renderedCopy;a.renderedCopy=function(){var m=f.apply(this,arguments);if(m&&a!=m&&(m.createWrapperRefBars(),a.curNode)){var b=a.curNode;if(twve.node.matches(b,"div"))m.curNode=m.wrappers.cap;else if(twve.node.matches(b,"td,th")){var d=m.dom.querySelectorAll("tr")[twve.table.cellRowIndex(b)];twve.node.matches(b,"[missed]")?m.curNode=m.appendCell(d,b.length||1):m.curNode=twve.table.getRowCell(d,twve.table.cellColIndex(b))}}return m};
a.getCell=function(m,b,d){d||(d=a.dom.querySelectorAll("tr"));for(var l=null,n=0,c=d.length;n<c;n++)if(l=twve.table.getRowCell(d[n],b)){var e=twve.table.cellRowSpan(l);if(1==e&&n==m||n+e-1>=m)break;l=null}else if(n==m)return null;return l};a.appendCell=function(a,b,d){if("number"!=typeof b||0>=b)b=1;"number"!=typeof d&&(d=a.querySelectorAll("th,td"),d=twve.table.cellColIndex(d[d.length-1]));for(var l=[],n=0;n<b;n++){var c=document.createElement("td");c.setAttribute("missed",!0);c.setAttribute("col",
++d);a.appendChild(c);l[l.length]=c}return l};a.editNextCell=function(m,b,d,l){void 0===d&&(d=0);void 0===l&&(l=0);var n=a.getLastCurElem(),c=twve.table.cellRowIndex(n)+d,n=twve.table.cellColIndex(n)+l;switch(m){case "left":n-=b;break;case "up":c-=b;break;case "right":n+=b;break;case "down":c+=b}twve.tiddler.updateText();var e=a.dom.querySelectorAll("tr");0>c?c=e.length-1:c>=e.length&&(c=0);var f=a.maxColumns(e);0>n?n=f-1:n>=f&&(n=0);a.curNode=a.getCell(c,n,e);if(a.curNode){if(a.curNode.classList.contains("noedit"))return a.editNextCell(m,
b,d,l)}else m=e[c].querySelectorAll("th,td"),m=twve.table.cellColIndex(m[m.length-1]),a.curNode=a.appendCell(e[c],n-m,m)[n-m-1];a.editText(null,c,n);return a};var g=a.editNeighbor;a.editNeighbor=function(m,b){if(!a.curNode||twve.node.matches(a.curNode,"div"))return g?g.apply(this,arguments):null;void 0===b&&(b=1);var d=twve.tiddler.getEditBox(),l=d.length;1==l&&(d=d[0],l=d.length);if(!(1<l))return a.editNextCell(m,b);for(var n=0;n<l&&!twve.node.matches(d[n],"textarea:focus");n++);var c=twve.table.cellSpans(a.curNode);
if(1<c.col){c=c.col-n-1;switch(m){case "left":n-=b;break;case "right":n+=b;break;case "up":case "down":return a.editNextCell(m,b,0,-c)}if(0>n||n>=l)return a.editNextCell(m,b,0,-c)}else if(1<c.row){c=n;switch(m){case "left":case "right":return a.editNextCell(m,b,c,0);case "up":n-=b;break;case "down":n+=b}if(0>n||n>=l)return a.editNextCell(m,b,c,0)}d=d[n].nodeType?d[n]:d[n][0];d.focus();twve.tiddler.previewEditBox(d);return a};var e=null,q="";a.getCellText=function(m,b){b||(b=a.tiddler.text);var d=
q="";if(m.length){for(var d=[],l=0;l<m.length;l++){d[l]=[];for(var c=0;c<m[l].length;c++)d[l][c]=b.substring(m[l][c].open,m[l][c].close)}config.options.chktwveTableIncludeCSS||(q=twve.text.nodeWideStyleText(d[0][m[0].length-1]),d[0][m[0].length-1]=d[0][m[0].length-1].substring(q.length));config.options.chktwveTableMultiLine&&(d[0][m[0].length-1]=twve.text.brToLineBreak(d[0][m[0].length-1]))}else d=b.substring(m.open,m.close),config.options.chktwveTableIncludeCSS||(q=twve.text.nodeWideStyleText(d),
d=d.substring(q.length)),config.options.chktwveTableMultiLine&&(d=twve.text.brToLineBreak(d));e=m;return d};a.setCellText=function(b,c){c||(c=e);if(!c)return displayMessage("Internal error: twtable.setCellText(). Trying to set text without a specific position."),null;if(c.length){q&&(b[0][c[0].length-1]=q+b[0][c[0].length-1]);for(var d=c.length-1;0<=d;d--)for(var l=c[d].length-1;0<=l;l--)config.options.chktwveTableMultiLine&&0<=b[d][l].indexOf("\n")&&(b[d][l]=twve.text.lineBreakToBr(b[d][l])),a.setText(b[d][l],
c[d][l].open,c[d][l].close)}else config.options.chktwveTableMultiLine&&0<=b[0].indexOf("\n")&&(b[0]=twve.text.lineBreakToBr(b[0])),q&&(b[0]=q+b[0]),a.setText(b[0],c.open,c.close);return a};a.updateCell=function(b){for(var c=b[0],d=a.curNode.length?a.curNode:[a.curNode],l=d.length-1;0<=l;l--)twve.node.matches(d[l],"[missed]")&&(b[0]="|"+b[0]);if(!a.setCellText(b))return!1;if(e.length||q||twve.text.nodeWideStyleText(c)||"~"==c||">"==c)a.refreshTable();else{for(l=d.length-1;0<=l;l--)d[l].removeAttribute("missed");
d=d[d.length-1];b=twve.object.create();b.what="MODIFIED";b.row=twve.table.cellRowIndex(d);b.col=twve.table.cellColIndex(d);b.text=a.tiddler.text;a.refreshAll(c,b);a.curNode=null}q="";e=null;return!0};a.rowAt=function(b,c){c||(c=a.dom.querySelectorAll("tr"));for(var d=0,l=c.length;d<l;d++)if(twve.node.box(c[d]).containsY(b.y))return c[d];return null};a.rowIndexAt=function(b,c){var d=a.rowAt(b,c);return d?twve.table.rowIndex(d):-1};a.colIndexAt=function(b){for(var c=a.refbarH.dom.querySelectorAll("span"),
d=c.length-1;0<=d&&!twve.node.box(c[d]).containsX(b.x);d--);return d};var p=a.previewEditBox;a.previewEditBox=function(b,c){var d=p.apply(this,arguments);if("TABLE"==c.nodeName){var l=a.direct_wrapper.clone();l.dom=d.querySelector("div.board");l.waitAndPrepare()}return d};a.editCell=function(b,c,d){var l=a.dom,n=a.getLastCurElem(),e=twve.table.cellRowIndex(n),f,h=l.querySelectorAll("tr");if(0==e&&twve.table.hasHeader(l,h)&&twve.table.sortingEnabled()&&!twve.table.inEditArea(n,b)||twve.node.matches(n,
".noedit"))return null;if(b){var g=twve.tiddler.createEditableWrapper(b.target);if(g)return g.editText(b)}var g=twve.table.cellColIndex(n),k=twve.table.cellSpans(n);f=twve.table.cellPos(a.tiddler.text,a.start,a.end,e,g,k);var q=a.getCellText(f);if(!f.length)return r.call(this,b,q,n);twve.tiddler.cur_editable=a;var p,t;f=a.refbarH.dom.querySelectorAll("span");for(var s=window.getComputedStyle(n),v=twve.node.cssSize(s.getPropertyValue("font-size")),y=s.getPropertyValue("text-align"),s=twve.node.cssSize(s.getPropertyValue("line-height")),
l=twve.node.closest(l,twve.tiddler.displaySelector()),D=[],E=v*config.options.txttwveCoreMinEditWidth,w=0,H=q.length;w<H;w++){var F=[],A=twve.node.box(h[e+w]);A.height<s&&(A.height=s);for(var z=0,I=q[w].length;z<I;z++){var G=g-k.col+1+z,B=twve.node.box(f[G]);B.top=A.top;B.height=A.height;var x=document.createElement("textarea"),C=q[w][z];x.value=C;x.defaultValue=C;l.appendChild(x);a.prepareEditBox(x,C,B,y,v);B.width<E&&twve.node.setDimension(x,E);F[z]=x;w+e==c&&G==d&&(t=C,p=x)}D[w]=F}twve.tiddler.setEditBox(D);
a.focusEditBox(p,t,b,n);a.previewEditBox(p,n,l);return p};a.signaturePosByTail=function(b){var c=twve.object.create();c.open=-1;c.close=-1;var d=a.tiddler.text;c.close=d.indexOf(b.toLowerCase(),a.start.ndx);-1==c.close&&(c.close=d.indexOf(b.toUpperCase()));-1<c.close&&c.close<a.end.ndx&&(c.close+b.length==d.length||"\n"==d.charAt(c.close+b.length))?c.open=d.lastIndexOf("|",c.close-1)+1:c.close=-1;return c};a.headPos=function(){return a.signaturePosByTail("|h")};a.classPos=function(){return a.signaturePosByTail("|f")};
a.classPos=function(){return a.signaturePosByTail("|k")};a.captionPos=function(){return a.signaturePosByTail("|c")};a.getCaption=function(b){var c=a.captionPos();b||(b=a.tiddler.text);return-1!=c.open?b.substring(c.open,c.close):""};a.setCaption=function(b){var c=a.captionPos();-1!=c.open?(b||(0<c.open&&c.open--,c.close+=3),a.setText(b,c.open,c.close)):b&&(c=a.classPos(),-1<c.open?(c.open=c.close+=3,a.setText("|"+b+"|c",c.open,c.close)):(c.open=c.close=a.start.ndx,a.setText("|"+b+"|c\n",c.open,c.close)));
return!0};var r=a.editText;a.editText=function(b,c,d){twve.tiddler.clearFocusBox();if(a.curNode){var l=null;switch(a.curNode.nodeName){case "TABLE":if(l=twve.tiddler.mousePosition(b),a.curNode=a.rowAt(l,a.curNode.querySelectorAll("tr")),!a.curNode)return null;case "TR":l||(l=twve.tiddler.mousePosition(b));d=a.colIndexAt(l);c=twve.table.rowIndex(a.curNode);var n=a.curNode.querySelectorAll("th,td"),n=twve.table.cellColIndex(n[n.length-1]);a.curNode=a.appendCell(a.curNode,d-n,n);case "TH":case "TD":return!l&&
b&&(l=twve.tiddler.mousePosition(b),d=a.colIndexAt(l),c=a.rowIndexAt(l)),-1<c&&-1<d?a.editCell(b,c,d):null;case "DIV":return r.call(this,b,a.getCaption(),a.curNode)}}return r.apply(this,arguments)};a.refreshSelf=function(b){var c=a.getElement();switch(c.nodeName){case "DIV":b||(b=" ");twve.node.wikify(b,c);break;case "TH":case "TD":twve.node.wikify(b,c);break;default:b=twve.tiddler.getPreviewedNodes(b),c=a.filter(b,"table"),a.replaceWith(b,a.wrappers.all),a.setElement(c),a.prepare()}return a};
var t=a.removeSelf;a.removeSelf=function(b){if(!a.curNode)return a.wrappers.all.parentNode.removeChild(a.wrappers.all),t.apply(this,arguments);a.refreshSelf("");b&&b.what&&a.changed(b);return a};a.adjustRefBars=function(){a.refbarV.adjust(a);a.refbarH.adjust(a);return a};a.refreshTable=function(){a.curNode=null;a.hideRefBars();a.refreshAll();a.showRefBars();a.drawFocusBorders();return a};a.updateText=function(b){switch(a.getElement().nodeName){case "TH":case "TD":return a.updateCell(b);case "DIV":a.setCaption(b[0]);
var c=twve.object.create();c.what="CAPTION MODIFIED";c.text=a.tiddler.text;return a.refreshAll(b[0],c);default:a.setText(b[0])}return a.refreshAll(b[0])};a.drawFocusBorders=function(b){b||(b=twve.node.box(a.wrappers.all));twve.tiddler.drawFocusBorders(b);var c=twve.object.create();c.left=b.right;c.top=b.top;b=twve.table.getTableMenu();b.showRoot(c,"left");b.isVisible()&&(c.left=b.root.dom.offsetLeft,b.show(c,"left"));return a};var s=a.changed;a.changed=function(b){b&&(b.text=s.apply(this,arguments));
a.resize();a.adjustRefBars();return b?b.text:""};a.insertRow=function(b,c){var d=a.tiddler.text,l,n;"below"==c.toLowerCase().substring(0,5)&&b++;0==b?n=l=twve.table.rowStarts(d,a.start,a.end,0):(l=twve.table.rowStarts(d,a.start,a.end,b-1),n=twve.table.rowEnds(d,l));var e="|";l=twve.table.rowCells(d,l,n).length;for(var f=0;f<l;f++)e+="|";d=d.substring(0,n)+(0<b?"\n"+e:e+"\n")+(n<d.length?d.substring(n):"");a.end.ndx+=e.length+1;n=twve.table.copiedRowIndex();b<=n&&twve.table.copiedRowIndex(++n);d=a.changed({what:"ROW INSERTED",
where:b,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.deleteRow=function(b,c){var d=a.tiddler.text,l,n,e;if(!1!==c&&config.options.chktwveCoreConfirmToDelete&&!confirm("Delete row "+b))return a;l=twve.table.rowStarts(d,a.start,a.end,b);n=twve.table.rowEnds(d,l);var f=0;if(n<a.end.ndx){e=twve.table.rowCells(d,l,n);for(var h=twve.table.rowStarts(d,a.start,a.end,0,n),g=twve.table.rowEnds(d,h),h=twve.table.rowCells(d,h,g),g=1;g<e.length;g++)if(e[g]&&h[g]){var k=d.substring(e[g].open,
e[g].close);"~"==d.substring(h[g].open,h[g].close)&&"~"!=k&&(d=d.substring(0,h[g].open)+d.substring(h[g].close),f++)}}d=(0<l?d.substring(0,l-1):"")+(n<d.length?d.substring(n):"");a.end.ndx-=n-l+(0<l?1:0)+f;l=twve.table.copiedRowIndex();b<l&&twve.table.copiedRowIndex(--l);d=a.changed({what:"ROW DELETED",where:b,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.moveRow=function(b,c){var d=a.tiddler.text,l=twve.table.rowStarts(d,a.start,a.end,b),n=twve.table.rowEnds(d,l),e=d.substring(l,
n),f=b+c,g=twve.table.rowStarts(d,a.start,a.end,f),h=twve.table.rowEnds(d,g),d=0<c?d.substring(0,l)+d.substring(n+1,h)+"\n"+e+d.substring(h):d.substring(0,g)+e+"\n"+d.substring(g,l-1)+d.substring(n),l=twve.table.copiedRowIndex();b==l&&twve.table.copiedRowIndex(l+c);d=a.changed({what:"ROW EXCHANGED",from:b,to:f,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.copyRow=function(b){var c=a.tiddler.text,d=twve.table.rowStarts(c,a.start,a.end,b),l=twve.table.rowEnds(c,d);twve.table.copiedRowText(c.substring(d,
l));twve.table.copiedRowIndex(b);twve.table.copiedColumnText(null);twve.table.copiedColumnIndex(-1);return a};a.pasteRow=function(b){var c=a.tiddler.text,d=twve.table.rowStarts(c,a.start,a.end,b),l=twve.table.rowEnds(c,d),n=twve.table.copiedRowText(),c=c.substring(0,d)+n+(l<c.length?c.substring(l):"");a.end.ndx+=n.length-(l-d);c=a.changed({what:"ROW PASTED",where:b,from:twve.table.copiedRowIndex(),text:c});twve.tiddler.saveText(a,c);return a.refreshTable()};a.insertColumn=function(b,c){for(var d=
a.tiddler.text,l=twve.table.allCells(d,a.start,a.end),n="left"==c.toLowerCase().substring(0,4),e=0,f=l.length-1;0<=f;f--){var g=l[f].length-1;if(b<=g)d=n?d.substring(0,l[f][b].open)+"|"+d.substring(l[f][b].open):d.substring(0,l[f][b].close+1)+"|"+d.substring(l[f][b].close+1);else{for(var h="",k=g;k<b;k++)h+="|";n||(h+="|");d=d.substring(0,l[f][g].close+1)+h+d.substring(l[f][g].close+1);e+=h.length}}a.end.ndx+=l.length+e;n||b++;l=twve.table.copiedColumnIndex();b<=l&&twve.table.copiedColumnIndex(++l);
d=a.changed({what:"COL INSERTED",where:b,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.deleteColumn=function(b,c){var d=a.tiddler.text,l=twve.table.allCells(d,a.start,a.end);if(!1!==c&&config.options.chktwveCoreConfirmToDelete&&!confirm("Delete column "+twve.table.colRef(b)))return-1;for(var e=0,f=l.length-1;0<=f;f--)if(!(b>=l[f].length)){var g=d.substring(l[f][b].open,l[f][b].close),h=0<b?d.substring(l[f][b-1].open,l[f][b-1].close):"",d=d.substring(0,l[f][b].open-1)+d.substring(l[f][b].close),
e=e+(g.length+1);">"!=g&&">"==h&&(d=d.substring(0,l[f][b-1].open-1)+d.substring(l[f][b-1].close),e+=h.length+1)}a.end.ndx-=e;l=twve.table.copiedColumnIndex();b<l&&twve.table.copiedColumnIndex(--l);d=a.changed({what:"COL DELETED",where:b,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.moveColumn=function(b,c){for(var d=a.tiddler.text,l=twve.table.allCells(d,a.start,a.end),e=b+c,f=twve.table.copiedColumnIndex(),g=e==f?[]:null,h=0,k=l.length-1;0<=k;k--){var u=l[k].length-1,q=b<=u?d.substring(l[k][b].open,
l[k][b].close):"";g&&g.unshift(q);if(b<=u)if(0<c)if(e<=u)d=d.substring(0,l[k][b].open-1)+d.substring(l[k][b].close,l[k][e].close)+"|"+q+d.substring(l[k][e].close);else{for(var p="",r=u;r<e;r++)p+="|";d=d.substring(0,l[k][b].open)+p+q+d.substring(l[k][u].close);h+=p.length}else d=d.substring(0,l[k][e].open)+q+"|"+d.substring(l[k][e].open,l[k][b].open-1)+d.substring(l[k][b].close);else e<=u&&(d=d.substring(0,l[k][e].open)+"|"+d.substring(l[k][e].open),h+=1)}a.end.ndx+=h;b==f&&twve.table.copiedColumnIndex(f+
c);d=a.changed({what:"COL EXCHANGED",from:b,to:e,text:d});twve.tiddler.saveText(a,d);return a.refreshTable()};a.copyColumn=function(b){for(var c=a.tiddler.text,d=twve.table.allCells(c,a.start,a.end),l=[],e=0;e<d.length;e++)l[l.length]=b<d[e].length?c.substring(d[e][b].open,d[e][b].close):"";twve.table.copiedColumnText(l);twve.table.copiedColumnIndex(b);twve.table.copiedRowText(null);twve.table.copiedRowIndex(-1);return a};a.pasteColumn=function(b){for(var c=a.tiddler.text,d=twve.table.allCells(c,
a.start,a.end),e=twve.table.copiedColumnText(),f=Math.min(d.length,e.length),h=0,g=f-1;0<=g;g--){var k=d[g].length-1;if(b<=k)c=c.substring(0,d[g][b].open)+e[g]+c.substring(d[g][b].close),h+=e[g].length-(d[g][b].close-d[g][b].open);else{for(var q="",u=k;u<b;u++)q+="|";c=c.substring(0,d[g][k].close+1)+q+e[g]+"|\n"+c.substring(0,d[g][k].close+1);h+=q.length+e[g].length+2}}a.end.ndx+=h;for(g=f;g<e.length;g++){q="\n";for(u=0;u<b;u++)q+="|";c=c.substring(0,a.end.ndx)+q+e[g]+"|"+c.substring(a.end.ndx);a.end.ndx+=
q.length+e[g].length+1}c=a.changed({what:"COL PASTED",where:b,from:twve.table.copiedColumnIndex(),text:c});twve.tiddler.saveText(a,c);return a.refreshTable()};a.resize=function(){if(!a.wrappers)return a;var b=twve.tiddler.focusElem();b&&(b=b.is(a));var c=0,d=0;b&&(c=a.refbarV.getSize(),d=a.refbarH.getSize());a.wrappers.resize(c,d);b&&a.drawFocusBorders();return a};a.adjustWidth=function(){for(var b=twve.node.width(a.dom,"outer",!0),c=1*config.options.txttwveTableMinCellWidth,d=0,e=a.rowWithMaxCol().querySelectorAll("th,td"),
f=0,g=e.length;f<g;f++){var h=e[f],k=twve.node.width(h,"outer",!0),q=twve.node.cssSize(window.getComputedStyle(h).getPropertyValue("font-size"))*c,u=2==version.major&&7>version.minor?(twve.node.width(h)-k)/2:0;k<q?(twve.node.setDimension(h,q+u),k=twve.node.width(h,"outer",!0)):0<u&&(twve.node.setDimension(h,k+u),k=twve.node.width(h,"outer",!0));d+=k}twve.node.setDimension(a.dom,(d<b?b:d+1)+(config.tableSorting||config.macros.sortableGridPlugin?1:0));return a};a.initRows=function(){for(var b=a.dom.querySelectorAll("tr"),
c=0,d=b.length;c<d;c++)b[c].setAttribute("row",c);var d=a.tiddler.text,e=a.start,f=a.end,g,h,k=config.options.chktwveTableIncludeCSS;config.options.chktwveTableIncludeCSS=!1;for(var c=0,q=b.length;c<q;c++){g=twve.table.rowStarts(d,e,f,c);h=twve.table.rowEnds(d,g);for(var u=0,p,r=b[c].querySelectorAll("th,td"),t=0,s=r.length;t<s;t++)for(var v=r[t];g<h;){p=twve.table.locateCell(d,g,h,0);g=p.close;p=a.getCellText(p);if(!p){v.setAttribute("col",u++);break}if("~"==p||">"==p)u++;else{if(config.options.chktwveTableMultiLine){var y=
p.charAt(0);("*"==y||"#"==y||-1<p.indexOf("\n"))&&twve.node.wikify(p,v)}v.setAttribute("col",u++);break}}}config.options.chktwveTableIncludeCSS=k};var v=a.multiLine;a.multiLine=function(){return a.curNode?twve.node.matches(a.curNode,"th,td")?config.options.chktwveTableMultiLine:!1:v.apply(this,arguments)};var y=a.hasClass;a.hasClass=function(b){if(config.macros.sortableGridPlugin){var c=a.classPos();if(-1<c.open){var d=text.indexOf(b,c.open);if(-1<d&&d<c.close)return!0}return!1}return y.apply(this,
arguments)};a.startSorting=function(){return!twve.tiddler.getEditBox()};a.sortingCell=function(b){a.curNode="A"==b.nodeName?b.parentNode:b;return a.curNode};a.sort=function(b,c){return twve.table.sort(a,b,c)};a.transpose=function(){for(var b=a.getText(),c=0;;){c=b.indexOf("|>|",c);if(-1==c)break;var d=c+3;c++;var e=b.indexOf("\n",d);-1==e&&(e=b.length);for(;d<e&&">|"==b.substring(d,d+2);)d+=2;if(d<e){var f=twve.text.skipToActive(b,"|",d,e).ndx;if(-1==f)break;f<e&&(b=b.substring(0,c)+b.substring(d,
f)+b.substring(c+1,d)+">"+b.substring(f));c=f}else c=d}for(var c=twve.position.create(0),g=twve.position.create(b.length),e=twve.table.allCells(b,c,g),h=e.length,k=0,q,p,r,f=0;f<h;f++)e[f].length>k&&(k=e[f].length);for(p=k-1;0<=p;p--){d=-1;for(f=h-1;0<=f;f--)p>=e[f].length||(q=b.substring(e[f][p].open,e[f][p].close),"~"==q?-1==d&&(d=f):-1!=d&&(b=b.substring(0,e[f][p].open)+"~"+b.substring(e[f][p].close,e[d][p].open)+q+b.substring(e[d][p].close),e=twve.table.allCells(b,c,g),d=-1))}d="";for(p=0;p<k;p++){d+=
"|";for(f=0;f<h;f++)d+="|";p<k-1&&(d+="\n")}g.ndx=d.length;var t=twve.table.allCells(d,c,g),g=/^\s*\!/,s=/\S/;for(p=k-1;0<=p;p--)for(f=h-1;0<=f;f--)p>=e[f].length||(q=b.charAt(e[f][e[f].length-1].close+1),k="h"==q||"H"==q,q=b.substring(e[f][p].open,e[f][p].close),"~"==q?q=">":">"==q?q="~":k&&!g.test(q)&&(q=0<(r=q.search(s))?q.substring(0,r)+"!"+q.substring(r):"!"+q),d=d.substring(0,t[p][f].open)+q+d.substring(t[p][f].close));r=twve.position.create(0);e=twve.position.create(d.length);e=twve.table.allCells(d,
r,e);for(f=0;f<e.length;f++){k=!1;for(p=e[f].length-1;0<=p;p--){q=d.substring(e[f][p].open,e[f][p].close);if(!g.test(q))break;k=0==p}if(k){p=e[f].length-1;for(d=d.substring(0,e[f][p].close+1)+"h"+d.substring(e[f][p].close+1);0<=p;p--)q=d.substring(e[f][p].open,e[f][p].close),r=q.indexOf("!"),q=q.substring(0,r)+q.substring(r+1),d=d.substring(0,e[f][p].open)+q+d.substring(e[f][p].close);break}}(r=a.getCaption(b))&&(d="|"+r+"|c\n"+d);rend=b.indexOf("|k\n",c.ndx);-1==rend&&(rend=b.indexOf("|K\n",c.ndx));
-1<rend&&(rstart=b.lastIndexOf("|",rend-1),d=b.substring(rstart,rend+3)+d);a.setText(d);d=a.changed({what:"TRANSPOSED",text:a.tiddler.text});twve.tiddler.saveText(a,d);return a.refreshTable()};return a.created(b)}};
twve.table.wrapper={class_all:"twveTable",class_tbref:"twveTBRef",class_tb:"twveTB",class_cap:"twveTCap",getSelector:function(b){if(!b)return"div."+twve.table.wrapper.class_all;b=b.toLowerCase();return"cap"==b?"div."+twve.table.wrapper.class_cap:"tbref"==b?"div."+twve.table.wrapper.class_tbref:"tb"==b?"div."+twve.table.wrapper.class_tb:"div."+twve.table.wrapper.class_all+",div."+twve.table.wrapper.class_cap+",div."+twve.table.wrapper.class_tbref+",div."+twve.table.wrapper.class_tb},create:function(b){var a=
twve.object.create();a.clear=function(){a.all=null;a.tbref=null;a.tb=null;a.cap=null;return a};a.captionHeight=function(){return a.cap?twve.node.height(a.cap,"outer",!0):0};a.setTable=function(b){b=b.dom;if(!b)return a;a.all=twve.table.getWrapper(b);if(a.all)return a.tbref=a.all.querySelector(twve.table.wrapper.getSelector("tbref")),a.tb=a.all.querySelector(twve.table.wrapper.getSelector("tb")),a.cap=a.all.querySelector(twve.table.wrapper.getSelector("cap")),a;a.all=document.createElement("div");
a.tbref=document.createElement("div");a.tb=document.createElement("div");a.cap=document.createElement("div");var k=b.caption?b.childNodes[0]:null;k?(b.removeChild(k),a.cap.innerHTML=k.innerHTML):a.cap.innerHTML=" ";twve.node.copyMargin(a.all,b);b.style.marginLeft="4px";b.parentNode.replaceChild(a.all,b);a.tb.appendChild(b);a.all.style.position="relative";a.all.style.paddingTop="8px";a.all.classList.add(twve.table.wrapper.class_all);b.style.marginTop="4px";a.cap.style.position="relative";a.cap.style.textAlign=
"center";a.cap.style.overflow="hidden";a.cap.classList.add(twve.table.wrapper.class_cap);a.all.appendChild(a.cap);a.tbref.style.border=0;a.tbref.style.overflow="hidden";a.tbref.style.position="relative";a.tbref.classList.add(twve.table.wrapper.class_tbref);a.all.appendChild(a.tbref);a.tb.style.border=0;a.tb.style.overflow="auto";a.tb.style.position="relative";a.tb.classList.add(twve.table.wrapper.class_tb);a.tbref.appendChild(a.tb);return a};a.resize=function(b,k){var c=a.tb.childNodes[0];a.width=
twve.node.width(c,"outer",!0)+(b||0);a.height=twve.node.height(c,"outer",!0)+(k||1);twve.node.setDimension(a.cap,a.width-b);twve.node.setDimension(a.tb,a.width,a.height);twve.node.setDimension(a.tbref,a.width,a.height);twve.node.setDimension(a.all,a.width,a.height+a.captionHeight());return a};a.clear();return b?a.setTable(b):a}};
twve.table.refBar={create:function(){var b=twve.node.create();b.getSelector=function(){return"div."+b.getClass()};b.getMenuSelector=function(){return"div."+b.getMenuClass()};b.getMenu=function(){return twve.tiddler.getDisplay().querySelector(b.getMenuSelector())};b.hideMenu=function(){var a=b.getMenu();a&&twve.node.hide(a);return b};b.menuButtonStyle=function(a){a.dom.style.position="absolute";a.dom.style.backgroundColor="#fff8dc";a.dom.style.color="#000000"};b.show=function(a){twve.node.show(b.dom);
return b};b.hide=function(){twve.node.hide(b.dom);b.hideMenu();return b};b.setTable=function(a){b.dom=a.wrappers.tbref.querySelector(b.getSelector());return b.dom?b:null};b.create=function(a){b.dom=document.createElement("div");b.dom.style.position="absolute";twve.node.hide(b.dom);a&&a.wrappers.tb.parentNode.insertBefore(b.dom,a.wrappers.tb);return b};return b.clear()}};
twve.table.refBarV={getClass:function(){return"twveTRefBarV"},getSelector:function(){var b="div."+twve.table.refBarV.getClass();return b+","+b+" span"},create:function(b){var a=twve.table.refBar.create();a.getClass=function(){return twve.table.refBarV.getClass()};a.getMenuClass=function(){return"twveTMenuV"};a.getSize=function(){return twve.node.width(a.dom,"outer",!0)};var h=a.getMenu;a.getMenu=function(){var b=h.apply(this);if(!b){var b=twve.tiddler.getOptionsMenu().root,f=1.1*b.width(),g=0.9*b.height(),
e=4.2*f,k=4.2*g,b=document.createElement("div");twve.tiddler.getDisplay().appendChild(b);b.style.position="absolute";b.style.border=0;twve.node.setDimension(b,e,k);e=twve.button.create(null,String.fromCharCode(5169),"Insert row above",function(){twve.tiddler.focusElem().insertRow(1*this.parentNode.getAttribute("row"),"above")},"rU");a.menuButtonStyle(e);twve.node.setPosition(e.dom,2.6*f+2,0.6*g);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(5167),"Insert row below",function(){twve.tiddler.focusElem().insertRow(1*
this.parentNode.getAttribute("row"),"below")},"rD");a.menuButtonStyle(e);twve.node.setPosition(e.dom,2.6*f+2,2.6*g);b.appendChild(e.dom);e=twve.button.createDelete("Delete this row",function(){twve.tiddler.focusElem().deleteRow(1*this.parentNode.getAttribute("row"))},"xR");a.menuButtonStyle(e);twve.node.setPosition(e.dom,3.2*f+0,1.6*g);b.appendChild(e.dom);e=twve.button.create(null,"C","Copy this row",function(){var a=this.parentNode;twve.tiddler.focusElem().copyRow(1*a.getAttribute("row"));twve.button.activate(a.querySelector("[id^=twveTbtnrP]"),
!0)},"rC");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0.6*f+0,0.6*g);b.appendChild(e.dom);e=twve.button.create(null,"X","Cut this row",function(){var a=this.parentNode,b=1*a.getAttribute("row"),c=twve.tiddler.focusElem();c.copyRow(b);c.deleteRow(b,!1);twve.button.activate(a.querySelector("[id^=twveTbtnrP]"),!0)},"rX");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0,1.6*g);b.appendChild(e.dom);e=twve.button.create(null,"P","Paste to this row",function(){twve.button.isActive(this)&&twve.tiddler.focusElem().pasteRow(1*
this.parentNode.getAttribute("row"))},"rP");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0.6*f+0,2.6*g);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(8657),"Exchange with above",function(){twve.button.isActive(this)&&twve.tiddler.focusElem().moveRow(1*this.parentNode.getAttribute("row"),-1)},"mU");a.menuButtonStyle(e);twve.node.setPosition(e.dom,1.6*f+0,0);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(8659),"Exchange with below",function(){twve.button.isActive(this)&&
twve.tiddler.focusElem().moveRow(1*this.parentNode.getAttribute("row"),1)},"mD");a.menuButtonStyle(e);twve.node.setPosition(e.dom,1.6*f+0,3.2*g);b.appendChild(e.dom);b.classList.add(a.getMenuClass());twve.node.hide(b)}return b};a.adjust=function(b){var f=twve.node.cssSize(window.getComputedStyle(b.wrappers.tb).getPropertyValue("line-height")),g=a.dom.querySelectorAll("span");b=b.dom.querySelectorAll("tr");if(config.browser.isiOS)for(var e=0,h=b.length;e<h;e++){var k=b[e],r=twve.node.height(k,"outer",
!0),t=k.cells,s=twve.table.cellRowSpan(t[0]);if(1<s){s=twve.table.cellRowSpanMin(t);r/=s;for(t=0;t<s;t++)twve.node.setPosition(g[e+t],null,k.offsetTop+t*r+0.5*(r-f));e+=s-1}else twve.node.setPosition(g[e],null,k.offsetTop+0.5*(r-f))}else{e=0;for(h=b.length;e<h;e++)k=b[e],r=twve.node.height(k,"outer",!0),twve.node.setPosition(g[e],null,k.offsetTop+0.5*(r-f))}return a};a.showMenu=function(b){var f=a.getMenu(),g=twve.node.index(b);b=twve.node.box(b);var e=twve.tiddler.focusElem().dom;twve.node.show(f);
twve.node.setPosition(f,b.left-(twve.node.width(f,"outer",!0)-b.width)/2-(config.browser.isLinux?1:0),b.top-(twve.node.height(f,"outer",!0)-b.height)/2-4);f.setAttribute("row",g);twve.button.activate(f.querySelector("[id^=twveTbtnrP]"),0<=twve.table.copiedRowIndex());twve.button.activate(f.querySelector("[id^=twveTbtnrU]"),!(twve.table.hasHeader(e)&&0==g));twve.button.activate(f.querySelector("[id^=twveTbtnmU]"),!(0==g||twve.table.hasHeader(e)&&1==g||e.tFoot&&g==e.rows.length-1));twve.button.activate(f.querySelector("[id^=twveTbtnrD]"),
!(e.tFoot&&g==e.rows.length-1));twve.button.activate(f.querySelector("[id^=twveTbtnmD]"),!(g==e.rows.length-1||twve.table.hasHeader(e)&&0==g||e.tFoot&&g==e.rows.length-2));return a};a.getWidth=function(a){for(var b=a.rows.length,g=a=twve.node.cssSize(window.getComputedStyle(a).getPropertyValue("font-size"));1<(b/=10);)g+=a;g==a&&(g=a);return g};var k=a.setTable;a.setTable=function(b){if(k.apply(this,arguments))return a;var f=twve.node.cssSize(window.getComputedStyle(b.wrappers.tb).getPropertyValue("line-height")),
g=a.getWidth(b.dom);a.create(b);twve.node.setDimension(a.dom,g);a.dom.classList.add(a.getClass());for(var e=0,h=b.dom.querySelectorAll("tr").length;e<h;e++){var p=document.createElement("span");p.style.border=0;p.style.position="absolute";p.style.padding=0;p.style.margin=0;p.style.textAlign="center";twve.node.setDimension(p,g,f);p.setAttribute("row",e);p.addEventListener("click",function(a){a=1*this.getAttribute("row");var b=this.parentNode,c=b.parentNode.querySelector("table").querySelectorAll("tr"),
e=b.getAttribute("last-row"),e=e?1*e:-1;-1<e&&c[e]&&(c[e].style.opacity=0);a!=e&&c[a]?(c[a].style.opacity=0,b.setAttribute("last-row",a)):b.removeAttribute("last-row")});p.addEventListener("mouseenter",function(b){a.showMenu(this)});p.textContent=twve.table.rowRef(e);a.dom.appendChild(p)}return a};return b?a.setTable(b):a}};
twve.table.refBarH={getClass:function(){return"twveTRefBarH"},getSelector:function(){var b="div."+twve.table.refBarH.getClass();return b+","+b+" span"},create:function(b){var a=twve.table.refBar.create();a.getClass=function(){return twve.table.refBarH.getClass()};a.getMenuClass=function(){return"twveTMenuH"};a.getSize=function(){return twve.node.height(a.dom,"outer",!0)};var h=a.getMenu;a.getMenu=function(){var b=h.apply(this);if(!b){var b=twve.tiddler.getOptionsMenu().root,f=1.2*b.width(),g=0.8*
b.height(),e=4.2*f,k=4.2*g,b=document.createElement("div");twve.tiddler.getDisplay().appendChild(b);b.style.position="absolute";twve.node.setDimension(b,e,k);e=twve.button.create(null,String.fromCharCode(8658),"Exchange with right",function(){twve.button.isActive(this)&&twve.tiddler.focusElem().moveColumn(1*this.parentNode.getAttribute("col"),1)},"mR");a.menuButtonStyle(e);twve.node.setPosition(e.dom,3.2*f,1.6*g);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(8656),"Exchange with left",
function(){twve.button.isActive(this)&&twve.tiddler.focusElem().moveColumn(1*this.parentNode.getAttribute("col"),-1)},"mL");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0,1.6*g);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(5176),"Insert column left",function(){twve.tiddler.focusElem().insertColumn(1*this.parentNode.getAttribute("col"),"left")},"cL");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0.6*f,2.6*g+2);b.appendChild(e.dom);e=twve.button.createDelete("Delete this column",
function(){twve.tiddler.focusElem().deleteColumn(1*this.parentNode.getAttribute("col"))},"xC");a.menuButtonStyle(e);twve.node.setPosition(e.dom,1.6*f,3.2*g);b.appendChild(e.dom);e=twve.button.create(null,String.fromCharCode(5171),"Insert column right",function(){twve.tiddler.focusElem().insertColumn(1*this.parentNode.getAttribute("col"),"right")},"cR");a.menuButtonStyle(e);twve.node.setPosition(e.dom,2.6*f,2.6*g+2);b.appendChild(e.dom);e=twve.button.create(null,"C","Copy this column",function(){var a=
this.parentNode;twve.tiddler.focusElem().copyColumn(1*a.getAttribute("col"));twve.button.activate(a.querySelector("[id^=twveTbtncP]"),!0)},"cC");a.menuButtonStyle(e);twve.node.setPosition(e.dom,0.6*f,0.6*g);b.appendChild(e.dom);e=twve.button.create(null,"X","Cut this column",function(){var a=this.parentNode,b=1*a.getAttribute("col"),c=twve.tiddler.focusElem();c.copyColumn(b);c.deleteColumn(b,!1);twve.button.activate(a.querySelector("[id^=twveTbtncP]"),!0)},"cX");a.menuButtonStyle(e);twve.node.setPosition(e.dom,
1.6*f,0);b.appendChild(e.dom);e=twve.button.create(null,"P","Paste to this column",function(){twve.button.isActive(this)&&twve.tiddler.focusElem().pasteColumn(1*this.parentNode.getAttribute("col"))},"cP");a.menuButtonStyle(e);twve.node.setPosition(e.dom,2.6*f,0.6*g);b.appendChild(e.dom);b.classList.add(a.getMenuClass());twve.node.hide(b)}return b};a.adjust=function(b){var f=a.dom.querySelectorAll("span");b=b.rowWithMaxCol().querySelectorAll("th,td");for(var g=0,e=b.length;g<e;g++){var h=b[g];twve.node.setDimension(f[g],
twve.node.width(h,"inner"));twve.node.setPosition(f[g],h.offsetLeft)}return a};a.showMenu=function(b){var f=a.getMenu();twve.tiddler.focusElem();var g=1*b.getAttribute("col"),e=twve.node.box(b);twve.node.show(f);twve.node.setPosition(f,e.left+(e.width-twve.node.width(f,"outer",!0))/2-2,e.top-(twve.node.height(f,"outer",!0)-e.height)/2-4);f.setAttribute("col",g);twve.button.activate(f.querySelector("[id^=twveTbtncP]"),0<=twve.table.copiedColumnIndex());twve.button.activate(f.querySelector("[id^=twveTbtnmL]"),
0<g);twve.button.activate(f.querySelector("[id^=twveTbtnmR]"),g<b.parentNode.querySelectorAll("span").length-1)};var k=a.setTable;a.setTable=function(b){if(k.apply(this,arguments))return a;var f=b.wrappers,g=window.getComputedStyle(f.tb),e=twve.node.cssSize(g.getPropertyValue("line-height")),g=window.getComputedStyle(f.tbref),h=g.getPropertyValue("background-color"),p=g.getPropertyValue("color");a.create(b);a.dom.style.border=0;a.dom.style.paddingBottom="4px";twve.node.setDimension(a.dom,twve.node.width(f.tbref),
e);a.dom.classList.add(a.getClass());for(var f=b.rowWithMaxCol().querySelectorAll("th,td"),e=0,r=f.length;e<r;e++){var t=f[e],s=document.createElement("span"),g=window.getComputedStyle(t),g=twve.node.cssSize(g.getPropertyValue("border-left-width"))+twve.node.cssSize(g.getPropertyValue("border-right-width"));twve.node.copyMargin(s,t);twve.node.copyPadding(s,t);s.setAttribute("col",e);s.style.backgroundColor=h;s.style.color=p;s.style.position="absolute";s.style.border=0;s.style.textAlign="center";twve.node.setDimension(s,
twve.node.width(t)+g);a.dom.appendChild(s);s.addEventListener("click",function(a){a=1*this.getAttribute("col");for(var b=this.parentNode,c=b.parentNode.querySelector("table"),e=b.getAttribute("last-col"),e=e?1*e:-1,c=c.querySelectorAll("tr"),d=0,f=c.length;d<f;d++){var g=c[d].querySelectorAll("th,td");-1<e&&g[e]&&(g[e].style.opacity=1);a!=e&&g[a]&&(g[a].style.opacity=0)}b.setAttribute("last-col",a!=e?a:"")});s.addEventListener("mouseenter",function(b){a.showMenu(this)});s.textContent=twve.table.colRef(e)}return a};
return b?a.setTable(b):a}};
//}}}
// @@
/***
|editable|k
|''Name:''|twve.tablelarge|
|''Description:''|Large table support for twve, the ~TiddlyWiki View mode Editor|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.tablelarge.min / http://twve.tiddlyspace.com/#twve.tablelarge.min <br>* (regular) http://twve.tiddlyspot.com/#twve.tablelarge / http://twve.tiddlyspace.com/#twve.tablelarge|
|''Type:''|plugin|
|''Version:''|3.2.6|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2014/01/18 renamed to twve.tablelarge <br>2013/11/29 reformed <br>2013/06/02 released 2.0.0 <br>2013/05/24 separated from ~TWtid 1.6.4|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core, twve.table|
!!Features:
* Whatever twve.table supports, plus the support for large tables.
* Oversize tables are scrollable.
* First few rows/columns can be fixed from scrolling.
** Options txttwveTableFixRows and txttwveTableFixCols to specify the first few rows/cols to stay fixed when scrolling.
* Variable view port for tables.
** Options txtTWVEtableMaxWidth and txtTWVEtableMaxHeight to specify the max width/height of table view port.
!!Usage:
!!Options
Look for [[twve.table Options]] in the Options Panel.
!!Examples
[[twve.table Examples]]
!!Todo:
!!!Revision history
!!!! 2016/04/30 [3.2.6]
* Saves options in SystemSettings tiddler.
** If you disable autoSave option, you will need to manually save the tiddler after changing a ''twve'' option.
* Starts using strict mode.
!!!! 2015/03/23 [3.2.3]
* Some minor changes.
!!!! 2015/02/13 [3.2.2]
* Replaced regular expression with string operations whenever easy, hoping to save some time.
* Bug fix
** for fixed rows/columns in oversize tables (''better but still unstable'').
!!!! 2014/12/22 [3.2.1]
* Bug fix
** for transclusion synchronization.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
!!!! 2014/08/03 [3.1.0]
* Started to remove jQuery dependencies for migrating to TW5.
* Bug fix
** for transclusion synchronization with self inclusion and FoldedSectionPlugin.
!!!! 2014/05/23 [3.0.2]
* Bug fixes
** for table scrolling when there is edit box.
!!!! 2014/05/13 [3.0.0]
* Bug fixes
** for transclusion synchronization;
** for partial self inclusion;
** for large table support.
!!!! For earlier history please see [[TWtable.2.0.8]] or [[its external link|http://twtable.tiddlyspace.com/#TWtable.2.0.8]].
!!!! 2014/01/18
* Collected from [[TWtable|http://twtable.tiddlyspace.com/#TWtable.2.0.8]], adapted to the new code structure.
!!!Code
!!!The very beginning
***/
//{{{
(function(){
"use strict";
if ( typeof twve.table == 'undefined' ) {
alert('twve.tablelarge: This plugin needs twve.table to function properly. Please install twve.table before this one.');
}
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve.tablelarge = {
major: 3, minor: 2, revision: 6,
date: new Date('2016/04/30')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
merge(config.macros.twveTableOptions, {
preInit : config.macros.twveTableOptions.init,
init : function () {
config.macros.twveTableOptions.preInit.apply(this);
// twve.tiddler methods
twve.table.preMouseDown = twve.tiddler.mouseDown;
twve.tiddler.mouseDown = twve.table.mouseDown;
// MathJax
if ( twve.MathJax ) {
twve.table.preAfterMath = twve.MathJax.afterMath;
twve.MathJax.afterMath = twve.table.afterMath;
}
twve.checkOption(
'txttwveTableFixRows',
(config.options.txtTWtableFixRows ||
config.options.txtTWtidFixRows || '0')
);
twve.checkOption(
'txttwveTableFixCols',
(config.options.txtTWtableFixCols ||
config.options.txtTWtidFixCols || '0')
);
twve.checkOption(
'txttwveTableMaxWidth',
(config.options.txtTWtableMaxWidth ||
config.options.txtTWtidMaxWidth || '100%')
);
twve.checkOption(
'txttwveTableMaxHeight',
(config.options.txtTWtableMaxHeight ||
config.options.txtTWtidMaxHeight || '5000px')
);
merge(config.optionsDesc,{
txttwveTableFixRows:'(@@color:red;Unstable@@) Fix the first few rows. The number of first rows, including the header row, to fix at the top of table (so they do not scroll). Default is 0.',
txttwveTableFixCols:'(@@color:red;Unstable@@) Fix the first few columns. The number of first columns to fix at the left side of table (so they do not scroll). Default is 0.',
txttwveTableMaxWidth:'Maximum view port width for tables. Wider tables are scrollable. Default is 100% of that of the parent element.',
txttwveTableMaxHeight:'Maximum view port height for tables. Higher tables are scrollable. Default is 500px.'
});
merge(config.macros.twveTableOptions.order,{
txttwveTableFixRows:10,
txttwveTableFixCols:11,
txttwveTableMaxWidth:12,
txttwveTableMaxHeight:13
});
}
});
//}}}
/***
!!! twve.node extra features
***/
//{{{
merge(twve.node,{
//}}}
/***
!!!! twve.node.getBorderWidth
***/
//{{{
getBorderWidth : function(node,which,css){
// Get the border width of node. The 2nd argument which can be
// one of the followings:
// 1. one of 'left', 'top', 'right', and 'bottom'.
// 2. 'horizontal' -- sum of 'left' and 'right' margins.
// 3. 'vertical' -- sum of 'top' and 'bottom' margins.
// The 3rd argument css, if given, shall be the value returned from
// window.getComputedStyle(node).
if ( ! css ) css = window.getComputedStyle(node);
var m1, m2;
switch ( which.charAt(0) ) {
case 'h' :
case 'H' : // horizontal = left + right
m1 = node.style.borderLeftWidth ||
css.getPropertyValue('border-left-width');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.borderRightWidth ||
css.getPropertyValue('border-right-width');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'v' :
case 'V' : // vertical = top + bottom
m1 = node.style.borderTopWidth ||
css.getPropertyValue('border-top-width');
m1 = m1 ? parseInt(m1) : 0;
m2 = node.style.borderBottomWidth ||
css.getPropertyValue('border-bottom-width');
m2 = m2 ? parseInt(m2) : 0;
return m1 + m2;
case 'l' :
case 'L' : // left
m1 = node.style.borderLeftWidth ||
css.getPropertyValue('border-left-width');
return m1 ? parseInt(m1) : 0;
case 't' :
case 'T' : // top
m1 = node.style.borderTopWidth ||
css.getPropertyValue('border-top-width');
return m1 ? parseInt(m1) : 0;
case 'r' :
case 'R' : // right
m2 = node.style.borderRightWidth ||
css.getPropertyValue('border-right-width');
return m2 ? parseInt(m2) : 0;
case 'b' :
case 'B' : // bottom
m2 = node.style.borderBottomWidth ||
css.getPropertyValue('border-bottom-width');
return m2 ? parseInt(m2) : 0;
}
return 0;
},
//}}}
/***
!!!! twve.node.getBorderStyle
***/
//{{{
getBorderStyle : function(node,which,css){
// Get the border style of node. The 2nd argument which can be
// one of 'left', 'top', 'right', and 'bottom'.
// The 3rd argument css, if given, shall be the value returned from
// window.getComputedStyle(node).
if ( ! css ) css = window.getComputedStyle(node);
switch ( which.charAt(0) ) {
case 'l' :
case 'L' : // left
return node.style.borderLeftStyle ||
css.getPropertyValue('border-left-style');
case 't' :
case 'T' : // top
return node.style.borderTopStyle ||
css.getPropertyValue('border-top-style');
case 'r' :
case 'R' : // right
return node.style.borderRightStyle ||
css.getPropertyValue('border-right-style');
case 'b' :
case 'B' : // bottom
return node.style.borderBottomStyle ||
css.getPropertyValue('border-bottom-style');
}
return 0;
},
//}}}
/***
!!!! twve.node.getBorderColor
***/
//{{{
getBorderColor : function(node,which,css){
// Get the border color of node. The 2nd argument which can be
// one of 'left', 'top', 'right', and 'bottom'.
// The 3rd argument css, if given, shall be the value returned from
// window.getComputedStyle(node).
if ( ! css ) css = window.getComputedStyle(node);
switch ( which.charAt(0) ) {
case 'l' :
case 'L' : // left
return node.style.borderLeftColor ||
css.getPropertyValue('border-left-color');
case 't' :
case 'T' : // top
return node.style.borderTopColor ||
css.getPropertyValue('border-top-color');
case 'r' :
case 'R' : // right
return node.style.borderRightColor ||
css.getPropertyValue('border-right-color');
case 'b' :
case 'B' : // bottom
return node.style.borderBottomColor ||
css.getPropertyValue('border-bottom-color');
}
return 0;
},
//}}}
/***
!!!! twve.node.getBorder
***/
//{{{
getBorder : function(node,which,css){
// Get the border of node. The 2nd argument which can be
// one of 'left', 'top', 'right', and 'bottom'.
// The 3rd argument css, if given, shall be the value returned from
// window.getComputedStyle(node).
if ( ! css ) css = window.getComputedStyle(node);
var bw = twve.node.getBorderWidth(node,which,css)+'px';
var bs = twve.node.getBorderStyle(node,which,css);
var bc = twve.node.getBorderColor(node,which,css);
return bw+' '+bs+' '+bc;
},
//{{{
//}}}
/***
!!!! twve.node.copyBorder
***/
//{{{
copyBorder : function (dest,src,css) {
if ( ! css ) css = window.getComputedStyle(src);
dest.style.borderTop = twve.node.getBorder(src,'top',css);
dest.style.borderRight = twve.node.getBorder(src,'right',css);
dest.style.borderBottom = twve.node.getBorder(src,'bottom',css);
dest.style.borderLeft = twve.node.getBorder(src,'left',css);
return dest;
}
});
//}}}
/***
!!! twve.span extra features
***/
//{{{
if(twve.span)merge(twve.span, {
//}}}
/***
!!!! twve.span.twveSelector
***/
//{{{
preTwveSelector : twve.span.twveSelector,
twveSelector : function(selector){
selector = twve.span.preTwveSelector.apply(this,arguments);
return selector.excludeSelector('span[col],span[row]');
}
});
//}}}
/***
!!! twve.table
***/
//{{{
merge(twve.table, {
//}}}
/***
!!!! twve.table.afterMath
***/
//{{{
preAfterMath : null,
afterMath : function(msg){
var table = twve.table.findTable(msg[1]);
if ( table && ! twve.node.closest(table,'div.board') ) {
// There is math in some table got rendered (and is not in the
// preview), which may change the size of the table. We want to
// make sure that table is taken care of.
twve.table.adjustFixedRows(table);
}
twve.table.preAfterMath.apply(this,arguments);
},
//}}}
/***
!!!! twve.table.mouseDown
***/
//{{{
preMouseDown : null,
mouseDown : function(ev){
var down_elem = twve.table.preMouseDown.apply(this,arguments);
if ( down_elem && down_elem.classList.contains(
twve.table.wrapper.class_tb
)) {
var focus = twve.tiddler.focusElem();
if ( focus && focus.wrappers ) {
var eb = twve.node.box(focus.wrappers.tb);
var pos = twve.tiddler.mousePosition(ev);
if (pos.x >= eb.right || pos.y >= eb.bottom)
twve.tiddler.down_elem = down_elem = null;
}
}
return down_elem;
},
//}}}
/***
!!!! twve.table.wrapper_in_sync
***/
//{{{
wrapper_in_sync : null,
sync_ndx : 0,
//}}}
/***
!!!! twve.table.getScrollPosition
***/
//{{{
getScrollPosition : function(tb){
var pos = twve.object.create();
pos.left = tb.scrollLeft;
pos.top = tb.scrollTop;
pos.leftRatio = tb.scrollLeft / tb.scrollWidth;
pos.topRatio = tb.scrollTop / tb.scrollHeight;
return pos;
},
//}}}
/***
!!!! twve.table.adjustFixedRows
***/
//{{{
adjustFixedRows : function(table){
var tb = twve.table.getWrapper(table,'tb');
if ( !tb ) return;
var fixedRows = tb.querySelector(
twve.table.fixedRows.getSelector()
);
if ( fixedRows )
twve.table.fixedRows.adjust(
table,tb,fixedRows
);
var fixedCols = tb.querySelector(
twve.table.fixedColumns.getSelector()
);
if ( fixedCols )
twve.table.fixedRows.adjust(
table,tb,fixedCols
);
},
//}}}
/***
!!!! twve.table.create
***/
//{{{
preCreateLarge : twve.table.create,
create : function(src,txt,start,dir){
var twtable = twve.table.preCreateLarge.apply(this);
// clear
//var preClear = twtable.clear;
twtable.clear = function(){
//preClear.apply(this);
twtable.fixed_row = null;
twtable.fixed_col = null;
return twtable;
};
// copy from
var preCopyFrom = twtable.copyFrom;
twtable.copyFrom = function(table){
preCopyFrom.apply(this,arguments);
twtable.fixed_row = table.fixed_row;
twtable.fixed_col = table.fixed_col;
return twtable;
};
// cellAtMouse
twtable.cellAtMouse = function(ev){
return twve.node.matches(
ev.target.parentNode,twve.table.fixedCell.getSelector()
) ? ev.target.parentNode : ev.target;
};
// is
var preIs = twtable.is;
twtable.is = function(elem){
return preIs.apply(this,arguments) ||
(elem && twve.node.matches(
elem.parentNode,twve.table.fixedCell.getSelector()
));
};
// isEditable
var preIsEditable = twtable.isEditable;
twtable.isEditable = function(elem){
var result = preIsEditable.apply(this,arguments);
if ( ! result ) {
var s = twve.table.fixedCell.getSelector();
var cell = null;
if ( twve.node.matches(elem,s) )
cell = elem;
else {
elem = elem.parentNode;
if (twve.node.matches(elem,s)) cell = elem;
}
if ( cell ) {
twtable.editNode = cell;
//twtable.editNode = twve.table.getRowCell(
// twtable.dom.querySelectorAll('tr')
// [twve.table.cellRowIndex(cell)],
// twve.table.cellColIndex(cell)
//);
result = true;
}
}
return result;
};
// scrollIntoView
var preScrollIntoView = twtable.scrollIntoView;
twtable.scrollIntoView = function(scroller){
// Scrolls the cell into view if currently not.
if ( twve.node.matches(twtable.editNode,'th,td') )
scroller = twtable.wrappers.tb;
return preScrollIntoView.call(this,scroller);
};
// editNeighbor
var preEditNeighbor = twtable.editNeighbor;
twtable.editNeighbor = function(){
preEditNeighbor.apply(this,arguments);
twtable.scrollIntoView();
return twtable;
};
// maxDim
twtable.maxDim = function(){
var w = twtable.direct_wrapper.dom;
if ( twve.node.matches(
w,twve.tiddlerSpan.twveSelector().include
) ) {
// <<tiddler>> transcluded content, may exceed the
// container width.
// This does not happen with <<slider>> nor <<tabs>>
// macros.
w = w.parentNode;
}
var dim = twve.object.create();
dim.width = twve.node.cssSize(
config.options.txttwveTableMaxWidth,w
);
dim.height = twve.node.cssSize(
config.options.txttwveTableMaxHeight,w
);
return dim;
};
// adjustDim
twtable.adjustDim = function(dim){
var max = twtable.maxDim();
var tbw = twtable.wrappers;
var scw = twve.node.scrollBarWidth(tbw.tb);
var oversize = false;
if (dim.width+scw > max.width) {
// Width is too large, need to have room horizontal
// scroll bar.
dim.height += scw;
if ( dim.height > max.height ) {
// Height is too large with a horizontal scroll bar,
// meaning we also need a vertical scroll bar.
dim.height = max.height-scw;
dim.width = max.width-scw;
} else
// Height is fine with a horizontal scroll bar, no
// need for a vertical scroll bar. Set the width to
// maximum.
dim.width = max.width;
oversize = true;
} else if (dim.height+scw > max.height) {
// Width is fine but height is larger than the
// user-specified maximum, need a vertical scroll bar.
dim.width += scw;
if ( dim.width > max.width ) {
// Width is too large with a vertical scroll bar,
// meaning we also need a horizontal scroll bar.
dim.width = max.width-scw;
dim.height = max.height-scw;
} else
// Width is fine with a vertical scroll bar, no
// need for a horizontal scroll bar. Set the height
// to maximum.
dim.height = max.height;
oversize = true;
}
return oversize;
};
// oversize
//var noupdate_size = false;
twtable.oversize = function (dim) {
if ( ! dim )
return twtable.dom.getAttribute('oversize');
// Check if the table is oversize.
var oversize = twtable.adjustDim(dim);
//if ( noupdate_size ) return oversize;
if ( oversize )
twtable.dom.setAttribute('oversize', 'true');
else
twtable.dom.removeAttribute('oversize');
return oversize;
};
// getScrollPosition
twtable.getScrollPosition = function(){
return twve.table.getScrollPosition(twtable.wrappers.tb);
};
// setScrollPosition
twtable.setScrollPosition = function(pos){
var tb = twtable.wrappers.tb;
tb.scrollLeft = pos.leftRatio * tb.scrollWidth;
tb.scrollTop = pos.topRatio * tb.scrollHeight;
return pos;
};
// getMargin
twtable.getMargin = function(which){
return twve.node.getMargin(twtable.dom,which)-2;
};
// putFixedRows
twtable.putFixedRows = function(toadjust){
var scrL = twtable.wrappers.tb.scrollLeft;
var scrT = twtable.wrappers.tb.scrollTop;
var css = null;
if ( twtable.fixedRows ) {
css = window.getComputedStyle(twtable.dom);
twve.node.setPosition(
twtable.fixedRows.dom,
twtable.dom.offsetLeft,
twtable.dom.offsetTop+scrT
-(scrT > 1
? (twve.node.getMargin(twtable.dom,'top',css)
+(config.browser.isIE?1:0))
: 0)
);
}
if ( twtable.fixedCols ) {
twve.node.setPosition(
twtable.fixedCols.dom,
twtable.dom.offsetLeft+scrL
-(scrL > 1
? twve.node.getMargin(twtable.dom,'left',css)
: 0),
twtable.dom.offsetTop
);
}
};
// adjustFixedRows
twtable.adjustFixedRows = function(){
if ( twtable.fixedRows )
twve.table.fixedRows.adjust(
twtable.dom,twtable.wrappers.tb,
twtable.fixedRows.dom
);
if ( twtable.fixedCols )
twve.table.fixedRows.adjust(
twtable.dom,twtable.wrappers.tb,
twtable.fixedCols.dom
);
return twtable;
};
// resize
var preWrapperResize = null;
twtable.wrapperResize = function(dw,dh){
var tbw = twtable.wrappers;
var pos = twve.table.getScrollPosition(tbw.tb);
preWrapperResize.apply(this,arguments);
if ( twtable.oversize(tbw) ) {
var ow = 0, oh = 0;
var max = twtable.maxDim();
var scw = twve.node.scrollBarWidth(twtable.wrappers.tb);
if ( tbw.width+scw >= max.width ) {
// Width is too large.
dw = (dw||twtable.refbarV.getSize())+scw;
if ( tbw.height+scw < max.height ) {
// Height is fine, reduce the height of tbw.tb
// so the horizontal scroll bar is close to the
// table body.
dh = (dh||0)+twtable.getMargin('vertical')-1;
//dh = (dh||twtable.refbarH.getSize())+scw;
ow = -scw;
} else {
// Height is also too large
oh = dh || 0;
dh = 0;
}
twve.node.setDimension(tbw.cap,max.width-dw);
twve.node.setDimension(
tbw.tb,max.width-dw,tbw.height-dh
);
} else if ( tbw.height+scw >= max.height ) {
// Width is fine but height is too large.
if ( ! dw ) dw = 0;
ow = -scw;
oh = dh || 0;
dh = 0;
twve.node.setDimension(tbw.cap,tbw.width-dw+ow);
twve.node.setDimension(
tbw.tb,
tbw.width-dw+ow+5,
tbw.height
);
}
twve.node.setDimension(
tbw.tbref,tbw.width+ow+6,tbw.height+oh
);
twve.node.setDimension(
tbw.wrap,tbw.width+ow+6,
tbw.height+tbw.captionHeight()+oh
);
// Necessary at focusing/defocusing
twtable.setScrollPosition(pos)
}
return tbw;
};
// scrolled
twtable.scrolled = function(scroll){
var tb = twtable.wrappers.tb;
twtable.putRefBars(
tb.offsetLeft-tb.scrollLeft,
tb.offsetTop-tb.scrollTop
);
twtable.putFixedRows();
//twtable.adjustFixedRows();
if ( ! twtable.beingEdited() ||
! twve.node.matches(twtable.editNode,'th,td') )
return twtable;
var edbox = twve.tiddler.getEditBox();
if ( edbox ) {
var eb = twve.node.box(
twve.tiddler.cur_editable.getElement()
);
var dL = eb.left - (edbox[0].offsetLeft ||
edbox[0][0].offsetLeft)-1;
var dT = eb.top - (edbox[0].offsetTop ||
edbox[0][0].offsetTop);
for ( var i=0,len=edbox.length; i<len; i++ )
twve.node.shiftPosition(edbox[i],dL,dT);
var preview = twve.tiddler.getPreviewer();
if ( preview )
twve.node.shiftPosition(preview,dL,dT);
}
return twtable;
};
// scroll
twtable.scroll = function(){
var twtable = twve.tiddler.focusElem();
if ( ! twtable || ! twtable.scrolled ) return;
var pos = twtable.getScrollPosition();
twtable.scrolled(pos);
if ( ! twve.table.wrapper_in_sync ) {
// Save and synchronize scroll position
twve.table.wrapper_in_sync = twtable.findWrappers();
// Synchronize all copies.
for(var n=0,len=twtable.wrapper.length;n<len;n++){
var t = twtable.renderedCopy(twtable.wrapper[n]);
if ( t ) t.setScrollPosition(pos);
}
}
if ( ++twve.table.sync_ndx
== twve.table.wrapper_in_sync.length ) {
twve.table.wrapper_in_sync = null;
twve.table.sync_ndx = 0;
}
};
// prepareFixedRows
twtable.prepareFixedRows = function(){
if ( twtable.oversize() ) {
twtable.wrappers.tb.addEventListener(
'scroll', twtable.scroll
);
var nrows = config.options.txttwveTableFixRows*1;
twtable.fixedRows = nrows > 0
? twve.table.fixedRows.create(twtable,nrows)
: null;
var ncols = config.options.txttwveTableFixCols*1;
twtable.fixedCols = ncols > 0
? twve.table.fixedColumns.create(twtable,ncols)
: null;
twtable.putFixedRows();
twtable.adjustFixedRows();
} else {
twtable.wrappers.tb.removeEventListener(
'scroll', twtable.scroll
);
}
return twtable;
};
// blur
var preBlur = twtable.blur;
twtable.blur = function(){
twtable.putFixedRows();
return preBlur.apply(this,arguments);
};
// hijackWrapperResize
twtable.hijackWrapperResize = function(){
if ( twtable.wrappers.resize != twtable.wrapperResize) {
preWrapperResize = twtable.wrappers.resize;
twtable.wrappers.resize = twtable.wrapperResize;
}
return twtable;
};
// setElement
var preSetElement = twtable.setElement;
twtable.setElement = function(){
preSetElement.apply(this,arguments);
if ( ! twtable.wrappers ) return twtable;
twtable.hijackWrapperResize();
twtable.prepareFixedRows();
return twtable;
};
// renderedCopy
var preRenderedCopy = twtable.renderedCopy;
twtable.renderedCopy = function(){
var the_copy = preRenderedCopy.apply(this,arguments);
return the_copy
? the_copy.hijackWrapperResize()
: null;
};
// prepare
var prePrepare = twtable.prepare;
twtable.prepare = function(){
if ( ! prePrepare.apply(this,arguments) ) return false;
twtable.prepareFixedRows();
//setTimeout(function(){
// twtable.adjustFixedRows();
//},0);
return true;
};
//}}}
/***
!!!!! twtable.reInitializeTable
***/
//{{{
var preReInitializeTable = twtable.reInitializeTable;
twtable.reInitializeTable = function(txt){
preReInitializeTable.call(this,txt);
twtable.hijackWrapperResize();
twtable.prepareFixedRows();
};
// refreshSelf
var preRefreshSelf = twtable.refreshSelf;
twtable.refreshSelf = function(){
// Save scroll position of tb
var pos = twtable.getScrollPosition();
// Refresh the table
preRefreshSelf.apply(this,arguments);
// Restore the scroll position of tb
twtable.setScrollPosition(pos);
return twtable;
};
// editText
var preEditText = twtable.editText;
twtable.editText = function(ev){
var cell = twtable.editNode;
if ( cell && twve.node.matches(
cell,twve.table.fixedCell.getSelector()
)) {
// Clicked on a fixed cell.
var txt = twtable.getText();
var pos = twve.table.cellPos(
txt,0,txt.length,
twve.table.cellRowIndex(cell),
twve.table.cellColIndex(cell),
twve.table.cellSpans(cell)
);
//if ( ! pos.length )
twtable.editNode = null;
return preEditText.call(
this,ev,
twtable.getCellText(pos,txt),
cell
);
//return twtable.editSpannedCells(pos);
}
return preEditText.apply(this,arguments);
};
// End of definition
return twtable.created(src,txt,start,dir);
}
});
//}}}
/***
!!! twve.table.fixedCell
***/
//{{{
twve.table.fixedCell = {
//}}}
/***
!!!! twve.table.fixedCell.getClass
***/
//{{{
getClass : function(){
return 'twveTfixedCell';
},
//}}}
/***
!!!! twve.table.fixedCell.getSelector
***/
//{{{
getSelector : function(){
return 'div.'+twve.table.fixedCell.getClass();
},
//}}}
/***
!!!! twve.table.fixedCell.adjust
***/
//{{{
adjust : function(fc,cell,scroll,trleft,trtop){
var css = window.getComputedStyle(cell);
var padding = {
left: twve.node.getPadding(cell,'left',css),
top: twve.node.getPadding(cell,'top',css),
right: twve.node.getPadding(cell,'right',css),
bottom: twve.node.getPadding(cell,'bottom',css)
};
var opera_or_chrome =
config.browser.isOpera || config.browser.isChrome;
var dleft =
-(opera_or_chrome?0:padding.left)
+(config.browser.firefoxDate?1:0)
// Cell position is relative to table instead of tr,
// therefore we need to subtract the offsetTop of tr to
// obtain the correct offset of a cell.
-trleft;
var dtop = ((config.browser.isIE ||
(config.browser.firefoxDate))
? (-padding.top)
: 0)
// Cell position is relative to table instead of tr,
// therefore we need to subtract the offsetTop of tr to
// obtain the correct offset of a cell.
-trtop;
twve.node.setPosition(
fc,
cell.offsetLeft+dleft,
cell.offsetTop+dtop
);
var w = cell.clientWidth;
var h = cell.clientHeight;
twve.node.setDimension(fc,w,h+padding.bottom);
// Adjust horizontal/vertical alignment
var sp_c = fc.firstChild;
var left = padding.left;
var top = padding.top;
twve.node.setPosition(sp_c,left,top);
var align = css.getPropertyValue('text-align');
if ( align.indexOf('center') > -1 )
left = (w-sp_c.offsetWidth)/2-padding.left;
else if ( align.indexOf('right') > -1 )
left = w-sp_c.offsetWidth-padding.left;
align = css.getPropertyValue('vertical-align');
if ( align.indexOf('middle') > -1 )
top = (h-sp_c.offsetHeight)/2;
else if ( align.indexOf('bottom') > -1 )
top = h-sp_c.offsetHeight;
if ( left > padding.left || top > padding.top )
twve.node.setPosition(sp_c,left,top);
},
//}}}
/***
!!!! twve.table.fixedCell.create
***/
//{{{
create : function(cell,row,col,scroll,created){
if ( created ) {
fixedCell = twve.node.create(cell);
} else {
var fixedCell = twve.node.create('div');
fixedCell.dom.classList.add(
twve.table.fixedCell.getClass()
);
fixedCell.dom.appendChild(document.createElement('span'));
fixedCell.dom.style.position = 'absolute';
fixedCell.dom.firstChild.style.position = 'absolute';
fixedCell.dom.style.display = 'table-cell';
}
//}}}
/***
!!!!! fixedCell.setTableCell
***/
//{{{
fixedCell.setTableCell = function(cell,row,col,scroll){
//var ch_org_out = twve.node.height(cell,'outer',true);
//var ch_org = twve.node.height(cell,'inner');
var css = window.getComputedStyle(cell);
var sp_c = fixedCell.dom;
// Copy margins, borders, and paddings.
twve.node.copyMargin(sp_c,cell,css);
twve.node.copyBorder(sp_c,cell,css);
if ( sp_c.style.borderLeftWidth == '0px' )
sp_c.style.borderLeftWidth = '1px';
if ( sp_c.style.borderRightWidth == '0px' )
sp_c.style.borderRightWidth = '1px';
twve.node.copyFontColor(sp_c,cell,(row==0));
// Copy .noedit class if there is.
if ( cell.classList.contains('noedit') )
sp_c.classList.add('noedit');
// Copy col, rowspan, and colspan attributes
sp_c.setAttribute('col',col);
var attr = cell.getAttribute('rowspan');
if ( attr ) sp_c.setAttribute('rowspan',attr);
attr = cell.getAttribute('colspan');
if ( attr ) sp_c.setAttribute('colspan',attr);
// Copy content
sp_c = sp_c.firstChild;
sp_c.innerHTML = cell.innerHTML;
// The next lines are for sorting purposes.
if ( row == 0 && twve.table.sortingEnabled() ) {
// Hide sorting indicators if necessary
var dir = sp_c.querySelector('span');
if ( dir && dir.classList.contains('hidden') )
twve.node.hide(dir);
}
return fixedCell;
};
//}}}
/***
!!!!! End of fixedCell
***/
//{{{
return created
? fixedCell
: fixedCell.setTableCell(cell,row,col,scroll);
}
};
//}}}
/***
!!! twve.table.fixedRow -- One fixed table row.
***/
//{{{
twve.table.fixedRow = {
//}}}
/***
!!!! twve.table.fixedRow.getClass
***/
//{{{
getClass : function(){
return 'twveTfixedRow';
},
//}}}
/***
!!!! twve.table.fixedRow.getSelector
***/
//{{{
getSelector : function(){
return 'div.'+twve.table.fixedRow.getClass();
},
//}}}
/***
!!!!! twve.table.fixedRow.adjust
***/
//{{{
adjust : function (fr,tr,row,scroll) {
var fixedCells = fr.querySelectorAll(
twve.table.fixedCell.getSelector()
);
twve.node.setPosition(
fr,tr.offsetLeft,
tr.offsetTop-(config.browser.isIE?0:(row>0?1:0))
);
//var css = window.getComputedStyle(tr);
var rowCells = tr.querySelectorAll('th,td');
for ( var n=0,clen=fixedCells.length; n<clen; n++ ) {
// Cell position is relative to table instead of tr,
// therefore we need to subtract the offsetTop of tr to
// obtain the correct offset of a cell.
twve.table.fixedCell.adjust(
fixedCells[n],rowCells[n],
scroll,tr.offsetLeft,tr.offsetTop
);
}
},
//}}}
/***
!!!! twve.table.fixedRow.create
***/
//{{{
create : function(tr,row,nCols,scroll,created){
var fixedRow;
if ( created ) {
fixedRow = twve.node.create(tr);
} else {
fixedRow = twve.node.create('div');
fixedRow.dom.classList.add(
twve.table.fixedRow.getClass()
);
fixedRow.dom.style.position = 'absolute';
fixedRow.dom.style.display = 'inline-block';
}
fixedRow.cells = [];
//}}}
/***
!!!!! fixedRow.setTableRow
***/
//{{{
fixedRow.setTableRow = function(tr,row,nCols,scroll,created){
var cells;
if ( created ) {
cells = tr.querySelectorAll(
twve.table.fixedCell.getSelector()
);
nCols = cells.length;
fixedRow.cells.length = nCols;
for (var c=0; c<nCols; c++) {
fixedRow.cells[c] =
twve.table.fixedCell.create(
cells[c],row,c,scroll,created
);
}
} else {
// Copy margins, borders, and paddings.
var css = window.getComputedStyle(tr);
twve.node.copyMargin(fixedRow.dom,tr,css);
twve.node.copyBorder(fixedRow.dom,tr,css);
twve.node.copyPadding(fixedRow.dom,tr,css);
fixedRow.dom.style.backgroundColor = twve.node.bgc(tr,css);
// Copy row attribute.
fixedRow.dom.setAttribute('row',row);
// Copy cells.
cells = tr.querySelectorAll('th,td');
if ( ! nCols || nCols > cells.length )
nCols = cells.length;
fixedRow.cells.length = nCols;
var w = 0;
for (var c=0; c<nCols; c++) {
//if ( twve.table.cellColSpan(cells[c]) > 1 ) continue;
fixedRow.cells[c] =
twve.table.fixedCell.create(
cells[c],row,c,scroll,created
);
w += cells[c].offsetWidth;
fixedRow.dom.appendChild(fixedRow.cells[c].dom);
}
twve.node.setDimension(
fixedRow.dom,w,tr.clientHeight
);
}
return fixedRow;
};
//}}}
/***
!!!!! End of fixedRow
***/
//{{{
return fixedRow.setTableRow(tr,row,nCols,scroll,created);
}
}
//}}}
/***
!!! twve.table.fixedRows -- Multiple fixed table rows.
***/
//{{{
twve.table.fixedRows = {
//}}}
/***
!!!! twve.table.fixedRows.getClass
***/
//{{{
getClass : function(){
return 'twveTfixedRows';
},
//}}}
/***
!!!! twve.table.fixedRows.getSelector
***/
//{{{
getSelector : function(){
return 'div.'+twve.table.fixedRows.getClass();
},
//}}}
/***
!!!!! twve.table.fixedRows.adjust
***/
//{{{
adjust : function (table,tb,fixedRows) {
var rows = fixedRows.querySelectorAll(
twve.table.fixedRow.getSelector()
);
var scroll = twve.table.getScrollPosition(
tb || twve.table.getWrapper(table,'tb')
);
var tr = table.querySelectorAll('tr');
for ( var r=0,rlen=rows.length; r<rlen; r++ )
twve.table.fixedRow.adjust(rows[r],tr[r],r,scroll);
},
//}}}
/***
!!!! twve.table.fixedRows.create
***/
//{{{
create : function(twtable,nRows,nCols){
var fixedRows = twve.node.create();
fixedRows.rows = [];
//}}}
/***
!!!!! fixedRows.setTable
***/
//{{{
fixedRows.setTable = function (twtable,nRows,nCols) {
// THIS IS NOT STABLE YET.
// This function is to fix the first few rows at the top
// of a table. It does so by cloning the table with only
// the few rows to fix, then put it over to cover the
// original.
if ( ! twtable.oversize() ) return fixedRows;
var scroll = twtable.getScrollPosition();
var tbw = twtable.wrappers;
var fixR = tbw.tb.querySelector(
twve.table.fixedRows.getSelector()
);
if ( fixR ) {
// Fixedrows are already created, collect them.
fixedRows.dom = fixR;
var rows = fixR.querySelectorAll(
twve.table.fixedRow.getSelector()
);
fixedRows.rows.length = rows.length;
for(var r=0,rlen=fixedRows.rows.length; r<rlen; r++){
fixedRows.rows[r] = twve.table.fixedRow.create(
rows[r],r,nCols,scroll,true
);
}
} else {
fixedRows.dom = fixR = document.createElement('div');
fixR.classList.add(twve.table.fixedRows.getClass());
fixR.style.position = 'absolute';
//fixR.style.zIndex = 1;
fixR.style.overflow = 'hidden';
var css = window.getComputedStyle(twtable.dom);
fixR.style.borderTop =
twve.node.getBorder(twtable.dom,'top',css);
fixR.style.borderLeft =
twve.node.getBorder(twtable.dom,'left',css);
fixR.style.borderRight =
twve.node.getBorder(twtable.dom,'right',css);
fixR.style.borderBottom =
twve.node.getBorder(twtable.dom,'bottom',css);
tbw.tb.appendChild(fixR);
// create the fixed rows.
var rows = twtable.dom.querySelectorAll('tr');
var rlen = fixedRows.rows.length = nRows > 0
? nRows : rows.length;
var h = 0;
for(var r=0; r<rlen; r++){
fixedRows.rows[r] = twve.table.fixedRow.create(
rows[r],r,nCols,scroll
);
fixR.appendChild(fixedRows.rows[r].dom);
h += rows[r].offsetHeight;
}
twve.node.setDimension(fixR,twtable.dom.clientWidth,h);
}
twtable.scrolled(scroll);
return fixedRows;
};
//}}}
/***
!!!!! End of fixedRows
***/
//{{{
return twtable
? fixedRows.setTable(twtable,nRows,nCols)
: fixedRows;
}
};
//}}}
/***
!!! twve.table.fixedColumns
***/
//{{{
twve.table.fixedColumns = {
//}}}
/***
!!!! twve.table.fixedColumns.getClass
***/
//{{{
getClass : function(){
return 'twveTfixedColumn';
},
//}}}
/***
!!!! twve.table.fixedColumns.getSelector
***/
//{{{
getSelector : function(){
return 'div.'+twve.table.fixedColumns.getClass();
},
//}}}
/***
!!!! twve.table.fixedColumns.create
***/
//{{{
create : function(twtable,nCols){
var fixedColumns=twve.table.fixedRows.create();
//}}}
/***
!!!!! twve.table.fixedColumns.createElement
***/
//{{{
var preSetTable = fixedColumns.setTable;
fixedColumns.setTable = function (twtable) {
var result = preSetTable.apply(this,arguments);
fixedColumns.dom.style.borderBottom =
twve.node.getBorder(twtable.dom,'bottom');
fixedColumns.dom.classList.remove(
twve.table.fixedRows.getClass()
);
fixedColumns.dom.classList.add(
twve.table.fixedColumns.getClass()
);
return result;
};
//}}}
/***
!!!!! End of fixedColumns
***/
//{{{
return twtable
? fixedColumns.setTable(twtable,null,nCols)
: fixedColumns;
}
};
}());
//}}}
/***
|editable|k
|''Name:''|twve.tablelarge|
|''Description:''|Large table support for twve, the ~TiddlyWiki View mode Editor|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.tablelarge.min / http://twve.tiddlyspace.com/#twve.tablelarge.min <br>* (regular) http://twve.tiddlyspot.com/#twve.tablelarge / http://twve.tiddlyspace.com/#twve.tablelarge|
|''Type:''|plugin|
|''Version:''|3.2.3|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2014/01/18 renamed to twve.tablelarge <br>2013/11/29 reformed <br>2013/06/02 released 2.0.0 <br>2013/05/24 separated from ~TWtid 1.6.4|
|''License:''|MIT|
|''Core Version:''|2.6.5|
|''Needs to have:''|twve.core, twve.table|
!!Options
Look for [[twve.table Options]] in the system Options panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve.table&&alert("twve.tablelarge: This plugin needs twve.table to function properly. Please install twve.table before this one.");version.extensions.twve.tablelarge={major:3,minor:2,revision:3,date:new Date("2015/03/23")};
merge(config.macros.twveTableOptions,{preInit:config.macros.twveTableOptions.init,init:function(){config.macros.twveTableOptions.preInit.apply(this);twve.table.preMouseDown=twve.tiddler.mouseDown;twve.tiddler.mouseDown=twve.table.mouseDown;twve.MathJax&&(twve.table.preAfterMath=twve.MathJax.afterMath,twve.MathJax.afterMath=twve.table.afterMath);void 0===config.options.txttwveTableFixRows&&(config.options.txttwveTableFixRows=void 0===config.options.txtTWtableFixRows?void 0===config.options.txtTWtidFixRows?
"0":config.options.txtTWtidFixRows:config.options.txtTWtableFixRows);void 0===config.options.txttwveTableFixCols&&(config.options.txttwveTableFixCols=void 0===config.options.txtTWtableFixCols?void 0===config.options.txtTWtidFixCols?"0":config.options.txtTWtidFixCols:config.options.txtTWtableFixCols);void 0===config.options.txttwveTableMaxWidth&&(config.options.txttwveTableMaxWidth=void 0===config.options.txtTWtableMaxWidth?void 0===config.options.txtTWtidMaxWidth?"100%":config.options.txtTWtidMaxWidth:
config.options.txtTWtableMaxWidth);void 0===config.options.txttwveTableMaxHeight&&(config.options.txttwveTableMaxHeight=void 0===config.options.txtTWtableMaxHeight?void 0===config.options.txtTWtidMaxHeight?"5000px":config.options.txtTWtidMaxHeight:config.options.txtTWtableMaxHeight);merge(config.optionsDesc,{txttwveTableFixRows:"(@@color:red;Unstable@@) Fix the first few rows. The number of first rows, including the header row, to fix at the top of table (so they do not scroll). Default is 0.",txttwveTableFixCols:"(@@color:red;Unstable@@) Fix the first few columns. The number of first columns to fix at the left side of table (so they do not scroll). Default is 0.",
txttwveTableMaxWidth:"Maximum view port width for tables. Wider tables are scrollable. Default is 100% of that of the parent element.",txttwveTableMaxHeight:"Maximum view port height for tables. Higher tables are scrollable. Default is 500px."});merge(config.macros.twveTableOptions.order,{txttwveTableFixRows:10,txttwveTableFixCols:11,txttwveTableMaxWidth:12,txttwveTableMaxHeight:13})}});
merge(twve.node,{getBorderWidth:function(b,a,c){c||(c=window.getComputedStyle(b));switch(a.charAt(0)){case "h":case "H":return a=(a=b.style.borderLeftWidth||c.getPropertyValue("border-left-width"))?parseInt(a):0,b=(b=b.style.borderRightWidth||c.getPropertyValue("border-right-width"))?parseInt(b):0,a+b;case "v":case "V":return a=(a=b.style.borderTopWidth||c.getPropertyValue("border-top-width"))?parseInt(a):0,b=(b=b.style.borderBottomWidth||c.getPropertyValue("border-bottom-width"))?parseInt(b):0,a+
b;case "l":case "L":return(a=b.style.borderLeftWidth||c.getPropertyValue("border-left-width"))?parseInt(a):0;case "t":case "T":return(a=b.style.borderTopWidth||c.getPropertyValue("border-top-width"))?parseInt(a):0;case "r":case "R":return(b=b.style.borderRightWidth||c.getPropertyValue("border-right-width"))?parseInt(b):0;case "b":case "B":return(b=b.style.borderBottomWidth||c.getPropertyValue("border-bottom-width"))?parseInt(b):0}return 0},getBorderStyle:function(b,a,c){c||(c=window.getComputedStyle(b));
switch(a.charAt(0)){case "l":case "L":return b.style.borderLeftStyle||c.getPropertyValue("border-left-style");case "t":case "T":return b.style.borderTopStyle||c.getPropertyValue("border-top-style");case "r":case "R":return b.style.borderRightStyle||c.getPropertyValue("border-right-style");case "b":case "B":return b.style.borderBottomStyle||c.getPropertyValue("border-bottom-style")}return 0},getBorderColor:function(b,a,c){c||(c=window.getComputedStyle(b));switch(a.charAt(0)){case "l":case "L":return b.style.borderLeftColor||
c.getPropertyValue("border-left-color");case "t":case "T":return b.style.borderTopColor||c.getPropertyValue("border-top-color");case "r":case "R":return b.style.borderRightColor||c.getPropertyValue("border-right-color");case "b":case "B":return b.style.borderBottomColor||c.getPropertyValue("border-bottom-color")}return 0},getBorder:function(b,a,c){c||(c=window.getComputedStyle(b));var e=twve.node.getBorderWidth(b,a,c)+"px",f=twve.node.getBorderStyle(b,a,c);b=twve.node.getBorderColor(b,a,c);return e+
" "+f+" "+b},copyBorder:function(b,a,c){c||(c=window.getComputedStyle(a));b.style.borderTop=twve.node.getBorder(a,"top",c);b.style.borderRight=twve.node.getBorder(a,"right",c);b.style.borderBottom=twve.node.getBorder(a,"bottom",c);b.style.borderLeft=twve.node.getBorder(a,"left",c);return b}});twve.span&&merge(twve.span,{preTwveSelector:twve.span.twveSelector,twveSelector:function(b){b=twve.span.preTwveSelector.apply(this,arguments);return b.excludeSelector("span[col],span[row]")}});
merge(twve.table,{preAfterMath:null,afterMath:function(b){var a=twve.table.findTable(b[1]);a&&!twve.node.closest(a,"div.board")&&twve.table.adjustFixedRows(a);twve.table.preAfterMath.apply(this,arguments)},preMouseDown:null,mouseDown:function(b){var a=twve.table.preMouseDown.apply(this,arguments);if(a&&a.classList.contains(twve.table.wrapper.class_tb)){var c=twve.tiddler.focusElem();if(c&&c.wrappers){var c=twve.node.box(c.wrappers.tb),e=twve.tiddler.mousePosition(b);if(e.x>=c.right||e.y>=c.bottom)twve.tiddler.down_elem=
a=null}}return a},wrapper_in_sync:null,sync_ndx:0,getScrollPosition:function(b){var a=twve.object.create();a.left=b.scrollLeft;a.top=b.scrollTop;a.leftRatio=b.scrollLeft/b.scrollWidth;a.topRatio=b.scrollTop/b.scrollHeight;return a},adjustFixedRows:function(b){var a=twve.table.getWrapper(b,"tb"),c=a.querySelector(twve.table.fixedRows.getSelector());c&&twve.table.fixedRows.adjust(b,a,c);(c=a.querySelector(twve.table.fixedColumns.getSelector()))&&twve.table.fixedRows.adjust(b,a,c)},preCreateLarge:twve.table.create,
create:function(b){var a=twve.table.preCreateLarge.apply(this);a.clear=function(){a.fixed_row=null;a.fixed_col=null;return a};var c=a.copyFrom;a.copyFrom=function(b){c.apply(this,arguments);a.fixed_row=b.fixed_row;a.fixed_col=b.fixed_col;return a};a.cellAtMouse=function(a){return twve.node.matches(a.target.parentNode,twve.table.fixedCell.getSelector())?a.target.parentNode:a.target};var e=a.is;a.is=function(a){return e.apply(this,arguments)||a&&twve.node.matches(a.parentNode,twve.table.fixedCell.getSelector())};
var f=a.isEditable;a.isEditable=function(b){var c=f.apply(this,arguments);if(!c){var d=twve.table.fixedCell.getSelector(),e=null;twve.node.matches(b,d)?e=b:(b=b.parentNode,twve.node.matches(b,d)&&(e=b));e&&(a.curNode=e,c=!0)}return c};var d=a.scrollIntoView;a.scrollIntoView=function(b){twve.node.matches(a.curNode,"th,td")&&(b=a.wrappers.tb);return d.call(this,b)};var n=a.editNeighbor;a.editNeighbor=function(){n.apply(this,arguments);a.scrollIntoView();return a};a.maxDim=function(){var b=a.direct_wrapper.dom;
twve.node.matches(b,twve.tiddlerSpan.twveSelector().include)&&(b=b.parentNode);var c=twve.object.create();c.width=twve.node.cssSize(config.options.txttwveTableMaxWidth,b);c.height=twve.node.cssSize(config.options.txttwveTableMaxHeight,b);return c};a.adjustDim=function(b){var c=a.maxDim(),d=twve.node.scrollBarWidth(a.wrappers.tb),e=!1;b.width+d>c.width?(b.height+=d,b.height>c.height?(b.height=c.height-d,b.width=c.width-d):b.width=c.width,e=!0):b.height+d>c.height&&(b.width+=d,b.width>c.width?(b.width=
c.width-d,b.height=c.height-d):b.height=c.height,e=!0);return e};a.oversize=function(b){if(!b)return a.dom.getAttribute("oversize");(b=a.adjustDim(b))?a.dom.setAttribute("oversize","true"):a.dom.removeAttribute("oversize");return b};a.getScrollPosition=function(){return twve.table.getScrollPosition(a.wrappers.tb)};a.setScrollPosition=function(b){var c=a.wrappers.tb;c.scrollLeft=b.leftRatio*c.scrollWidth;c.scrollTop=b.topRatio*c.scrollHeight;return b};a.getMargin=function(b){return twve.node.getMargin(a.dom,
b)-2};a.putFixedRows=function(b){b=a.wrappers.tb.scrollLeft;var c=a.wrappers.tb.scrollTop,d=null;a.fixedRows&&(d=window.getComputedStyle(a.dom),twve.node.setPosition(a.fixedRows.dom,a.dom.offsetLeft,a.dom.offsetTop+c-(1<c?twve.node.getMargin(a.dom,"top",d)+(config.browser.isIE?1:0):0)));a.fixedCols&&twve.node.setPosition(a.fixedCols.dom,a.dom.offsetLeft+b-(1<b?twve.node.getMargin(a.dom,"left",d):0),a.dom.offsetTop)};a.adjustFixedRows=function(){a.fixedRows&&twve.table.fixedRows.adjust(a.dom,a.wrappers.tb,
a.fixedRows.dom);a.fixedCols&&twve.table.fixedRows.adjust(a.dom,a.wrappers.tb,a.fixedCols.dom);return a};var m=null;a.wrapperResize=function(b,c){var d=a.wrappers,e=twve.table.getScrollPosition(d.tb);m.apply(this,arguments);if(a.oversize(d)){var f=0,h=0,g=a.maxDim(),n=twve.node.scrollBarWidth(a.wrappers.tb);d.width+n>=g.width?(b=(b||a.refbarV.getSize())+n,d.height+n<g.height?(c=(c||0)+a.getMargin("vertical")-1,f=-n):(h=c||0,c=0),twve.node.setDimension(d.cap,g.width-b),twve.node.setDimension(d.tb,
g.width-b,d.height-c)):d.height+n>=g.height&&(b||(b=0),f=-n,h=c||0,c=0,twve.node.setDimension(d.cap,d.width-b+f),twve.node.setDimension(d.tb,d.width-b+f+5,d.height));twve.node.setDimension(d.tbref,d.width+f+6,d.height+h);twve.node.setDimension(d.all,d.width+f+6,d.height+d.captionHeight()+h);a.setScrollPosition(e)}return d};a.scrolled=function(b){b=a.wrappers.tb;a.putRefBars(b.offsetLeft-b.scrollLeft,b.offsetTop-b.scrollTop);a.putFixedRows();if(!a.beingEdited()||!twve.node.matches(a.curNode,"th,td"))return a;
var c=twve.tiddler.getEditBox();if(c){var d=twve.node.box(twve.tiddler.cur_editable.getElement());b=d.left-(c[0].offsetLeft||c[0][0].offsetLeft)-1;for(var d=d.top-(c[0].offsetTop||c[0][0].offsetTop),e=0,f=c.length;e<f;e++)twve.node.shiftPosition(c[e],b,d);(c=twve.tiddler.getPreviewer())&&twve.node.shiftPosition(c,b,d)}return a};a.scroll=function(){var a=twve.tiddler.focusElem();if(a&&a.scrolled){var b=a.getScrollPosition();a.scrolled(b);if(!twve.table.wrapper_in_sync){twve.table.wrapper_in_sync=a.findWrappers();
for(var c=0,d=a.wrapper.length;c<d;c++){var e=a.renderedCopy(a.wrapper[c]);e&&e.setScrollPosition(b)}}++twve.table.sync_ndx==twve.table.wrapper_in_sync.length&&(twve.table.wrapper_in_sync=null,twve.table.sync_ndx=0)}};a.prepareFixedRows=function(){if(a.oversize()){a.wrappers.tb.addEventListener("scroll",a.scroll);var b=1*config.options.txttwveTableFixRows;a.fixedRows=0<b?twve.table.fixedRows.create(a,b):null;b=1*config.options.txttwveTableFixCols;a.fixedCols=0<b?twve.table.fixedColumns.create(a,b):
null;a.putFixedRows();a.adjustFixedRows()}else a.wrappers.tb.removeEventListener("scroll",a.scroll);return a};var g=a.blur;a.blur=function(){a.putFixedRows();return g.apply(this,arguments)};a.hijackWrapperResize=function(){a.wrappers.resize!=a.wrapperResize&&(m=a.wrappers.resize,a.wrappers.resize=a.wrapperResize);return a};var h=a.setElement;a.setElement=function(){h.apply(this,arguments);if(!a.wrappers)return a;a.hijackWrapperResize();a.prepareFixedRows();return a};var p=a.renderedCopy;a.renderedCopy=
function(){var a=p.apply(this,arguments);return a?a.hijackWrapperResize():null};var k=a.prepare;a.prepare=function(){if(!k.apply(this,arguments))return!1;a.prepareFixedRows();return!0};var l=a.refreshSelf;a.refreshSelf=function(){var b=a.getScrollPosition();l.apply(this,arguments);a.setScrollPosition(b);return a};var q=a.editText;a.editText=function(b){var c=a.curNode;if(c&&twve.node.matches(c,twve.table.fixedCell.getSelector())){var d=twve.table.cellPos(a.tiddler.text,a.start,a.end,twve.table.cellRowIndex(c),
twve.table.cellColIndex(c),twve.table.cellSpans(c));a.curNode=null;return q.call(this,b,a.getCellText(d),c)}return q.apply(this,arguments)};return a.created(b)}});
twve.table.fixedCell={getClass:function(){return"twveTfixedCell"},getSelector:function(){return"div."+twve.table.fixedCell.getClass()},adjust:function(b,a,c,e,f){c=window.getComputedStyle(a);var d=twve.node.getPadding(a,"left",c),n=twve.node.getPadding(a,"top",c);twve.node.getPadding(a,"right",c);var m=twve.node.getPadding(a,"bottom",c);twve.node.setPosition(b,a.offsetLeft+(-(config.browser.isOpera||config.browser.isChrome?0:d)+(config.browser.firefoxDate?1:0)-e),a.offsetTop+((config.browser.isIE||
config.browser.firefoxDate?-n:0)-f));e=a.clientWidth;a=a.clientHeight;twve.node.setDimension(b,e,a+m);b=b.firstChild;m=d;f=n;twve.node.setPosition(b,m,f);var g=c.getPropertyValue("text-align");-1<g.indexOf("center")?m=(e-b.offsetWidth)/2-d:-1<g.indexOf("right")&&(m=e-b.offsetWidth-d);g=c.getPropertyValue("vertical-align");-1<g.indexOf("middle")?f=(a-b.offsetHeight)/2:-1<g.indexOf("bottom")&&(f=a-b.offsetHeight);(m>d||f>n)&&twve.node.setPosition(b,m,f)},create:function(b,a,c,e,f){if(f)d=twve.node.create(b);
else{var d=twve.node.create("div");d.dom.classList.add(twve.table.fixedCell.getClass());d.dom.appendChild(document.createElement("span"));d.dom.style.position="absolute";d.dom.firstChild.style.position="absolute";d.dom.style.display="table-cell"}d.setTableCell=function(a,b,c,e){var f=window.getComputedStyle(a);e=d.dom;twve.node.copyMargin(e,a,f);twve.node.copyBorder(e,a,f);"0px"==e.style.borderLeftWidth&&(e.style.borderLeftWidth="1px");"0px"==e.style.borderRightWidth&&(e.style.borderRightWidth="1px");
twve.node.copyFontColor(e,a,0==b);a.classList.contains("noedit")&&e.classList.add("noedit");e.setAttribute("col",c);(c=a.getAttribute("rowspan"))&&e.setAttribute("rowspan",c);(c=a.getAttribute("colspan"))&&e.setAttribute("colspan",c);e=e.firstChild;e.innerHTML=a.innerHTML;0==b&&twve.table.sortingEnabled()&&(a=e.querySelector("span"))&&a.classList.contains("hidden")&&twve.node.hide(a);return d};return f?d:d.setTableCell(b,a,c,e)}};
twve.table.fixedRow={getClass:function(){return"twveTfixedRow"},getSelector:function(){return"div."+twve.table.fixedRow.getClass()},adjust:function(b,a,c,e){var f=b.querySelectorAll(twve.table.fixedCell.getSelector());twve.node.setPosition(b,a.offsetLeft,a.offsetTop-(config.browser.isIE?0:0<c?1:0));b=a.querySelectorAll("th,td");c=0;for(var d=f.length;c<d;c++)twve.table.fixedCell.adjust(f[c],b[c],e,a.offsetLeft,a.offsetTop)},create:function(b,a,c,e,f){var d;f?d=twve.node.create(b):(d=twve.node.create("div"),
d.dom.classList.add(twve.table.fixedRow.getClass()),d.dom.style.position="absolute",d.dom.style.display="inline-block");d.cells=[];d.setTableRow=function(a,b,c,e,f){var k;if(f){k=a.querySelectorAll(twve.table.fixedCell.getSelector());c=k.length;d.cells.length=c;for(var l=0;l<c;l++)d.cells[l]=twve.table.fixedCell.create(k[l],b,l,e,f)}else{k=window.getComputedStyle(a);twve.node.copyMargin(d.dom,a,k);twve.node.copyBorder(d.dom,a,k);twve.node.copyPadding(d.dom,a,k);d.dom.style.backgroundColor=twve.node.bgc(a,
k);d.dom.setAttribute("row",b);k=a.querySelectorAll("th,td");if(!c||c>k.length)c=k.length;d.cells.length=c;for(var q=0,l=0;l<c;l++)d.cells[l]=twve.table.fixedCell.create(k[l],b,l,e,f),q+=k[l].offsetWidth,d.dom.appendChild(d.cells[l].dom);twve.node.setDimension(d.dom,q,a.clientHeight)}return d};return d.setTableRow(b,a,c,e,f)}};
twve.table.fixedRows={getClass:function(){return"twveTfixedRows"},getSelector:function(){return"div."+twve.table.fixedRows.getClass()},adjust:function(b,a,c){c=c.querySelectorAll(twve.table.fixedRow.getSelector());a=twve.table.getScrollPosition(a||twve.table.getWrapper(b,"tb"));b=b.querySelectorAll("tr");for(var e=0,f=c.length;e<f;e++)twve.table.fixedRow.adjust(c[e],b[e],e,a)},create:function(b,a,c){var e=twve.node.create();e.rows=[];e.setTable=function(a,b,c){if(!a.oversize())return e;var m=a.getScrollPosition(),
g=a.wrappers,h=g.tb.querySelector(twve.table.fixedRows.getSelector());if(h){e.dom=h;g=h.querySelectorAll(twve.table.fixedRow.getSelector());e.rows.length=g.length;b=0;for(var p=e.rows.length;b<p;b++)e.rows[b]=twve.table.fixedRow.create(g[b],b,c,m,!0)}else{e.dom=h=document.createElement("div");h.classList.add(twve.table.fixedRows.getClass());h.style.position="absolute";h.style.overflow="hidden";p=window.getComputedStyle(a.dom);h.style.borderTop=twve.node.getBorder(a.dom,"top",p);h.style.borderLeft=
twve.node.getBorder(a.dom,"left",p);h.style.borderRight=twve.node.getBorder(a.dom,"right",p);h.style.borderBottom=twve.node.getBorder(a.dom,"bottom",p);g.tb.appendChild(h);var g=a.dom.querySelectorAll("tr"),p=e.rows.length=0<b?b:g.length,k=0;for(b=0;b<p;b++)e.rows[b]=twve.table.fixedRow.create(g[b],b,c,m),h.appendChild(e.rows[b].dom),k+=g[b].offsetHeight;twve.node.setDimension(h,a.dom.clientWidth,k)}a.scrolled(m);return e};return b?e.setTable(b,a,c):e}};
twve.table.fixedColumns={getClass:function(){return"twveTfixedColumn"},getSelector:function(){return"div."+twve.table.fixedColumns.getClass()},create:function(b,a){var c=twve.table.fixedRows.create(),e=c.setTable;c.setTable=function(a){var b=e.apply(this,arguments);c.dom.style.borderBottom=twve.node.getBorder(a.dom,"bottom");c.dom.classList.remove(twve.table.fixedRows.getClass());c.dom.classList.add(twve.table.fixedColumns.getClass());return b};return b?c.setTable(b,null,a):c}};
//}}}
// @@
This tiddler gives detailed information about the ''twve.tags'', an object representing wiki tags in ~TiddlyWiki.
/***
|editable|k
|''Name:''|twve.tcalc|
|''Description:''|A simple table calculator (spreadsheet) for ~TiddlyWiki|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.tcalc.min / http://twve.tiddlyspace.com/#twve.tcalc.min <br>* (regular) http://twve.tiddlyspot.com/#twve.tcalc / http://twve.tiddlyspace.com/#twve.tcalc|
|''Type:''|plugin|
|''Version:''|3.2.6|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery<br>2014/05/13 released 3.0.0 <br>2013/11/29 reformed <br>2013/06/02 released 1.0.0<br>2012/10/19 released 0.7.0<br>2012/09/04 started from ~TableCalculator 0.6.14|
|''License:''|MIT|
|''Core Version:''|2.7.0|
|''Needs to have:''|twve.core, twve.table|
!!Features
* Calculate numeric values in table cells.
* Cell references follow the ~OpenOffice.calc/Excel syntax.
** Column index starts from """A""" and ends at """ZZ""".
** Row index starts from 0 and has virtually no upper limit.
* ''Auto adjustment of cell references upon''
** __insertion/deletion of rows/columns__;
** __sorting with SortableGridPlugin or TableSortingPlugin__.
*** Block references such as """C4:H10""" are auto-adjusted only upon insertion/deletion of rows/columns, NOT upon sorting.
* Support several forms of decimal mark and thousands separation.
* Support user-defined functions written in Javascript.
** To define your own functions, see [[twve.tcalc--Defining your own functions]] or [[External link of that tiddler|http://twtable.tiddlyspace.com/#%5B%5BTWVE.tcalc--Defining%20your%20own%20functions%5D%5D]] for details.
** Several pre-defined functions are listed in section """Usage""" below.
!!Usage
* Add class """spreadsheet""" to a table to activate calculations on that table, or check the option """chktwveTcalcAllTables""" to activate calculations on all tables.
* At the cell to store calculation results, __starts with an equal sign """="""__, as in ~OpenOffice.calc or Excel, followed by cell references/Javascript expressions/user-defined functions for calculation. For example,
** """=Math.sqrt(3)""" and """=Math.cos(Math.PI)""" are valid Javascript statements;
** """=A3+B4""" and """=sum(A6:D10)""" are ~OpenOffice.calc/Excel-like expressions.
** See below for a brief list of user-defined functions.
** @@color:red;Starting v3.0.0 this plugin relaxes the """starts with an equal sign""" requirement and supports """in-text calculations""".@@
* @@color:blue;Cells for calculation can be specified in one of the following formats:@@
** """func(A3:H6)""": calculate over a block of consecutive cells
** """func(B3, H5, AC9,...)""": calculate over separated individual cells
** """func(AA3:CD5, B5, Z7...)""": a mixture of the two cases
* Use the following options to specify the decimal mark and thousands separation.
** Option txtTWVEtcalcDecimalMark specifies the default decimal mark.
** Option txtTWVEtcalcThousandSeparator specifies the default thousands separator.
** Option chktwveTcalcThousandSeparated toggles application of thousands separation.
* Pre-defined functions now include:
** sum
*** calculates the sum of cells
** product
*** calculates the product of cells
** average/mean
*** calculates the average value of cells
** count
*** counts the number of __numeric__ cells
*** supports conditional counting, see revision
**** Syntax: """=COUNT([condition], cell references)"""
***** condition is optional and if given must be quoted with single or double quotes
***** use the percentage symbol """%""" as a placeholder for the values to test
**** Example, """=COUNT('%>60 && %<70', A1:A90)""" returns the number of numerical values greater than 60 and less than 70 among cells between A1 and A90, inclusively.
** counta
*** counts the number of __non-empty__ cells
** concat
*** joins the contents of cells
** isNumeric
*** tests whether a cell contains a numerical expression
** today()/now()
*** Current date and time. See Javascript references for details.
** days(date1, date2, num_dec)
*** Number of days (and time) from date1 to date2. The 3^^rd^^ argument num_dec specifies the number of digits after decimal point.
**** If the 3^^rd^^ argument is given, this function returns """number of days""" such as """3.2""", with the number of digits after decimal point matching the 3^^rd^^ argument.
**** If the 3^^rd^^ argument is missing, the function returns days and time difference, such as """1D 00:10:05""".
** round(value, num_dec)
*** Returns the rounded result of value, up to num_dec digits after decimal point raised to the nth power.
** exp / ln / log10 / log
*** Returns the exponential / natural logarithm / base-10 logarithm / arbitrary based logarithm of a number.
** random(factor)
*** Returns a random number between 0 and factor, if factor is given. Otherwise returns a random number between 0 and 1.
** column/col
*** Returns the column index (number). Without an argument this function returns the index of the current column, with a cell reference as its argument, such as COLUMN(~AX3)/COL(~AX3), it returns the column index of that cell.
** columna/cola
*** Returns the column reference (letter). Without an argument this function returns the reference of the current column, with a cell reference as its argument, such as COLUMN(~AX3)/COL(~AX3), it returns the column reference of that cell.
** stderr/stdev
*** Calculates the standard deviation of a set of numbers. @@color:red;This function is not yet fully tested.@@
** if(condition, statement_if_true, statement_if_false)
*** Works the same way as the IF() function in MS Excel, except that the statements can include wiki style text.
**** For example, IF ($G1>=F1, color:blue;$~G1-F1, color:red;$~G1-F1) results in a @@color:blue;positive@@ or a @@color:red;negative@@ value of """|$G1-F1|""".
!!Options
Look for [[twve.tcalc Options]] in the Options Panel.
!!Example
[[twve.tcalc Examples]]
!!ToDo
!!!Revision history
!!!! 2016/04/30 [3.2.6]
* Added a simple parser to carry out the calculations.
** Now the use of """eval()""" is restricted to only JS Math functions.
** This parser currently handles only simple comparison and logical operations, though it carries out commonly seen math calculations.
** This parser supports conditional assignment through the ternary operator {{{?:}}}.
* Saves options in SystemSettings tiddler.
** If you disable autoSave option, you will need to manually save the tiddler after changing a ''twve'' option.
* Starts using strict mode.
* Added function
** TRIM() -- """=TRIM(B3[,how])""" will remove the leading and/or ending white spaces of the content in cell B3. The optional argument, if given, can be either "LEFT", "RIGHT", or "BOTH". It is assumed "BOTH" if missing.
** DAYOFWEEK(date) -- Returns a number from 0 (Sun) to 6 (Sat), indicating the day of week of a given date.
* Modified internal data structure.
* Bug fix
** for exchanging """row()""" and """col()""" functions after transposition;
** for Chinese date string handling with IE;
** for expression parser.
!!!! 2015/11/06 [3.2.5]
* Added functions
** CELL(row,col) -- retrieves the content of a table cell at (row, col), where row is the 0-based index of a table row, col is either the 0-based column index or its letter representation.
** LEFT(source,len) -- extracts the left part of a string.
** MID(source,start,len) -- extracts the middle part of a string.
** RIGHT(source,len) -- extracts the right part of a string.
** SUMCOL([col[,how]]) -- sums up numerical values in the column with index col (this column if omitted), excluding the cell containing this formula. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** SUMROW([row[,how]]) -- sums up numerical values in the row with index row (this row if omitted), excluding the cell containing this formula. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** SUMCOLWITH([COL[,preceding[,following[,how]]]]) -- sums up numerical values in the column with index col (this column if omitted), excluding the cell containing this formula, with signature text immediately preceding and/or following the value. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** SUMROWWITH([ROW[,preceding[,following[,how]]]]) -- sums up numerical values in the row with index row (this row if omitted), excluding the cell containing this formula, with optional signature text immediately preceding and/or following the value. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTCOL([col[,how]]) -- count the number of numerical values in the column with index col (this column if omitted), excluding this cell. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTROW([row[,how]]) -- count the number of numerical values in the row with index row (this row if omitted), excluding this cell. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTCOLA([col[,how]) -- count the number of non-empty values in the column with index col (this column if omitted), excluding this cell. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.
** COUNTROWA([row[,how]]) -- count the number of non-empty values in the row with index row (this row if omitted), excluding this cell. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.
** DATEADD(date,dy,dm,dd[,format])
*** Calculate a new date shifted by dy/dm/dd with respect to date.
*** date must be given.
*** At least one of the (dy, dm, dd) must be specified.
*** Optional format can be given in the usual way, such as mm/dd, yy/mm/dd, yyyy/mm/dd, etc.
** DATE(y,m,d[,format])
*** Specify a date of y/m/d.
*** At least one of the (y, m, d) must be specified, missing one(s) are taken as the corresponding part(s) of today.
**** For example, if year is missing while month and date are given, the year part will be taken as this year.
*** Optional format can be given in the usual way, such as mm/dd, yy/mm/dd, yyyy/mm/dd, etc.
* Changed the reference auto-adjustment behavior upon cut-and-pasting.
** Previously this plugin automatically adjusts the cell reference upon all cases of cut-and-pasting, partial or total cut. Now it adjusts the reference only upon a total cut-and-pasting.
*** A total cut means the source cell has been emptied, nothing is left behind, while a partial cut leaves something in the source cell.
* Merged some codes shared between functions.
* Bug fix
** for finding the correct reference of a column spanned cell;
** for evaluation of function arguments;
** for specifying style text in the function arguments;
** for minor stuffs.
!!!! 2015/06/15 [3.2.4]
* Supports cross table calculations.
** """Table0!A1""" refers to the cell """A1""" in the table with index """0""" (the first table in the tiddler).
* Added support for parts-per notations, such as
** % (1/100), ‰ (1/1000)
** ppm (1/1e6), ppb (1/1e9), ppt (1/1e12), ppq (1/1e15)
* Added support for time addition/subtraction in the format of hh:mm:ss or hh:mm or mm:ss.
** One can now easily calculate the sum or difference of times.
*** For example, ```=07:21-07:14``` will give the result of ```00:07```, while ```=07:21+9``` will give the result ```07:30```.
** Mixing time strings with different lengths is possible.
*** For example, both ```=07:21:35 + 01:02``` and its reversed expression ```01:02 + 07:21:35``` yield the same result ```07:22:37```.
** When only two parts are present, it could mean hh:mm or mm:ss. This function does not assume which case, it simply returns an array of two numbers and leaves it to the caller function to decide.
* Added function
** LENGTH/LEN(cell) -- Returns the content length (in characters) in a table cell.
** VALUE/VAL(cell,n) -- Returns the nth numerical value in a table cell.
*** Numerical digits separated by any number of non-numerals are considered different values, unless 1) thousands separation rules are applied, and 2) those non-numerals are valid thousands separators.
** VALUEWITH/VALWITH(cell,preceding[,following]) -- Returns in a table cell the first numerical value that matches one of the following situations:
*** If only preceding is given, those immediately preceded by preceding;
*** If only following is given, those immediately followed by following;
*** If both preceding and following are given, those immediately preceded by the preceding and immediately followed by the following.
** SUMIN(cell[,n1,n2,...]) -- Sum up in a table cell all or part of the numerical values, if there are more than one. See description in the definition for more details.
** SUMWITH(cell,preceding[,following]) -- Sum up in a table cell all the numerical values that match one of the following situations:
*** If only preceding is given, those immediately preceded by preceding;
*** If only following is given, those immediately followed by following;
*** If both preceding and following are given, those immediately preceded by the preceding and immediately followed by the following.
** DOT (cell1, cell2) -- dot product of two cells, each of which must contain a vector of the form (v1, v2, v3, ...).
* Bug fix
** for nested function calls;
*** Wrap the function stored data into a "data" property of that function.
*** Push the function data onto a call stack before evaluating its arguments (during which nested calls may happen), and pop it back afterwards.
** for multiplications.
!!!! 2015/03/23 [3.2.3]
* Added functions
** COMBIN(n,k) that calculates the number of combination (in any order) of choosing k objects out of a set of n objects.
** AND(condtion1, condition2[, condition3, ...]) that performs logical AND operations. There must be at least two arguments for such operations.
** OR(condtion1, condition2[, condition3, ...]) that performs logical OR operations. There must be at least two arguments for such operations (although practically one argument should do as well).
** XOR(condtion1, condition2) that performs logical XOR operations. There must be exactly two arguments for such operations.
* Bug fix
** for evaluating scientific expressions;
** for reference adjustment upon cut-and-pasting (copy-and-pasting is fine);
** for whole table recalculation with cells referring to a later cell;
*** The calculation codes follow the order of cell indexes: from top-left to bottom-right in a left-right and up-down manner. A later cell is one that is to the right in the same row, or in a row below the current cell. Since a later cell is recalculated after the current one, its content may not be updated in time to give the correct results. This version fixed such cases.
** for more precise function name identification.
!!!! 2015/02/13 [3.2.2]
* Replaced regular expression with string operations whenever easy, hoping to save some time.
* Disabled the current version of partial recalculation codes.
** It happens, in some extreme cases, that this version can be 50 times slower than a whole table recalculation.
* Bug fix
** for handling expressions of nested parentheses;
** for visually consistent cell references with spanned cells;
*** Some of the calculation codes are not using the visually consistent cell references, fixed in this version.
**** The cell references -- indexes of table cells -- are not consistent with what we see when there are spanned cells, which can easily cause confusion to the users when writing a calculation expression referring to a spanned cell.
** for correct reference adjustment upon insertion/deletion of rows/columns;
** for THISWEEK function to correctly test a week including the end of a year and the beginning of the next;
** for retrieving multi-lined cell content.
!!!! 2014/12/22 [3.2.1]
* Defined new functions
** THISWEEK(date or date1~date2) -- beta
*** This function tests if a given date is in this week, or a range of dates is partly or fully in this week.
*** This is designed for a schedule table that has its rows as the weeks over a period of time. This function can be used to mark the week (the row) that includes today.
** THISDAYOFWEEK(day_of_week) -- beta
*** This function tests if a given day of week is the same as that of today.
*** This is designed for a schedule table that has its columns as the days of week. This function can be used to mark the day of week (the column) that matches today.
** THISHOUR(time or time1~time2) -- beta
*** This function tests if a given time is of the same hour as now.
*** This is designed for a schedule table that has its rows as the hours of day. This function can be used to mark the hour of day (the row) that matches now.
** MONTHS(date1,date2) -- beta
*** This functions calculates the difference in months between two dates.
* Modified existing functions
** DAYS(date1,date2)
*** This function was defined long time ago, which calculated the difference in days between two dates. Now it is modified to include the time difference between the two dates.
!!!! 2014/11/21 [3.2.0]
* Removed jQuery dependencies for migrating to ~TW5.
** Could be incompatible with earlier browsers.
* Possible to mix Javascript Math functions and ''twve.tcalc'' functions.
** For example, {{{=Math.sqrt(random())}}} is using the result of a ''twve.tcalc'' function {{{random()}}} as the argument of a JS math function {{{Math.sqrt()}}}. Or we can do the opposite, {{{=sqrt(Math.random())}}}.
* Better in-text calculations: (@@color:red;Experimental!@@)
** the assignment sign can be anywhere in the text;
** it is possible to calculate multiple expressions in one piece of text.
*** For example, if a table cell contains the text {{{Group A is =50+40 and group B is =70+60}}}, which has two expressions, namely {{{=50+40}}} and {{{=70+60}}}. They will both be evaluated (separately) and the result would be @@color:blue;Group A is 90 and group B is 130.@@
* Bug fixes
** for arithmetic expressions including empty cells.
!!!! 2014/08/03 [3.1.0]
* Started to remove jQuery dependencies for migrating to TW5.
!!!! 2014/06/01 [3.0.3]
* Added function
** COUNTDOWN(//start//[,//end//[,//interval//]]) -- Counting down from //start// (optional), and stops at //end// (optional), at //interval// (optional).
*** The //start// can be a number, a date string, a text string, or a function.
**** For numbers and date strings the countdown works as we do in the New Year's Eve.
**** For text strings, the countdown works as numbers except that the result is interpreted as characters again.
**** For text strings it works as an array of characters, the countdown takes one character at a time, starting from the last one, treat it as a number and counts down, and moves to its previous neighbor when it reaches the ASCII 0.
**** For functions they should return one of the above types to start with.
*** The //end// (optional), if given, should be of the same type as //start//. If omitted, however, the countdown stops at 0 (number), now (date), or empty (string).
*** The //interval// (optional), if given, is a number to specify the step of countdown. It should be of the same type as //start//.
**** For numbers, characters and string, this should be a numerical value. Default to 1 when omitted.
**** For dates, this should be a time interval specified either as a number (in seconds), or as a string in the format HH:MM:SS.sss. Default to 1 second if omitted.
!!!! 2014/05/23 [3.0.2]
* Bug fix
** for re-calculation of Javascript built-in functions.
* Added functions
** EXP -- exponential
** LN -- natural logarithm (base e=2.71828...);
** LOG10 -- base-10 logarithm;
** LOG -- logarithm of an arbitrary base.
!!!! 2014/05/14 [3.0.1]
* Fixed a small bug that stops calculations when the tiddler is just loaded.
!!!! 2014/05/13 [3.0.0]
* New features
** Output of {{{DAYS()}}} function now includes days and time.
** @@color:blue:;Timers for periodic recalculations.@@
*** Functions such as {{{DAYS()}}} may be good to invoke in a periodic manner.
* @@color:red;Experimental feature@@
** @@color:blue;In-text calculations in a table cell@@
** Check/Uncheck option <<option chktwveTcalcInTextCalc>> to enable/disable this feature.
*** Previous versions require a table cell to start with an equal sign (=) if it contains formulas to calculate. Starting v3.0.0 this plugin relaxes the requirement and supports in-text calculations: the formulas can be anywhere within the cell content (still they need to start with an equal sign). For example,
**** This content {{{5000 INR (=round(5000*0.48) NTD)}}} generates this output {{{5000 INR (2400 NTD)}}} in a table cell, the formula {{{=round(5000*0.48)}}} would be evaluated to produce {{{2400}}}.
* Added {{{MAX}}}, {{{MIN}}}, and {{{SQRT}}} functions.
* Bug fixes
** for partial re-calculation;
*** virtually unlimited levels of implicit cell reference
*** block references (A1:C4, for example) now included in partial re-calculation
** for re-calculation upon table transposition
** for locating the correct cell when there are column-spanned cells;
*** A column-spanned cell is logically one single cell but physically occupies more than one columns in a table. Special care needs to be taken to find the correct content;
** for retaining cell alignment after function evaluation;
** for conditional {{{COUNT}}} function.
!!!! 2014/01/18
* Collected from [[TWtcalc|http://twtable.tiddlyspace.com/#TWtcalc.1.0.10]], adapted to the new code structure.
!!!! 2013/12/xx [1.0.10]
* Bug fixes
** for table calculations in the previewer;
** for reference adjustment upon sorting.
*** This turns out to be the most time-consuming part in the sorting process. Should think of some way to improve.
!!!! For earlier history please see [[TWtcalc.1.0.10]] or [[its external link|http://twtable.tiddlyspace.com/#TWtcalc.1.0.10]].
!!Code
!!!The very beginning
***/
//{{{
(function(){
"use strict";
if ( typeof twve.table == 'undefined' ) {
alert('twve.tcalc: This plugin needs twve.table to function properly. Please install twve.table before this one.');
}
//}}}
/***
!!! Version information
***/
//{{{
version.extensions.twve.tcalc = {
major: 3, minor: 2, revision: 6,
date: new Date('2016/04/30')
};
//}}}
/***
!!! Macro for initialization and option settings.
***/
//{{{
config.macros.twveTcalcOptions = {
init : function () {
// twve.tiddler methods
twve.tcalc.preReadOnly = twve.tiddler.readOnly;
twve.tiddler.readOnly = twve.tcalc.readOnly;
twve.tcalc.preGetOptionsMenu = twve.tiddler.getOptionsMenu;
twve.tiddler.getOptionsMenu = twve.tcalc.getOptionsMenu;
// twve.node methods
twve.tcalc.preWikify = twve.node.wikify;
twve.node.wikify = twve.tcalc.wikify;
// twve.table methods
twve.tcalc.preGetTableMenu = twve.table.getTableMenu;
twve.table.getTableMenu = twve.tcalc.getTableMenu;
twve.tcalc.preTableCreate = twve.table.create;
twve.table.create = twve.tcalc.tableCreate;
twve.table.colRef = twve.tcalc.colRef;
// twve.wrapper methods
twve.tcalc.prePrepareElements = twve.wrapper.prepareElements;
twve.wrapper.prepareElements = twve.tcalc.prepareElements;
twve.checkOption(
'chktwveTcalcEnabled',
(config.options.chkTWtcalcEnabled || true)+''
);
twve.checkOption(
'chktwveTcalcAllTables',
(config.options.chkTWtcalcAllTables || false)+''
);
twve.checkOption('chktwveTcalcInTextCalc','false');
twve.checkOption(
'chktwveTcalcThousandSeparated',
(config.options.chkTWtcalcThousandSeparated ||
config.options.chkTCalcThousandSeparated || false)+''
);
twve.checkOption(
'txttwveTcalcThousandSeparator',
(config.options.txtTWtcalcThousandSeparator ||
config.options.txtTCalcThousandSeparator || ',')
);
twve.checkOption(
'txttwveTcalcDecimalMark',
(config.options.txtTWtcalcDecimalMark ||
config.options.txtTCalcDecimalMark || '.')
);
twve.checkOption(
'chktwveTcalcDebugMode',
(config.options.chkTWtcalcDebugMode ||
config.options.chkTCalcDebugMode || false)
);
merge(config.optionsDesc,{
chktwveTcalcEnabled:"Enable ''twve.tcalc''",
chktwveTcalcAllTables:'Calculate all tables. Otherwise only calculate tables with class "spreadsheet"',
chktwveTcalcInTextCalc:'(@@color:red;Experimental@@) In text calculation. There can be multiple expressions in a statement.',
chktwveTcalcThousandSeparated:'Apply thousands separation on numerical results. Default is false.',
txttwveTcalcThousandSeparator:'Thousand separator. Default is comma (,).',
txttwveTcalcDecimalMark:'Decimal mark. Default is period (.).',
chktwveTcalcDebugMode:'Enter debug mode to show error/exception messages. Default is false.'
});
// Register editable elements
if (twve.span)
twve.tiddler.registerElement(twve.span.tcalc);
// prepare options panel
var optionsTitle = 'twve.tcalc Options';
var txt = config.shadowTiddlers['OptionsPanel'];
var twvecoreopt = '[[twve.core Options';
var p = txt.indexOf(twvecoreopt);
if ( p >= 0 ) {
p = txt.indexOf(']]\n',p+2)+3;
config.shadowTiddlers['OptionsPanel']=
txt.substring(0,p)
+'[['+optionsTitle+'|'+optionsTitle+']]\n'
+txt.substring(p);
}
merge(config.shadowTiddlers,{
'twve.tcalc Options':'<<twveTcalcOptions>>'
});
},
order : {
chktwveTcalcEnabled:0,
chktwveTcalcAllTables:1,
chktwveTcalcThousandSeparated:2,
txttwveTcalcThousandSeparator:3,
txttwveTcalcDecimalMark:4,
chktwveTcalcDebugMode:5
},
handler : function(place) {
// Collect all the twve.tcalc options for users to play with.
config.macros.twveCoreOptions.showOptionsTable(
place,
"''twve.tcalc'' Options",
'twveTcalc',
config.macros.twveTcalcOptions.order
);
}
};
//}}}
/***
!!! twve.tags.nested
***/
//{{{
if ( twve.tags.nested === undefined )
twve.tags.nested = {
//}}}
/***
!!!! twve.tags.nested.matchedCloseTag
***/
//{{{
matchedCloseTag : function(txt,start,open,close){
if ( typeof start === 'number' ) {
start = twve.position.create(start);
if ( typeof open ==='string' )
start.matched = open;
} else if ( start.ndx === undefined ) {
start = twve.position.create(0);
if ( typeof open ==='string' )
start.matched = open;
}
return twve.tags.nested.create(open,close)
.matchedCloseTag(txt,start);
},
//}}}
/***
!!!! twve.tags.nested.create
***/
//{{{
create : function(src,close){
var tags = twve.tags.create(src,close);
// clone
tags.clone = function(){
// Required, for this object has no markupTags() method.
return twve.tags.nested.create();
};
//}}}
/***
!!!!! tags.matchedCloseTag
***/
//{{{
tags.matchedCloseTag = function(txt,start){
var remained = 1;
var txtlen = txt.length;
var end = twve.position.create(start.ndx);
do {
// Look for next closing tag.
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
// Look for next opening tag.
start = twve.text.indexOf(txt,tags.open,start);
if ( start.ndx == -1 ) {
// No more opening but still closing tags.
// Check for remaining closing tags.
start.ndx = end.ndx;
do {
if ( --remained == 0 ) return end;
end.ndx += end.matched.length;
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
} while ( true );
}
if ( end.ndx == start.ndx ) {
if ( --remained == 0 ) {
// This situation is possible for elements that
// must start from the beginning of a line and
// end at a line break, such as list items.
return end;
}
start.ndx += start.matched.length;
end.ndx += end.matched.length;
} else if ( end.ndx < start.ndx ) {
// The next closing tag is before the next opening tag.
do {
// If this closing tag is the matched one, return
// its position info.
if ( --remained == 0 ) return end;
// Otherwise look for all closing tags before the
// next opening tag.
end.ndx += end.matched.length;
end = twve.text.indexOf(txt,tags.close,end);
if ( end.ndx == -1 )
// No more closing tags, do the last search.
return tags.lastCloseTag(txt,end,txtlen);
} while ( end.ndx < start.ndx );
// The matched closing tag is not found yet, add one
// more and go on.
++remained;
start.ndx += start.matched.length;
end.ndx = start.ndx;
} else { // end.ndx > start.ndx
// The next opening tag is before the next closing
// tag. Look for all opening tags before the next
// closing tag.
do {
++remained;
start.ndx += start.matched.length;
start = twve.text.indexOf(txt,tags.open,start);
} while ( start.ndx > -1 && start.ndx < end.ndx );
--remained;
end.ndx += end.matched.length;
start.ndx = end.ndx;
}
} while (true);
};
//}}}
/***
!!!!! End of tags
***/
//{{{
return tags;
}
};
//}}}
/***
!!! twve.url
***/
//{{{
twve.url = {
//}}}
/***
!!!! twve.url.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
['http://', 'https://', 'ftp://', 'file://'],
['[[', ']]', ', ', '. ', ' ']
);
// clone
tags.clone = function(){
return twve.url.markupTags();
};
// exactCloseTag
tags.exactCloseTag = function(){
return tags;
};
/*
// encloses
var preEncloses = tags.encloses;
tags.encloses = function(txt,ndx,start,end){
var close = preEncloses.apply(this,arguments);
if ( ! close ) {
var last = end ? end.ndx : txt.length;
var ch = txt.charAt(last-1);
switch(ch){
case '.' :
case ',' :
return twve.position.create(last,ch);
}
}
return close;
};
*/
/*
// locateNext
tags.locateNext = function(txt,start,end){
tags.exgopen.lastIndex = start.ndx;
var match = tags.exgopen.exec(txt);
if ( match ) {
start.ndx = match.index;
start.matched = match[0];
end.ndx = match.index+match[0].length;
tags.exgclose.lastIndex = end.ndx;
match = tags.exgclose.exec(txt);
if ( match ) {
end.ndx += match[0].length;
end.matched = match[0];
}
return true;
} else {
start.ndx = -1; start.matched = '';
end.ndx = -1; end.matched = '';
return false;
}
};
*/
/*
// encloses
var preEncloses = tags.encloses;
tags.encloses = function(txt,ndx,start,end){
if ( twve.core ) return preEncloses.apply(this,arguments);
tags.exgopen = /\b(http|https|file):\/{2,3}/ig;
tags.exgclose = /(\[\[|\]\]|\b)/ig; // for TiddlyWiki syntax
// tags.exgclose = /(\S*)\b/ig // for regular HTML syntax
start = twve.position.create(start || 0);
end = twve.position.create(end || txt.length);
while ( tags.locateNext(txt,start,end) ) {
if ( start.ndx > ndx ) return false;
if ( ndx < end.ndx ) return true;
start.ndx = end.ndx;
}
return false;
};
*/
return tags;
},
//}}}
/***
!!!! twve.url.getTarget
***/
//{{{
getTarget : function(txt,start,end,tags){
if ( !start || start.ndx===undefined )
start = twve.position.create(start);
else start.ensureValid();
if ( ! tags ) tags = twve.url.markupTags();
txt = txt.toLowerCase();
twve.text.indexOf(txt,tags.open,start);
if ( start.ndx > -1 ) {
if ( !end ) end = twve.position.create(start);
else end.ndx = start.ndx;
twve.text.indexOf(txt,tags.close,end);
if ( end.ndx < 0 ) end.ndx = txt.length;
return txt.substring(start.ndx,end.ndx);
}
return '';
},
//}}}
/***
!!!! twve.url.create
***/
//{{{
create : function(target,start){
var url = twve.element.create();
url.tags = twve.url.markupTags();
// locate
//url.locate = function(text){
// return url.tags.locateNext(text,url.start,url.end);
//};
url.setTarget = function(target,start){
var prev = url.target;
if ( target ) {
// set the target
url.start.ensureValid(start);
url.target = twve.url.getTarget(
target,url.start,url.end,url.tags
);
} else
url.target = null;
return prev;
};
// getTarget
url.getTarget = function(){
return url.target;
};
// waitForContent
url.waitForContent = function(){
if(url.xmlhttp.readyState==4 && url.xmlhttp.status==200){
clearInterval(url.pid);
if ( url.callback )
url.callback(url.xmlhttp.responseText);
return url.xmlhttp.responseText;
}
return ('Waiting for response from '+url.target);
};
// getContent
url.getContent = function(callback){
if ( ! url.target ) return null;
url.xmlhttp = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
if ( ! url.xmlhttp ) return null;
url.xmlhttp.open('GET',url.target,callback);
try{url.xmlhttp.send();}
catch(e){console.log(e);}
if ( callback ) {
url.callback = callback;
url.pid = setInterval(url.waitForContent, 50);
} else
return url.waitForContent();
};
// postData
url.postData = function(){
};
// If no target, or a valid target is given, return this url.
// Otherwise returns null (in cases of invalid target).
return (!target || url.setTarget(target,start)) ? url : null;
}
};
if(twve.span){
//}}}
/***
!!! twve.span.tcalc
***/
//{{{
twve.span.tcalc = {
//}}}
/***
!!!! twve.span.tcalc.twveSelector
***/
//{{{
enableEdit : true,
twveSelector : function(selector){
// This is the required method for an editable object that
// should/could
// 1. (required) add the selector to include
// 2. (optional) add the selector to exclude
if ( ! selector ) selector = twve.selector.create();
return selector.includeSelector('span.tcalc');
},
//}}}
/***
!!!! twve.span.tcalc.is
***/
//{{{
is : function(node,selector){
return twve.node.matches(
node,twve.span.tcalc.twveSelector(selector).include
);
},
//}}}
/***
!!!! twve.span.tcalc.markupTags
***/
//{{{
markupTags : function(){
var tags = twve.tags.create(
'=',
''
);
//tags.matchedCloseTag = function(txt,pos,txtlen,inactiveTags){
//twve.tcalc.evaluate(txt,pos.ndx+1,false,true,inactiveTags)
//};
return tags;
},
//}}}
/***
!!!! twve.span.tcalc.create
***/
//{{{
create : function(src,txt,start,dir){
if ( ! config.options.chktwveTcalcInTextCalc ) return null;
var span = twve.element.create();
// markupTags
span.markupTags = function(){
// Optional, may save a tiny bit of time.
return twve.span.tcalc.markupTags();
};
// clone
span.clone = function(){
// Optional, may save a tiny bit of time.
return twve.span.tcalc.create(span);
};
// twveSelector
span.twveSelector = function(){
// Optional, may save a tiny bit of time.
return twve.span.tcalc.twveSelector();
};
//preEnds = span.ends;
span.ends = function(){
//preEnds.apply(this,arguments);
span.end.ndx =
span.start.ndx + span.dom.title.length;
span.end.matched = '';
return span.end;
};
return span.created(src,txt,start,dir);
}
};
}
//}}}
/***
!!! twve.tcalc
***/
//{{{
twve.tcalc = {
//}}}
/***
!!!! twve.tcalc.readOnly
***/
//{{{
preReadOnly : null,
readOnly : function(title){
var str = 'twve.tcalc--example';
return (title && title.toLowerCase().substring(0,str.length)==str)
? false
: twve.tcalc.preReadOnly.apply(this,arguments);
},
//}}}
/***
!!!! twve.tcalc.cur_twtable
***/
//{{{
cur_twtable : null,
prev_twtable : [],
//}}}
/***
!!!! twve.tcalc.colRef
***/
//{{{
colRef : function ( col ) {
return twve.tcalc.indexToReferenceCol( col );
},
//}}}
/***
!!!! twve.tcalc.startBlockUpdate
***/
//{{{
block_update : false,
startBlockUpdate : function(){
twve.tcalc.block_update = true;
},
//}}}
/***
!!!! twve.tcalc.doingBlockUpdate
***/
//{{{
doingBlockUpdate : function(){
return twve.tcalc.block_update;
},
//}}}
/***
!!!! twve.tcalc.endBlockUpdate
***/
//{{{
endBlockUpdate : function ( txt ) {
twve.tcalc.block_update = false;
// Unpack the updated cell references.
// All the block-updated cell references were packed into the
// format {A.3} to prevent multiple modification, we need to
// unpack them for further use.
var cpos = twve.table.allCells(txt,0,txt.length);
var r, c, ctxt, ref, i, pl, pr, pdot;
for ( r=cpos.length-1; r>=0; r-- ) {
for ( c=cpos[r].length-1; c>=0; c-- ) {
ctxt=txt.substring(cpos[r][c].open,cpos[r][c].close);
if ( !(ref=ctxt.match(/\{[\$A-Za-z]+\.[\$0-9]+\}/g)) )
continue;
for ( i = 0; i < ref.length; i++ ) {
pl = ctxt.indexOf(ref[i]);
pdot = ctxt.indexOf('.', pl+1);
pr = ctxt.indexOf('}', pl+1);
ctxt = ctxt.substring(0,pl)
+ ctxt.substring(pl+1,pdot)
+ ctxt.substring(pdot+1,pr)
+ ctxt.substring(pr+1);
}
txt = txt.substring(0,cpos[r][c].open)
+ ctxt
+ txt.substring(cpos[r][c].close);
}
}
return txt;
},
//}}}
/***
!!!! twve.tcalc.updateCellReference
***/
//{{{
updateCellReference : function (
txt,row_cur,col_cur,row_new,col_new,how
) {
// Update cell reference from (row_cur, col_cur) to
// (row_new, col_new). This function is called when a table
// row or column index is changed. For example, table row
// index is changing during sorting.
var cpos = twve.table.allCells(txt,0,txt.length);
var r, c, ctxt, chndx, ref;
var cell_changed, ref_changed;
// The last argument {{{how}}} is to indicate the update condition,
// which can be one of the followings:
//
// 1. rel -- update only relative references (default);
// 2. abs -- update only absolute references;
// 3. both -- update both kinds of references.
switch ( typeof how ) {
case 'number' :
how = how > 3 ? 3 : (how < 1 ? 1 : how);
break;
case 'string' :
how = how == 'abs' ? 2 : (how == 'both' ? 3 : 1);
break;
default :
how = 1;
break;
}
for ( r = cpos.length-1; r >= 0; r-- ) {
for ( c = cpos[r].length-1; c >= 0; c-- ) {
ctxt = txt.substring(cpos[r][c].open,cpos[r][c].close);
cell_changed = false;
chndx = twve.tcalc.indexOfExpression(ctxt);
if (chndx == -1) continue;
chndx++;
while ( chndx < ctxt.length ) {
var thistable = true;
ref_changed=false;
if((ref=twve.tcalc.tiddlerReference(ctxt,chndx))){
thistable = false;
chndx += ref.length;
}
if((ref=twve.tcalc.tableReference(ctxt,chndx))){
thistable = false;
chndx += ref.length;
}
if( (ref=twve.tcalc.cellReference(ctxt,chndx)) ) {
if ( ! thistable ) {
chndx += ref.length;
continue;
}
var row = twve.tcalc.referenceToIndexRow(ref);
var col = twve.tcalc.referenceToIndexCol(ref);
var abs_row = twve.tcalc.absoluteRow(ref);
var abs_col = twve.tcalc.absoluteCol(ref);
if ( row_cur === null ) {
// Only col_cur is given, meaning one column of
// cells were changed. Adjust all cell references
// having a column index matching col_cur.
// Currently we leave absolute references unchanged,
// but this might not be the best choice.
if(col==col_cur && (how==3 ||
(how==2 && abs_col) ||
(how==1 && !abs_col))
){
col = col_new;
ref_changed = true;
}
} else if ( col_cur === null ) {
// Only row_cur is given, meaning one row of
// cells were changed. Adjust all cell references
// having a row index matching row_cur.
// Currently we leave absolute references unchanged,
// but this might not be the best choice.
if(row==row_cur && (how==3 ||
(how==2 && abs_row) ||
(how==1 && !abs_row))
){
row = row_new;
ref_changed = true;
}
} else {
// Both row_cur and col_cur are given,
// meaning single cell was changed. In such
// cases we adjust only exactly matched
// single cell references.
if ( ctxt.charAt(chndx+ref.length)==':' ) {
// The cell reference is immediately followed
// by a colon, meaning its a block reference.
// Skip it.
chndx += ref.length + 1;
ref = twve.tcalc.cellReference(ctxt,chndx);
chndx += ref.length;
} else {
// Currently we leave absolute references
// unchanged, but this might not be the best
// choice.
if ( row == row_cur && col == col_cur ){
if ( how == 3 ) {
// Both relative and absolute.
row = row_new;
col = col_new;
ref_changed = true;
} else if ( how == 2 ) {
// Only absolute.
if ( abs_row ) {
row = row_new;
ref_changed = true;
}
if ( abs_col ) {
col = col_new;
ref_changed = true;
}
} else {
// Only relative.
if ( ! abs_row ) {
row = row_new;
ref_changed = true;
}
if ( ! abs_col ) {
col = col_new;
ref_changed = true;
}
}
}
}
}
if ( ref_changed ) {
var newref;
if ( twve.tcalc.doingBlockUpdate() )
newref='{'
+twve.tcalc.indexToReferenceCol(
col,abs_col
) + '.'
+twve.tcalc.indexToReferenceRow(
row,abs_row
)+'}';
else
newref=twve.tcalc.indexToReferenceCol(
col,abs_col
) + twve.tcalc.indexToReferenceRow(
row,abs_row
);
ctxt = ctxt.substring(0,chndx) +
newref +
ctxt.substring(chndx+ref.length);
ref = newref;
cell_changed = true;
}
chndx += ref.length;
}
else chndx++;
}
if ( cell_changed ) {
txt = txt.substring(0,cpos[r][c].open)
+ ctxt
+ txt.substring(cpos[r][c].close);
}
}
}
return txt;
},
//}}}
/***
!!!! twve.tcalc.incCellReference
***/
//{{{
incCellReference : function(txt,row,dr,c0,cl,col,dc) {
// Increase cell reference by dr and dc for
// row_ndx >= row and col_ndx >= col. This function is Used
// in cases of row/column insertion/deletion.
var cpos = twve.table.allCells(txt,0,txt.length);
var r, c, chndx, ref, ctxt;
if ( !row ) row = 0; if ( !dr ) dr = 0;
if ( !c0 ) c0 = 0; if ( !col ) col = 0; if ( !dc) dc = 0;
var rcl;
for ( r=cpos.length-1; r>=0; r-- ) {
if ( typeof cl != 'number' || cl < 0
|| cl >= cpos[r].length ) {
rcl = cpos[r].length-1;
} else rcl = cl;
for ( c = rcl; c >= c0; c-- ) {
ctxt = txt.substring(cpos[r][c].open,cpos[r][c].close);
chndx = twve.tcalc.indexOfExpression(ctxt);
if (chndx == -1) continue;
chndx++;
var clen = ctxt.length;
while ( chndx < clen ) {
var thistable = true;
if((ref=twve.tcalc.tiddlerReference(ctxt,chndx))){
thistable = false;
chndx += ref.length;
}
if((ref=twve.tcalc.tableReference(ctxt,chndx))){
var thistable = false
chndx += ref.length;
}
if((ref=twve.tcalc.cellReference(ctxt,chndx))) {
if ( ! thistable ) {
chndx += ref.length;
continue;
}
var row_org=twve.tcalc.referenceToIndexRow(ref);
var col_org=twve.tcalc.referenceToIndexCol(ref);
var abs_row=twve.tcalc.absoluteRow(ref);
var abs_col=twve.tcalc.absoluteCol(ref);
if ( (row_org >= row && col_org >= col) ) {
//|| (r > row && c > c0) ) {
var newref =
twve.tcalc.indexToReferenceCol(
col_org+(abs_col?0:dc), abs_col
)+(abs_row?('$'+row_org):(row_org+dr));
// Update cell text.
ctxt = ctxt.substring(0,chndx)
+ newref
+ ctxt.substring(chndx+ref.length);
// Update length if necessary.
var newlen = ctxt.length;
if ( clen != newlen ) {
//end.ndx += (newlen-clen);
clen = newlen;
}
ref = newref;
}
chndx += ref.length;
} else chndx++;
}
txt = txt.substring(0,cpos[r][c].open)
+ ctxt
+ txt.substring(cpos[r][c].close);
}
}
return txt;
},
//}}}
/***
!!!! twve.tcalc.incReferencesInRow
***/
//{{{
incReferencesInRow : function(txt,row,row_cur,row_new){
var end = txt.length;
var rstart = twve.table.rowStarts(txt,0,end,row);
var rend = twve.table.rowEnds(txt,rstart);
return (rstart>0?txt.substring(0,rstart):'')+
twve.tcalc.incCellReference(
txt.substring(rstart,rend),0,(row_new-row_cur)
)+(rend<end?txt.substring(rend):'');
},
//}}}
/***
!!!! twve.tcalc.transposed
***/
//{{{
transposed : function ( txt ) {
// Update cell references after transposition.
var cpos = twve.table.allCells(txt,0,txt.length);
var r, c, ctxt, chndx, peq;
var cell_changed, ref, newref;
for ( r = cpos.length-1; r >= 0; r-- ) {
for ( c = cpos[r].length-1; c >= 0; c-- ) {
ctxt = txt.substring(cpos[r][c].open,cpos[r][c].close);
cell_changed = false;
peq = twve.tcalc.indexOfExpression(ctxt);
if (peq == -1) continue;
// Switch cell references
chndx = peq + 1;
while ( chndx < ctxt.length ) {
var thistable = true;
newref = '';
if((ref=twve.tcalc.tiddlerReference(ctxt,chndx))){
thistable = false;
chndx += ref.length;
}
if((ref=twve.tcalc.tableReference(ctxt,chndx))){
thistable = false;
chndx += ref.length;
}
if( (ref=twve.tcalc.cellReference(ctxt,chndx)) ) {
// Cell reference found. Adjust it if necessary.
if ( ! thistable ) {
chndx += ref.length;
continue;
}
var row = twve.tcalc.referenceToIndexRow(ref);
var col = twve.tcalc.referenceToIndexCol(ref);
var abs_row = twve.tcalc.absoluteRow(ref);
var abs_col = twve.tcalc.absoluteCol(ref);
newref = twve.tcalc.indexToReferenceCol(
row,abs_row
) + twve.tcalc.indexToReferenceRow(
col,abs_col
);
}
if ( newref ) {
ctxt = ctxt.substring(0,chndx) + newref +
ctxt.substring(chndx+ref.length);
ref = newref;
cell_changed = true;
chndx += ref.length;
} else chndx++;
}
// Switch row() and col()
chndx = peq + 1;
while ( chndx < ctxt.length ) {
newref = '';
if((ref=twve.tcalc.isFunction(ctxt,chndx))){
// Function name encountered. If it is either
// row() or col(), switch to the other.
newref = ref.toUpperCase();
if ( newref.substring(0,3) == 'ROW' ) {
newref = 'COL'+ref.substring(3);
} else if (newref.substring(0,3) == 'COL' ) {
newref = 'ROW'+ref.substring(3);
} else
newref = ref;
}
if ( newref ) {
ctxt = ctxt.substring(0,chndx) + newref +
ctxt.substring(chndx+ref.length);
ref = newref;
cell_changed = true;
chndx += ref.indexOf('(');
} else chndx++;
}
if ( cell_changed ) {
txt = txt.substring(0,cpos[r][c].open)
+ ctxt
+ txt.substring(cpos[r][c].close);
}
}
}
return txt;
},
//}}}
/***
!!!! twve.tcalc.copyOrCut
***/
//{{{
copied_from : null,
cut_from : null,
//}}}
/***
!!!! twve.tcalc.replaceDecimalMark
***/
//{{{
replaceDecimalMark : function(txt,start,end,oldmark,newmark){
var p = txt.indexOf(oldmark,start);
return p == -1 ? txt :
txt.substring(0,p)+newmark+txt.substring(p+1,end);
},
//}}}
/***
!!!! twve.tcalc.nonEmpty
***/
//{{{
nonEmpty : function(txt,start,end){
if ( start !== undefined ) {
txt = end !== undefined
? txt.substring(start,end)
: txt.substring(start);
//var result =
}
return txt.trim() ? txt : '';
},
//}}}
/***
!!!! twve.tcalc.halfWay
***/
//{{{
partialSearch : false,
halfWay : function(txt,start,end,force){
return force || twve.tcalc.partialSearch
? twve.tcalc.nonEmpty(txt,start,end)
: '';
},
//}}}
/***
!!!! twve.tcalc.isPartsPerNotation
***/
//{{{
isPartsPerNotation : function(txt,start,end,i){
// Check in txt the sub text at index i (between start and end)
// to see whether or not it is a parts-per notation.
//
// Note: The caller function MUST make sure there is a numerical
// value immediately preceding the character at index i.
var factor = 1;
switch (txt.charAt(i)){
case '%' :
factor = 1e-2; break;
case '‰' :
factor = 1e-3; break;
case 'p' :
// Check for ppm.
if ( i+2>=end ||
txt.charAt(i+1) != 'p' ) return '';
// ppm, ppb, ppt, ppq
var factor = 1;
switch (txt.charAt(i+2)) {
case 'm' : // ppm
factor = 1e-6; break;
case 'b' : // ppb
factor = 1e-9; break;
case 't' : // ppb
factor = 1e-12; break;
case 'q' : // ppq
factor = 1e-15; break;
default : return '';
}
break;
default : return '';
}
//if ( twve.tcalc.partialSearch )
// return twve.tcalc.halfWay(txt,start,i+1,true);
var result = twve.tcalc.halfWay(txt,start,i,true);
return (result*factor)+'';
},
//}}}
/***
!!!! twve.tcalc.isDigit
***/
//{{{
digit : '0123456789',
digit_zh : '零一二三四五六七八九',
digit_zh_cap : '壹貳叁肆伍陸柒捌玖',
//digit_zh_large : '拾百佰千仟万萬億兆',
isDigit : function(ch){
var n = twve.tcalc.digit.indexOf(ch);
if ( n >= 0 ) return ch;
//n = twve.tcalc.digit_zh.indexOf(ch);
//if ( n >= 0 ) return n+'';
//n = twve.tcalc.digit_zh_cap.indexOf(ch)+1;
//if ( n > 0 ) return n+'';
return '';
},
//}}}
/***
!!!! twve.tcalc.isNumeric
***/
//{{{
isNumeric : function(txt,start,end,update,given_mark,to_sep){
// See [[Testing a numeric expression -- isNumeric -- The Crazy Version!]]
// or [[its external link|http://twtable.tiddlyspace.com/#%5B%5BTesting%20a%20numeric%20expression%20--%20isNumeric%20--%20The%20Crazy%20Version!%5D%5D]]
// for details.
if ( typeof txt == 'number' ) return txt;
var ncomma = 0, nperiod = 0;
var npm = 0, nspace = 0, ndot = 0;
start = twve.position.getPosition(start);
var ch, i;
var len = txt.length;
if (typeof end == 'number') {
if ( end < 0 ) {
// Count back from the end.
end += len+1;
if ( end < 0 ) end = 0;
} else if ( end > len )
end = len;
if ( end < start ) {
i = start;
start = end;
end = start;
}
} else end = len;
i = start;
var withsymbol = false;
var lastch = '';
var numtxt = '';
// skip the leading white spaces
var regwhite = /\s/;
while ( regwhite.test((ch=txt.charAt(i))) ) {
if ( ++i == end ) return '';
numtxt += ch;
}
var ndig = 0;
for ( ; i<end; i++ ) {
lastch = ch;
ch = txt.charAt(i);
var dig = twve.tcalc.isDigit(ch);
if ( dig == '' ) {
switch ( ch ) {
case 'e' :
case 'E' :
if ( ndig == 0 )
return twve.tcalc.halfWay(txt,start,i,true);
withsymbol = true;
numtxt += ch;
break;
//case '−' :
// ch = '-';
case '+' :
case '-' :
if(ndig>0 && lastch!='e' &&
lastch!='E' && lastch !=':')
//return twve.tcalc.halfWay(txt,start,i);
return twve.tcalc.halfWay(numtxt);
if ( ++npm > (withsymbol?2:1) )
return ndig > 0
//? twve.tcalc.halfWay(txt,start,i)
? twve.tcalc.halfWay(numtxt)
: '';
if ( ch == '+' ) return '';
numtxt += ch;
break;
case ':' :
// Time string
if ( ndig == 0 )
// No digits found.
return '';
numtxt += ch;
ch = txt.charAt(i+1);
dig = twve.tcalc.isDigit(ch);
if ( dig == '' ) {
// Some digits before this ':' and no more
// after.
switch ( ch ) {
case '+' :
case '-' :
npm = 1;
break;
case '.' :
nperiod = 1;
break;
default :
//return twve.tcalc.halfWay(txt,start,i);
return twve.tcalc.halfWay(numtxt);
}
numtxt += ch;
} else {
ndig++;
numtxt += dig;
npm = 0; // reset number of plus/minus
}
// Some digits before and some after this ':', a
// valid time string.
withsymbol = true;
i++;
break;
case '.' :
if ( ++nperiod > 1 && ncomma > 1)
return ndig > 0
//? twve.tcalc.halfWay(txt,start,i)
? twve.tcalc.halfWay(numtxt)
: '';
numtxt += ch;
break;
case ',' :
if ( ! config.options.chktwveTcalcThousandSeparated ||
(++ncomma > 1 && nperiod > 1) )
//return twve.tcalc.halfWay(txt,start,i);
return twve.tcalc.halfWay(numtxt);
numtxt += ch;
break;
//case '·' :
case '·' :
if(!config.options.chktwveTcalcThousandSeparated ||
(++ndot>1 && (nspace>1||nperiod>1||ncomma>1)))
//return twve.tcalc.halfWay(txt,start,i);
return twve.tcalc.halfWay(numtxt);
numtxt += ch;
break;
case ' ' :
if(config.options.chktwveTcalcThousandSeparated &&
ndot<=1 && nperiod<=1 && ncomma<=1){
++nspace;
numtxt += ch;
break;
}
i++; // Skip this white space.
default :
return ndig > 0
? (twve.tcalc.isPartsPerNotation(
txt,start,end,i
) || twve.tcalc.halfWay(numtxt))
: '';
}
} else {
ndig++;
numtxt += dig;
}
}
if ( !update && (ncomma == 0 && nperiod == 0 &&
nspace == 0 && ndot == 0) )
//return twve.tcalc.nonEmpty(txt,start,end);
return ndig > 0
? twve.tcalc.nonEmpty(numtxt)
: '';
var mark = '.', separator = ',',
nmark = nperiod, nsep = ncomma;
if ( nperiod > 1 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( nspace > 0 ) {
if ( (nperiod > 0 && ncomma > 0) ||
(nperiod > 1 || ncomma > 1) )
return twve.tcalc.halfWay(
txt,start,txt.indexOf(' ',start)
);
separator = ' '; nsep = nspace;
if ( ncomma > 0 ) {
mark = ','; nmark = ncomma;
} else {
mark = '.'; nmark = nperiod;
}
} else if ( ndot > 0 ) {
if ( nperiod > 0 || ncomma > 1 )
return twve.tcalc.halfWay(
txt,start,txt.indexOf('·',start)
);
//separator = '·'; mark = ',';
separator = '·'; mark = ',';
nsep = ndot; nmark = ncomma;
} else if ( ncomma == 1 && nperiod == 0 ) {
i = txt.indexOf(',',start);
if ( i==start || end-i!=4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( end-i < 4 )
return twve.tcalc.halfWay(txt,start,i);
} else if ( nperiod == 1 && ncomma == 1 ) {
var pcomma = txt.indexOf(',',start);
var pperiod = txt.indexOf('.',start);
i = pcomma - pperiod;
if ( i == 4 ) {
separator = '.'; mark = ',';
nsep = nperiod; nmark = ncomma;
} else if ( i != -4 )
return twve.tcalc.halfWay(
txt,start,(i > 0 ? pperiod : pcomma)
);
}
if(nsep > 0 && !config.options.chktwveTcalcThousandSeparated)
return twve.tcalc.halfWay(
txt, start,
txt.indexOf(
config.options.txttwveTcalcThousandSeparator,start
)
);
if ( nsep > 1 ) {
var p1 = txt.indexOf(separator,start), p2;
while( (p2=txt.indexOf(separator,p1+1))>-1 ) {
if ( p2 - p1 != 4 )
return twve.tcalc.halfWay(txt,start,p1);
p1 = p2;
}
}
if ( (nmark>0 && mark!=config.options.txttwveTcalcDecimalMark)
|| (nsep>0 &&
separator!=config.options.txttwveTcalcThousandSeparator) )
return twve.tcalc.halfWay(
txt,start,txt.indexOf(separator,start)
);
var opt_mark = given_mark ? given_mark :
config.options.txttwveTcalcDecimalMark;
if ( nsep == 0 ) {
txt = opt_mark != mark
? twve.tcalc.replaceDecimalMark(
txt,start,end,mark,opt_mark
)
: twve.tcalc.nonEmpty(numtxt);
return to_sep
? twve.tcalc.separateNumber(txt)
: txt;
}
var opt_separator = to_sep ?
config.options.txttwveTcalcThousandSeparator : '';
if ( (mark!=opt_mark) || (separator!=opt_separator) ) {
for ( i=start; i<end; i++ ) {
ch = txt.charAt(i);
if ( ch == separator ) {
if(separator!=opt_separator)
txt=txt.substring(start,i)+
opt_separator+txt.substring(i+1);
}
else if( ch == mark ) {
if(mark!=opt_mark)
txt=txt.substring(start,i)+
opt_mark+txt.substring(i+1);
}
}
}
return twve.tcalc.nonEmpty(txt,start,end);
},
//}}}
/***
!!!! twve.tcalc.unseparateNumber
***/
//{{{
/*
unseparateNumber : function ( txt ) {
var txt2=txt.split(config.options.txttwveTcalcThousandSeparator);
var txt3 = '';
for ( var i = 0; i < txt2.length; i++ )
txt3 += txt2[i];
return txt3;
},
*/
//}}}
/***
!!!! twve.tcalc.separateNumber
***/
//{{{
// //The following function was obtained from
// //http://www.mredkj.com/javascript/numberFormat.html
separateNumber : function (nStr) {
nStr += '';
var x = nStr.split(config.options.txttwveTcalcDecimalMark);
var x1 = x[0];
var x2 = x.length > 1 ?
config.options.txttwveTcalcDecimalMark + x[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' +
config.options.txttwveTcalcThousandSeparator + '$2');
}
return x1 + x2;
},
//}}}
/***
!!!! twve.tcalc.spannedCellReference
***/
//{{{
spannedCellReference : function(ref){
var p = ref.lastIndexOf('|');
if ( p >= 0 ) {
// Column spanned cell, get the last reference
return ref.substring(p+1).trim();
}
p = ref.indexOf('\n');
if ( p >= 0 ) {
// Row spanned cell, get the first reference
return ref.substring(0,p).trim();
}
// Regular reference, just return it.
return ref;
},
//}}}
/***
!!!! twve.tcalc.absoluteRow
***/
//{{{
absoluteRow : function (ref) {
var p = ref.indexOf('$',1);
return (p > 0 && p < 4);
},
//}}}
/***
!!!! twve.tcalc.referenceToIndexRow
***/
//{{{
referenceToIndexRow : function ( ref ) {
if ( ! ref ) return null;
ref = twve.tcalc.spannedCellReference(ref);
var p = ref.indexOf('$',1);
if ( p > 0 && p < 4 )
// Absolute row reference
return parseInt(ref.substring(p+1));
p = ref.charAt(0)=='$' ? 1 : 0;
// Relative row reference
if ( /[a-zA-Z]/.test(ref.charAt(p+1)) )
return parseInt(ref.substring(p+2));
else
return parseInt(ref.substring(p+1));
},
//}}}
/***
!!!! twve.tcalc.indexToReferenceRow
***/
//{{{
indexToReferenceRow : function ( r, abs ) {
return (abs?'$':'')+r;
},
//}}}
/***
!!!! twve.tcalc.absoluteCol
***/
//{{{
absoluteCol : function (ref) {
return ref.charAt(0)=='$';
},
//}}}
/***
!!!! twve.tcalc.referenceToIndexCol
***/
//{{{
referenceToIndexCol : function ( ref ) {
if ( ! ref ) return null;
ref = twve.tcalc.spannedCellReference(ref);
var p = ref.indexOf('$');
if ( p == 0 ) p++; // Absolute col reference
else p = 0; // Relative col reference
var a = 'A'.charCodeAt();
ref = ref.toUpperCase();
if ( /[A-Z]/.test(ref.charAt(p+1)) ) {
var c1 = ref.charAt(p).charCodeAt()-a+1,
c2 = ref.charAt(p+1).charCodeAt()-a;
return c1*26 + c2;
} else {
return ref.charAt(p).charCodeAt()-a;
}
},
//}}}
/***
!!!! twve.tcalc.indexToReferenceCol
***/
//{{{
indexToReferenceCol : function ( c, abs ) {
var a = 'A'.charCodeAt(),
pre = (abs ? '$' : '');
if ( c < 26 )
return pre+String.fromCharCode(c+a);
else {
var c1 = c/26-1, c2 = c%26;
return pre+String.fromCharCode(c1+a)+
String.fromCharCode(c2+a);
}
},
//}}}
/***
!!!! twve.tcalc.indexOfExpression
***/
//{{{
indexOfExpression : function (text,start,op) {
// Find the index of a valid expression. By definition in this
// plugin a valid expression is preceded by an equal sign. There
// are, however, situations that the equal sign is in an
// inactive region, such as
// 1. in a preformatted or code block,
// 2. in a URL,
// 3. in a MathJax expression
// 4. ...
start = twve.position.create(start);
var pos = twve.position.create(start);
var tags = twve.url.markupTags();
var opstr = '=?:';
if ( ! op || opstr.indexOf(op) == -1 )
op = '=';
while ( true ) {
pos = twve.text.skipToActive(text,op,pos);
if ( pos.ndx == -1 || pos.ndx < start.ndx )
return -1;
if ( ! tags.encloses(text,pos.ndx,start) ) {
// If there are consecutive equal signs, skip to the
// last one.
while ( text.charAt(pos.ndx+1)=='=' ) pos.ndx++;
return pos.ndx;
}
pos.ndx++;
}
},
//}}}
/***
!!!! twve.tcalc.isLogicalOperator
***/
//{{{
isLogicalOperator : function(txt,start){
var chndx = start;
var ch = txt.charAt(chndx);
var nch = txt.charAt(chndx+1);
var nnch = txt.charAt(chndx+2);
switch ( ch ){
case '!' :
return nch == '='
? (nnch == '=' ? (ch+nch+nnch) : (ch+nch))
: ch;
case '=' :
return nch == '='
? (nnch == '=' ? (ch+nch+nnch) : (ch+nch))
: '';
case '>' :
case '<' :
return nch == '='
? (ch+nch)
: ch;
}
return '';
},
//}}}
/***
!!!! twve.tcalc.isUserFunction
***/
//{{{
isUserfunction : function ( txt, start, end ) {
if ( start==0 || txt.charAt(start-1)!='.' )
return txt.substring(start,end);
//twve.tcalc.fn_ndx = -1;
},
//}}}
/***
!!!! twve.tcalc.isFunction
***/
//{{{
fn_ndx : -1,
isFunction : function ( txt, start ) {
// Returns the function string fn(arg) if any.
//twve.tcalc.fn_ndx = -1;
if ( !start ) start = 0;
var txt2 = txt.substring(start).toUpperCase();
for(var i=0,ilen=twve.tcalc.fn.length;i<ilen;i++){
var fn_name = '';
var name = twve.tcalc.fn[i].name;
if ( typeof name == 'string' ) {
if(txt2.substring(0,name.length) == name){
fn_name = name;
}
} else {
// Must be an array of names.
for(var j=0,jlen=name.length;j<jlen;j++){
if(txt2.substring(0,name[j].length)==name[j]){
fn_name = name[j]; break;
}
}
}
if ( fn_name ) {
// There is a function name found, record its starting
// position.
var pfn = txt2.indexOf(fn_name)+start;
// Look for function arguments.
var pl = txt.indexOf('(',pfn+1);
if ( pl == -1 ) return '';
var pr = twve.tags.nested.matchedCloseTag(
txt,pl+1,'(',')'
);
twve.tcalc.fn_ndx = i;
return twve.tcalc.isUserfunction(
txt,pfn,(pr.ndx>-1?(pr.ndx+1):null)
);
}
}
},
//}}}
/***
!!!! twve.tcalc.parseSingleTerm
***/
//{{{
parseSingleTerm : function (cell_exp, last_one, start) {
var expression = '', tmp_exp = null;
if (twve.tcalc.fn[twve.tcalc.fn_ndx].parseSingleTerm) {
tmp_exp = twve.tcalc.fn[twve.tcalc.fn_ndx]
.parseSingleTerm(cell_exp,start);
if ( tmp_exp ) expression += tmp_exp;
} else {
expression += (tmp_exp=cell_exp);
}
if (!last_one && tmp_exp
&& twve.tcalc.fn[twve.tcalc.fn_ndx].operator)
expression += twve.tcalc.fn[twve.tcalc.fn_ndx].operator;
return expression;
},
//}}}
/***
!!!! twve.tcalc.consecutiveCells
***/
//{{{
consecutiveCells : function (txt) {
// Extract the indexes of the starting (top-left) and ending
// (bottom-right) cells in a consecutive range.
var txt2 = txt.split(':');
// Now txt2[0] is the beginning while txt2[1] is the
// ending cell. Find the (row, col) indexes of the
// cells and evaluate the cells in between, inclusively.
var c1 = twve.tcalc.referenceToIndexCol(txt2[0]);
var r1 = twve.tcalc.referenceToIndexRow(txt2[0]);
var c2 = twve.tcalc.referenceToIndexCol(txt2[1]);
var r2 = twve.tcalc.referenceToIndexRow(txt2[1]);
if ( r2 < r1 ) { r = r1; r1 = r2; r2 = r; }
if ( c2 < c1 ) { c = c1; c1 = c2; c2 = c; }
var indexes = twve.object.create();
indexes.start = twve.object.create();
indexes.start.row = r1;
indexes.start.col = c1;
indexes.end = twve.object.create();
indexes.end.row = r2;
indexes.end.col = c2;
return indexes;
},
//}}}
/***
!!!! twve.tcalc.copyFunctionData
***/
//{{{
duplicateFunctionData : function(fn_or_ndx){
var fn = typeof fn_or_ndx == 'number'
? twve.tcalc.fn[fn_or_ndx]
: fn_or_ndx;
var holder = {};
var src = fn.data ? fn.data : fn;
twve.object.copyKeys(
holder, // destination
src, // source
twve.object.keys(src) // source keys
);
return holder;
},
//}}}
/***
!!!! twve.tcalc.pushFunctionCall
***/
//{{{
call_stack : [],
pushFunctionCall : function(fn_ndx){
// Push the data of function twve.tcalc.fn[fn_ndx] into
// the call stack.
var fn_obj = {ndx : fn_ndx};
if ( twve.tcalc.fn[fn_ndx].data !== undefined )
fn_obj.data = twve.tcalc.duplicateFunctionData(fn_ndx);
twve.tcalc.call_stack.push(fn_obj);
},
//}}}
/***
!!!! twve.tcalc.popFunctionCall
***/
//{{{
popFunctionCall : function(){
// Pop the data at the end of call stack back to its function.
var fn_obj = twve.tcalc.call_stack.pop();
twve.tcalc.fn_ndx = fn_obj.ndx;
if ( fn_obj.data !== undefined )
twve.object.copyKeys(
twve.tcalc.fn[fn_obj.ndx].data, // destination
fn_obj.data, // source
twve.object.keys(fn_obj.data) // source keys
);
},
//}}}
/***
!!!! twve.tcalc.parseConsecutive
***/
//{{{
parseConsecutive : function ( txt ) {
// calculate over consecutive cells
// The argument txt should be something like cell1..cell2,
// otherwise the behavior of this function is undefined.
var tmp_exp, r, c, tmp_exp,
rows = twve.tcalc.cur_twtable.dom.querySelectorAll('tr');
var ndx = twve.tcalc.consecutiveCells(txt);
var partial = twve.tcalc.fn[twve.tcalc.fn_ndx].partialSearch;
var expression = partial ? [] : '';
for ( r = ndx.start.row; r <= ndx.end.row; r++ ) {
var tr = rows[r];
var cells = tr.querySelectorAll('th,td');
for ( c = ndx.start.col; c <= ndx.end.col; c++ ) {
var cell = twve.table.getRowCell(cells,c);
if ( ! cell ) continue;
// Push this function call to the call stack.
twve.tcalc.pushFunctionCall(twve.tcalc.fn_ndx);
tmp_exp = twve.tcalc.evaluateCell(
cell,!twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval
);
// Pop it back.
twve.tcalc.popFunctionCall();
if ( partial )
expression[expression.length] = tmp_exp;
else
expression += twve.tcalc.parseSingleTerm(
tmp_exp, (r==ndx.end.row && c==ndx.end.col)
);
}
}
if ( expression )
if ( partial )
expression = twve.tcalc.parseSingleTerm(expression,true);
else if ( twve.tcalc.fn[twve.tcalc.fn_ndx].operator )
if (expression.charAt(expression.length-1)==
twve.tcalc.fn[twve.tcalc.fn_ndx].operator) {
expression = expression.substring(
0,expression.length-1
);
}
return expression;
},
//}}}
/***
!!!! twve.tcalc.allArguments
***/
//{{{
allArguments : function (txt) {
var p0 = txt.indexOf('(');
// No left parenthesis found.
if ( p0 == -1 ) return '';
// There is left parenthesis, look for its paired right
// parenthesis. Make use of the .matchedCloseTag() method
// defined in twve.tags.nested object.
var end = twve.tags.nested.matchedCloseTag(txt,p0+1,'(',')');
if ( end.ndx < 0 ) return '';
// If found, the end.ndx will be right at the right parenthesis.
return txt.substring(p0+1, end.ndx);
},
//}}}
/***
!!!! twve.tcalc.splitArguments
***/
//{{{
splitArguments : function (txt) {
// Split one line of text that contains several arguments.
//
// Arguments: txt The txt to split.
// sep The separator of items
// brackets An array of pairs of opening/closing
// brackets that should not be split.
var tags = twve.tags.nested.create(
['(', '[', '{'],
[')', ']', '}']
);
var pos = twve.position.create(0);
var start = twve.position.create(0);
var end = twve.position.create(txt.length);
var result = [];
while(true){
pos = twve.text.skipToActive(txt,',',start,end,tags);
if (pos.ndx == -1){
// No more comma's
result[result.length] = txt.substring(start.ndx,end.ndx);
return result;
} else {
// A comma is found.
result[result.length] = txt.substring(start.ndx,pos.ndx);
start.ndx = pos.ndx+1;
}
}
},
//}}}
/***
!!!! twve.tcalc.parseFunction
***/
//{{{
parseFunction : function ( txt ) {
// Start parsing twve.tcalc-defined functions.
// Calculations are done in one of the following ways:
// 1. over consecutive cells
// txt should look like func(cell1:cell2)
// 2. over separated individual cells
// txt should look like func(cell1,cell2,cell3,...)
// 3. mixing both cases
// txt can be like
// func(cell1:cell2,cell3,cell4,cell5:cell7...)
// get rid of parentheses in txt
var txt2 = twve.tcalc.allArguments(txt);
var expression = '', tmp_exp;
if ( !txt2 ) {
// Functions without arguments.
if ( twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing )
if ((tmp_exp=
twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing(txt)))
expression = tmp_exp;
if ( twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing )
if((tmp_exp=
twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing()))
expression += tmp_exp;
return expression;
}
// Functions with arguments.
// If some of the arguments contain function calls,
// evaluate them before further parsing.
var fn_ndx = twve.tcalc.fn_ndx, chndx = 0;
// Push the current function call to the call stack.
twve.tcalc.pushFunctionCall(fn_ndx);
while ( chndx < txt2.length ) {
chndx += twve.text.skipStyleText(txt2,chndx,true);
var fntxt = twve.tcalc.isFunction(txt2,chndx);
if ( fntxt ) {
var fnval = twve.tcalc.parseFunction(fntxt);
txt2 = txt2.substring(0,chndx)
+ fnval
+ txt2.substring(chndx+fntxt.length);
chndx += fnval.length;
} else chndx++;
}
// Pop it back.
twve.tcalc.popFunctionCall();
// Initialize this function.
if ( twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing )
if ((tmp_exp=
twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing(txt)))
expression = tmp_exp;
// Now parse each of the arguments.
var jsfunc = txt.indexOf('Math.')>-1;
txt2 = twve.tcalc.splitArguments(txt2);
for (var i=0,ilen=txt2.length; i<ilen; i++ ) {
var txt3 = txt2[i].trim();
if ( txt3 == '' ) txt3 = txt2[i];
var p0 = twve.text.skipStyleText(txt3,0,true);
if ( (chndx=txt3.indexOf(':',p0)) > -1
&& twve.tcalc.cellReference(txt3,chndx+1) ) {
// Block reference.
expression += twve.tcalc.fn[twve.tcalc.fn_ndx].no_deref
? twve.tcalc.parseSingleTerm(
txt3,(i==txt2.length-1)
)
: twve.tcalc.parseConsecutive(txt3);
if ( i<txt2.length-1 &&
twve.tcalc.fn[twve.tcalc.fn_ndx].operator )
expression+=
twve.tcalc.fn[twve.tcalc.fn_ndx].operator;
} else {
// Either single cell reference or no cell references
// at all.
// Push the current function call to the call stack.
twve.tcalc.pushFunctionCall(fn_ndx);
tmp_exp = twve.tcalc.evaluate(
txt3, p0,
!twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval,
twve.tcalc.fn[twve.tcalc.fn_ndx].no_deref
);
// Pop it back.
twve.tcalc.popFunctionCall();
expression+=twve.tcalc.parseSingleTerm(
(p0 ? txt3.substring(0,p0) : '')+tmp_exp,
(i==txt2.length-1), p0
);
}
if ( jsfunc && i < ilen-1 && expression )
expression += ',';
}
if (twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing){
if ((tmp_exp=twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing()))
expression += tmp_exp;
// The function index may be changed in stopParsing(), restore
// it back to ensure correct execution.
twve.tcalc.fn_ndx = fn_ndx;
}
return expression;
},
//}}}
/***
!!!! twve.tcalc.wrappedReference
***/
//{{{
wrappedReference : function(txt,start,open,close){
// Returns possible reference text wrapped by a pair of wrappers,
// such as a pair of quotes, double quotes, square brackets or
// curly brackets, etc. The return value includes the wrappers.
var chndx=(start?start:0);
if ( chndx < 0 || chndx >= txt.length ) return '';
var ch = txt.charAt(chndx);
if ( ch != open ) return '';
var end = chndx;
// Possible file name.
end = txt.indexOf(close,end+1);
return end < 0 ? '' : txt.substring(chndx,end+1);
},
//}}}
/***
!!!! twve.tcalc.externalReference
***/
//{{{
exteranlReference : function(txt,start){
// To allow cross file calculations, one must be able to
// specify the file name that the formula is referring to. This
// plugin considers the text wrapped within a pair of curly braces
// as a possible file name.
return twve.tcalc.wrappedReference(txt,start,'{','}');
},
//}}}
/***
!!!! twve.tcalc.tiddlerReference
***/
//{{{
tiddlerReference : function(txt,start){
// To allow cross tiddler calculations, one must be able to
// specify the tiddler that the formula is referring to. This
// plugin recognizes a tiddler by its title. The syntax follows
// the external reference in OpenOffice.calc or MS Excel: wrap
// the title of the tiddler in a pair of square bracket.
var result = twve.tcalc.wrappedReference(txt,start,'[',']');
return result
? (result.indexOf(',')==-1 ? result : '')
: '';
},
//}}}
/***
!!!! twve.tcalc.tableReference
***/
//{{{
tableReference : function(txt,start){
// To allow cross table calculations, one must be able to specify
// the table that the formula is referring to. This plugin
// recognizes two ways of table referencing:
// 1. by index;
// 2. by caption.
// The syntax follows the worksheet syntax in the OpenOffice.calc
// or MS Excel: Use the exclamation mark (!).
//
// Examples
// 1. To refer to a table by its index.
// """Table0!A1""" refers to the cell """A1""" in """Table0"""
// (the table with index 0, i.e., the first table in this
// tiddler).
// 2. To refer to a table by its caption.
// In this case,
// a. the user is responsible for the uniqueness of the
// table caption;
// b. the caption must be specified with a pair of quotes
// or double quotes enclosing it.
// """'Table Caption'!B3""" refers to the cell """B3""" in
// the table with caption """Table Caption""". Note that in
// this example the table caption is enclosed by a pair of
// quotes.
var chndx=(start?start:0);
if ( chndx < 0 || chndx >= txt.length ) return '';
var ch = txt.charAt(chndx);
if ( ch == 't' || ch == 'T' ) {
var end = chndx;
// Possible table reference by index, look for the format
// TableN!, where N is the index of the table.
end += 5; // Table
if(txt.substring(chndx,end).toLowerCase()=='table'){
// There is the text Table, look for an immediately
// following number that is immediately followed by
// an exclamation mark (!).
var partial = twve.tcalc.partialSearch;
twve.tcalc.partialSearch = true;
var ndx = twve.tcalc.isNumeric(txt,end);
twve.tcalc.partialSearch = partial;
if ( ndx == '' ) return '';
end += ndx.length;
return txt.charAt(end) == '!'
? txt.substring(chndx,end+1)
: '';
}
} else {
var ref = twve.tcalc.wrappedReference(txt,chndx,"'","'") ||
twve.tcalc.wrappedReference(txt,chndx,'"','"');
if ( ref == '' ) return '';
// Possible table reference by caption.
return txt.charAt(chndx+ref.length) == '!'
? (ref+'!')
: '';
}
return '';
},
//}}}
/***
!!!! twve.tcalc.cellReference
***/
//{{{
cellReference : function(txt,start){
// Returns the cell reference from txt at position start, or an
// empty string if no cell reference is found at that position.
var chndx=(start?start:0);
if ( chndx < 0 || chndx >= txt.length ) return '';
var ch, col = '', row = '';
if ( (ch=txt.charAt(chndx++)) == '$' ) {
// Absolute column reference
col = ch;
ch = txt.charAt(chndx++);
}
if ( /[\a-zA-Z]/.test(ch) ) {
col += ch;
ch = txt.charAt(chndx++);
if ( /[a-zA-Z]/.test(ch) ) {
col += ch;
ch = txt.charAt(chndx++);
}
}
if ( col ) {
// There is possible column reference,
// check for row reference.
if ( ch == '$' ) {
// Absolute row reference
row = ch;
ch = txt.charAt(chndx++);
}
if (twve.tcalc.isDigit(ch)) {
row += ch;
while (chndx<txt.length &&
twve.tcalc.isDigit((ch=txt.charAt(chndx++))))
row += ch;
return col+row;
}
}
return '';
},
//}}}
/***
!!!! twve.tcalc.isPossibleExp
***/
//{{{
/*
isPossibleExp : function (txt) {
if ( !txt ) return false;
//if ( /[^\x21-\x7e ]/g.test(txt) ) return false;
var pl = txt.indexOf('(');
if ( pl > -1 ) {
var pr = txt.lastIndexOf(')');
txt = txt.substring(pl+1,pr);
}
var term1 = txt.split(/[\*\/]/);
for ( var i = 0; i < term1.length; i++ ) {
if ( ! term1[i] ) return false;
var term2 = term1[i].split(/[\+\-]/);
for ( var j = 0; j < term2.length; j++ )
if(term2[j] && ! twve.tcalc.isNumeric(term2[j].trim()))
return false;
}
return true;
},
*/
//}}}
/***
!!!! twve.tcalc.cellContent
***/
//{{{
cellContent : function(tr,ref,js_eval,expression){
var cell = null;
if (tr.nodeName == 'TR') {
// tr is a table row, ref must be a column index.
cell = twve.table.getRowCell(tr,ref);
} else if ( tr.length ) {
// tr is an array.
if ( tr[0].nodeName == 'TR' )
// tr is an array of TR's, ref must be a cell
// reference string.
cell = twve.table.getRowCell(
tr[twve.tcalc.referenceToIndexRow(ref)],
twve.tcalc.referenceToIndexCol(ref)
);
else
// tr must be an array of cells, ref must be an index.
cell = tr[ref];
}
//if(twve.tcalc.cyclicReference(cell))
// return twve.tcalc.nodeText(cell);
var cexp = cell
? twve.tcalc.evaluateCell(cell,js_eval)
: null;
if ( cexp ) return cexp;
if ( expression ) {
var len = expression.length;
var ch = expression.charAt(len-1);
if (ch=='+' || ch=='-' || ch=='*' || ch=='/')
//expression = expression.substring(0,len-1);
return '0';
}
return '';
},
//}}}
/***
!!!! twve.tcalc.inactive_block
***/
//{{{
inactive_block : function(txt,chndx,tags){
if ( ! tags.start )
tags.start = twve.position.create(chndx);
else
tags.start.ndx = chndx;
if(tags.firstOpenTag(txt,tags.start).ndx == chndx){
if ( ! tags.end )
tags.end = twve.position.create(tags.start);
tags.end.ndx = tags.start.ndx+tags.start.matched.length;
twve.text.indexOf(
txt,
tags.exactCloseTag(tags.start.matched).close,
tags.end
);
if(tags.end.ndx > chndx) return tags;
}
return null;
},
regwhite : /\s/,
//regurl : /[hHfF]/,
opary : [
[ '*', '/', '^' ], // pass 2 operators
[ '+', '-' ] // pass 3 operators
],
//}}}
/***
!!!! twve.tcalc.expression
***/
//{{{
/*
expression : {
source : '',
start : 0,
callback : null,
evaluate_numerals : function(){
},
evaluate_references : function(){
}
},
*/
//}}}
/***
!!!! twve.tcalc.evaluate_arithmetics
***/
//{{{
evaluate_arithmetics : function(txt,start,inc_tags,callback){
// Evaluate a regular mathematical expression that contains
// only numerals and arithmetic operators.
//
// The first two arguments, txt and start, are required.
//
// The evaluation process is divided into the following passes:
// 1: Evaluate constants, powers, multiplications and divisions.
// 2: Evaluate additions and subtractions.
if ( ! txt ) return '';
// Now there should be only arithmetic operators and
// numerals, time strings, or vectors.
start = twve.position.getPosition(start);
var pstyle = 0;
// Skip the leading white spaces or tabs.
while ( start+pstyle < txt.length &&
' \t\n'.indexOf(txt.charAt(start+pstyle)) > -1)
pstyle++;
// Skip the leading CSS style text.
pstyle += twve.text.skipStyleText(txt,start+pstyle,true);
var chndx = start+pstyle;
var expression = txt.substring(start,chndx);
if(!inc_tags) inc_tags = twve.tags.inactiveTags();
var ch = '', subtxt = '', result = '';
var op = '';
var pos = -1;
var val = [ null, null ];
function evaluate_operation () {
var t0 = typeof val[0];
var t1 = typeof val[1];
if (t0 == 'number' && t1 == 'number' ) {
// Both are numbers
switch(op){
case '*' :
result = val[0] * val[1];
break;
case '/' :
result = val[0] / val[1];
break;
case '^' :
result = Math.pow(val[0],val[1]);
break;
case '+' :
result = val[0] + val[1];
break;
case '-' :
result = val[0] - val[1];
break;
default :
// Undefined operation.
if (config.options.chktwveTcalcDebugMode)
console.log(
'Evaluation error: Undefined operator ('+
op+') for numbers.'
);
op = ''; val[0] = val[1] = null;
return null;
}
} else if (t0 == 'string' || t1 == 'string' ) {
// At least one of them is a string.
switch (op) {
case '+' :
case '-' :
result = twve.tcalc.calculateTime(
val[0]+op+val[1]
).normalize();
break;
default :
// Undefined operation.
if (config.options.chktwveTcalcDebugMode)
console.log(
'Evaluation error: Undefined operator ('+
op+') for time strings.'
);
op = ''; val[0] = val[1] = null;
return null;
}
} else {
// Assuming both of them are vectors.
var v0 = twve.tcalc.vector.create(val[0],true);
var v1 = twve.tcalc.vector.create(val[1],true);
if ( v0.length == 0 && v1.length == 0 ){
// Unknown objects.
if (config.options.chktwveTcalcDebugMode)
console.log(
'Evaluation error: Unknown objects '+
v0+' and '+v1+'.'
);
op = ''; val[0] = val[1] = null;
return null;
} else {
// At least one of them is a vector.
switch (op) {
case '+' :
result = v0.length > 0
? v0.add(v1).toString()
: v1.add(v0).toString();
break;
case '-' :
result = v0.length > 0
? v0.subtract(v1).toString()
: v1.subtract(v0).toString();
break;
//case '*' :
// Assuming dot product.
//result = v0.dot(v1).toString();
//break;
default :
// Undefined operation.
if (config.options.chktwveTcalcDebugMode)
console.log(
'Evaluation error: Undefined operator ('+
op+') for vectors.'
);
op = ''; val[0] = val[1] = null;
return null;
}
}
}
// Check result type and store it as the first operand
// for consecutive operations.
if (typeof result == 'number'){
val[0] = result;
result = ''+result;
} else if (typeof result == 'string') {
// result must be a string.
val[0] = result;
} else {
result = result.toString();
val[0] = result;
}
txt = txt.substring(0,pos)+result+txt.substring(chndx);
chndx = pos+result.length;
// Cleart the operation.
op = ''; val[1] = null;
return result;
};
function store_value(exp,explen){
// The exp should be a string of valid number format
// or time format.
result = typeof exp == 'string'
? (exp.indexOf(':')>=0 ? exp : parseFloat(exp))
: exp;
if ( val[0] === null || !op ) {
// First operand is empty, or there is no operator,
// record the position and store it.
pos = chndx;
val[0] = result;
if ( typeof explen == 'number' )
chndx += explen;
} else {
// First operand is stored, and there is an operator.
// Store the 2nd operand and carry the operation.
val[1] = result;
if ( typeof explen == 'number' )
chndx += explen;
evaluate_operation();
}
};
var partial = twve.tcalc.partialSearch, remained = '';
twve.tcalc.partialSearch = true;
for (var p=0,plen=twve.tcalc.opary.length; p<plen; p++){
chndx = start+pstyle;
subtxt = op = '';
val[0] = val[1] = null;
while ( chndx < txt.length ) {
if(twve.tcalc.inactive_block(txt,chndx,inc_tags)){
expression += (subtxt=txt.substring(
inc_tags.start.ndx,
inc_tags.end.ndx+inc_tags.end.matched.length
));
chndx += subtxt.length;
} else if((subtxt=twve.tcalc.isNumeric(txt,chndx))){
// A number
if((ch=subtxt.trim().charAt(0))=='-' &&
!op && val[0]!==null && p==1){
// A negative number, no operator, the first
// operand is stored, and we are in the
// addition/subtraction pass, take the negative
// sign as a subtraction operator.
op = ch;
subtxt = subtxt.substring(1);
chndx++;
}
store_value(subtxt,subtxt.length);
} else if((subtxt=twve.tcalc.isLogicalOperator(
txt,chndx
))){
//op = subtxt;
chndx += subtxt.length;
} else if((subtxt=twve.tcalc.vector.is(txt,chndx))){
// A vector
if ( subtxt.length == txt.length ) {
p = plen;
chndx += subtxt.length;
break;
}
store_value(
twve.tcalc.vector.create(subtxt,true),subtxt.length
);
} else {
ch = txt.charAt(chndx);
if (twve.tcalc.opary[p].indexOf(ch) >= 0){
op = ch;
} else if (! twve.tcalc.regwhite.test(ch) &&
twve.tcalc.opary[(p+1)%2].indexOf(ch)==-1) {
remained = txt.substring(chndx);
break;
}
chndx++;
}
}
}
twve.tcalc.partialSearch = partial;
result = txt.substring(start,chndx);
if ( callback )
callback({
text: txt,
position: start,
expression: expression,
result: result
});
return result+remained;
},
//}}}
/***
!!!! twve.tcalc.tmpValues
***/
//{{{
tmpValues : {
// Temperary values during the current evaluation.
storage : [],
indexOf : function(exp) {
// Binary search for the index of the expression.
// Copied from http://oli.me.uk/2013/06/08/searching-javascript-arrays-with-a-binary-search/
/*
* @param {*} searchElement The item to search for within the array.
* @return {Number} The index of the element which defaults to -1 when not found.
*/
var max = this.storage.length - 1;
var min = 0;
var cur;
var curexp;
while (min <= max) {
cur = (min + max) >>> 1;
curexp = this.storage[cur].expression;
if (curexp < exp) {
min = cur + 1;
} else if (curexp > exp) {
max = cur - 1;
} else {
return cur;
}
}
return ~max;
},
clearStorage : function(){
this.storage = [];
},
insertPos : -1,
getValue : function(exp){
if ( this.storage.length === 0 ) return null;
var ndx = this.indexOf(exp);
if ( ndx >= 0 )
return this.storage[ndx].value;
this.insertPos = Math.abs(ndx);
return null;
},
storeValue : function(exp,val){
if ( this.storage.length == 0 )
this.storage[0] = {
expression : exp,
value : val
};
else {
if ( this.insertPos < 0 )
this.insertPos = Math.abs(this.indexOf(exp));
this.storage.splice(this.insertPos,0,{
expression : exp,
value : val
});
//console.log('added '+this.insertPos+'/'+this.storage.length+' '+exp+'='+val);
this.insertPos = -1;
}
}
},
//}}}
/***
!!!! twve.tcalc.evaluate
***/
//{{{
evaluate : function(txt,start,js_eval,no_deref,inc_tags,callback){
// Evaluate a twve.tcalc expression.
// A valid twve.tcalc expression can be one or the mixture of
// the followings:
// 1. A simple arithmetic on numbers or table cells, such as
// =25/6, =A1+E4 or =(B3+C4)/D7, etc.
// 2. A twve.tcalc-defined function, such as
// =SUM(A1..D8) or PRODUCT(A1,C3,D5) or
// =AVERAGE(B3..B20;B30..B40;C3,E2), etc.
// 3. A valid Javascript math expression, such as
// =Math.sin(Math.PI) or =Math.sqrt(3), etc.
//
// The callback function shall receive a parameter object with the
// following properties:
// {
// position: index of the begining of expression (the equal sign),
// expression: the expression to be evaluated,
// result: the evaluated result
// }
// If provided, it will be called after the evaluation is done.
//
// The evaluation process is divided into the following passes:
// 1: Evaluate table cells and function calls.
// 2: Evaluate conditional assignments, and sub-expressions
// enclosed within parenthesis, brackets, curly brackets.
// 3: Evaluate constants, powers, multiplications and divisions.
// 4: Evaluate additions and subtractions.
//
// The first two passes are implemented in this function, while
// the rest are in the evaluate_arithmetics function defined
// above.
if ( ! txt ) return '';
var result = ''; //twve.tcalc.tmpValues.getValue(txt);
//if ( result !== null ) return result;
//console.log('evaluating ['+txt+']');
start = twve.position.getPosition(start);
var pstyle = twve.text.skipStyleText(txt,start,true);
var chndx = start+pstyle;
var expression = txt.substring(start,chndx);
var remained = '', pre_exp = '', nop = 0;
var next = 0, ch = '', subtxt = '';
var cur_twtable = twve.tcalc.cur_twtable;
var rows = cur_twtable && cur_twtable.is('table')
? cur_twtable.dom.querySelectorAll('tr')
: null;
if(!inc_tags) inc_tags = twve.tags.inactiveTags();
var plogical = -1, pquestion = -1, pcolon = -1;
var partial = twve.tcalc.partialSearch;
twve.tcalc.partialSearch = true;
//var tags = {
// '(' : twve.tags.nested.create('(',')'),
// '[' : twve.tags.nested.create('[',']'),
// '{' : twve.tags.nested.create('{','}')
//};
var openstr = '([{', popen;
var closestr = ')]}', pclose;
// Pass 1 and 2, evaluate table cells, function calls,
// and sub-expressions enclosed within parenthesis,
// brackets, and curly brackets.
while ( chndx < txt.length ) {
if(twve.tcalc.inactive_block(txt,chndx,inc_tags)){
expression += (subtxt=txt.substring(
inc_tags.start.ndx,
inc_tags.end.ndx+inc_tags.end.matched.length
));
chndx += subtxt.length;
}else if((subtxt=twve.tcalc.tiddlerReference(txt,chndx))){
// tiddler reference found, look for immediately
// following table reference
// Not implemented yet.
expression += subtxt;
chndx += subtxt.length;
subtxt=twve.tcalc.tableReference(txt,chndx);
expression += subtxt;
chndx += subtxt.length;
subtxt=twve.tcalc.cellReference(txt,chndx);
expression += subtxt;
chndx += subtxt.length;
}else if((subtxt=twve.tcalc.tableReference(txt,chndx))){
// table reference found, look for immediately following
// cell reference
pre_exp += subtxt;
if(cur_twtable && twve.node.matches(
cur_twtable.direct_wrapper.dom,'div.board'
)){
// A table in preview, don't know how to find the
// right table yet.
expression += subtxt;
chndx += subtxt.length;
subtxt=twve.tcalc.cellReference(txt,chndx);
expression += subtxt;
chndx += subtxt.length;
continue;
}
// Not in preivew, do the cross-table calculations.
ch = subtxt.charAt(0);
if ( ch == 't' || ch == 'T' ) {
// TableN format, referring a table with its
// index.
var rIndex=subtxt.substring(5,subtxt.length-1)*1;
var twtable = null;
if ( cur_twtable ) {
// Currently the cur_twtable is having the focus.
var cur_rIndex = cur_twtable.rIndex;
if ( rIndex == cur_rIndex ) {
// Same table, continue to process the cell
// reference.
expression += subtxt;
chndx += subtxt.length;
continue;
}
// Not the same one, find the target table.
var cur_dIndex = cur_twtable.dIndex;
cur_twtable.rIndex = rIndex;
cur_twtable.dIndex = -1;
twtable = twve.wrapper.renderedElement(
cur_twtable
);
cur_twtable.rIndex = cur_rIndex;
cur_twtable.dIndex = cur_dIndex;
} else if (twve.tcalc.cur_twwrap) {
// Currently no table is having the focus.
// It should be an expression in a non-table
// element.
var tables = twve.tcalc.cur_twwrap.findElements(
twve.table.twveSelector(null,'table')
);
if ( tables ) {
twtable = twve.table.create(tables[rIndex]);
}
}
if ( ! twtable ) {
expression += subtxt;
chndx += subtxt.length;
continue;
}
} else if ( ch == '"' || ch == "'" ) {
// Caption format, referring a table with its
// caption.
// Not yet implemented.
expression += subtxt;
chndx += subtxt.length;
subtxt=twve.tcalc.cellReference(txt,chndx);
expression += subtxt;
chndx += subtxt.length;
continue;
}
if ( !no_deref ) {
// To evaluate, look for the cell first.
next = chndx + subtxt.length;
if((subtxt=twve.tcalc.cellReference(txt,next))){
// Cell found, push the table to the calculation
// stack and do the calculation.
pre_exp += subtxt;
next += subtxt.length
twve.tcalc.prev_twtable.push(cur_twtable);
twve.tcalc.cur_twtable = twtable;
expression += twve.tcalc.cellContent(
twtable.dom.querySelectorAll('tr'),
subtxt,js_eval
);
twve.tcalc.cur_twtable=
twve.tcalc.prev_twtable.pop();
}
chndx = next;
}
}else if((subtxt=twve.tcalc.cellReference(txt,chndx))){
// cell reference found
if ( rows && !no_deref ) {
expression += twve.tcalc.cellContent(
rows,subtxt,js_eval
) || '0';
//console.log(subtxt+'=['+twve.tcalc.cellContent(rows,subtxt)+']');
} else
expression += subtxt;
chndx += subtxt.length;
}else if((subtxt=twve.tcalc.isFunction(txt,chndx))){
// No cell reference, but found some
// twve.tcalc-defined function.
//popen = subtxt.indexOf('(');
//pclose = subtxt.lastIndexOf(')');
//if ( pclose > popen + 1 ) {
// Function with arguments
// result = twve.tcalc.tmpValues.getValue(subtxt);
// if ( result === null ) {
// result = twve.tcalc.parseFunction(subtxt);
// twve.tcalc.tmpValues.storeValue(subtxt,result);
// }
// expression += result;
//} else
expression += twve.tcalc.parseFunction(subtxt);
js_eval = !twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval;
chndx += subtxt.length;
}else if((subtxt=twve.tcalc.isNumeric(txt,chndx))){
if ( expression !== '' && subtxt.charAt(0) == '-' )
nop++;
expression += subtxt;
if ( pquestion > -1 && pcolon == -1 ) {
// We are in the middle of collecting a-b
// conditional assignment.
pcolon = subtxt.indexOf(':');
if ( pcolon > -1 ) pcolon += chndx;
}
chndx += subtxt.length;
}else if((subtxt=twve.tcalc.isLogicalOperator(txt,chndx))){
plogical = chndx;
expression += subtxt;
chndx += subtxt.length;
//nop++;
}else if((popen=twve.text.skipStyleText(txt,chndx,true))){
expression += txt.substring(chndx,chndx+popen);
chndx += popen;
}else{
ch = txt.charAt(chndx);
if ( '+-*/'.indexOf(ch) > -1 ) {
expression += ch;
chndx++;
nop++;
} else if ( ' \t'.indexOf(ch) > -1 ) {
expression += ch;
chndx++;
} else if ( (popen=openstr.indexOf(ch)) > -1 ) {
// An opening parenthesis is found, look for its
// matched closing counterpart.
pclose = twve.tags.nested.matchedCloseTag(
txt,chndx+1,ch,closestr.charAt(popen)
);
if ( pclose.ndx == -1 ) {
// Unbalanced parentheses, stop here
remained = txt.substring(chndx);
break;
}
// We've found the mathed closing parenthesis,
// evaluate the expression in-between.
expression += popen == 0
? twve.tcalc.evaluate(
txt.substring(chndx+1,pclose.ndx),0,true
)
: txt.substring(chndx,pclose.ndx+1);
chndx = pclose.ndx+1;
} else if ( ch == '&' ) {
// Could be an HTML entity.
pclose = txt.indexOf(';',chndx+1);
if ( pclose > -1 ) {
// Yes this is an HTML entity, skip it.
expression += txt.substring(chndx,pclose+1);
chndx = pclose + 1;
} else {
// No this is not an HTML entity.
expression += ch;
chndx++;
}
} else if ( ch == '?' ) {
// A question mark, check for conditional assignment.
if ( pquestion > -1 ) {
// There was already a question mark before,
// stop here and evaluate.
remained = txt.substring(chndx);
break;
}
// Keep the this question mark and continue.
pquestion = expression.length;
expression += ch;
chndx++;
} else if ( ch == ':' ) {
// A colon mark, check for conditioanl assignment.
if ( pquestion == -1 ) {
// There was no question mark, stop here
// to evaluate.
remained = txt.substring(chndx);
break;
}
//
pcolon = expression.length;
expression += ch;
chndx++;
} else if ( plogical > -1 || pcolon > -1 ) {
// There was a logical operator, or a conditional
// assignment,
if ( '\n;'.indexOf(ch) > -1 ) {
// and this character is breaking the logic
// flow, stop here to evaluate;
remained = txt.substring(chndx);
break;
}
// otherwise keep it and continue.
expression += ch;
chndx++;
} else {
// Otherwise break to evaluate.
remained = txt.substring(chndx);
break;
}
}
}
twve.tcalc.partialSearch = partial;
if ( ! expression.trim() )
// No expression to evaluate, return it.
return expression+remained;
// All the remaining passes are implemented in
// twve.tcalc.evaluate_arithmetics().
if ( ! js_eval )
result = expression;
else if ( pcolon > pquestion+1 ) {
// Conditional assignment.
var TFexp = expression.substring(pstyle,pquestion);
//var TF = twve.tcalc.tmpValues.getValue(TFexp);
//if ( TF === null ) {
var TF = twve.tcalc.parseTrueFalse(
twve.tcalc.evaluate_arithmetics(TFexp,0,inc_tags)
);
// twve.tcalc.tmpValues.storeValue(TFexp,TF);
//}
var Texp = expression.substring(pquestion+1,pcolon);
//var Tresult = twve.tcalc.tmpValues.getValue(Texp);
//if ( Tresult === null ) {
var Tresult=twve.tcalc.evaluate_arithmetics(Texp,0,inc_tags);
// twve.tcalc.tmpValues.storeValue(Texp,Tresult);
//}
var Fexp = expression.substring(pcolon+1);
//var Fresult = twve.tcalc.tmpValues.getValue(Fexp);
//if ( Fresult === null ) {
var Fresult=twve.tcalc.evaluate_arithmetics(Fexp,0,inc_tags);
// twve.tcalc.tmpValues.storeValue(Fexp,Fresult);
//}
result = TF ? Tresult : Fresult;
//if ( ! remained )
// twve.tcalc.tmpValues.storeValue(txt,result);
} else if ( nop > 0 ) {
//result = twve.tcalc.tmpValues.getValue(expression);
//if ( result === null ) {
result = twve.tcalc.evaluate_arithmetics(
expression,0,inc_tags
);
// twve.tcalc.tmpValues.storeValue(expression,result);
//}
//if ( ! remained )
// twve.tcalc.tmpValues.storeValue(txt,result);
} else
result = expression;
if ( callback )
callback({
text: txt,
position: start,
expression: (pre_exp || expression),
result: (result+'')
});
return result + remained;
},
//}}}
/***
!!!! twve.tcalc.calculate
***/
//{{{
result_being_calculated : '',
calculate : function(txt,peq,js_eval,no_deref,callback){
//var rspace = txt.charAt(txt.length-1) == ' ';
if ( typeof peq != 'number' || peq < 0 ) peq = 0;
if ( txt.charAt(peq) != '=' )
peq = twve.tcalc.indexOfExpression(txt,peq);
twve.tcalc.result_being_calculated = '';
//var tc = new Date();
while (peq > -1) {
txt = txt.substring(0,peq)+twve.tcalc.evaluate(
txt,peq+1,js_eval,no_deref,null,function(param){
if ( callback ) {
param.position--;
param.expression = '='+param.expression;
callback(param);
}
});
if ( ! config.options.chktwveTcalcInTextCalc )
break;
twve.tcalc.result_being_calculated = txt;
peq = twve.tcalc.indexOfExpression(txt,peq);
}
//if ( config.options.chktwveTcalcDebugMode )
// displayMessage(
// 'calculation time: '+(new Date()-tc)+' ms'
// );
return txt;
},
//}}}
/***
!!!! twve.tcalc.calculateTextNode
***/
//{{{
calculateTextNode : function(node){
// Calculate the expressions in a text node.
var txt = node.nodeValue;
var nodestart = 0;
if ( txt ) {
twve.tcalc.calculate(txt,0,true,false,function(param){
var expnode = node.splitText(param.position-nodestart);
node = expnode.splitText(
param.expression.length
);
nodestart = param.position+param.result.length;
var resultnode = document.createElement('span');
resultnode.className = 'tcalc';
resultnode.appendChild(
document.createTextNode(param.result)
);
resultnode.title = param.expression;
node.parentNode.replaceChild(resultnode,expnode);
});
}
},
//}}}
/***
!!!! twve.tcalc.calculateNode
***/
//{{{
calculateNode : function(node,exclude){
// Calculate a dom node, excluding those specified by exlude (CSS expression).
if ( node.nodeType == 3 ) {
twve.tcalc.calculateTextNode(node);
} else if (!twve.node.matches(node,exclude)) {
var curNode = node.firstChild;
while ( curNode ){
node = curNode.nextSibling;
if ( curNode.nodeName!='BR' &&
! twve.node.matches(curNode,exclude) ){
if ( twve.MathJax ){
if (!twve.MathJax.inline.is(curNode)){
twve.tcalc.calculateNode(curNode,exclude);
}
} else
twve.tcalc.calculateNode(curNode,exclude);
}
curNode = node;
}
}
},
//}}}
/***
!!!! twve.tcalc.prepareElements
***/
//{{{
prePrepareElements : null,
cur_twwrap : null,
prepareElements : function(twwrap){
// Prepare elements in the wrapper.
if ( ! twve.tcalc.prePrepareElements.apply(this,arguments)
|| ! config.options.chktwveTcalcEnabled
|| ! config.options.chktwveTcalcInTextCalc
|| ! twwrap.tiddler )
return null;
// Calculate expressions in all elements
twve.tcalc.cur_twwrap = twwrap;
var sel = twve.selector.create();
sel.excludeSelector(
twve.table.wrapper.getSelector()+',CODE'
);
if ( twve.MathJax ){
sel.excludeSelector(
twve.MathJax.displayed.getSelector()
).excludeSelector(
twve.MathJax.inline.getSelector()
);
}
twve.tcalc.calculateNode(twwrap.dom,sel.exclude);
twve.tcalc.cur_twwrap = null;
return twwrap;
},
//}}}
/***
!!!! twve.tcalc.wikify
***/
//{{{
preWikify : null,
wikify : function(txt,elem,children) {
var focus = twve.tiddler.focusElem();
var node = focus ? focus.getElement() : null;
if ( node && twve.node.matches(node,'table') )
return twve.tcalc.preWikify.call(this,txt,elem,children);
if ( twve.node.matches(elem,'div.board') ) {
// Preview board
var no_deref = true;
if ( twve.node.matches(node,'th,td') ) {
no_deref = false;
twve.tcalc.cells_being_evaluated.push(node);
}
try {
var cur_twtable = twve.tcalc.cur_twtable;
if ( ! cur_twtable && focus && focus.is('table') )
twve.tcalc.cur_twtable = focus;
txt = twve.tcalc.calculate(txt,0,true,no_deref);
} catch(e){
} finally {
if ( ! cur_twtable ) twve.tcalc.cur_twtable = null;
if ( ! no_deref ) {
twve.tcalc.cells_being_evaluated.pop();
}
}
return twve.tcalc.preWikify.call(this,txt,elem,children);
} else {
// Regular element
elem = twve.tcalc.preWikify.apply(this,arguments);
if ( ! twve.node.matches(elem,'th,td') ) {
if ( elem.length )
for(var i=0,len=elem.length; i<len; i++)
twve.tcalc.calculateNode(elem[i]);
else
twve.tcalc.calculateNode(elem);
}
return elem;
}
},
//}}}
/***
!!!! twve.tcalc.cyclicReference
***/
//{{{
cells_being_evaluated : [],
cyclicReference : function ( cell ) {
for (var c=0; c<twve.tcalc.cells_being_evaluated.length; c++)
if ( twve.tcalc.cells_being_evaluated[c] == cell )
return 'Error: ' +
(c == twve.tcalc.cells_being_evaluated.length-1) ?
'Self reference.' :
'Cyclic reference.';
},
//}}}
/***
!!!! twve.tcalc.curCellCol
***/
//{{{
curCellCol : function(){
var cells = twve.tcalc.cells_being_evaluated;
return (cells && cells.length>0)
? twve.table.cellColIndex(cells[cells.length-1])
: -1;
},
//}}}
/***
!!!! twve.tcalc.curCellRow
***/
//{{{
curCellRow : function(){
var cells = twve.tcalc.cells_being_evaluated;
return (cells && cells.length>0)
? twve.table.cellRowIndex(cells[cells.length-1])
: -1;
},
//}}}
/***
!!!! twve.tcalc.sortableGridHtml
***/
//{{{
sortableGridHtml : function ( txt ) {
return '<a href="#" class="sortheader" onclick="config.macros.sortableGridPlugin.ts_resortTable(this);return false;">'
+ txt
+ '<span class="sortarrow"> </span></a>';
},
//}}}
/***
!!!! twve.tcalc.cellTitleExpression
***/
//{{{
cellTitleExpression : function (cell) {
if ( ! cell.title ) return -1;
var pn = cell.title.indexOf('\n');
if ( pn < 0 ) return -1;
// There is a \n in cell title, check its row-spanning property.
var rspan = twve.table.cellRowSpan(cell);
if ( rspan > 1 ) {
// A row spanned cell, there would be a total of (rspan-1)
// \n's in the cell title. Skip to the last one.
for ( var r=2; r<rspan; r++ )
pn = cell.title.indexOf('\n',pn+1);
}
// Now look for the signature of an expression, an equal sign.
var pe = cell.title.indexOf('=',pn+1);
return pe < 0 ? -1 : (pn+1);
// The above codes can be wrong when the equal sign is not really
// indicating an expression, such as within a URL, but that should
// be fine in most cases. If, however, we really need to consider
// the cases that could lead to a wrong determination, we can use
// var pe = twve.text.skipToActive(
// cell.title,'=',pn+1,null,twve.tags.inactiveTags()
// );
// to take care of that. For now I will stay with what it is and
// see how it goes. Vincent 2015/02/16
},
//}}}
/***
!!!! twve.tcalc.nodeText
***/
//{{{
nodeText : function(node,txt,thisonly) {
if ( ! node ) return '';
if ( txt === undefined || txt === null ) {
// Get the text.
if ( node.nodeType == 3 ) return node.nodeValue;
else if ( node.nodeName == 'BR' ) return '\n';
txt = '';
var child = node.childNodes;
if ( thisonly ) {
for ( var i=0,len=child.length; i<len; i++ )
if ( child[i].nodeType == 3 )
txt += child[i].nodeValue;
} else{
for ( var i=0,len=child.length; i<len; i++ )
txt += twve.tcalc.nodeText(node.childNodes[i]);
}
if ( txt
&& (config.tableSorting
|| config.macros.sortableGridPlugin) ) {
var p = txt.indexOf('\u2191');
if ( p == -1 ) p = txt.indexOf('\u2193');
if ( config.macros.sortableGridPlugin )
p -= 2;
if ( p > -1 ) txt = txt.substring(0,p);
}
switch ( node.style.textAlign || node.align ) {
case 'left' :
if(txt.charAt(txt.length-1)!==' ') txt += ' ';
break;
case 'right' :
if(txt.charAt(0)!==' ') txt = ' '+txt;
break;
case 'center' :
if(txt.charAt(0)!==' ') txt = ' '+txt;
if(txt.charAt(txt.length-1)!==' ') txt +=' ';
}
return txt;
} else {
// Set the text.
var p = -1;
while ( (p=txt.indexOf('\\',p+1)) >= 0 )
txt = txt.substring(0,p)+txt.substring(p+1);
if ( twve.table.cellRowIndex(node) == 0 ) {
if ( config.tableSorting ) {
var span = node.querySelector('span');
node.textContent = txt;
if ( span ) {
var html = span.innerHTML,
p = html.indexOf('\u2191');
if ( p == -1 ) p = html.indexOf('\u2193');
if ( p > -1 ) node.appendChild(span);
}
} else if ( config.macros.sortableGridPlugin ) {
var spanselector = 'a.sortheader span';
var span = node.querySelector(spanselector);
// This is copied from SortableGridPlugin
// for sorting purposes.
if ( span ) {
node.innerHTML =
twve.tcalc.sortableGridHtml(txt);
var newspan = node.querySelector(spanselector);
newspan.innerHTML = span.innerHTML;
newspan.setAttribute(
'sortdir', span.getAttribute('sortdir')
);
} else node.textContent = txt;
} else
node.textContent = txt;
} else if (node.nodeType == 3 ){
node.nodeValue = txt;
} else {
twve.node.wikify(txt,node);
}
}
},
//}}}
/***
!!!! twve.tcalc.evaluateCell
***/
//{{{
evaluateCell : function (cell,js_eval,ctxt,peq,force) {
// Evaluate a table cell
var cur_txt = '';
if ( ! force ) {
cur_txt = twve.tcalc.nodeText(cell);
if ( twve.tcalc.indexOfExpression(cur_txt) == -1 )
// There is no need to do calculations in this cell.
return cur_txt;
}
if ( ! ctxt ) {
ctxt = cur_txt ? cur_txt : twve.tcalc.nodeText(cell);
if ( !ctxt ) {
// No text in this cell, meaning no expressions at all.
if ( cell.title ) {
// If there is a cell title,
var p = twve.tcalc.cellTitleExpression(cell);
if ( p > 0 )
// and it contains an expression, remove it.
cell.title = cell.title.substring(0,p-1);
}
return '';
}
}
//console.log('already evaluated: ctxt=['+ctxt+']=['+cur_txt+']');
if (!peq) peq = twve.tcalc.indexOfExpression(ctxt);
var txt2;
if ( peq > -1 ) {
// There is an expression in the cell content.
if ( cell.title ) {
// We already has a title,
var p = twve.tcalc.cellTitleExpression(cell);
cell.title = (p > 0
? cell.title.substring(0,p)
: (cell.title+'\n'))
+ ctxt;
} else cell.title = ctxt;
var err = twve.tcalc.cyclicReference ( cell );
if ( err ) {
twve.tcalc.nodeText(
cell,
ctxt+'<br><br>'+cell.title+'<br>'+err
);
} else {
twve.tcalc.cells_being_evaluated.push(cell);
txt2 = twve.tcalc.calculate(ctxt,peq,js_eval);
/*
txt2 = twve.tcalc.isNumeric(
ctxt,0,-1,true,'',
config.options.chktwveTcalcThousandSeparated
);
*/
if ( txt2 != ctxt )
twve.tcalc.nodeText(cell,txt2);
twve.tcalc.cells_being_evaluated.pop();
return txt2;
}
} else {
//if ( cell.title ) cell.title = cell.title.split('\n')[0];
if ( cell.childNodes.length==1 &&
config.options.chktwveTcalcThousandSeparated &&
(txt2=twve.tcalc.isNumeric(ctxt,0,-1,true,'.',false)) ) {
if ( ctxt.trim() != txt2.trim() ) {
// The .trim() above is necessary because there could be
// white-spaces/line-breaks in the cell content.
var p = ctxt.indexOf(txt2);
twve.tcalc.nodeText(
cell,
(ctxt.substring(0,p) +
twve.tcalc.separateNumber(txt2) +
ctxt.substring(p+txt2.length))
);
return txt2;
} else {
twve.tcalc.nodeText(
cell,twve.tcalc.separateNumber(txt2)
);
return txt2;
}
}
}
return ctxt;
},
//}}}
/***
!!!! twve.tcalc.addOptionsMenu
***/
//{{{
addOptionsMenu : function(menu){
return menu.addMenu(
"''twve.tcalc'' Options",
config.macros.twveTcalcOptions.handler
);
},
//}}}
/***
!!!! twve.tcalc.getOptionsMenu
***/
//{{{
preGetOptionsMenu : null,
getOptionsMenu : function(item){
// Replacing the twve.tiddler.getOptionsMenu for twve.table.
var menu = twve.tcalc.preGetOptionsMenu.apply(this,arguments);
twve.tcalc.addOptionsMenu(menu);
return menu;
},
//}}}
/***
!!!! twve.tcalc.timer
***/
//{{{
timer : null,
//}}}
/***
!!!! twve.tcalc.findTimer
***/
//{{{
findTimer : function(twtable){
if ( twve.tcalc.timer )
for ( var t = twve.tcalc.timer.length-1; t >= 0; t-- ) {
var timer = twve.tcalc.timer[t];
if ( timer.table.wrapper_title == twtable.wrapper_title
&& timer.table.dIndex == twtable.dIndex ) {
return timer;
}
}
return null;
},
//}}}
/***
!!!! twve.tcalc.removeTimer
***/
//{{{
removeTimer : function(twtable){
var timer = twve.tcalc.findTimer(twtable);
if ( timer ) {
clearInterval(timer.pid);
twve.tcalc.timer.splice(
twve.tcalc.timer.indexOf(timer),1
);
}
return timer;
},
//}}}
/***
!!!! twve.tcalc.addTimer
***/
//{{{
addTimer : function(twtable,interval){
if ( ! twve.tcalc.timer )
twve.tcalc.timer = [];
else
twve.tcalc.removeTimer(twtable);
var timer = twve.object.create();
timer.table = twtable;
timer.pid = setInterval(function(){
twtable.calculate('',true);
},interval);
timer.interval = interval;
twve.tcalc.timer.push(timer);
return timer;
},
//}}}
/***
!!!! twve.tcalc.getTableMenu
***/
//{{{
preGetTableMenu : null,
getTableMenu : function(){
var menu = twve.tcalc.preGetTableMenu.apply(this,arguments);
twve.tcalc.addOptionsMenu(menu);
var label = 'Recalculate';
if ( ! menu.findItem(label) ) {
var item = menu.addItem(
label,'Recalculate this table'
);
// create sub menu for recalculation
item.submenu = twve.menu.create(item,true);
// Add the 1st item "now".
item.submenu.addItem(
'now (and stop the timer)'
).click = function(){
var twtable = twve.tiddler.focusElem();
twve.tcalc.removeTimer(twtable);
twtable.calculate('',true);
};
// Then the 2nd item "every 1 sec".
item.submenu.addItem(
'every 1 sec (start the timer)'
).click = function(){
twve.tcalc.addTimer(twve.tiddler.focusElem(),1000);
};
// And the 3rd item "every 30 sec".
item.submenu.addItem(
'every 30 sec (start the timer)'
).click = function(){
var twtable = twve.tiddler.focusElem();
twtable.calculate('',true);
twve.tcalc.addTimer(twtable,30000);
};
}
return menu;
},
//}}}
/***
!!!! twve.tcalc.tableCreate
***/
//{{{
preTableCreate : null,
tableCreate : function(){
var twtable = twve.tcalc.preTableCreate.apply(this,arguments);
// startSorting
var preStartSorting = twtable.startSorting;
twtable.startSorting = function() {
return (twve.tcalc.block_update =
preStartSorting.apply(this,arguments));
};
// refreshSelf
var preRefreshSelf = twtable.refreshSelf;
twtable.refreshSelf = function(){
if ( ! twtable.editNode ) {
var timer = twve.tcalc.removeTimer(twtable);
preRefreshSelf.apply(this,arguments);
if ( timer )
twve.tcalc.addTimer(twtable,timer.interval);
} else
preRefreshSelf.apply(this,arguments);
return twtable;
};
// cellReferenceTitle
twtable.cellReferenceTitle = function ( cell, r, c ) {
var span = twve.table.cellSpans(cell);
var ref = '';
// Prepare cell references
for ( var rr = 0; rr < span.row; rr++ ) {
for ( var cc = span.col-1; cc >= 0; cc-- ) {
ref += twve.tcalc.indexToReferenceCol(c-cc)+(r+rr);
if ( cc > 0 ) ref += ' | ';
}
if ( rr < span.row-1 ) ref += '\n';
}
// Set the cell title
if ( cell.title ) {
// If the cell already has a title,
if ( cell.title != ref ) {
// that is different from the new one, then we change
// the current title.
var p = twve.tcalc.cellTitleExpression(cell);
cell.title = ref + (p > 0
? cell.title.substring(p-1)
: ('\n'+cell.title));
}
} else cell.title = ref;
};
// referenceToTest
twtable.referenceToTest = function (r, c) {
var refr=twve.tcalc.indexToReferenceRow(r);
var refc=twve.tcalc.indexToReferenceCol(c);
return {
ndx:{row:r, col:c},
rel:(refc+refr),
abs:(refc+'$'+refr)
};
};
// includeCell
twtable.includeCell = function (args,r,c) {
args = twve.tcalc.splitArguments(args);
for (var i=0; i<args.length; i++){
var arg=twve.tcalc.allArguments(args[i]);
if ( arg ) {
if (twtable.includeCell (arg)) return true;
} else {
arg = args[i];
arg = twve.text.removeStyleText(arg).trim();
if ( arg.indexOf(':') > -1 ) {
var ndx=twve.tcalc.consecutiveCells(arg);
if ((ndx.start.row<=r) && (r<=ndx.end.row)
&& (ndx.start.col<=c) && (c<=ndx.end.col))
return true;
}
}
}
return false;
};
//}}}
/***
!!!!! twtable.isReferring
***/
//{{{
/*
twtable.isReferring = function(ctxt,ref){
// Tests if a cell text (ctxt) is referring the cell reference ref.
// The cell reference must be an object returned by
// twtable.referenceToTest().
var pref = -1;
do {
pref = ctxt.indexOf(ref.rel,pref+1);
if ( pref == -1 ) prel = ctxt.indexOf(ref.abs);
if ( pref == -1 ) return false;
// There is something like ref in ctxt, make sure it is
// an exact match.
var rtxt = twve.tcalc.cellReference(ctxt,pref);
if ( rtxt == ref.rel || rtxt == ref.abs ) return true;
} while ( true );
};
*/
//}}}
/***
!!!!! twtable.calculate
***/
//{{{
twtable.calculate = function (txt,changed) {
// Calculates a table represented by twtable.
// Arguments:
// txt -- (Optional) The text (containing the table).
// Normally twtable already contains the
// tiddler text so you are fine to leave
// this argument undefined. However, in
// cases where the modified text hasn't been
// saved back to twtable, the modified text
// can be passed in here.
// changed -- (Optional) An object containing the
// (row, col) indexes of the changed cells.
// It is given when the table was not
// refreshed before calling this function.
// In such cases we need to first clear the
// calculated results and then do the
// calculations.
if ( ! (config.options.chktwveTcalcEnabled
|| config.options.chktwveTcalcAllTables
|| twtable.hasClass('spreadsheet')) )
return false;
// Deactivate the MathJax handler if installed.
if ( twve.MathJax ) twve.MathJax.enabled = false;
// Announcing calculation if desired.
if ( config.options.chktwveTcalcDebugMode )
displayMessage(
'Calculating table: '+twtable.getCaption()
);
if ( ! txt ) {
txt = twtable.getText();
if ( txt.charAt(txt.length-1) != '|' )
// We are in the preview board and an extract
// character has been inserted to mimic the caret.
txt += '|';
}
// Temporarily disable the IncludeCSS option
var includeCSS = config.options.chktwveTableIncludeCSS;
config.options.chktwveTableIncludeCSS = false;
// Record the beginning time of preparation.
var tp = new Date();
// Start preparation
// Catch the table for calculation
twve.tcalc.cur_twtable = twtable;
// Get the table cell positions in the wiki text.
var cpos = twve.table.allCells(txt,0,txt.length);
// Table rows.
var tr = twtable.dom.querySelectorAll('tr');
// Row cells.
var trc = [];
for (var r=0,rlen=tr.length; r<rlen; r++){
trc[r] = tr[r].querySelectorAll('th,td');
}
/*
if ( changed ) {
// Changed cell is specified, clear the calculated
// results.
var ti = new Date();
for (var r=0,rlen=tr.length; r<rlen; r++){
for (var c=0,clen=trc[r].length; c<clen; c++){
var cell = trc[r][c];
var cndx = twve.table.cellColIndex(cell);
var ctxt = cpos[r][cndx]
? twtable.getCellText(cpos[r][cndx],txt)
: '';
// Leave for empty cells
if ( ! ctxt ) continue;
// Check for formulas to calculate.
var peq = twve.tcalc.indexOfExpression(ctxt);
if ( peq >= 0 )
// If the cell text contains a formula,
// put it back for recalculation.
twve.tcalc.nodeText(cell,ctxt);
}
}
// Report clear time if desired.
if ( config.options.chktwveTcalcDebugMode )
displayMessage(
'clear time: '+(new Date()-ti)+' ms'
);
}
*/
// Calculate preparation time.
tp = new Date() - tp;
// Start the calculation
var tc = new Date(); // Record the beginning time.
// Calculate caption
var ctxt = twtable.getCaption(txt);
var peq = twve.tcalc.indexOfExpression(ctxt);
if ( peq >= 0 )
// If the caption text contains a formula
twve.node.wikify(
twve.tcalc.calculate(ctxt,peq,true),
twtable.wrappers.cap
);
// Calculate cells
for (var r=0,rlen=tr.length; r<rlen; r++){
for (var c=0,clen=trc[r].length; c<clen; c++){
var cell = trc[r][c];
//var rndx = twve.table.cellRowIndex(cell);
var cndx = twve.table.cellColIndex(cell);
ctxt = cpos[r][cndx]
? twtable.getCellText(cpos[r][cndx],txt)
: '';
twtable.cellReferenceTitle(cell,r,cndx);
// Leave for empty cells
if ( ! ctxt ) continue;
// Check for formulas to calculate.
peq = twve.tcalc.indexOfExpression(ctxt);
if ( peq >= 0 ) {
// If the cell text contains a formula
//console.log('calculating cell['+r+','+c+')');
//try {
twve.tcalc.evaluateCell(
cell,true,ctxt,peq,changed
);
//} catch ( e ) {
// console.log(e);
// if ( config.options.chktwveTcalcDebugMode ) {
// twve.tcalc.nodeText(
// cell,
// twve.tcalc.nodeText(cell) +
// '<br><br>'+expression+'<br>'+e
// );
// }
//}
}
}
}
// End of calculation.
// Calculate the time of calculation.
tc = new Date() - tc;
// Release the table.
twve.tcalc.cur_twtable = null;
// Report preparation/calculation time if desired.
if ( config.options.chktwveTcalcDebugMode ) {
displayMessage(
'preparation time: '+tp+' ms'
);
displayMessage(
'calculation time: '+tc+' ms'
);
displayMessage(
'total time: '+(tc+tp)+' ms'
);
}
// Restore the IncludeCSS option.
config.options.chktwveTableIncludeCSS = includeCSS;
// Reactivate the MathJax handler if installed.
if ( twve.MathJax ) twve.MathJax.enabled = true;
// Clear temperary storage.
twve.tcalc.tmpValues.clearStorage();
return true;
};
//}}}
/***
!!!!! twtable.initRows
***/
//{{{
var preInitRows = twtable.initRows;
twtable.initRows = function (txt) {
if ( preInitRows.apply(this,arguments) ) {
if ( config.options.chktwveTcalcEnabled
&& (config.options.chktwveTcalcAllTables
|| twtable.hasClass('spreadsheet')) ) {
// Do calculations.
twtable.calculate(txt);
}
}
return true;
};
//}}}
/***
!!!!! twtable.changed
***/
//{{{
var preChanged = twtable.changed;
twtable.changed = function (param) {
var txt = param.text;
if (!txt) return preChanged.call(this,param);
switch ( param.what ) {
case 'CAPTION MODIFIED' :
case 'MODIFIED' :
twtable.calculate(txt,true);
break;
case 'ROW INSERTED' :
param.text = twve.tcalc.incCellReference(
txt,param.where,1
);
break;
case 'ROW DELETED' :
param.text = twve.tcalc.incCellReference(
txt,param.where+1,-1
);
break;
case 'ROW PASTED' :
// Adjust the relative row references in the just
// pasted row.
twve.tcalc.incReferencesInRow(
txt,param.where,param.from,param.where
);
break;
case 'COL INSERTED' :
param.text = twve.tcalc.incCellReference(
txt,null,null,0,-1,param.where,1
);
break;
case 'COL DELETED' :
param.text = twve.tcalc.incCellReference(
txt,null,null,0,-1,param.where+1,-1
);
break;
case 'COL PASTED' :
// Adjust the relative column references in the just
// exchanged column.
param.text = twve.tcalc.incCellReference(
txt, null, null,
param.where, param.where, 0,
(param.where-param.from)
);
break;
case 'ROW MOVED' :
// During sorting process.
// Adjust the relative row references in the just
// moved row.
txt = twve.tcalc.incReferencesInRow(
txt,param.to,param.from,param.to
);
// Then adjust the absolute row references referring
// to the just moved row.
param.text = twve.tcalc.updateCellReference(
txt,param.from,null,param.to,null,'abs'
);
break;
case 'ROW EXCHANGED' :
twve.tcalc.startBlockUpdate();
// Adjust the relative row references in the just
// exchanged rows.
txt = twve.tcalc.incReferencesInRow(
txt,param.to,param.from,param.to
);
txt = twve.tcalc.incReferencesInRow(
txt,param.from,param.to,param.from
);
// Then adjust the absolute row references referring
// to the just exchanged rows.
txt = twve.tcalc.updateCellReference(
txt,param.from,null,param.to,null,'abs'
);
txt = twve.tcalc.updateCellReference(
txt,param.to,null,param.from,null,'abs'
);
param.text = twve.tcalc.endBlockUpdate(txt);
break;
case 'COL EXCHANGED' :
twve.tcalc.startBlockUpdate();
// Adjust the relative column references in the just
// exchanged columns.
txt = twve.tcalc.incCellReference(
txt, null, null,
param.to, param.to, 0,
(param.to-param.from)
);
txt = twve.tcalc.incCellReference(
txt, null, null,
param.from, param.from, 0,
(param.from-param.to)
);
// Then adjust the absolute column references referring
// to the just exchanged columns.
txt = twve.tcalc.updateCellReference(
txt,null,param.from,null,param.to,'abs'
);
txt = twve.tcalc.updateCellReference(
txt,null,param.to,null,param.from,'abs'
);
param.text = twve.tcalc.endBlockUpdate(txt);
break;
case 'SORTED' :
param.text = twve.tcalc.endBlockUpdate(txt);
twtable.calculate(param.text);
break;
case 'TRANSPOSED' :
param.text = twve.tcalc.transposed(txt);
break;
}
return preChanged.call(this,param);
};
//}}}
/***
!!!!! twtable.copyOrCut
***/
//{{{
twtable.copyOrCut = function (ta,cut) {
if ( twve.node.matches(twtable.editNode,'th,td') ) {
twve.tcalc.copied_from = twtable.editNode;
twve.tcalc.cut_from = cut ? twtable.editNode : null;
}
};
//}}}
/***
!!!!! twtable.pasateIn
***/
//{{{
twtable.pasteIn = function (ta) {
// Auto adjustment of cell reference upon copy-and-pasting
// within the same table.
var from_cell = twve.tcalc.copied_from;
if ( from_cell ) {
// Something pasted from another cell.
var title_from = from_cell.title.split('\n');
var r_from=twve.tcalc.referenceToIndexRow(title_from[0]);
var c_from=twve.tcalc.referenceToIndexCol(title_from[0]);
var title_to = twtable.editNode.title.split('\n');
var r_to = twve.tcalc.referenceToIndexRow(title_to[0]);
var c_to = twve.tcalc.referenceToIndexCol(title_to[0]);
if ( title_from.length > 1 ) {
// The source cell contains an expression for
// calculations. Obtain the paste-in text and adjust
// cell reference if necessary.
var txt = ta.value, ref, newref;
var r, c, abs_r, abs_c;
var chndx = twve.tcalc.indexOfExpression(txt)+1;
if ( chndx > 0 ) {
while ( chndx < txt.length ) {
if((ref=twve.tcalc.tiddlerReference(txt,chndx)))
chndx += ref.length;
if((ref=twve.tcalc.tableReference(txt,chndx)))
chndx += ref.length;
if((ref=twve.tcalc.cellReference(txt,chndx))){
r = twve.tcalc.referenceToIndexRow(ref);
abs_r = twve.tcalc.absoluteRow(ref);
c = twve.tcalc.referenceToIndexCol(ref);
abs_c = twve.tcalc.absoluteCol(ref);
if (!abs_r) r = r_to + (r-r_from);
if (!abs_c) c = c_to + (c-c_from);
newref = c>=0
? twve.tcalc.indexToReferenceCol(c,abs_c)
: '{'+c+'}';
newref += r>=0
? ((abs_r?'$':'')+r)
: '{'+r+'}';
txt=txt.substring(0,chndx)+
newref+
txt.substring(chndx+ref.length);
chndx += newref.length;
} else chndx++;
}
ta.value = txt;
}
} else {
// The source cell contains regular wikitext.
twve.tcalc.copied_from = null;
}
if ( twve.tcalc.cut_from &&
twve.tcalc.nodeText(twve.tcalc.cut_from)=='' ) {
// The last cell was fully cut and then pasted
// to this one.
var table = twve.table.create(twtable.dom);
var txt = twtable.getText();
txt = twve.tcalc.updateCellReference(
txt,table.start,table.end,
r_from,c_from,r_to,c_to
);
table.setText(txt);
}
twve.tiddler.previewEditBox(ta);
}
};
//}}}
/***
!!!!! End of twtable.
***/
//{{{
return twtable;
}
};
//}}}
/***
!!!! twve.tcalc.vector
***/
//{{{
twve.tcalc.vector = {
//}}}
/***
!!!! twve.tcalc.vector.is
***/
//{{{
is : function(src,start){
if ( typeof src == 'string' ) {
var pl = twve.position.getPosition(start);
// Skip the leading white spaces, tabs and \n's.
while ( pl < src.length &&
' \t\n'.indexOf(src.charAt(pl))>-1 ) pl++;
// If src does not start from a '(', stop here.
if ( src.charAt(pl) !== '(' ) return '';
// Look for its matched ')'.
var pr = twve.tags.nested.matchedCloseTag(
src,pl+1,'(',')'
);
// If there is no ')' found, stop here.
if ( pr.ndx == -1 ) return '';
// Make sure there are comma's in the parentheses.
var p = src.indexOf(',',pl+1);
return (p>pl+1 && p<pr.ndx)
? src.substring(pl,pr.ndx+1) : '';
} else
return src.dot && src.cross;
},
//}}}
/***
!!!! twve.tcalc.vector.create
***/
//{{{
create : function(src,tested){
var v = [];
//}}}
/***
!!!!! vector.dim
***/
//{{{
v.dim = function(){
return v.length;
};
//}}}
/***
!!!!! vector.add
***/
//{{{
v.add = function(u){
var vlen = v.length;
var i;
if ( typeof u == 'number' ){
// Addition of a scalar to this vector.
for(i=0; i<vlen; i++)
v[i] += u;
} else {
// Addition of two vectors.
var ulen = u.length;
if ( vlen >= ulen ) {
// v has more components
for (i=0; i<ulen; i++)
v[i] += u[i];
} else {
// u has more components
for (i=0; i<vlen; i++)
v[i] += u[i];
for ( ; i<ulen; i++)
v[i] = u[i];
}
}
return v;
};
//}}}
/***
!!!!! vector.subtract
***/
//{{{
v.subtract = function(u){
var vlen = v.length;
var i;
if ( typeof u == 'number' ) {
// Subtraction of a scalar from this vector.
for(i=0; i<vlen; i++)
v[i] -= u;
} else {
// Subtraction of two vectors.
var ulen = u.length;
if ( vlen >= ulen ) {
// v has more components
for (i=0; i<ulen; i++)
v[i] -= u[i];
} else {
// u has more components
for (i=0; i<vlen; i++)
v[i] -= u[i];
for ( ; i<ulen; i++)
v[i] = -u[i];
}
}
return v;
};
//}}}
/***
!!!!! vector.multiply
***/
//{{{
v.multiply = function(s){
// Multiplication with a scalar s.
for (var i=0,len=v.length; i<len; i++)
v[i] *= s;
return v;
};
//}}}
/***
!!!!! vector.divideBy
***/
//{{{
v.divideBy = function(s){
// Division by a scalar s.
for (var i=0,len=v.length; i<len; i++)
v[i] /= s;
return v;
};
//}}}
/***
!!!!! vector.dot
***/
//{{{
v.dot = function(u){
// v dot u
var len = Math.min(v.length,u.length);
var result = 0;
for (var i=0; i<len; i++)
result += v[i] * u[i];
return result;
};
//}}}
/***
!!!!! vector.cross
***/
//{{{
v.cross = function(u){
// v cross u
// Currently we only implement the cross product in 3-D world.
// Later we may extend it to higher dimensions.
var vlen = v.length;
var ulen = u.length;
if ( vlen == ulen ) {
switch (vlen) {
case 3 :
var w = twve.tcalc.vector.create();
w[0] = v[1]*u[2] - v[2]*u[1];
w[1] = v[2]*u[0] - v[0]*u[2];
w[2] = v[0]*u[1] - v[1]*u[0];
return w;
}
}
return null;
};
//}}}
/***
!!!!! vector.toString
***/
//{{{
v.toString = function(){
var result = '(';
for (var i=0,len=v.length-1; i<len; i++)
result += v[i]+', ';
return result+v[len]+')'
};
//}}}
/***
!!!!! End of vector definition.
***/
//{{{
if ( ! tested ) tested = twve.tcalc.vector.is(src);
if ( tested ) {
if ( typeof src === 'string' ) {
// src is a string in the form of (v1, v2, v3, ...)
var pl = src.indexOf('(')+1;
var n = 0;
while ( true ) {
var pr = src.indexOf(',',pl);
if ( pr > -1 ) {
v[n++] = parseFloat(src.substring(pl,pr));
pl = pr+1;
} else {
v[n] = parseFloat(
src.substring(pl,src.length-1)
);
break;
}
}
} else {
// src is a vector
for (var i=0,len=src.length; i<len; i++)
v[i] = src[i];
}
}
return v;
}
};
//}}}
/***
!!!! twve.tcalc.addNumeral
***/
//{{{
twve.tcalc.addNumeral = function(result,numtxt){
if ( result ) {
var ch = numtxt.charAt(0);
result += (ch=='+' || ch=='-')
? numtxt
: ('+'+numtxt);
} else
result = numtxt;
return result;
};
//}}}
/***
!!!! twve.tcalc.getSourceText
***/
//{{{
twve.tcalc.getSourceText = function(txt){
if ( typeof txt != 'string' ) return txt;
switch ( txt.toLowerCase() ) {
case 'this' :
// Content of this cell being evaluated.
if ( twve.tcalc.result_being_calculated )
return twve.tcalc.result_being_calculated;
var cells = twve.tcalc.cells_being_evaluated;
return twve.tcalc.result_being_calculated ||
twve.tcalc.nodeText(cells[cells.length-1]);
case 'caption' :
case 'title' :
// Caption of the table.
if ( twve.tcalc.cur_twtable )
return twve.tcalc.cur_twtable.getCaption();
default :
var url = twve.url.create(txt);
return url && url.getTarget()
? url.getContent()
: txt;
}
};
//}}}
/***
!!!! twve.tcalc.valueIn
***/
//{{{
twve.tcalc.valueIn = function(param){
// Find the param.ndx(th) numerical value in param.txt
var p = twve.position.create(0);
if ( param.curp === undefined )
param.curp = twve.position.create(0);
else {
p.ndx = param.curp.ndx;
if ( p.ndx < 0 ) p.ndx = 0;
}
if (typeof param.curn != 'number' || param.curn < -1)
param.curn = -1;
var len = param.txt.length;
var numtxt = '';
var tags = twve.tags.nested.create(
['(', '[', '{'],
[')', ']', '}']
);
while ( true ) {
var partial = twve.tcalc.partialSearch;
twve.tcalc.partialSearch = true;
while ( p.ndx < len && ! numtxt ) {
if(tags.open.indexOf(param.txt.charAt(p.ndx))>=0){
p.ndx++;
p = tags.matchedCloseTag(param.txt,p);
if ( p.ndx < 0 ) return '';
p.ndx++;
}
numtxt = twve.tcalc.isNumeric(param.txt,p.ndx++);
}
twve.tcalc.partialSearch = partial;
if ( numtxt ) {
p.ndx += numtxt.length-1;
// The -1 above is because of the p++ in the above
// while loop.
if ( ++param.curn == param.ndx ) {
param.curp.ndx = p.ndx;
p.ndx = numtxt.length;
return twve.tcalc.isPartsPerNotation(
numtxt,0,p.ndx,p.ndx-1
) || numtxt;
}
numtxt = '';
} else
return '';
}
};
//}}}
/***
!!!! twve.tcalc.matchPreceding
***/
//{{{
twve.tcalc.matchPreceding = function(param){
param.curp = twve.text.indexOf(
param.txt,param.preceding,param.curp
);
if ( param.curp.ndx < 0 ) return '';
param.curp.ndx += param.curp.matched.length;
var result = '';
if ( param.everything ) {
// Returns everything after param.preceding.
result = param.txt.substring(param.curp.ndx);
} else {
// Look for numeric value that is immediately preceded by
// param.preceding.
var partial = twve.tcalc.partialSearch;
twve.tcalc.partialSearch = true;
result = twve.tcalc.isNumeric(
param.txt,param.curp.ndx
);
twve.tcalc.partialSearch = partial;
if ( result && result.indexOf('\n')<0 ) {
// There is numeric value immedialy following the preceding.
param.curp.ndx += result.length;
}
}
return result;
};
//}}}
/***
!!!! twve.tcalc.matchFollowing
***/
//{{{
twve.tcalc.matchFollowing = function(param,all){
// Check if the following is matched in param.txt at index
// param.curp.ndx.
if ( ! param.following ) return '';
// The following is given.
// Look for numerical value that is immediately followed by the
//
// Skip the white spaces at index p.
var regwhite = /\s/, ch;
while (regwhite.test((ch=param.txt.charAt(param.curp.ndx)))){
param.curp.ndx++;
//if ( ch == '\n' ) return null;
}
if ( ! param.post )
param.post = twve.position.create(param.curp);
else
param.post.ndx = param.curp.ndx;
// Check if matched.
param.post = twve.text.indexOf(
param.txt,param.following,param.post
);
if(param.post.ndx<0 ||
(!all && param.post.ndx!=param.curp.ndx))
return null;
// Found a match.
param.curp.ndx = param.post.ndx+param.post.matched.length;
return param.post.matched;
};
//}}}
/***
!!!! twve.tcalc.valueWithText
***/
//{{{
twve.tcalc.valueWithText = function(param){
var result = '';
if ( param.preceding && param.preceding != 'null' ) {
// The preceding is given, look for numerical values
// immediately preceded by the preceding.
while ( true ) {
var numtxt = twve.tcalc.matchPreceding(param);
if ( param.curp.ndx < 0 )
// No more preceding found, stop it.
break;
if ( ! numtxt )
// A preceding text is found but not immediately
// followed by a number, go on to the next.
continue;
// A preceding immediately followed by a number is found,
// return it if appropriate.
if(typeof twve.tcalc.matchFollowing(param)=='string'){
result = numtxt;
break;
}
}
} else {
// The preceding is not given, look for all numerical values
// and return the first one that matches.
param.ndx = 0;
param.curn = -1;
while ( true ) {
var numtxt = twve.tcalc.valueIn(param);
if ( ! numtxt ) break;
if(typeof twve.tcalc.matchFollowing(param)=='string'){
result = numtxt;
break;
}
param.ndx++;
}
}
return result;
};
//}}}
/***
!!!! twve.tcalc.sumWithText
***/
//{{{
twve.tcalc.sumWithText = function(param){
var result = '';
if ( param.preceding && param.preceding != 'null' ) {
// The preceding is given, look for numerical values
// immediately preceded by the preceding.
while ( true ) {
var numtxt = twve.tcalc.matchPreceding(param);
if ( param.curp.ndx < 0 )
// No more preceding found, stop.
break;
if ( ! numtxt )
// A preceding text is found but not followed by a
// number, go on to the next.
continue;
if(typeof twve.tcalc.matchFollowing(param)=='string')
result = twve.tcalc.addNumeral(result,numtxt);
}
} else {
// The preceding is not given, look for all numerical values
// and check the following if given.
param.ndx = param.curp.ndx = 0; param.curn = -1;
while (true) {
var numtxt = twve.tcalc.valueIn(param);
if ( ! numtxt ) break;
if(typeof twve.tcalc.matchFollowing(param) == 'string')
result = twve.tcalc.addNumeral(result,numtxt);
// Check the next one.
param.ndx++;
};
}
return result
? (twve.tcalc.evaluate_arithmetics(result)+'')
: '0';
};
//}}}
/***
!!!! twve.tcalc.arrayOrText
***/
//{{{
twve.tcalc.arrayOrText = function(txt){
// txt = txt.trim();
var close = txt.length-1;
if(txt.charAt(0)=='[' &&
txt.charAt(close)==']' &&
txt.indexOf(',')>=0){
// An array of preceding or following
txt = txt.substring(1,close).split(',');
}
return txt;
};
//}}}
/***
!!!! twve.tcalc.parseTrueFalse
***/
//{{{
twve.tcalc.parseTrueFalse = function(txt){
if ( !txt || txt=='0' ) return false;
if (typeof txt != 'string') return txt;
txt = txt.toLowerCase();
if ( txt == 'false' ) return false;
if ( txt == 'true' ) return true;
var opary = [
'===', '!==',
'==', '!=', '>=', '<=', // '||', '&&'
'>', '<'
];
var i = 0, len = opary.length, p = 0;
var op = '', left = '', right = '', tmp = '';
for (i=0; i<len; i++){
p = txt.indexOf(opary[i]);
if ( p == 0 ) return false;
if ( p < 0 ) continue;
op = opary[i];
left = txt.substring(0,p).trim();
if((tmp=twve.tcalc.isNumeric(left)) && tmp==left){
left = parseFloat(left);
}
right = txt.substring(p+op.length).trim();
if((tmp=twve.tcalc.isNumeric(right)) && tmp==right){
right = parseFloat(right);
}
break;
}
switch (op){
case '===' : return left === right;
case '!==' : return left !== right;
case '==' : return left == right;
case '!=' : return left != right;
case '>=' : return left >= right;
case '<=' : return left <= right;
case '>' : return left > right;
case '<' : return left < right;
default : return true;
}
};
//}}}
/***
!!!! twve.tcalc.round
***/
//{{{
twve.tcalc.round = function(value,num_dec){
if ( num_dec===null || num_dec===undefined || num_dec===0 ) {
return Math.round(value);
} else {
var factor = Math.pow(10,num_dec);
return Math.round(factor*value)/factor;
}
};
//}}}
/***
!!!! twve.tcalc.factorial
***/
//{{{
twve.tcalc.factorial = function(n){
var result = 1;
for (var i=Math.round(n); i>1; i--) result *= i;
return result;
};
//}}}
/***
!!!! twve.tcalc.days_of_month
***/
//{{{
twve.tcalc.days_in_month = function(year,month){
return new Date(year,month+1,0).getDate();
};
//}}}
/***
!!!! twve.tcalc.same_year_month
***/
//{{{
twve.tcalc.same_year_month = function(d1,d2){
return d1.getFullYear() == d2.getFullYear() &&
d1.getMonth() == d2.getMonth();
};
//}}}
/***
!!!! twve.tcalc.separator_positions
***/
//{{{
twve.tcalc.separator_positions = function(txt,start,sep,verify){
var psep = [];
var pos = twve.position.create(start);
var pattern = sep;
while ( true ) {
pos = twve.text.indexOf(txt,pattern,pos);
if ( pos.ndx < 0 ) break;
if ( ! verify || verify.call(this,pos) ) {
psep[psep.length] = twve.position.create(pos);
pattern = pos.matched;
}
pos.ndx += pos.matched.length;
}
return psep;
};
//}}}
/***
!!!! twve.tcalc.timestr_after_date
***/
//{{{
twve.tcalc.timestr_after_date = function(txt,start){
var sep = ' \t\nT';
var len = txt.length;
while ( true ) {
if (start === len) return -1;
if (sep.indexOf(txt.charAt(start))>-1) return start;
start++;
}
};
//}}}
/***
!!!! twve.tcalc.parse_time
***/
//{{{
twve.tcalc.time_separator = ':';
twve.tcalc.parse_time = function(txt){
// Parse a time string. This function assumes the following time format:
// hh:mm:ss,
// either hh:mm or mm:ss,
// any one of hh or mm or ss
//
// Returns an array
// a. of length 3, if hh:mm:ss is given, with [0] holding the hh,
// [1] holding the mm, while [2] is the ss;
// b. of length 2, if hh:mm or mm:ss is given, with [0] holding
// the first number while [1] is the second;
// c. of length 1, if only single number is given;
// d. of length 0, if no valid time string is given.
var tpsep = twve.tcalc.separator_positions(
txt,twve.tcalc.time_separator
);
if ( tpsep.length == 0 ) {
return (txt=twve.tcalc.isNumeric(txt))
? [ parseFloat(txt) ] // hh or mm or ss
: null; // No valid time string at all.
}
// Check for date string and skipt it if any.
var dpsep = twve.tcalc.separator_positions(
txt,twve.tcalc.date_separator
);
if ( dpsep.length > 0 && dpsep[0].ndx < tpsep[0].ndx ) {
// There is date string before the time string,
// extract the time string after it.
var ps = twve.tcalc.timestr_after_date(txt,dpsep[0].ndx);
if ( ps > -1 ) {
txt = txt.substring(ps);
if ( ! txt ) return null;
}
}
// Analyze time string.
var result = [];
var sub = '';
var partial = twve.tcalc.partialSearch;
twve.tcalc.partialSearch = true;
switch ( tpsep.length ) {
case 2 :
// hh:mm:ss
// Extract the hours part
sub = twve.tcalc.isNumeric(
txt.substring(0,tpsep[0].ndx)
);
if ( sub ) result[0] = parseFloat(sub);
// Extract the minutes part
sub = twve.tcalc.isNumeric(
txt.substring(tpsep[0].ndx+1)
);
if ( sub ) result[1] = parseFloat(sub);
// Extract the seconds part
sub = twve.tcalc.isNumeric(
txt.substring(tpsep[1].ndx+1)
);
if ( sub ) result[2] = parseFloat(sub);
break;
case 1 :
// mm:ss
// Extract the minutes part
sub = twve.tcalc.isNumeric(
txt.substring(0,tpsep[0].ndx)
);
if ( sub ) result[0] = parseFloat(sub);
// Extract the seconds part
sub = twve.tcalc.isNumeric(
txt.substring(tpsep[0].ndx+1)
);
if ( sub ) result[1] = parseFloat(sub);
break;
}
twve.tcalc.partialSearch = partial;
if ( result )
result.toString = function(){
switch ( result.length ) {
case 1 :
return result[0]+'';
case 2 :
return result[0]+':'+result[1];
case 3 :
return result[0]+':'+result[1]+':'+result[2];
default :
return '';
}
};
return result;
};
//}}}
/***
!!!! twve.tcalc.parse_time_arg
***/
//{{{
twve.tcalc.parse_time_arg = function(data,txt){
if ( ! txt ) return '';
var p = txt.indexOf('~');
//if ( p < 0 ) p = txt.indexOf('-');
if ( p < 0 ) {
// Expecting a single time
data.arg[0] = twve.tcalc.parse_time(txt);
} else {
// Expecting a range of time
data.arg[0] = twve.tcalc.parse_time(txt.substring(0,p));
data.arg[1] = twve.tcalc.parse_time(txt.substring(p+1));
}
return '';
};
//}}}
/***
!!!! twve.tcalc.calculateTime
***/
//{{{
twve.tcalc.calculateTime = function(expression){
// Calculate the sum or difference of time strings.
expression = twve.tcalc.allArguments(expression) || expression;
var sep = ['+', '-'];
var pos = twve.tcalc.separator_positions(
expression,sep,function(pos){
return expression.charAt(pos.ndx-1)!=':';
}
);
var lpos = 0, i, len=pos.length;
var time = [];
for ( i=0; i<len; i++ ) {
time[i] = twve.tcalc.parse_time(
expression.substring(lpos,pos[i].ndx)
);
if ( i > 0 ) time[i].operator = pos[i-1].matched;
lpos = pos[i].ndx+1;
}
time[i] = twve.tcalc.parse_time(expression.substring(lpos));
time[i].operator = pos[i-1].matched;
// Do the calculation
var result = time[0].slice();
var rlen = result.length;
for ( i=1; i<=len; i++ ) {
var ilen = time[i].length;
if ( rlen < ilen ) {
// The result is shorter than time[i], add necessary 0's at
// the beginning of result.
for ( var j=0; j<ilen-rlen; j++ )
result.unshift(0);
rlen = ilen;
}
// Now rlen is either equal to or greater than ilen, meaning
// the array result has enough space to hold the calculation
// result.
var j0 = rlen - ilen;
for ( var j=0; j<ilen; j++ ) {
if (time[i].operator == '-')
result[j0+j] -= time[i][j];
else
result[j0+j] += time[i][j];
}
}
// test functions
// isPositive
result.isPositive = function(){
for(var i=0,rlen=result.length; i<rlen; i++){
if (result[i]) return result[i]>0;
}
return false;
};
// conversion functions
// normalize
result.normalize = function(ndx,limit){
// Normalize the time result, meaning to ensure that the second
// and the minute are both between 0 and 59, while the hour is
// between 0 and 23.
var rlen = result.length;
if ( typeof ndx == 'number' ) {
switch(ndx) {
case 1 :
case 2 :
if ( ! limit ) limit = 60;
else if ( limit < 0 ) limit = -limit;
if ( result[ndx] > 0 ) {
if ( result[ndx] >= limit ) {
var up = Math.round(result[ndx]/limit);
result[ndx] = result[ndx]-up*limit;
result[ndx-1] += up;
}
} else if ( result[ndx] < 0 ) {
if ( result[ndx] < -limit ) {
var down = Math.round(result[ndx]/limit);
result[ndx] = result[ndx]-down*limit;
result[ndx-1] += down;
}
if ( result[ndx-1] > 0 ) {
result[ndx-1]--;
result[ndx] += limit;
}
}
break;
case -1 :
// The limit should be an array of limits.
var len = limit.length;
switch (len) {
case 3 : // hh:mm:ss
switch ( rlen ) {
case 3 :
result.normalize(2,limit[2]).
normalize(1,limit[1]).
normalize(0,limit[0]);
break;
case 2 :
result.normalize(1,limit[2]).
normalize(0,limit[1]);
break;
};
break;
case 2 : // hh:mm or mm:ss
switch ( rlen ) {
case 3 : // result is hh:mm:ss
if ( limit[0] == 60 ) {
// mm:ss
result.normalize(2,limit[1]).
normalize(1,limit[0]);
break;
}
/*
else
// hh:mm
result.normalize(1,limit[1]).
normalize(0,limit[0]);
*/
case 2 : // result is also hh:mm or mm:ss
result.normalize(1,limit[1]).
normalize(0,limit[0]);
break;
}
break;
}
}
return result;
}
// ndx is not given
switch ( rlen ) {
case 2 :
return result.normalize(1,60);
case 3 :
return result.normalize(2,60).normalize(1,60);
}
return result;
};
// format
result.format = function(val){
return val >= 0
? ((val >= 10 ? '' : '0')+val)
: (val <= -10 ? (''+val) : ('-0'+(-val)));
};
// toString
result.toString = function(){
var str = result.format(result[0]);
for (var i=1,rlen=result.length; i<rlen; i++)
str += ':'+result.format(result[i]);
return str;
};
// getSeconds
result.getSeconds = function(){
var rlen = result.length;
var sec = result[rlen-1];
switch ( rlen ) {
case 3 :
sec += result[rlen-3]*3600;
case 2 :
sec += result[rlen-2]*60;
break;
}
return sec;
};
// getMinutes
result.getMinutes = function(){
var rlen = result.length;
var min = 0;
switch ( rlen ) {
case 3 :
min += result[rlen-3]*60;
case 2 :
min += result[rlen-2];
case 1 :
min += result[rlen-1]/60;
break;
}
return min;
};
// getHours
result.getHours = function(){
var rlen = result.length;
var hour = 0;
switch ( rlen ) {
case 3 :
hour += result[rlen-3];
case 2 :
hour += result[rlen-2]/60;
case 1 :
hour += result[rlen-1]/3600;
break;
}
return hour;
};
return result;
};
//}}}
/***
!!!! twve.tcalc.day_of_week
***/
//{{{
twve.tcalc.dow_zh = [ '日', '一', '二', '三', '四', '五', '六' ];
twve.tcalc.dow_en = [
'SUN', 'MON', 'TUE', 'WED', 'THR', 'THU', 'FRI', 'SAT'
];
twve.tcalc.dow_jp = [ '日', '月', '火', '水', '木', '金', '土' ];
//}}}
/***
!!!! twve.tcalc.isZH
***/
//{{{
twve.tcalc.isZH = function(){
return /^zh/.test(
navigator.language || navigator.userLanguage
);
};
//}}}
/***
!!!! twve.tcalc.isJP
***/
//{{{
twve.tcalc.isJP = function(){
return /^jp/.test(
navigator.language || navigator.userLanguage
);
};
//}}}
/***
!!!! twve.tcalc.isYMD
***/
//{{{
twve.tcalc.isYMD = function(){
return /^(zh|jp|ko|mn|hu|lt|)/.test(
navigator.language || navigator.userLanguage
);
};
//}}}
/***
!!!! twve.tcalc.isMDY
***/
//{{{
twve.tcalc.isMDY = function(){
return (navigator.language || navigator.userLanguage)=='en-US';
};
//}}}
/***
!!!! twve.tcalc.parse_date_format
***/
//{{{
twve.tcalc.date_separator = ['/', '.', '-', ' '];
twve.tcalc.parse_date_format = function(txt){
// Parse a date string or a date format string. The parameter txt MUST
// be of valid format of a date, such as
// yyyy/mm/dd or dd/mm/yyyy or mm/dd/yyyy,
// yyyy-mm-dd or dd-mm-yyyy or mm-dd-yyyy,
// yyyy.mm.dd or dd.mm.yyyy or mm.dd.yyyy,
// yyyy mm dd or dd mm yyyy or mm dd yyyy,
// etc.
if ( ! txt ) return null;
// Skip the leading white spaces
var start = 0;
while(' \t'.indexOf(txt.charAt(start))>-1) start++;
var psep = twve.tcalc.separator_positions(
txt,start,twve.tcalc.date_separator
);
var y = '', m = '', d = '', time = ''
var separator = '/', ps = 0;
if ( psep.length == 0 ) {
// There is no separator found, check for 年 月 日 format.
var param = {
txt: txt,
curp: twve.position.create(0),
following: '年'
}
y = twve.tcalc.valueWithText(param); // year
if ( y ) ps = param.post.ndx+param.post.matched.length;
param.following = '月';
m = twve.tcalc.valueWithText(param); // month
if ( y ) ps = param.post.ndx+param.post.matched.length;
param.following = '日';
d = twve.tcalc.valueWithText(param); // day of month
if ( y ) ps = param.post.ndx+param.post.matched.length;
time = twve.tcalc.parse_time(txt.substring(ps));
} else {
separator = psep[0].matched;
// Remove time string if there is any.
ps = twve.tcalc.timestr_after_date(txt,psep[0].ndx);
if ( ps > -1 ) {
time = txt.substring(ps);
txt = txt.substring(0,ps);
}
if ( psep.length == 1 ) {
// If there is only 1 separator, meaning one of the
// year/month/day is missing. Check for which one.
if ( psep[0].ndx > 3 ) {
// The beginning part has more than 3 characters, should
// be the year. Assume month is following and day is
// missing.
y = txt.substring(0,psep[0].ndx);
m = txt.substring(psep[0].ndx+1);
} else if ( txt.length-psep[0].ndx > 3 ) {
// The ending part has more than 3 characters, should
// be the year. Assume the beginning part is month
// and day is missing.
m = txt.substring(0,psep[0].ndx);
y = txt.substring(psep[0].ndx+1);
} else {
// Not sure which one is missing. Assume it is the
// year that's missing.
if(twve.tcalc.isYMD() || twve.tcalc.isMDY()){
// Asia or US, mm/dd
m = txt.substring(0,psep[0].ndx);
d = txt.substring(psep[0].ndx+1);
} else {
// Otherwise dd/mm
d = txt.substring(0,psep[0].ndx);
m = txt.substring(psep[0].ndx+1);
}
}
} else {
// More than one seperators are found in txt.
if ( txt.length-psep[1].ndx > 3 ) {
// The ending part has more than 3 characters, should
// be the year.
if(twve.tcalc.isMDY()){
// mm/dd/yyyy
m = txt.substring(0,psep[0].ndx);
d = txt.substring(psep[0].ndx+1,psep[1].ndx);
} else {
// dd/mm/yyyy
d = txt.substring(0,psep[0].ndx);
m = txt.substring(psep[0].ndx+1,psep[1].ndx);
}
y = txt.substring(psep[1].ndx+1);
//} else if ( psep[0].ndx > 3 ) {
// The beginning part has more than 3 characters, should
// be the year.
} else {
// Otherwise assume year is the begining part.
y = txt.substring(0,psep[0].ndx);
m = txt.substring(psep[0].ndx+1,psep[1].ndx);
d = txt.substring(psep[1].ndx+1);
}
}
}
return {
year: y,
month: m,
date: d,
separator: separator,
time: (time ? (time.trim ? time.trim() : time) : '')
};
};
//}}}
/***
!!!! twve.tcalc.parse_date
***/
//{{{
twve.tcalc.parse_date = function(txt){
// Parse a date string. The parameter txt MUST be of valid
// format of a date, such as
// 2014/11/03,
// 2014-11-03,
// 2014.11.03,
// 2014 11 03,
// etc.
var today = new Date();
var format = twve.tcalc.parse_date_format(txt);
//if ( ! format ) return today;
if ( ! format.year ) format.year = today.getFullYear();
if ( ! format.month ) format.month = today.getMonth()+1;
if ( ! format.date ) format.date = today.getDate();
if ( ! format.time ) format.time = today.toTimeString();
var datestr = twve.tcalc.isYMD()
? (format.year+'/'+format.month+'/'+format.date)
: (twve.tcalc.isMDY()
? format.month+'/'+format.date+'/'+format.year
: format.date+'/'+format.month+'/'+format.year);
return new Date(datestr+' '+format.time);
};
//}}}
/***
!!!! twve.tcalc.parse_date_arg
***/
//{{{
twve.tcalc.parse_date_arg = function(data,txt){
if ( ! txt ) return '';
var p = txt.indexOf('~');
//if ( p < 0 ) p = txt.indexOf('-');
if ( p < 0 ) {
// Expecting a single date
data.arg[0] = twve.tcalc.parse_date(txt);
} else {
// Expecting a range of dates
data.arg[0] = twve.tcalc.parse_date(txt.substring(0,p));
data.arg[1] = twve.tcalc.parse_date(txt.substring(p+1));
}
return '';
};
//}}}
/***
!!!! twve.tcalc.format_date_string
***/
//{{{
twve.tcalc.format_date_string = function(date,format){
var datestr = '';
if ( ! format ) {
datestr = date.toLocaleDateString();
if ( config.browser.isIE ){
// IE (11?) inserts invisible char \u8206 before and after
// the year, month, date numerals. Remove them.
var i=0, c;
while(i<datestr.length){
c = datestr.charCodeAt(i);
if ( c == 8206 )
datestr = datestr.substring(0,i) +
datestr.substring(i+1);
else i++;
}
}
return datestr;
}
var n;
if ( format.year ){
n = date.getFullYear();
datestr += (format.year.length==2
? (n%100) : n);
}
if ( format.month ){
n = date.getMonth()+1;
datestr += (datestr?format.separator:'')+
(format.month.length==1
? n : ((n<10?'0':'')+n));
}
if ( format.date ){
n = date.getDate();
datestr += (datestr?format.separator:'')+
(format.date.length==1
? n : ((n<10?'0':'')+n));
}
return datestr;
};
//}}}
/***
!!!! twve.tcalc.months
***/
//{{{
twve.tcalc.months = function(m1,d1,m2,d2,y,num_dec){
// Calculate the difference in months between two dates, given by
// (m1, d1) and (m2, d2), in the same year y.
if ( ! y ) y = new Date().getFullYear();
m1 += d1/twve.tcalc.days_in_month(y,m1);
m2 += d2/twve.tcalc.days_in_month(y,m2);
return Math.round(m2-m1,num_dec);
};
//}}}
/***
!!!! twve.tcalc.days
***/
//{{{
twve.tcalc.days = function(date1,date2,num_dec){
var sec = (date2-date1)/1000;
if ( typeof num_dec === 'number' ) {
return Math.round(sec/86400,num_dec);
}
var days = Math.round(sec/86400);
if ( Math.abs(days) >= 1 )
sec = sec - days*86400;
var y = 0, m = 0;
if ( Math.abs(days) > 365 ) {
y = days > 0 ? Math.floor(days/365) : Math.ceil(days/365);
days = days - 365 * y;
}
if ( Math.abs(days) > 30 ) {
m = days > 0 ? Math.floor(days/30) : Math.ceil(days/30);
days = days - 30 * m;
}
var result = (days<0?'-(':'') +
(y!=0?((y<0?-y:y)+'Y'):'') +
(m!=0?((m<0?-m:m)+'M'):'');
if ( sec === 0 )
return result+(days<0?-days:days)+'D'+(days<0?')':'');
function time_str(t){
return t == 0
? '00'
: (
t > 0
? (t < 10 ? ('0'+t) : t)
: (t > -10 ? ('-0'+(-t)) : t)
)
}
if ( Math.abs(sec) < 60 )
return result + (days!=0?((days<0?-days:days)+'D'):'') +
' 00:00:' + time_str(sec)+(days<0?')':'');
var min = Math.round(sec / 60);
sec = sec - min*60;
var hour = Math.round(min / 60);
min = min - hour*60;
if ( date2 > date1 ) {
// date2 is later, should be positive
if ( sec < 0 ) {
sec += 60;
min -= 1;
}
if ( min < 0 ) {
min += 60;
hour -= 1;
}
if ( hour < 0 ) {
hour += 24;
days -= 1;
}
} else {
// date2 is earlier, should be negative
if ( sec > 0 ) {
sec -= 60;
min += 1;
}
if ( min > 0 ) {
min -= 60;
hour += 1;
}
if ( hour > 0 ) {
hour -= 24;
days += 1;
}
}
return result +
(days!=0
? ((days<0 ? -days : days)+'D ')
: ((!result && sec<0) ? '-(' : '')) +
time_str(hour<0?-hour:hour) +
(Math.abs(days) < 1
? (':'+time_str(min<0?-min:min)+
(Math.abs(hour) < 1
? (':'+time_str(sec<0?-sec:sec))
: ''))
: 'H') +
((days<0||(days==0&&sec<0))?')':'');
};
//}}}
/***
!!!! twve.tcalc.functionData
***/
//{{{
twve.tcalc.functionData = {
create : function(){
var data = twve.object.create();
data.clear = function(){
data.arg = [];
return data;
};
data.parseArgument = function(txt,how){
var result = txt;
if ( (txt=twve.tcalc.isNumeric(txt)) )
result = txt*1;
if ( ! how || how === typeof result )
data.arg[data.arg.length] = result;
};
data.how = function(){
var arglen = data.arg.length;
return arglen > 0 //&& typeof data.arg[arglen-1]==='string'
? data.arg[arglen-1]
: '';
};
return data.clear();
}
};
//}}}
/***
!!!! twve.tcalc.sourcedData
***/
//{{{
twve.tcalc.sourcedData = {
create : function(){
var data = twve.tcalc.functionData.create();
var preParseArgument = data.parseArgument;
data.parseArgument = function(txt){
if ( data.arg.length == 0 )
data.arg[0] = twve.tcalc.getSourceText(txt);
else
preParseArgument.call(this,txt);
};
return data;
}
};
//}}}
/***
!!!! twve.tcalc.rowColData
***/
//{{{
twve.tcalc.rowColData = {
getRange : function(data,thisone,last){
switch ( data.how() ) {
case 'BEFORE' :
data.first = 0;
data.last = thisone;
break;
case 'AFTER' :
data.first = thisone + 1;
data.last = last;
break;
default :
data.first = 0;
data.last = last;
break;
}
},
create : function(){
var data = twve.tcalc.functionData.create();
var preParseArgument = data.parseArgument;
data.parseArgument = function(txt){
switch ( data.arg.length ) {
case 0:
preParseArgument.call(this,txt);
break;
case 1:
this.data.arg[1] = txt.toUpperCase();
break;
}
};
data.getRange = function(thisone,last){
return twve.tcalc.rowColData.getRange(
data,thisone,last
);
};
return data;
}
};
//}}}
/***
!!!! twve.tcalc.columnData
***/
//{{{
twve.tcalc.columnData = {
columnNumber : function(col){
return typeof col == 'number'
? col
: ((!col)
? twve.tcalc.curCellCol()
: twve.tcalc.referenceToIndexCol(col));
},
getRange : function(data,thisone,last){
switch ( data.how() ) {
case 'ABOVE' :
data.first = 0;
data.last = thisone;
break;
case 'BELOW' :
data.first = thisone + 1;
data.last = last;
break;
default :
twve.tcalc.rowColData.getRange(data,thisone,last);
break;
}
},
create : function(){
var data = twve.tcalc.rowColData.create();
var preParseArgument = data.parseArgument;
data.parseArgument = function(txt){
preParseArgument.call(this,txt);
if ( data.arg.length == 1 )
data.arg[0] =
twve.tcalc.columnData.columnNumber(data.arg[0]);
};
data.getRange = function(thisone,last){
twve.tcalc.columnData.getRange(data,thisone,last);
};
return data;
}
};
//}}}
/***
!!!! twve.tcalc.rowData
***/
//{{{
twve.tcalc.rowData = {
rowNumber : function(row){
return (typeof row == 'number' || row)
? row : twve.tcalc.curCellRow();
},
getRange : function(data,thisone,last){
switch ( data.how() ) {
case 'LEFT' :
data.first = 0;
data.last = thisone;
break;
case 'RIGHT' :
data.first = thisone + 1;
data.last = last;
break;
default :
twve.tcalc.rowColData.getRange(data,thisone,last);
break;
}
},
create : function(){
var data = twve.tcalc.rowColData.create();
var preParseArgument = data.parseArgument;
data.parseArgument = function(txt){
preParseArgument.call(this,txt);
if ( data.arg.length == 1 )
data.arg[0] =
twve.tcalc.rowData.rowNumber(
data.arg[0]
);
};
data.getRange = function(thisone,last){
twve.tcalc.rowData.getRange(data,thisone,last);
};
return data;
}
};
//}}}
/***
!!!! twve.tcalc.signatureData
***/
//{{{
twve.tcalc.signatureData = {
create : function(){
var data = twve.tcalc.functionData.create();
data.parseArgument = function(txt){
var len = data.arg.length;
switch ( len ) {
case 0:
data.arg[0] = twve.tcalc.getSourceText(txt);
break;
case 1:
case 2:
data.arg[len] = twve.tcalc.arrayOrText(txt);
break;
case 3:
data.arg[3] = txt.toUpperCase();
break;
}
};
return data;
}
};
//}}}
/***
!!!! twve.tcalc.fn
***/
//{{{
twve.tcalc.fn = [
//}}}
/***
!!!!! JS Math
***/
//{{{
{
name: 'MATH', // JS Math functions
data: twve.tcalc.functionData.create(),
startParsing: function (txt) {
this.data.clear();
// Store the function name in data.
this.data.arg[0] = txt.substring(0,txt.indexOf('(')+1);
},
stopParsing: function () {
var len = this.data.arg.length-1;
for(var i=0; i<len; i++)
this.data.arg[0] += this.data.arg[i]+',';
this.data.arg[0] += this.data.arg[len]+')';
return eval(this.data.arg[0]);
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
// user defined functions
//}}}
/***
!!!!! CELL
***/
//{{{
{
description: 'Get the content of a table cell specified by its row and column indexes.',
usage: '=CELL(row,col). For example, =CELL(3,2) or =CELL(3,B) will get the content of the table cell in row 3 (4th row), column 2 (3rd column).',
name: 'CELL',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var twtable=twve.tcalc.cur_twtable || twve.tiddler.cur_focus;
if ( ! twtable || this.data.arg.length < 2 ) return null;
var row = this.data.arg[0];
if ( row < 0 ) return null;
var col = twve.tcalc.columnData.columnNumber(
this.data.arg[1]
);
if ( col < 0 ) return null;
var rows = twtable.dom.querySelectorAll('tr');
if ( row >= rows.length || col > twtable.maxColumns(rows) )
return null;
return twve.tcalc.nodeText(
twve.table.getRowCell(rows[row],col)
);
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! LENGTH
***/
//{{{
{
name: ['LENGTH', 'LEN'],
parseSingleTerm: function ( txt ) {
return txt ? (txt.length+'') : '0';
}
},
//}}}
/***
!!!!! LEFT
***/
//{{{
{
description: 'Extract the left part of a string.',
usage: '=LEFT(B3,4) will extract the left 4 characters out of the content in table cell B3.',
name: 'LEFT',
//no_eval: true,
data: twve.tcalc.sourcedData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function(){
switch ( this.data.arg.length ) {
case 0:
return '';
case 1:
return this.data.arg[0];
default:
return this.data.arg[0].substring(
0,this.data.arg[1]
);
};
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! MID
***/
//{{{
{
description: 'Extract the middle part of a string.',
usage: '=MID(B3,start_pos,number_of_char) will extract number_of_char characters, starting from index start_pos, out of the content in table cell B3.',
name: 'MID',
//no_eval: true,
data: twve.tcalc.sourcedData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function(){
switch ( this.data.arg.length ) {
case 0:
return '';
case 1:
return this.data.arg[0];
case 2:
return this.data.arg[0].substring(
this.data.arg[1]
);
default:
return this.data.arg[0].substring(
this.data.arg[1],
this.data.arg[1]+this.data.arg[2]
);
};
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! TRIM
***/
//{{{
{
description: 'Remove the leading and/or ending white spaces.',
usage: '=TRIM(B3[,how]) will remove the leading and/or ending white spaces of the content in cell B3. The optional argument, if given, can be either "LEFT", "RIGHT", or "BOTH". It is assumed "BOTH" if missing.',
name: 'TRIM',
//no_eval: true,
parseSingleTerm: function ( txt ) {
switch (typeof txt){
case 'number' :
txt = ''+txt;
case 'string' :
return txt.trim();
default :
return txt;
}
}
},
//}}}
/***
!!!!! RIGHT
***/
//{{{
{
description: 'Extract the right part of a string.',
usage: '=RIGHT(B3,4) will extract the right 4 characters out of the content in table cell B3.',
name: 'RIGHT',
//no_eval: true,
data: twve.tcalc.sourcedData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function(){
switch ( this.data.arg.length ) {
case 0:
return '';
case 1:
return this.data.arg[0];
default:
return this.data.arg[0].substring(
this.data.arg[0].length-this.data.arg[1]
);
};
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! FIND/SEARCH
***/
//{{{
{
description: 'Find a specific string inside another.',
usage: '=FIND(JR,B4[,start]) or =SEARCH(JR,B4[,start]) will find/search "JR" in the ceontent of cell B4. The optional argument specifies the starting location to find/search. Returns the location if found, or -1 otherwise.',
name: ['FIND', 'SEARCH'],
data: twve.tcalc.sourcedData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function(){
return this.data.arg[1].indexOf(
this.data.arg[0],
(this.data.arg[2] || 0)
);
},
parseSingleTerm: function ( txt ) {
var len = this.data.arg.length;
switch ( len ) {
case 0:
case 1:
this.data.arg[len] = txt;
//this.data.arg[len] = twve.tcalc.getSourceText(txt);
break;
case 2:
if ( (txt=twve.tcalc.isNumeric(txt)) ){
this.data.arg[2] = txt*1;
}
break;
};
}
},
//}}}
/***
!!!!! PRODUCT
***/
//{{{
{
description: 'Multiply the numerical values in multiple cells.',
usage: '=PRODUCT(A1,B5,H7) returns the product of the numerical values in cells A1, B5, and H7. =PRODUCT(C3:D6) returns the product of the numerical values in cells from C3 to D6.',
name: 'PRODUCT',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function(){
var prod = 1;
for(var i=0,len=this.data.arg.length; i<len; i++){
prod *= this.data.arg[i];
if ( prod === 0 ) break;
}
return prod+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt.trim() || 0);
}
},
//}}}
/***
!!!!! DOT
***/
//{{{
{
name: 'DOT',
// Dot product of two vectors in the form of (v1, v2, v3, ...)
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if (this.data.arg[0] && this.data.arg[1])
return this.data.arg[0].dot(this.data.arg[1])+'';
},
parseSingleTerm: function ( txt ) {
this.data.arg[this.data.arg.length] =
twve.tcalc.vector.create(txt,true);
}
},
//}}}
/***
!!!!! CROSS
***/
//{{{
{
name: 'CROSS',
// Cross product of two vectors in the form of (v1, v2, v3, ...)
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if (this.data.arg[0] && this.data.arg[1])
return this.data.arg[0].cross(this.data.arg[1]);
},
parseSingleTerm: function ( txt ) {
this.data.arg[this.data.arg.length] =
twve.tcalc.vector.create(txt,true);
}
},
//}}}
/***
!!!!! TEXTWITH
***/
//{{{
{
no_eval: true,
name: 'TEXTWITH',
// Returns in a table cell the sub-text that's immediately preceded
// and/or immediately followed by certain text.
// Usage: =TEXTWITH(cell,preceding,following)
// 1. If neither preceding nor following is given, this
// function returns the whole content in that table cell.
// 2. If given, they should be text.
// a. If preceding is give and following is not, this
// function returns everything after the preceding.
// b. If following is give and preceding is not, this
// function returns everything before the following.
// c. If both preceding and following are given, this
// function returns everything between the preceding
// and the following.
data: twve.tcalc.signatureData.create(),
textWithText: function(param){
var result = '';
param.everything = true;
if ( param.preceding && param.preceding!='null' ) {
// The preceding is given, look for it and return everything
// after it, or until the following string is found.
if ( (result=twve.tcalc.matchPreceding(param)) ) {
// A preceding is found, check if following is given
// and matched.
var curp = param.curp.ndx;
if ( ! param.following )
param.following = ['\n', ' ', '.', ','];
if ( ! twve.tcalc.matchFollowing(param,true) )
param.post.ndx = param.txt.length;
result = param.txt.substring(curp,param.post.ndx);
}
} else if (twve.tcalc.matchFollowing(param,true)) {
// The preceding is not given but the following is given
// and found. Return everything before it.
result = param.txt.substring(0,param.post.ndx);
}
return result;
},
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var result = '';
var param = {
preceding: this.data.arg[1],
following: this.data.arg[2],
curp: twve.position.create(0),
curn: -1
};
if ( typeof this.data.arg[0] == 'string' ) {
// Single cell content.
param.txt = this.data.arg[0];
result = this.textWithText(param);
} else {
// Multiple cells content.
result = [];
for (var i=0,len=this.data.arg[0].length; i<len; i++){
param.txt = this.data.arg[0][i];
param.curp.ndx = 0; param.curn = -1;
result[result.length] =
this.textWithText(param) || '0';
}
}
return result;
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! VALUEWITH
***/
//{{{
{
name: ['VALUEWITH', 'VALWITH'],
partialSearch: true,
// Returns in a table cell the first numerical value that is
// immediately preceded by the preceding and/or followed by the
// following.
// Usage: =VALUEWITH(cell,preceding,following)
// 1. If neither preceding nor following is given, this function
// returns the first numerical values in that table cell,
// just as the function VALUE(cell) does.
// 2. If given, they should be text.
// a. If preceding is give and following is not, this function
// returns the first numerical value that is immediately
// preceded by the preceding.
// b. If following is give and preceding is not, this function
// returns the first numerical value that is immediately
// followed by the following.
// c. If both preceding and following are given, this function
// returns the first numerical value that is immediately
// preceded by the preceding and immediately followed by
// the following.
//
// Note: White spaces between the numerals and preceding/following
// are ignored.
data: twve.tcalc.signatureData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var result = '';
var param = {
preceding: this.data.arg[1],
following: this.data.arg[2],
curp: twve.position.create(0),
curn: -1
};
if ( typeof this.data.arg[0] == 'string' ) {
// Single cell content.
param.txt = this.data.arg[0];
result = twve.tcalc.valueWithText(param) || '0';
} else {
// Multiple cells content.
result = [];
for (var i=0,len=this.data.arg[0].length; i<len; i++){
param.txt = this.data.arg[0][i];
param.curp.ndx = 0; param.curn = -1;
result[result.length] =
twve.tcalc.valueWithText(param) || '0';
}
}
return result;
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! VALUE
***/
//{{{
{
name: ['VALUE', 'VAL'],
partialSearch: true,
// Returns the specified numerical value in a table cell.
// Usage: =VALUEIN(cell[,n]) or =VAL(cell[,n]).
// The 2nd parameter n, if given, specifies the index number
// (0-based) of the numerical value to return.
// For example, =VALUE(A1,1) will return the numerical value
// with index number 1 (the 2nd in a 0-based system), in the
// table cell A1.
// If omitted, the first numerical value (with index number 0)
// is assumed.
data: twve.tcalc.sourcedData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if (! this.data.arg[0]) return '0';
var param = {
ndx: (this.data.arg[1] || 0),
curp: twve.position.create(0)
}
if ( typeof this.data.arg[0] == 'string' ) {
// The content of a single cell, look for the
// "this.data.arg[1]"th numerical value.
param.txt = this.data.arg[0];
param.curn = -1;
return twve.tcalc.valueIn(param) || '0';
}
// An array containing the content of multiple cells.
// Return an array of "this.data.ndx"th numerical value
// in each of the cells.
var result = [];
for (var i=0,len=this.data.arg[0].length; i<len; i++) {
param.txt = this.data.arg[0][i];
param.curp.ndx = 0; param.curn = -1;
result[result.length] = twve.tcalc.valueIn(param) || '0';
}
return result;
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! SUMIN
***/
//{{{
{
name: 'SUMIN',
partialSearch: true,
// Calculate the sum of all (or part of) numerical values in
// a table cell, if there are more than one.
// Usage: =SUMIN(cell[,n1,n2,...])
// 1. The simplest way to use this function is =SUMIN(cell),
// which sums up ALL the numerical values in that table
// cell.
// 2. A different way is to include/exclude part of the
// numerical values in that table cell.
// For example,
// a. =SUMIN(cell,n1,n2,...) includes only the n1th,
// n2th, ... numerical values in the summation.
// b. =SUMIN(cell,-n1,-n2,...) excludes the n1th,
// n2th, ... numerical values from the summation.
// It does not make much sense to both include and exclude
// some of the values, you simply don't include the ones you
// want to exclude. Therefore the excluded ones will be ignored
// when there are included ones.
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
this.data.arg[0] = null;
this.data.arg[1] = [];
this.data.arg[2] = [];
},
sumInText: function(param){
var result = '';
var len = param.include.length;
if ( len ) {
// There are included ones.
if ( ! param.include.sorted ) {
param.include.sort(function(a,b){return a-b;});
param.include.sorted = true;
}
for (var i=0; i<len; i++) {
param.ndx = param.include[i];
var numtxt = twve.tcalc.valueIn(param);
if ( numtxt )
result = twve.tcalc.addNumeral(result,numtxt);
}
} else {
// Sum up all or part of the numerical values.
len = param.exclude.length;
if ( len && ! param.exclude.sorted ) {
param.exclude.sort(function(a,b){return a-b;});
param.exclude.sorted = true;
}
var exi = 0;
param.ndx = 0;
while (true) {
var numtxt = twve.tcalc.valueIn(param);
if ( ! numtxt ) break;
if ( len && param.ndx == param.exclude[exi] ) {
// This one is excluded.
exi++;
} else {
// This one is NOT excluded
result = twve.tcalc.addNumeral(result,numtxt);
}
// Check the next one.
param.ndx++;
};
}
return result
? (twve.tcalc.evaluate_arithmetics(result)+'')
: result;
},
stopParsing: function () {
var result = '0';
var param = {
include: this.data.arg[1],
exclude: this.data.arg[2],
curp: twve.position.create(0)
};
if ( typeof this.data.arg[0] == 'string' ) {
// Single cell content.
param.txt = this.data.arg[0];
param.curn = -1;
result = this.sumInText(param);
} else {
// Multiple cells content.
for (var i=0,len=this.data.arg[0].length; i<len; i++){
param.txt = this.data.arg[0][i];
param.curp.ndx = 0; param.curn = -1;
var cellresult = this.sumInText(param);
if ( cellresult )
result += '+'+cellresult;
}
if ( result !== '0' )
result = twve.tcalc.evaluate_arithmetics(result)+'';
}
return result;
},
parseSingleTerm: function ( txt ) {
if ( this.data.arg[0] === null )
this.data.arg[0] = twve.tcalc.getSourceText(txt);
else {
var pminus = txt.indexOf('-');
if ( pminus == -1 )
this.data.arg[1][
this.data.arg[1].length
] = txt*1;
else
this.data.arg[2][
this.data.arg[2].length
] = txt.substring(pminus+1)*1;
}
}
},
//}}}
/***
!!!!! SUMWITH
***/
//{{{
{
description: 'Sum over numerical values in specified cells, with optional signature text (immediately preceding and/or following).',
usage: '=SUMWITH(A1[,pre[,post]]) will sum up all the numerical values with an immediately preceding signature "pre" in cell A1.',
name: 'SUMWITH',
partialSearch: true,
// Calculate in a table cell the sum of all numerical values
// immediately preceded by some preceding and/or followed by some
// following.
// Usage: =SUMWITH(cell,preceding,following)
// 1. If neither preceding nor following is given, this function
// sums up all the numerical values in that table cell,
// just as the function SUMIN(cell) does.
// 2. If given, they should be text.
// a. If preceding is give and following is not, this function
// only sums up numerical values immediately preceded
// by the preceding.
// b. If following is give and preceding is not, this function
// only sums up numerical values immediately followed
// by the following.
// c. If both preceding and following are given, this function
// only sums up numerical values immediately preceded
// by the preceding and immediately followed by the
// following.
//
// Note: White spaces between the numerals and preceding/
// following are ignored.
data: twve.tcalc.signatureData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var result = '0';
var param = {
preceding: this.data.arg[1],
following: this.data.arg[2],
curp: twve.position.create(0),
curn: -1
};
if ( typeof this.data.arg[0] == 'string' ) {
// Single cell content.
param.txt = this.data.arg[0];
result = twve.tcalc.sumWithText(param);
} else {
// Multiple cells content.
for (var i=0,len=this.data.arg[0].length; i<len; i++){
param.txt = this.data.arg[0][i];
var cellresult = twve.tcalc.sumWithText(param);
if ( cellresult !== '0' )
//result=twve.tcalc.addNumeral(result,cellresult);
result +=
(cellresult.charAt(0)=='-'?'':'+')+cellresult;
}
if ( result !== '0' )
result = twve.tcalc.evaluate_arithmetics(result)+'';
}
return result;
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! SUMCOLWITH
***/
//{{{
{
description: 'Sum over numerical values in one column, excluding this cell, with optional signature text (immediately preceding and/or following) and/or range of summation.',
usage: '=SUMCOLWITH([1,[pre[,post[,how]]]]) will sum up all numerical vlaues with an immediately preceding signature "pre" in column 1 (the 2nd column). Current column is assumed if the 1st argument is missing. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'SUMCOLWITH',
data: twve.tcalc.signatureData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var col = this.data.arg[0];
if ( col < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( col > twtable.maxColumns(rows) ) return '0';
var thisrow = twve.tcalc.curCellRow();
twve.tcalc.columnData.getRange(
this.data,thisrow,rows.length
);
var result = '0';
var param = {
preceding: this.data.arg[1],
following: this.data.arg[2],
curp: twve.position.create(0),
curn: -1
};
for(var r=this.data.first; r<this.data.last; r++){
if (r == thisrow) continue;
param.txt = twve.tcalc.cellContent(rows[r],col,true);
var cellresult = twve.tcalc.sumWithText(param);
if ( cellresult !== '0' )
if ( r == this.data.first ) result = cellresult;
else result +=
(cellresult.charAt(0)=='-'?'':'+')+cellresult;
}
return result === '0'
? '0'
: twve.tcalc.evaluate_arithmetics(result)+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
if ( this.data.arg.length == 1 ) {
// Just received the first argument.
this.data.arg[0] =
twve.tcalc.columnData.columnNumber(
this.data.arg[0]
);
}
}
},
//}}}
/***
!!!!! SUMCOL
***/
//{{{
{
description: 'Sum over numerical values in one column, excluding this cell, with optional argument spacifying the range of summation.',
usage: '=SUMCOL([1[,how]]) or =SUMCOL([B[,how]]) will sum up column 1 or B (the 2nd column). Current column is assumed if the first argument is missing. The 2nd argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'SUMCOL',
data: twve.tcalc.columnData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var col = this.data.arg[0] =
twve.tcalc.columnData.columnNumber(this.data.arg[0]);
if ( col < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( col > twtable.maxColumns(rows) ) return '0';
var thisrow = twve.tcalc.curCellRow();
this.data.getRange(thisrow,rows.length);
var result = 0;
for(var r=this.data.first; r<this.data.last; r++){
if (r == thisrow) continue;
var tmp = twve.tcalc.cellContent(rows[r],col,true);
if ( tmp ) {
tmp = parseFloat(tmp);
if ( ! isNaN(tmp) ) result += tmp;
}
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt,'number');
}
},
//}}}
/***
!!!!! SUMROWWITH
***/
//{{{
{
description: 'Sum over numerical values in one row, excluding this cell, with optional signature text (immediately preceding and/or following) and/or the range of summation.',
usage: '=SUMROW([1,[pre[,post[,how]]]]) will sum up all numerical vlaues with an immediately preceding signature "pre" in row 1 (the 2nd row). Current row is assumed if the first argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'SUMROWWITH',
data: twve.tcalc.signatureData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var row = this.data.arg[0];
if ( row < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( row >= rows.length ) return '0';
var cells = rows[row].querySelectorAll('th,td');
var thiscol = twve.tcalc.curCellCol();
twve.tcalc.rowData.getRange(this.data,thiscol,cells.length);
var result = '0';
var param = {
preceding: this.data.arg[1],
following: this.data.arg[2],
curp: twve.position.create(0),
curn: -1
};
for(var c=this.data.first; c<this.data.last; c++){
if (c == thiscol) continue;
param.txt = twve.tcalc.cellContent(cells,c,true);
var cellresult = twve.tcalc.sumWithText(param);
if ( cellresult !== '0' )
if ( c==this.data.first ) result = cellresult;
else result +=
(cellresult.charAt(0)=='-'?'':'+')+cellresult;
}
return result === '0'
? '0'
: twve.tcalc.evaluate_arithmetics(result)+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
if ( this.data.arg.length == 1 ) {
// Just received the first argument.
this.data.arg[0] =
twve.tcalc.rowData.rowNumber(
this.data.arg[0]
);
}
}
},
//}}}
/***
!!!!! SUMROW
***/
//{{{
{
description: 'Sum over numerical values in one row, excluding this cell, with optional range of summation.',
usage: '=SUMROW(1[,how]) will sum up row 1 (the 2nd row). Current row is assumed if the argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'SUMROW',
data: twve.tcalc.rowData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var row = this.data.arg[0] =
twve.tcalc.rowData.rowNumber(this.data.arg[0]);
if ( row < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( row >= rows.length ) return '0';
var cells = rows[row].querySelectorAll('th,td');
var thiscol = twve.tcalc.curCellCol();
this.data.getRange(thiscol,cells.length);
var result = 0;
for(var c=this.data.first; c<this.data.last; c++){
if (c == thiscol) continue;
var tmp = twve.tcalc.cellContent(cells,c,true);
if ( tmp ) {
tmp = parseFloat(tmp);
if ( ! isNaN(tmp) ) result += tmp;
}
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt,'number');
}
},
//}}}
/***
!!!!! SUM
***/
//{{{
{
description: 'Sum over numerical values in one or more cells.',
usage: '=SUM(A1:C3) will sum up cells A1, A2, A3, B1, B2, B3, and C1, C2, C3.',
name: 'SUM',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var sum = 0;
for(var i=0,len=this.data.arg.length; i<len; i++){
sum += this.data.arg[i];
}
return sum+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt.trim() || 0,'number');
}
},
//}}}
/***
!!!!! COUNTA
***/
//{{{
{
// COUNTA should be defined before COUNT, otherwise it
// has no chance to be matched. It is so because twve.tcalc
// plugin matches a function name with startsWith() method,
// and both functions starts with COUNT, if COUNT is defined
// before COUNTA, then the match will always be COUNT and
// never COUNTA.
name: 'COUNTA',
description: 'Count the number of non-empty values in the specified cells.',
usage: '=COUNTA(A3:B6) will count the number of non-empty values in the cells from A3 to B6.',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
this.data.arg[0] = 0;
},
stopParsing: function () {
return ''+this.data.arg[0];
},
parseSingleTerm: function ( txt ) {
if (/\S/.test(txt)) this.data.arg[0]++;
}
},
//}}}
/***
!!!!! COUNTCOLA
***/
//{{{
{
description: 'Count the number of non-empty cells in one column, excluding this cell, with optional range of counting.',
usage: '=COUNTCOLA([1[,how]]) or =COUNTCOLA([B[,how]]) will count the number of non-empty values in column 1 or B (the 2nd column). Current column is assumed if the 1st argument is omitted. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'COUNTCOLA',
data: twve.tcalc.columnData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var col = this.data.arg[0] =
twve.tcalc.columnData.columnNumber(this.data.arg[0]);
if ( col < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( col > twtable.maxColumns(rows) ) return '0';
var thisrow = twve.tcalc.curCellRow();
this.data.getRange(thisrow,rows.length);
var result = 0;
for(var r=this.data.first; r<this.data.last; r++){
if ( r != thisrow &&
/\S/.test(twve.tcalc.cellContent(rows[r],col,true)) )
result++;
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! COUNTCOL
***/
//{{{
{
description: 'Count the number of numerical values in one column, excluding this cell, with optioanl range of counting.',
usage: '=COUNTCOL([1[,how]]) or =COUNTCOL([B[,how]]) will count the number of numerical values in column 1 or B (the 2nd column). Current column is assumed if the 1st argument is missing. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'COUNTCOL',
data: twve.tcalc.columnData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var col = this.data.arg[0] =
twve.tcalc.columnData.columnNumber(this.data.arg[0]);
if ( col < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( col > twtable.maxColumns(rows) ) return '0';
var thisrow = twve.tcalc.curCellRow();
this.data.getRange(thisrow,rows.length);
var result = 0;
for(var r=this.data.first; r<this.data.last; r++){
if (r == thisrow) continue;
var tmp = twve.tcalc.cellContent(rows[r],col,true);
if ( tmp ) {
tmp = tmp*1;
if ( ! isNaN(tmp) ) result++;
}
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! COUNTROWA
***/
//{{{
{
description: 'Count the number of non-empty values in one row, excluding this cell, with optional range of counting.',
usage: '=COUNTROWA([1[,how]]) will count the number of non-empty values in row 1 (the 2nd row). Current row is assumed if the 1st argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'COUNTROWA',
data: twve.tcalc.rowData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var row = this.data.arg[0] =
twve.tcalc.rowData.rowNumber(this.data.arg[0]);
if ( row < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( row >= rows.length ) return '0';
var cells = rows[row].querySelectorAll('th,td');
var thiscol = twve.tcalc.curCellCol();
this.data.getRange(thiscol,cells.length);
var result = 0;
for(var c=this.data.first; c<this.data.last; c++){
if ( c != thiscol &&
/\S/.test(twve.tcalc.cellContent(cells,c,true)) )
result++;
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! COUNTROW
***/
//{{{
{
description: 'Count the number of numerical values in one row, excluding this cell, with optional range of counting.',
usage: '=COUNTROW([1[,how]]) will count the number of numerical values in row 1 (the 2nd row). Current row is assumed if the 1st argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.',
name: 'COUNTROW',
data: twve.tcalc.rowData.create(),
startParsing: function(){
this.data.clear();
},
stopParsing: function () {
var row = this.data.arg[0] =
twve.tcalc.rowData.rowNumber(this.data.arg[0]);
if ( row < 0 ) return '0';
var twtable = twve.tcalc.cur_twtable;
if ( ! twtable ) return '0';
var rows = twtable.dom.querySelectorAll('tr');
if ( row >= rows.length ) return '0';
var cells = rows[row].querySelectorAll('th,td');
var thiscol = twve.tcalc.curCellCol();
this.data.getRange(thiscol,cells.length);
var result = 0;
for(var c=this.data.first; c<this.data.last; c++){
if (c == thiscol) continue;
var tmp = twve.tcalc.cellContent(cells,c,true);
if ( tmp ) {
tmp = tmp*1;
if ( ! isNaN(tmp) ) result++;
}
}
return result+'';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! COUNT
***/
//{{{
{
name: 'COUNT',
data: {
count: 0,
condition: null
},
startParsing: function () {
this.data.condition = null;
this.data.count = 0;
},
stopParsing: function () {
return ''+this.data.count;
},
parseSingleTerm: function ( txt ) {
//this.data.parseArgument(txt);
if (twve.tcalc.isNumeric(txt)) {
if ( this.data.condition ) {
if(twve.tcalc.parseTrueFalse(
this.data.condition.replace(/\%/g,txt)
))
this.data.count++;
} else this.data.count++;
} else if ( ! this.data.condition ) {
txt = txt.trim();
if (/%\s*[><=!]/.test(txt)) {
var ch = txt.charAt(0);
this.data.condition = (ch=="'" || ch=='"')
? txt.substring(1,txt.length-1)
: txt;
}
}
}
},
//}}}
/***
!!!!! MAX
***/
//{{{
{
name: 'MAX',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0];
},
parseSingleTerm: function ( txt ) {
if ( (txt=twve.tcalc.isNumeric(txt)) ) {
if ( this.data.arg.length === 0 )
this.data.arg[0] = txt;
else if ( this.data.arg[0] < txt )
this.data.arg[0] = txt;
}
}
},
//}}}
/***
!!!!! MIN
***/
//{{{
{
name: 'MIN',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0];
},
parseSingleTerm: function ( txt ) {
if ( (txt=twve.tcalc.isNumeric(txt)) ) {
if ( this.data.arg.length === 0 )
this.data.arg[0] = txt;
else if ( this.data.arg[0] > txt )
this.data.arg[0] = txt;
}
}
},
//}}}
/***
!!!!! AVERAGE/AVG/MEAN
***/
//{{{
{
name: ['AVERAGE', 'AVG', 'MEAN'],
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
this.data.arg[0] = 0; // sum
this.data.arg[1] = 0; // count
},
stopParsing: function () {
return ''+(this.data.arg[0]/this.data.arg[1]);
},
parseSingleTerm: function ( txt ) {
if ( (txt=twve.tcalc.isNumeric(txt)) ) {
this.data.arg[0] += (txt*1);
this.data.arg[1]++;
}
}
},
//}}}
/***
!!!!! FACT, factorial
***/
//{{{
{
name: 'FACT',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return twve.tcalc.factorial(this.data.arg[0]);
},
parseSingleTerm: function ( txt ) {
if((txt=twve.tcalc.isNumeric(txt))){
this.data.arg[0] = parseInt(txt);
}
}
},
//}}}
/***
!!!!! COMBIN
***/
//{{{
{
name: 'COMBIN',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return Math.round(
twve.tcalc.factorial(this.data.arg[0]) /
twve.tcalc.factorial(this.data.arg[1]) /
twve.tcalc.factorial(this.data.arg[0]-this.data.arg[1])
);
},
parseSingleTerm: function ( txt ) {
if ( this.data.arg.length === 0 ) {
if ( (txt=twve.tcalc.isNumeric(txt)) )
this.data.arg[0] = parseInt(txt);
} else if((txt=twve.tcalc.isNumeric(txt)))
this.data.arg[1] = parseInt(txt);
}
},
//}}}
/***
!!!!! THISHOUR
***/
//{{{
{
name: 'THISHOUR',
//no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( ! this.data.arg[0] )
// No valid time specified.
return 'false';
var d = new Date();
var h = d.getHours()+(d.getMinutes()/60);
var h1 = this.data.arg[0][0];
if ( ! this.data.arg[1] ) {
// There is one time specified, returns true if it is the
// same as this hour.
return ''+ (h1 == h);
} else {
// There is a range of time specified, returns true if this
// hour is within the range.
var h2 = this.data.arg[1][0];
return ''+((h1 <= h && h < h2)||
(h2 <= h && h < h1));
}
},
parseSingleTerm: function ( txt ) {
return twve.tcalc.parse_time_arg(this.data,txt);
}
},
//}}}
/***
!!!!! THISDAYOFWEEK
***/
//{{{
{
name: ['THISDOW', 'THISDAYOFWEEK'],
//no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return ''+(typeof this.data.arg[0] === 'number' &&
this.data.arg[0] == new Date().getDay());
},
parseSingleTerm: function ( txt ) {
if ( /[0-7]/.test(txt) ){
// Valid numerals for the day of week.
this.data.arg[0] = txt*1;
return;
}
txt = txt.toLowerCase();
var pos = twve.position.create(0);
// Check for Chinese day of week.
var dow = twve.tcalc.dow_zh;
pos = twve.text.indexOf(txt,dow,pos);
if ( pos.ndx > -1 ) {
this.data.arg[0] = dow.indexOf(pos.matched);
return;
}
// If not found, check for English
dow = twve.tcalc.dow_en;
pos.ndx = 0;
pos = twve.text.indexOf(txt,dow,pos);
if ( pos.ndx > -1 ) {
this.data.arg[0] = dow.indexOf(pos.matched);
if ( this.data.arg[0] > 4 ) this.data.arg[0]--;
return;
}
// If still not found, check for Japanese
dow = twve.tcalc.dow_jp;
pos.ndx = 0;
pos = twve.text.indexOf(txt,dow,pos);
if ( pos.ndx > -1 ) {
this.data.arg[0] = dow.indexOf(pos.matched);
}
}
},
//}}}
/***
!!!!! THISWEEK
***/
//{{{
{
name: 'THISWEEK',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
same_week : function(d1,d2){
// Compare day of month.
var dm1 = d1.getDate();
var dm2 = d2.getDate();
if ( dm1 == dm2 )
// Same day.
return true;
if ( Math.abs(dm1-dm2) > 7 )
// The two dates are more than 7 days apart, can not be
// in the same week.
return false;
// The two dates are not the same but within 7 days apart.
// Compare day of week.
var dw1 = d1.getDay();
var dw2 = d2.getDay();
// If day of month is larger, then the day of week must also
// be larger, if they are really in the same week.
if ( dm2 > dm1 ) return dw2 > dw1;
// Otherwise check the opposite.
return dw2 < dw1;
},
stopParsing: function () {
if ( ! this.data.arg[0] )
// No valid dates given.
return 'false';
var today = new Date();
if ( ! this.data.arg[1] ) {
// A single date is given, check if it is within the same
// week as today.
// First we compare the year and month
if ( ! twve.tcalc.same_year_month(this.data.arg[0],today) )
// Not the same year and month as today, must not be in
// this week.
return 'false';
// Same year and month as today, check for same week.
return ''+this.same_week(this.data.arg[0],today);
}
// A range of dates
if ( twve.tcalc.same_year_month(this.data.arg[0],today) )
// Date1 is within the same year and same month as today.
return ''+this.same_week(this.data.arg[0],today);
if ( twve.tcalc.same_year_month(this.data.arg[1],today) )
// Date2 is within the same year and same month as today.
return ''+this.same_week(this.data.arg[1],today);
// Neither date is within the same week as today,
// return false.
return 'false';
},
parseSingleTerm: function ( txt ) {
return twve.tcalc.parse_date_arg(this.data,txt);
}
},
//}}}
/***
!!!!! THISMONTH
***/
//{{{
{
name: 'THISMONTH',
//no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( ! this.data.arg[0] )
// No valid dates given.
return 'false';
var today = new Date();
if ( ! this.data.arg[1] ) {
// A single date is given, check if it is within the same
// month as today.
return twve.tcalc.same_year_month(this.data.arg[0],today)
? 'true' : 'false';
}
// A range of dates, returns true if either one is whithin the
// same month as today.
return twve.tcalc.same_year_month(this.data.arg[0],today) ||
twve.tcalc.same_year_month(this.data.arg[1],today)
? 'true' : 'false';
},
parseSingleTerm: function ( txt ) {
return twve.tcalc.parse_date_arg(this.data,txt);
}
},
//}}}
/***
!!!!! DAYOFWEEK
***/
//{{{
{
name: ['DOW', 'DAYOFWEEK'],
description: 'Returns a number from 0 (Sun) to 6 (Sat), indicating the day of week of a given date.',
usage: 'For example, =DAYOFWEEK(2016/12/06) returns 0 (Sun).',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( ! this.data.arg[0] ) return '';
var dow = this.data.arg[0].getDay();
return this.data.how() == 'NUM'
? dow
: (twve.tcalc.isZH()
? twve.tcalc.dow_zh[dow]
: (twve.tcalc.isJP()
? twve.tcalc.dow_jp[dow]
: twve.tcalc.dow_en[dow]));
},
parseSingleTerm: function ( txt ) {
switch ( this.data.arg.length ) {
case 0 :
this.data.arg[0] = twve.tcalc.parse_date(txt);
break;
case 1 :
this.data.arg[1] = txt.toUpperCase();
break;
}
}
},
//}}}
/***
!!!!! DAYS
***/
//{{{
{
name: 'DAYS',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return (!this.data.arg[0] || !this.data.arg[1])
? null
: twve.tcalc.days(
this.data.arg[0],this.data.arg[1],this.data.arg[2]
);
},
parseSingleTerm: function ( txt ) {
switch ( this.data.arg.length ) {
case 0 :
this.data.arg[0] = twve.tcalc.parse_date(txt);
break;
case 1 :
this.data.arg[1] = twve.tcalc.parse_date(txt);
break;
case 2 :
this.data.parseArgument(txt);
break;
}
}
},
//}}}
/***
!!!!! MONTHS
***/
//{{{
{
name: 'MONTHS',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( ! this.data.arg[0] || ! this.data.arg[1] ) return null;
var y = [
this.data.arg[0].getFullYear(),
this.data.arg[1].getFullYear()
];
var m = [
this.data.arg[0].getMonth(),
this.data.arg[1].getMonth()
];
var d = [
this.data.arg[0].getDate(),
this.data.arg[1].getDate()
];
if ( typeof this.data.num_dec != 'number' )
this.data.num_dec = 0;
if ( y[0] == y[1] ) {
// Same year
return twve.tcalc.months(
m[0],d[0],m[1],d[1],y[1],this.data.num_dec
);
} else {
// Different years
var earlier = 0, later = 1;
if ( y[1] < y[0] ) {
earlier = 1; later = 0;
}
var dm =
// months between the years
(y[later]-y[earlier]-1)*12 +
// months in the earlier year to the end
twve.tcalc.months(
m[earlier],d[earlier],11,31,y[earlier],
this.data.num_dec
) +
// months in the later year from the beginning
twve.tcalc.months(
0,1,m[later],d[later],y[later],
this.data.num_dec
);
return earlier == 0 ? dm : -dm;
}
},
parseSingleTerm: function ( txt ) {
switch ( this.data.arg.length ) {
case 0 :
this.data.arg[0] = twve.tcalc.parse_date(txt);
break;
case 1 :
this.data.arg[1] = twve.tcalc.parse_date(txt);
break;
case 2 :
this.data.parseArgument(txt);
break;
}
}
},
//}}}
/***
!!!!! DATEADD
***/
//{{{
{
name: 'DATEADD',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var date = this.data.arg[0] || new Date();
date = new Date(
date.getFullYear()+this.data.arg[1],
date.getMonth()+(this.data.arg[2]||0),
date.getDate()+(this.data.arg[3]||0)
);
return twve.tcalc.format_date_string(
date,
twve.tcalc.parse_date_format(
this.data.arg[4]
)
);
},
parseSingleTerm: function ( txt ) {
var len = this.data.arg.length;
switch ( len ) {
case 0 :
this.data.arg[0] = twve.tcalc.parse_date(txt);
break;
case 1 :
case 2 :
case 3 :
txt = twve.tcalc.evaluate_arithmetics(txt);
//txt = twve.tcalc.isNumeric(txt);
this.data.arg[len] = txt ? (txt*1) : 0;
break;
case 4 :
this.data.arg[4] = txt;
break;
}
}
},
//}}}
/***
!!!!! DATE
***/
//{{{
{
name: 'DATE',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var date = new Date();
date = new Date(
this.data.arg[0] || today.getFullYear(),
(this.data.arg[1]?(this.data.arg[1]-1):today.getMonth()),
this.data.arg[2] || today.getDate()
);
return twve.tcalc.format_date_string(
date,
twve.tcalc.parse_date_format(
this.data.format
)
);
},
parseSingleTerm: function ( txt ) {
var len = this.data.arg.length;
switch ( len ) {
case 0 :
case 1 :
case 2 :
txt = twve.tcalc.evaluate_arithmetics(txt);
//txt = twve.tcalc.isNumeric(txt);
this.data.arg[len] = txt ? (txt*1) : 0;
break;
case 3 :
this.data.arg[3] = txt;
break;
}
}
},
//}}}
/***
!!!!! TODAY
***/
//{{{
{
name: 'TODAY',
no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return twve.tcalc.format_date_string(
new Date(),
twve.tcalc.parse_date_format(
this.data.arg[0]
)
);
},
parseSingleTerm: function ( txt ) {
this.data.arg[0] = txt;
}
},
//}}}
/***
!!!!! ROUND
***/
//{{{
{
name: 'ROUND',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
this.data.arg[0] = twve.tcalc.round(
this.data.arg[0],this.data.arg[1]
);
return this.data.arg[0]?(''+this.data.arg[0]):'0';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! EXP
***/
//{{{
{
name: 'EXP',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0]?(''+this.data.arg[0]):'0';
},
parseSingleTerm: function ( txt ) {
this.data.arg[0] = Math.exp(txt*1);
}
},
//}}}
/***
!!!!! LN
***/
//{{{
{
name: 'LN',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0]?(''+this.data.arg[0]):'0';
},
parseSingleTerm: function ( txt ) {
this.data.arg[0] = Math.log(txt*1);
}
},
//}}}
/***
!!!!! LOG10
***/
//{{{
{
name: 'LOG10',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0]?(''+this.data.arg[0]):'0';
},
parseSingleTerm: function ( txt ) {
this.data.arg[0] = Math.log(txt*1)/Math.log(10);
}
},
//}}}
/***
!!!!! LOG
***/
//{{{
{
name: 'LOG',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( !this.data.arg[1] ) this.data.arg[1] = 10;
var result =
Math.log(this.data.arg[0])/Math.log(this.data.arg[1]);
return result?(''+result):'0';
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! AND
***/
//{{{
{
name: 'AND',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var len = this.data.arg.length;
if ( len < 2 ) return 'false';
for ( var i=0; i<len; i++ )
if ( ! this.data.arg[i] ) return 'false';
return 'true';
},
parseSingleTerm: function ( txt ) {
this.data.arg[this.data.arg.length] =
twve.tcalc.parseTrueFalse(txt);
}
},
//}}}
/***
!!!!! OR
***/
//{{{
{
name: 'OR',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var len = this.data.arg.length;
if ( len == 0 ) return 'false';
for ( var i=0; i<len; i++ )
if ( this.data.arg[i] ) return 'true';
return 'false';
},
parseSingleTerm: function ( txt ) {
this.data.arg[this.data.arg.length] =
twve.tcalc.parseTrueFalse(txt);
}
},
//}}}
/***
!!!!! XOR
***/
//{{{
{
name: 'XOR',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( this.data.arg.length < 2 ) return 'false';
return this.data.arg[0]
? (! this.data.arg[1])
: this.data.arg[1];
},
parseSingleTerm: function ( txt ) {
this.data.arg[this.data.arg.length] =
twve.tcalc.parseTrueFalse(txt);
}
},
//}}}
/***
!!!!! EQUAL
***/
//{{{
{
description: 'Tests if two contents are equal.',
usage: '=EQUAL(A1,B2) returns true if the content in cells A1 and B2 are equal to each other.',
name: 'EQUAL',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( this.data.arg.length < 2 ) return 'false';
return this.data.arg[0] == this.data.arg[1];
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! RANDOM
***/
//{{{
{
description: 'Returns a psuedo random number.',
usage: '=RANDOM().',
name: 'RANDOM',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var result = Math.random();
if ( this.data.arg[0] ) result *= this.data.arg[0];
return result;
},
parseSingleTerm: function ( txt ) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! CONCAT
***/
//{{{
{
description: 'Returns the concatenated result.',
usage: '=CONCAT(A2,C5,H8)/CONCAT(D3:Z5) returns the concatenated result of the contents in the given cells.',
name: 'CONCAT',
no_eval: true
},
//}}}
/***
!!!!! ISNUMERIC
***/
//{{{
{
description: 'Tests if a piece of text is a numeric value.',
usage: '=ISNUMERIC(D6) returns true if the content in cell D6 is a numerical value.',
name: 'ISNUMERIC',
no_eval: true,
parseSingleTerm: function (txt) {
var txt2=twve.tcalc.isNumeric(txt);
return txt2 ? txt2 : 'false';
}
},
//}}}
/***
!!!!! STDERR/STDEV
***/
//{{{
{
description: 'Returns the standard deviation.',
usage: '=STDERR(C3:G8)/STDDEV(C3:G8) returns the standard deviation of the numerical values contained in cells from C3 to G8.',
name: ['STDERR', 'STDEV'],
//no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
var npt = this.data.arg.length;
if ( npt == 0 ) return 0;
var mean = 0, variance = 0, i;
for(i=0; i<npt; i++)
mean += this.data.arg[i];
mean /= npt;
for(i=0; i<npt; i++ ) {
variance += Math.pow(this.data.arg[i]-mean,2);
}
return Math.sqrt(variance/(npt-1));
},
parseSingleTerm: function (txt) {
this.data.parseArgument(txt);
}
},
//}}}
/***
!!!!! COLUMNA/COLA
***/
//{{{
{
description: 'Returns the alphabet reference of a given column or the current column.',
usage: '=COL()/COLUMN() returns the alphabet reference of the current column, while =COL(H4)/COLUMN(H4) returns the alphabet reference of the column containing cell H4.',
name: ['COLUMNA', 'COLA'],
//no_eval: true,
no_deref: true,
data: twve.tcalc.columnData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return twve.tcalc.colRef(
twve.tcalc.columnData.columnNumber(this.data.arg[0])
);
},
parseSingleTerm: function (txt) {
if ( this.data.arg.length == 0 )
this.data.arg[0] = twve.tcalc.referenceToIndexCol(txt);
}
},
//}}}
/***
!!!!! COLUMNS/COLS
***/
//{{{
{
description: 'Count the number of columns among given cells.',
usage: '=COLS(B1:H4)/COLS(A1,C4:H5) returns the number of columns containing those given cells.',
name: ['COLUMNS', 'COLS'],
no_eval: true,
no_deref: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
this.data.arg[0] = 0;
},
stopParsing: function () {
return ''+this.data.arg[0];
},
parseSingleTerm: function (txt) {
txt = txt.split(':');
switch ( txt.length ) {
case 1 :
this.data.arg[0]++; break;
case 2 :
// a range of cells
var c0 = twve.tcalc.referenceToIndexCol(txt[0]),
c1 = twve.tcalc.referenceToIndexCol(txt[1]);
this.data.arg[0] += (c1-c0+1);
break;
default :
// Syntax error
throw 'Syntax error: '+txt;
break;
}
}
},
//}}}
/***
!!!!! COLUMN/COL
***/
//{{{
{
description: 'Returns the index of a given column or the current column.',
usage: '=COL()/COLUMN() returns the index of the current column, while =COL(H4)/COLUMN(H4) returns the index of the column containing cell H4.',
name: ['COLUMN', 'COL'],
//no_eval: true,
no_deref: true,
data: twve.tcalc.columnData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
this.data.arg[0] =
twve.tcalc.columnData.columnNumber(this.data.arg[0]);
return this.data.arg[0] < 0 ? '' : (''+this.data.arg[0]);
},
parseSingleTerm: function (txt) {
if ( this.data.arg.length == 0 )
this.data.arg[0] = twve.tcalc.referenceToIndexCol(txt);
}
},
//}}}
/***
!!!!! ROWS
***/
//{{{
{
description: 'Count the number of rows among given cells.',
usage: '=ROWS(B1:H4)/ROWS(A1,C4:H5) returns the number of rows containing those given cells.',
name: 'ROWS',
no_eval: true,
no_deref: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
this.data.arg[0] = 0;
},
stopParsing: function () {
return ''+this.data.arg[0];
},
parseSingleTerm: function (txt) {
txt = txt.split(':');
switch ( txt.length ) {
case 1 :
this.data.arg[0]++; break;
case 2 :
// a range of cells
var r0 = twve.tcalc.referenceToIndexRow(txt[0]),
r1 = twve.tcalc.referenceToIndexRow(txt[1]);
this.data.arg[0] += (r1-r0+1);
break;
default :
// Syntax error
throw 'Syntax error: '+txt;
break;
}
}
},
//}}}
/***
!!!!! ROW
***/
//{{{
{
description: 'Returns the index of a given row or the current row.',
usage: '=ROW() returns the index of the current row, while =ROW(H4) returns the index of the row containing cell H4.',
name: 'ROW',
no_deref: true,
data: twve.tcalc.rowData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
this.data.arg[0] =
twve.tcalc.rowData.rowNumber(this.data.arg[0]);
return this.data.arg[0] < 0
? '' : (''+this.data.arg[0]);
},
parseSingleTerm: function (txt) {
if ( this.data.arg.length == 0 )
this.data.arg[0] = twve.tcalc.referenceToIndexRow(txt);
}
},
//}}}
/***
!!!!! SQRT
***/
//{{{
{
description: 'Square root funciton.',
usage: '=SQRT(A3) returns the square root of the number in cell A3.',
name: 'SQRT',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( this.data.arg[0] == 0 ) return 0;
return this.data.arg[0] > 0
? Math.sqrt(this.data.arg[0])
: (Math.sqrt(-this.data.arg[0])+' i');
},
parseSingleTerm: function (txt) {
if (this.data.arg.length == 0)
this.data.arg[0] = parseFloat(txt);
}
},
//}}}
/***
!!!!! IF
***/
//{{{
{
description: 'Conditional assignment function.',
usage: '=IF(condition,resultT,resultF) will assign the resultT when condition is evaluated to true, or result F when false.',
name: 'IF',
//no_eval: true,
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
return this.data.arg[0] == true
? (this.data.arg[1] || '')
: (this.data.arg[2] || '');
},
parseSingleTerm: function (txt,p0) {
var len = this.data.arg.length;
p0 += twve.text.skipStyleText(txt,p0,true);
try{
switch ( len ) {
case 2 :
case 1 :
//this.data.arg[len] = txt.substring(0,p0) +
// twve.tcalc.evaluate_arithmetics(txt,p0);
this.data.arg[len] = txt;
break;
case 0 :
this.data.arg[len] =
twve.tcalc.parseTrueFalse(txt);
break;
}
} catch(e) {
this.data.arg[len] = txt;
}
}
},
//}}}
/***
!!!!! COUNTDOWN
***/
//{{{
{
description: '...',
usage: '...',
name: 'COUNTDOWN',
data: twve.tcalc.functionData.create(),
startParsing: function () {
this.data.clear();
},
stopParsing: function () {
if ( this.data.arg[0] === null ) return;
var result = '';
// Determine end and interval of countdown.
if ( this.data.type == 'number' ) {
// The end
result = result
? twve.tcalc.isNumeric(this.data.arg[1]) : 0;
if ( result ) {
this.data.arg[1] = result*1;
if ( Math.abs(this.data.arg[1]) < 1e-5 )
this.data.arg[1] = 0;
} else
// Default to 0.
this.data.arg[1] = 0;
// The interval
result = this.data.arg[2]
? twve.tcalc.isNumeric(this.data.arg[2]) : 1;
if ( result ) {
this.data.arg[2] = result*1;
if(this.data.arg[2]/
(this.data.arg[1]-this.data.arg[0])<1e-4)
this.data.arg[2] =
(this.data.arg[1]-this.data.arg[0])*(1e-4);
} else
// Default to 1.
this.data.arg[2] = 1;
} else if ( this.data.type == 'date' ) {
// The end
result = Date.parse(this.data.arg[1]);
if ( ! isNaN(result) ) {
this.data.arg[1] = result;
} else
// Default to now.
this.data.arg[1] = new Date();
// The interval
result = Date.parse(this.data.arg[2]);
if ( ! isNaN(result) ) {
this.data.arg[2] = result;
} else
// Default to 1 second.
this.data.arg[2] = 1000;
} else {
// String
if ( this.data.arg[1] === null ) this.data.arg[1] = '';
if ( this.data.arg[2] === null ) this.data.arg[2] = 1;
}
// Do the countdown
},
parseSingleTerm: function (txt) {
var len = this.data.arg.length;
switch ( len ) {
case 0 :
var result = twve.tcalc.isNumeric(txt);
if ( result ){
this.data.arg[0] = result*1;
this.data.type = 'number';
} else {
result = Date.parse(txt);
if ( ! isNaN(result) ) {
this.data.arg[0] = result;
this.data.type = 'date';
} else {
this.data.arg[0] = txt;
this.data.type = 'string';
}
}
break;
case 1 :
case 2 :
this.data.arg[len] = txt;
break;
}
}
}
];
}());
//}}}
[[twve.tcalc--Example--Simple Arithmetics]]
[[twve.tcalc--Example--Commonly Used Funtions]]
[[twve.tcalc--Example--Less Commonly Used Funtions]]
[[twve.tcalc--Example--Date and Time]]
[[twve.tcalc--Example--IF and COUNT]]
[[twve.tcalc--Example--Auto-djustment of Cell References]]
[[twve.tcalc--Example--Simple Statistics]]
[[twve.tcalc--Defining Your Own Functions]]
[[twve.tcalc--Example--The Javascript Math Funtions]]
The ''twve.tcalc'' plugin (formerly [[TWtcalc|http://twtable.tiddlyspace.com/#TWtcalc.1.0.10]] and [[TableCalculator|http://twtable.tiddlyspace.com/#TableCalculator]]) suports user defined functions written in Javascript. If you are not familiar with it, there are tons of useful references over the internet for you to dig in.
All user defined functions accepts cell references in one of the following ways:
{{{
FUNCTION(A3:H5) // calculate over the cells between A3 and H5.
FUNCTION(A1, B2, C3, D4) // calculate over separated cells
FUNCTION(A2:B4,C6,C8,D7:D10) // well, if you know OpenOffice.calc and Excel...
}}}
The ''twve.tcalc'' takes care of the de-referencing/evaluation of cell contents for your functions to use. You simply focus on dealing with the cell contents and the return values from other functions.
User defined functions are collected as an array in the ''twve.tcalc'' as the following:
{{{
config.macros.twve.tcalc.fn = [
{
// Function 0
},
{
// Function 1
},
{
// Function 2
}
];
}}}
New functions can be added into this array in any order. The ''twve.tcalc'' takes care of their use if they are properly defined.
!!! Defining your own functions
There are a few things you need to know to define your own functions. First of all, a function is ''required to have a name'', __everything else is optional__. Therefore the simplest function definition is:
{{{
{
name: 'FUNCTION', // Required and ALL CAPITAL.
},
}}}
>The user defined functions are case insensitive in ''twve.tcalc''. Their names are converted to uppercase for internal use. Therefore the name here has to be ALL CAPITAL otherwise it is not recognized by ''twve.tcalc''.
>>Supports name aliasing in user-defined functions. Simply list all the aliases/nicknames of your function in an array and assign it to the {{{NAME}}} property of the function. For example,
{{{
{
name: ['AVERAGE', 'MEAN'],
...
},
...
...
{
name: ['STDERR', 'STDEV'],
...
},
}}}
>Note that the function is enclosed by a pair of curly brackets, followed by a comma after the closing one. Also note the ending comma at the end of each line, except for the opening curly brace and the last statement of definition. These are required by Javascript which is not covered in this tiddler. Please search over the Internet for references if you need to know about it.
>>There is NO NEED to add a comma at the end of the last statement of definition. It is supposed to be optional (and it is for FF and Chrome), but ''~IE9'' seems quite strict on this - you can not add a comma to the end of the last statement of definition.
Such a function does not do anything, of course. To make it useful, you can define either
# its operator or
# one (or more) of the three parsing functions.
!!! The operator
The operator is for Javascript to evaluate the return value of your function, so it has to be a valid Javascript operator. Let's look at a very simple example to see how it works. The simplest version of SUM function can be defined as
{{{
{
name: 'SUM',
operator: '+' // The last statement of definition needs no comma.
},
}}}
This function accepts cell references as shown above. It simply converts the accepted formats into valid Javascript expressions and leave them to Javascript for evaluation. For example,
{{{
SUM(A3:H5) would be converted to A3+A4+A5+B3+B4+B5+...+H3+H4+H5
}}}
The cell contents will be extracted before passing to Javascript for sure.
>The ''twve.tcalc'' uses the {{{eval()}}} function in Javascript for evaluation of expressions. With this {{{eval()}}} function the {{{+}}} operator (and {{{*}}} too) is supposed to deal with numeric expressions only. Mixing numeric and non-numeric expressions with the {{{+}}} operator sometimes generates {{{NaN}}} (Not a Number), which is considered a number in Javascript but really not so useful. This function, however, does not do any kind of checking on the cell contents, so if some of the cells contain non-numeric expressions, they will be passed to Javascript and may give rise to such a situation. To avoid such things, you can define the {{{parse_single_term ()}}} function (see below) and do the proper checking on cell contents.
!!! The parsing functions
There are three parsing functions you can define in your function:
# {{{start_parsing ()}}}
** You can do initialization for your function here.
# {{{stop_parsing ()}}}
** You can clear and close up your function here.
# {{{parse_single_term ()}}}
** If your function takes arguments, you need to take care of them here, one at a time.
All these parsing functions are optional. You can define any or none of them, depending on your function's designated purpose.
!!!! The {{{parse_single_term()}}} function
Let's look at the {{{parse_single_term()}}} function first. This function takes only one argument, because all the user defined functions are designed to handle one argument at a time. Let's use the SUM function again as an example, but this time we define its {{{parse_single_term()}}} function.
{{{
{
name: 'SUM',
operator: '+',
parse_single_term: function ( txt ) {
return config.macros.TableCalculator.isnumeric(txt) ? txt : '0';
} // The last statement of definition needs no comma.
},
}}}
In this slightly advanced version we look at the argument in the {{{parse_single_term()}}} function, convert it to '0' if it is non-numeric. This way we can avoid the possible situation mentioned above. We can certainly do more things in this function if necessary.
>Note that the argument passed here has already been de-referenced/evaluated. It could be the evaluated content of a cell, the return value of a function, or whatever the user typed in upon calling this function. There is no cell reference or function names in the argument, you simply focus on what your function would do on the arguments, one at a time.
!!!! The other two parsing functions
The {{{start_parsing()}}} and {{{stop_parsing()}}} are for the initialization and closing up of your function, useful for functions with no arguments or functions that do the actual calculations (instead of making a valid Javascript statement and do nothing). They do not accept arguments though.
For functions without arguments, such as {{{TODAY()}}}, you can return its value in either of these two functions.
For functions that do the actual calculations, see the template below or functions defined in the ''twve.tcalc'' plugin for details. For example, {{{DAYS(date1, date2, num_dec)}}}, {{{ROUND(value, num_dec)}}}, and {{{RANDOM(factor)}}} functions do not generate a Javascript statement but the results of their designated calculations.
!!! More to go
There are much more things you can do with the user defined functions: functions with other Javascript operators, functions with more than one arguments, etc. Look into the already-defined functions in the ''twve.tcalc'' for references.
!!! Template
The following codes show a general template for user defined functions. You can use it to define your own.
{{{
{
name: 'FUNCTION', // Required and ALL CAPITAL.
no_eval: true, // Optional. If your function is dealing with
// numeric stuffs and you'd like Javascript to
// evaluate the return value of this function,
// remove this line.
operator: '+', // Optional. This is for Javascript to evaluate
// the return value of this function. Remove if not needed.
var1: null, // Optional. Remove if not needed.
var2: null, // Optional. Remove if not needed.
// You can define more variables here.
start_parsing: function () {
// Optional initialization. Remove this function if not needed.
// No arguments here.
this.var1 = this.var2 = null;
},
stop_parsing: function () {
// Optional stopping function. Remove this function if not needed.
// No arguments here.
return (some calculations on var1 and var2);
},
parse_single_term: function ( txt ) {
// Optional single term parsing function. Remove this function if not needed.
// Only one argument here. The argument passed here is already
// de-referenced/evaluated. There is NO cell references or function names
// in the argument.
if ( !this.var1 ) {
// First argument. Do whatever you want.
this.var1 = some_function(txt);
}
else if ( !this.var2 ) {
// Second argument. Do whatever you want.
this.var2 = do_some_thing_on(txt);
} else if ( more variables defined ) {
// The following arguments...
}
}
},
}}}
<<<
The ''twve.tcalc'' supports //auto adjustment of cell references// in the following situations:
# change in cell content,
** See [[twve.table--Example--A Simple Table]] for how to esit a cell.
# copy-and-pasting of
** a single table cell,
** a table row/column;
*** See [[twve.table--Example--Copy and Paste]] for how to copy and paste a cell or a row/column.
# insertion/deletion/exchange of table rows/columns,
** See [[twve.table--Example--Insertion, Deletion, and Exchange]] for how to insert/delete a table row/column, and how to exchange it with its neighbor.
# table transposition.
** See [[twve.table--Example--Transposition]] for how to transpose a table.
<<<
@@In the simple table below you can check the formula before and after making one of the above changes to see the correctness (or incorrectness) of auto-adjustment.@@
|Simple Calculations|c
|editable|k
| A | B |h
| X1 | 15000|
|||
| X2 | 10000|
|>|background-color:yellow;|
| X1 + X2 | =B1+B3|
| X1/X2 | =B1/B3|
| X2 - X1 | =B3-B1|
| (X2 - X1)/X1 | =(B3-B1)/B1|
| Rounded to 0.01 | =round(b8,2)|
| as % (down to 0.1%) | =round(B8*100,1)%|
<<<
This tiddler shows some ''commonly used'' built-in functions in ''twve.tcalc'', including
# SUM
# AVERAGE/AVG/MEAN
** These are aliases of the same function.
# SQRT
# MAX, MIN
# COUNTA
** Counts the number of non-empty cells.
# COUNT
** Counts the number of numerical values.
** __Capable of doing conditional counting__, see [[twve.tcalc--Example--IF and COUNT]] for an example.
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Commonly Used Built-in Functions|c
|editable|k
| A | B |h
| Y1 | 1200|
| Y2 | 1500|
| Y3 | 1450|
|>|background-color:yellow;|
| SUM(B1:B3) | =sum(b1:b3)|
| AVERAGE(B1:B3) | =average(b1:b3)|
| Rounded to 0.01 | =round(avg(b1:b3),2)|
| SQRT(SUM) | =sqrt(b5)|
| Max of Y's | =max(b1:b3)|
| Min of Y's | =min(b1:b3)|
| COUNT(B1:B10) | =count(B1:B10)|
| COUNTA(B1:B10) | =COUNTA(B1:B10)|
<<<
This tiddler shows some ''Date and Time'' related built-in functions in ''twve.tcalc'', including
# TODAY
# DAYS(begin,end,num_dec) -- Calculates the date and time differences between two dates, from begin to end.
** If the 3^^rd^^ argument //num_dec// is given, this function returns the result in //number of days//, down to //num_dec// digits after decimal point.
** If, however, the 3^^rd^^ argument is missing, this function returns the result in D HH:MM:SS format, down to seconds.
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Date and Time|c
|editable|k
| A | B |h
| TODAY() | =today()|
| Date Started | 2014/05/01 12:00|
| Date Ended | 2014/05/13 18:00|
| DAYS(B2,B3,2) | =days(B2,B3,2)|
| DAYS(B2,B3) | =days(B2,B3)|
<<<
This tiddler shows a example of two built-in functions,
# {{{IF(condition, true statement, false statement)}}}
# {{{COUNT(condition statement, range of cells)}}}.
** The first argument {{{condition}}} uses the percentage symbol (%) as the placeholder for cell values.
** For example, in the table below, the formula in cell G4 is {{{=COUNT('%<60',F1:F20)}}}, meaning //to count the number of cells, from F1 to F20, that have numerical values less than 60.//
They are very similar to the ~OpenOffice.calc or MS Excel versions.
<<<
|IF Statement and Conditional Counting|c
|editable|k
| 1 | 2 | 3 | 4 | 5 | Total ||h
| 34| 7.5| 14| 17.5| 10| =if(sum(A1:E1)<60,color:red;sum(A1:E1),sum(A1:E1)) | (>=60) |
| 30| 10| 6| 10| 10| =if(sum(A2:E2)<60,color:red;sum(A2:E2),sum(A2:E2)) | =count('%>=60', F1:F20) |
| 34| 10| 14| 20| 10| =if(sum(A3:E3)<60,color:red;sum(A3:E3),sum(A3:E3)) | @@color:red;(<60)@@ |
| 34| 10| 6| 15| 10| =if(sum(A4:E4)<60,color:red;sum(A4:E4),sum(A4:E4)) | =count('%<60', F1:F20) |
| 34| 7.5| 16| 10| 10| =if(sum(A5:E5)<60,color:red;sum(A5:E5),sum(A5:E5)) | Max |
| 34| 10| 6| 20| 10| =if(sum(A6:E6)<60,color:red;sum(A6:E6),sum(A6:E6)) | =max(F1:F20) |
| 24| 10| 6| 0| 10| =if(sum(A7:E7)<60,@@color:red;sum(A7:E7)@@,sum(A7:E7)) | Min |
| 34| 10| 14| 10| 10| =if(sum(A8:E8)<60,color:red;sum(A8:E8),sum(A8:E8)) | =min(F1:F20) |
| 34| 10| 10| 10| 10| =if(sum(A9:E9)<60,color:red;sum(A9:E9),sum(A9:E9)) | Avg |
| 29| 10| 12| 20| 10| =if(sum(A10:E10)<60,color:red;sum(A10:E10),sum(A10:E10)) | =Avg(F1:F20) |
| 34| 10| 6| 15| 10| =if(sum(A11:E11)<60,color:red;sum(A11:E11),sum(A11:E11)) | \(\sigma\) |
| 34| 10| 6| 20| 10| =if(sum(A12:E12)<60,color:red;sum(A12:E12),sum(A12:E12)) | =round(stderr(f1:f20),2) |
| 9| 5| 6| 20| 10| =if(sum(A13:E13)<60,@@color:red;sum(A13:E13)@@,sum(A13:E13)) ||
| 34| 10| 6| 15| 20| =if(sum(A14:E14)<60,color:red;sum(A14:E14),sum(A14:E14)) ||
| 34| 10| 6| 20| 10| =if(sum(A15:E15)<60,color:red;sum(A15:E15),sum(A15:E15)) ||
| 34| 7.5| 6| 20| 10| =if(sum(A16:E16)<60,color:red;sum(A16:E16),sum(A16:E16)) ||
| 34| 7.5| 6| 18| 10| =if(sum(A17:E17)<60,color:red;sum(A17:E17),sum(A17:E17)) ||
| 21| 10| 8| 15| 10| =if(sum(A18:E18)<60,color:red;sum(A18:E18),sum(A18:E18)) ||
| 21| 5| 6| 5| 10| =if(sum(A19:E19)<60,@@color:red;sum(A19:E19)@@,sum(A19:E19)) ||
| 34| 7.5| 12| 18| 10| =if(sum(A20:E20)<60,color:red;sum(A20:E20),sum(A20:E20)) ||
<<<
This tiddler shows some ''less commonly used'' built-in functions in ''twve.tcalc'', including
# PRODUCT
# RANDOM(max)
# ISNUMERIC
# COLUMNA/COLA -- Literal column reference.
** {{{A}}} for the first column, {{{B}}} for the second, etc.
# COLUMNS/COLS -- Counts the number of columns in the specified range.
# COLUMN/COL -- Numerical column reference.
** {{{0}}} for the first column, {{{1}}} for the second, etc.
# ROWS -- Counts the number of rows in the specified range.
# ROW -- Numerical reference of a table row.
** {{{0}}} for the first row, {{{1}}} for the second, etc.
# CONCAT -- Concatenate the content in the specified cells, literally.
# EXP / LN / ~LOG10 / LOG
** Exponential / Natural logarithm / Base-10 logarithm / Arbitrary based logarithm.
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Less Commonly Used Built-in Functions|c
|editable|k
| A | B |h
| Y1 random(100) | =random(100)|
| Y2 random(100) | =random(100)|
| Y3 random(100) | =random(100)|
|>|background-color:yellow;|
| PRODCUCT(B1:B3) | =product(b1:b3)|
| ISNUMERIC(B4) | =isnumeric(b4)|
| COL() | =col()|
| COLA() | =cola()|
| COLS(A1:B1) | =cols(a1:b1)|
| ROW() | =row()|
| CONCAT(B8:B11) | =concat(B7:B10)|
|>|background-color:yellow;|
| EXP(2) | =exp(2)|
| LN(10) | =ln(10)|
| ~LOG10(100) | =log(100)|
| LOG(512,2) | =log(512,2)|
<<<
This tiddler shows the basic arithmetics, the most simple calculations supported in ''twve.tcalc''.
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Simple Calculations|c
|editable|k
| A | B |h
| X1 | 15000|
|||
| X2 | 10000|
|>|background-color:yellow;|
| X1 + X2 | =B1+B3|
| X1/X2 | =B1/B3|
| X2 - X1 | =B3-B1|
| (X2 - X1)/X1 | =(B3-B1)/B1|
| Rounded to 0.01 | =round(b8,2)|
| as % (down to 0.1%) | =round(B8*100,1)%|
<<<
This tiddler shows some ''statistics'' related built-in functions in ''twve.tcalc'', including
# MEAN/AVG/AVERAGE
# STDERR/STDEV
** This function implements the //corrected sample standard deviation// \[\sigma = \sqrt{{1 \over N-1}\sum_{i=1}^N\ \left(x_i - \bar x\right)^2},\] as described in [[Wikipedia Standard Deviation page|http://en.wikipedia.org/wiki/Standard_deviation]].
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Simple Statistics|c
|editable|k
| A | B |h
| Y1 random(100) | =random(100)|
| Y2 random(100) | =random(100)|
| Y3 random(100) | =random(100)|
| Y4 random(100) | =random(100)|
| Y5 random(100) | =random(100)|
|>|background-color:yellow;|
| MEAN(B1:B5) | =avg(b1:b5)|
| STDERR(B1:B5) \[\sigma = \sqrt{{1 \over N-1}\sum_{i=1}^N\ \left(x_i - \bar x\right)^2},\] | =stderr(b1:b5)|
| STDERR of the MEAN \[\sigma_\text{mean} = {\sigma \over \sqrt N}\] | =b8/sqrt(count(b1:b5))|
| \(\sigma_\text{mean}\) in % | =round(b9/b7*100,1)%|
<<<
The ''twve.tcalc'' supports the use of Javascript Math functions in a formula.
@@color:blue;Hover you mouse over a table cell, the tooltip text shows its cell reference and formula therein.@@
<<<
|Javascript Math functions|c
|editable|k
| A | B |h
| \(\pi\) | =4*Math.atan(1)|
| \(\pi \over 2\) | =B1/2|
| a random number | =random()|
|>|background-color:yellow;|
| Math.sin(B1) | =round(Math.sin(B1),2)|
| Math.sin(B2) | =Math.sin(B2)|
| Math.cos(B1) | =Math.cos(B1)|
| Math.cos(B2) | =round(Math.cos(B2),2)|
| Math.sqrt(B3) | =Math.sqrt(B3)|
| B9^^2^^ (equals B3) | =B9*B9|
!! Mixing JS Math functions with ''twve.tcalc'' functions
|editable|k
|It is possible to mix JS math functions with ''twve.tcalc'' functions.|c
|>|One can use the result of a ''twve.tcalc'' function as the argument <br>of a JS math function, as shown below.|
| Math.sqrt(random())| =Math.sqrt(random())|
|>|Or the opposite way.|
| sqrt(Math.random())| =sqrt(Math.random())|
/***
|editable|k
|''Name:''|twve.tcalc|
|''Description:''|A simple table calculator (spreadsheet) for ~TiddlyWiki|
|''Author:''|Vincent Yeh (qmo.wcy2@gmail.com)|
|''Source:''|* (minimized) http://twve.tiddlyspot.com/#twve.tcalc.min / http://twve.tiddlyspace.com/#twve.tcalc.min <br>* (regular) http://twve.tiddlyspot.com/#twve.tcalc / http://twve.tiddlyspace.com/#twve.tcalc|
|''Type:''|plugin|
|''Version:''|3.2.5|
|''Status:''|This plugin is still under development.<br>You are welcome to try and send feedback. :-)|
|''Date:''|2014/11/21 said goodbye to jQuery <br>2014/05/13 released 3.0.0 <br>2013/11/29 reformed <br>2013/06/02 released 1.0.0 <br>2012/10/19 released 0.7.0 <br>2012/09/04 started from ~TableCalculator 0.6.14|
|''License:''|MIT|
|''Core Version:''|2.6.5|
|''Needs to have:''|twve.core, twve.table|
!!Options
Look for [[twve.tcalc Options]] in the system Options panel.
***/
// @@display:none;
//{{{
"undefined"==typeof twve.table&&alert("twve.tcalc: This plugin needs twve.table to function properly. Please install twve.table before this one.");version.extensions.twve.tcalc={major:3,minor:2,revision:5,date:new Date("2015/11/06")};
config.macros.twveTcalcOptions={init:function(){twve.tcalc.preReadOnly=twve.tiddler.readOnly;twve.tiddler.readOnly=twve.tcalc.readOnly;twve.tcalc.preGetOptionsMenu=twve.tiddler.getOptionsMenu;twve.tiddler.getOptionsMenu=twve.tcalc.getOptionsMenu;twve.tcalc.preWikify=twve.node.wikify;twve.node.wikify=twve.tcalc.wikify;twve.tcalc.preGetTableMenu=twve.table.getTableMenu;twve.table.getTableMenu=twve.tcalc.getTableMenu;twve.tcalc.preTableCreate=twve.table.create;twve.table.create=twve.tcalc.tableCreate;
twve.table.colRef=twve.tcalc.colRef;twve.tcalc.prePrepareElements=twve.wrapper.prepareElements;twve.wrapper.prepareElements=twve.tcalc.prepareElements;void 0===config.options.chktwveTcalcEnabled&&(config.options.chktwveTcalcEnabled=void 0===config.options.chkTWtcalcEnabled?!0:config.options.chkTWtcalcEnabled);void 0===config.options.chktwveTcalcAllTables&&(config.options.chktwveTcalcAllTables=void 0===config.options.chkTWtcalcAllTables?!1:config.options.chkTWtcalcAllTables);void 0===config.options.chktwveTcalcInTextCalc&&
(config.options.chktwveTcalcInTextCalc=!1);void 0===config.options.chktwveTcalcThousandSeparated&&(config.options.chktwveTcalcThousandSeparated=void 0===config.options.chkTWtcalcThousandSeparated?void 0===config.options.chkTCalcThousandSeparated?!1:config.options.chkTCalcThousandSeparated:config.options.chkTWtcalcThousandSeparated);void 0===config.options.txttwveTcalcThousandSeparator&&(config.options.txttwveTcalcThousandSeparator=void 0===config.options.txtTWtcalcThousandSeparator?void 0===config.options.txtTCalcThousandSeparator?
",":config.options.txtTCalcThousandSeparator:config.options.txtTWtcalcThousandSeparator);void 0===config.options.txttwveTcalcDecimalMark&&(config.options.txttwveTcalcDecimalMark=void 0===config.options.txtTWtcalcDecimalMark?void 0===config.options.txtTCalcDecimalMark?".":config.options.txtTCalcDecimalMark:config.options.txtTWtcalcDecimalMark);void 0===config.options.chktwveTcalcDebugMode&&(config.options.chktwveTcalcDebugMode=void 0===config.options.chkTWtcalcDebugMode?void 0===config.options.chkTCalcDebugMode?
!1:config.options.chkTCalcDebugMode:config.options.chkTWtcalcDebugMode);merge(config.optionsDesc,{chktwveTcalcEnabled:"Enable ''twve.tcalc''",chktwveTcalcAllTables:'Calculate all tables. Otherwise only calculate tables with class "spreadsheet"',chktwveTcalcInTextCalc:"(@@color:red;Experimental@@) In text calculation. There can be multiple expressions in a statement.",chktwveTcalcThousandSeparated:"Apply thousands separation on numerical results. Default is false.",txttwveTcalcThousandSeparator:"Thousand separator. Default is comma (,).",
txttwveTcalcDecimalMark:"Decimal mark. Default is period (.).",chktwveTcalcDebugMode:"Enter debug mode to show error/exception messages. Default is false."});var a=config.shadowTiddlers.OptionsPanel,b=a.indexOf("[[twve.core Options");0<=b&&(b=a.indexOf("]]\n",b+2)+3,config.shadowTiddlers.OptionsPanel=a.substring(0,b)+"[[twve.tcalc Options|twve.tcalc Options]]\n"+a.substring(b));merge(config.shadowTiddlers,{"twve.tcalc Options":"<<twveTcalcOptions>>"})},order:{chktwveTcalcEnabled:0,chktwveTcalcAllTables:1,
chktwveTcalcThousandSeparated:2,txttwveTcalcThousandSeparator:3,txttwveTcalcDecimalMark:4,chktwveTcalcDebugMode:5},handler:function(a){config.macros.twveCoreOptions.showOptionsTable(a,"''twve.tcalc'' Options","twveTcalc",config.macros.twveTcalcOptions.order)}};
twve.tags.nested={create:function(a,b){var d=twve.tags.create(a,b);d.clone=function(){return twve.tags.nested.create()};d.matchedCloseTag=function(a,b){var g=1,h=a.length,k=twve.position.create(b.ndx);do{k=twve.text.indexOf(a,d.close,k);if(-1==k.ndx)return d.lastCloseTag(a,k,h);b=twve.text.indexOf(a,d.open,b);if(-1==b.ndx){b.ndx=k.ndx;do{if(0==--g)return k;k.ndx+=k.matched.length;k=twve.text.indexOf(a,d.close,k);if(-1==k.ndx)return d.lastCloseTag(a,k,h)}while(1)}if(k.ndx==b.ndx){if(0==--g)return k;
b.ndx+=b.matched.length;k.ndx+=k.matched.length}else if(k.ndx<b.ndx){do{if(0==--g)return k;k.ndx+=k.matched.length;k=twve.text.indexOf(a,d.close,k);if(-1==k.ndx)return d.lastCloseTag(a,k,h)}while(k.ndx<b.ndx);++g;b.ndx+=b.matched.length;k.ndx=b.ndx}else{do++g,b.ndx+=b.matched.length,b=twve.text.indexOf(a,d.open,b);while(-1<b.ndx&&b.ndx<k.ndx);--g;k.ndx+=k.matched.length;b.ndx=k.ndx}}while(1)};return d}};
twve.url={markupTags:function(){var a=twve.tags.create(["http://","https://","file://"],["[[","]]",", ",". "," "]);a.clone=function(){return twve.url.markupTags()};a.exactCloseTag=function(){return a};return a},create:function(a){var b=twve.element.create();b.tags=twve.url.markupTags();b.setTarget=function(a){var e=b.target;if(a&&(b.start.ensureValid(),twve.text.indexOf(a,b.tags.open,b.start),-1<b.start.ndx))return b.end.ndx=b.start.ndx,twve.text.indexOf(a,b.tags.close,b.end),0>b.end.ndx&&(b.end.ndx=
a.length),b.target=a.substring(b.start.ndx,b.end.ndx),e;b.target=null;return e};b.getTarget=function(){return b.target};b.waitForContent=function(){return 4==b.xmlhttp.readyState&&200==b.xmlhttp.status?(clearInterval(b.pid),b.callback&&b.callback(b.xmlhttp.responseText),b.xmlhttp.responseText):"Waiting for response from "+b.target};b.getContent=function(a){if(!b.target)return null;b.xmlhttp=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP");if(!b.xmlhttp)return null;b.xmlhttp.open("GET",
b.target,a);try{b.xmlhttp.send()}catch(e){console.log(e)}if(a)b.callback=a,b.pid=setInterval(b.waitForContent,50);else return b.waitForContent()};b.postData=function(){};return!a||b.setTarget(a)?b:null}};
twve.tcalc={preReadOnly:null,readOnly:function(a){return a&&"twve.tcalc--example"==a.toLowerCase().substring(0,19)?!1:twve.tcalc.preReadOnly.apply(this,arguments)},cur_twtable:null,prev_twtable:[],colRef:function(a){return twve.tcalc.indexToReferenceCol(a)},block_update:!1,startBlockUpdate:function(){twve.tcalc.block_update=!0},doingBlockUpdate:function(){return twve.tcalc.block_update},endBlockUpdate:function(a,b,d){twve.tcalc.block_update=!1;b=twve.table.allCells(a,b,d);var e,f,g,h,k,n,l,s;for(e=
b.length-1;0<=e;e--)for(f=b[e].length-1;0<=f;f--)if(g=a.substring(b[e][f].open,b[e][f].close),h=g.match(/\{[\$A-Za-z]+\.[\$0-9]+\}/g)){for(k=0;k<h.length;k++)n=g.indexOf(h[k]),s=g.indexOf(".",n+1),l=g.indexOf("}",n+1),g=g.substring(0,n)+g.substring(n+1,s)+g.substring(s+1,l)+g.substring(l+1);a=a.substring(0,b[e][f].open)+g+a.substring(b[e][f].close);d.ndx+=g.length-(b[e][f].close-b[e][f].open)}return a},updateCellReference:function(a,b,d,e,f,g,h,k){b=twve.table.allCells(a,b,d);var n,l,s,p,m,q,u;for(n=
b.length-1;0<=n;n--)for(l=b[n].length-1;0<=l;l--)if(s=a.substring(b[n][l].open,b[n][l].close),q=!1,p=twve.tcalc.indexOfExpression(s),-1!=p){for(p++;p<s.length;){var t=!0;u=!1;if(m=twve.tcalc.tiddlerReference(s,p))t=!1,p+=m.length;if(m=twve.tcalc.tableReference(s,p))t=!1,p+=m.length;if(m=twve.tcalc.cellReference(s,p)){if(t){var t=twve.tcalc.referenceToIndexRow(m),v=twve.tcalc.referenceToIndexCol(m),x=twve.tcalc.absoluteRow(m),w=twve.tcalc.absoluteCol(m);if(null===e){if(v==f&&(k||!w))v=h,u=!0}else if(null===
f){if(t==e&&(k||!x))t=g,u=!0}else if(":"==s.charAt(p+m.length))p+=m.length+1,m=twve.tcalc.cellReference(s,p),p+=m.length;else if(t==e&&v==f&&(k||!x)&&(k||!w))t=g,v=h,u=!0;u&&(q=twve.tcalc.doingBlockUpdate()?"{"+twve.tcalc.indexToReferenceCol(v,w)+"."+twve.tcalc.indexToReferenceRow(t,x)+"}":twve.tcalc.indexToReferenceCol(v,w)+twve.tcalc.indexToReferenceRow(t,x),s=s.substring(0,p)+q+s.substring(p+m.length),m=q,q=!0)}p+=m.length}else p++}q&&(a=a.substring(0,b[n][l].open)+s+a.substring(b[n][l].close),
d.ndx+=s.length-(b[n][l].close-b[n][l].open))}return a},incCellReference:function(a,b,d,e,f,g,h,k,n){b=twve.table.allCells(a,b,d);var l,s,p,m,q;e||(e=0);f||(f=0);g||(g=0);k||(k=0);n||(n=0);for(l=b.length-1;0<=l;l--)for(s="number"!=typeof h||0>h||h>=b[l].length?b[l].length-1:h;s>=g;s--)if(q=a.substring(b[l][s].open,b[l][s].close),p=twve.tcalc.indexOfExpression(q),-1!=p){p++;for(var u=q.length;p<u;){var t=!0;if(m=twve.tcalc.tiddlerReference(q,p))t=!1,p+=m.length;if(m=twve.tcalc.tableReference(q,p))t=
!1,p+=m.length;if(m=twve.tcalc.cellReference(q,p)){if(t){var t=twve.tcalc.referenceToIndexRow(m),v=twve.tcalc.referenceToIndexCol(m),x=twve.tcalc.absoluteRow(m),w=twve.tcalc.absoluteCol(m);t>=e&&v>=k&&(t=twve.tcalc.indexToReferenceCol(v+(w?0:n),w)+(x?"$"+t:t+f),q=q.substring(0,p)+t+q.substring(p+m.length),m=q.length,u!=m&&(d.ndx+=m-u,u=m),m=t)}p+=m.length}else p++}a=a.substring(0,b[l][s].open)+q+a.substring(b[l][s].close)}return a},transposed:function(a,b,d){b=twve.table.allCells(a,b,d);var e,f,g,
h,k,n;for(e=b.length-1;0<=e;e--)for(f=b[e].length-1;0<=f;f--)if(g=a.substring(b[e][f].open,b[e][f].close),n=!1,h=twve.tcalc.indexOfExpression(g),-1!=h){for(h++;h<g.length;){var l=!0;if(k=twve.tcalc.tiddlerReference(g,h))l=!1,h+=k.length;if(k=twve.tcalc.tableReference(g,h))l=!1,h+=k.length;if(k=twve.tcalc.cellReference(g,h)){if(l){n=twve.tcalc.referenceToIndexRow(k);var l=twve.tcalc.referenceToIndexCol(k),s=twve.tcalc.absoluteRow(k),p=twve.tcalc.absoluteCol(k);n=twve.tcalc.indexToReferenceCol(n,s)+
twve.tcalc.indexToReferenceRow(l,p);g=g.substring(0,h)+n+g.substring(h+k.length);k=n;n=!0}h+=k.length}else h++}n&&(a=a.substring(0,b[e][f].open)+g+a.substring(b[e][f].close),d.ndx+=g.length-(b[e][f].close-b[e][f].open))}return a},copied_from:null,cut_from:null,replaceDecimalMark:function(a,b,d,e,f){b=a.indexOf(e,b);return-1==b?a:a.substring(0,b)+f+a.substring(b+1,d)},nonEmpty:function(a,b,d){void 0!==b&&(a=void 0!==d?a.substring(b,d):a.substring(b));return a.trim()?a:""},partialSearch:!1,halfWay:function(a,
b,d,e){return e||twve.tcalc.partialSearch?twve.tcalc.nonEmpty(a,b,d):""},isPartsPerNotation:function(a,b,d,e){var f=1;switch(a.charAt(e)){case "%":f=0.01;break;case "\u2030":f=0.001;break;case "p":if(e+2>=d||"p"!=a.charAt(e+1))return"";switch(a.charAt(e+2)){case "m":f=1E-6;break;case "b":f=1E-9;break;case "t":f=1E-12;break;case "q":f=1E-15;break;default:return""}break;default:return""}return twve.tcalc.partialSearch?twve.tcalc.halfWay(a,b,e+1,!0):twve.tcalc.halfWay(a,b,e,!0)*f+""},digit:"0123456789",
digit_zh:"\u96f6\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d",digit_zh_cap:"\u58f9\u8cb3\u53c1\u8086\u4f0d\u9678\u67d2\u634c\u7396",isDigit:function(a){var b=twve.tcalc.digit.indexOf(a);if(0<=b)return a;b=twve.tcalc.digit_zh.indexOf(a);if(0<=b)return b+"";b=twve.tcalc.digit_zh_cap.indexOf(a)+1;return 0<b?b+"":""},isNumeric:function(a,b,d,e,f,g){var h=0,k=0,n=0,l=0,s=0;b="number"==typeof b&&0<b?b:0;var p,m,q=a.length;"number"==typeof d?(0>d?(d+=q+1,0>d&&(d=0)):d>q&&(d=q),d<b&&(d=b=d)):d=
q;m=b;for(var u=!1,q="",t=/\s/;t.test(p=a.charAt(m));){if(++m==d)return"";q+=p}for(t=0;m<d;m++){p=a.charAt(m);var v=twve.tcalc.isDigit(p);if(""==v)switch(p){case "e":case "E":if(0==t)return twve.tcalc.halfWay(a,b,m,!0);u=!0;q+=p;break;case "\u2212":p="-";case "+":case "-":if(0<t&&!u)return twve.tcalc.halfWay(q);if(++n>(u?2:1))return 0<t?twve.tcalc.halfWay(q):"";q+=p;break;case ":":if(0==t)return"";q+=p;p=a.charAt(m+1);v=twve.tcalc.isDigit(p);if(""==v){switch(p){case "+":case "-":n=1;break;case ".":k=
1;break;default:return twve.tcalc.halfWay(q)}q+=p}else t++,q+=v,n=0;u=!0;m++;break;case ".":if(1<++k&&1<h)return 0<t?twve.tcalc.halfWay(q):"";q+=p;break;case ",":if(!config.options.chktwveTcalcThousandSeparated||1<++h&&1<k)return twve.tcalc.halfWay(q);q+=p;break;case "\u00b7":if(!config.options.chktwveTcalcThousandSeparated||1<++s&&(1<l||1<k||1<h))return twve.tcalc.halfWay(q);q+=p;break;case " ":if(config.options.chktwveTcalcThousandSeparated&&1>=s&&1>=k&&1>=h){++l;q+=p;break}m++;default:return 0<
t?twve.tcalc.isPartsPerNotation(a,b,d,m)||twve.tcalc.halfWay(q):""}else t++,q+=v}if(!e&&0==h&&0==k&&0==l&&0==s)return twve.tcalc.nonEmpty(q);e=".";n=",";u=k;p=h;if(1<k)n=".",e=",",p=k,u=h;else if(0<l){if(0<k&&0<h||1<k||1<h)return twve.tcalc.halfWay(a,b,a.indexOf(" ",b));n=" ";p=l;0<h?(e=",",u=h):(e=".",u=k)}else if(0<s){if(0<k||1<h)return twve.tcalc.halfWay(a,b,a.indexOf("\u00b7",b));n="\u00b7";e=",";p=s;u=h}else if(1==h&&0==k)if(m=a.indexOf(",",b),m==b||4!=d-m)n=".",e=",",p=k,u=h;else{if(4>d-m)return twve.tcalc.halfWay(a,
b,m)}else if(1==k&&1==h)if(l=a.indexOf(",",b),s=a.indexOf(".",b),m=l-s,4==m)n=".",e=",",p=k,u=h;else if(-4!=m)return twve.tcalc.halfWay(a,b,0<m?s:l);if(0<p&&!config.options.chktwveTcalcThousandSeparated)return twve.tcalc.halfWay(a,b,a.indexOf(config.options.txttwveTcalcThousandSeparator,b));if(1<p)for(m=a.indexOf(n,b);-1<(h=a.indexOf(n,m+1));){if(4!=h-m)return twve.tcalc.halfWay(a,b,m);m=h}if(0<u&&e!=config.options.txttwveTcalcDecimalMark||0<p&&n!=config.options.txttwveTcalcThousandSeparator)return twve.tcalc.halfWay(a,
b,a.indexOf(n,b));f=f?f:config.options.txttwveTcalcDecimalMark;if(0==p)return a=f!=e?twve.tcalc.replaceDecimalMark(a,b,d,e,f):twve.tcalc.nonEmpty(q),g?twve.tcalc.separateNumber(a):a;g=g?config.options.txttwveTcalcThousandSeparator:"";if(e!=f||n!=g)for(m=b;m<d;m++)p=a.charAt(m),p==n?n!=g&&(a=a.substring(b,m)+g+a.substring(m+1)):p==e&&e!=f&&(a=a.substring(b,m)+f+a.substring(m+1));return twve.tcalc.nonEmpty(a,b,d)},separateNumber:function(a){var b=(a+"").split(config.options.txttwveTcalcDecimalMark);
a=b[0];for(var b=1<b.length?config.options.txttwveTcalcDecimalMark+b[1]:"",d=/(\d+)(\d{3})/;d.test(a);)a=a.replace(d,"$1"+config.options.txttwveTcalcThousandSeparator+"$2");return a+b},spannedCellReference:function(a){var b=a.lastIndexOf("|");if(0<=b)return a.substring(b+1).trim();b=a.indexOf("\n");return 0<=b?a.substring(0,b).trim():a},absoluteRow:function(a){a=a.indexOf("$",1);return 0<a&&4>a},referenceToIndexRow:function(a){if(!a)return null;a=twve.tcalc.spannedCellReference(a);var b=a.indexOf("$",
1);if(0<b&&4>b)return parseInt(a.substring(b+1));b="$"==a.charAt(0)?1:0;return/[a-zA-Z]/.test(a.charAt(b+1))?parseInt(a.substring(b+2)):parseInt(a.substring(b+1))},indexToReferenceRow:function(a,b){return(b?"$":"")+a},absoluteCol:function(a){return"$"==a.charAt(0)},referenceToIndexCol:function(a){if(!a)return null;a=twve.tcalc.spannedCellReference(a);var b=a.indexOf("$");0==b?b++:b=0;var d="A".charCodeAt();a=a.toUpperCase();if(/[A-Z]/.test(a.charAt(b+1))){var e=a.charAt(b).charCodeAt()-d+1;a=a.charAt(b+
1).charCodeAt()-d;return 26*e+a}return a.charAt(b).charCodeAt()-d},indexToReferenceCol:function(a,b){var d="A".charCodeAt(),e=b?"$":"";if(26>a)return e+String.fromCharCode(a+d);var f=a%26;return e+String.fromCharCode(a/26-1+d)+String.fromCharCode(f+d)},indexOfExpression:function(a,b){b=twve.position.create(b);for(var d=twve.position.create(b),e=twve.url.markupTags();;){d=twve.text.skipToActive(a,"=",d);if(-1==d.ndx||d.ndx<b.ndx)return-1;if(!e.encloses(a,d.ndx,b)){for(;"="==a.charAt(d.ndx+1);)d.ndx++;
return d.ndx}d.ndx++}},isUserfunction:function(a,b,d){if(0==b||"."!=a.charAt(b-1))return a.substring(b,d);twve.tcalc.fn_ndx=-1},fn_ndx:-1,isFunction:function(a,b){twve.tcalc.fn_ndx=-1;b||(b=0);for(var d=a.substring(b).toUpperCase(),e=0,f=twve.tcalc.fn.length;e<f;e++){var g="",h=twve.tcalc.fn[e].name;if("string"==typeof h)d.substring(0,h.length)==h&&(g=h);else for(var k=0,n=h.length;k<n;k++)if(d.substring(0,h[k].length)==h[k]){g=h[k];break}if(g){d=d.indexOf(g)+b;f=a.indexOf("(",d+1);if(-1==f)return"";
f=twve.tags.nested.create("(",")").matchedCloseTag(a,twve.position.create(f+1,"("));twve.tcalc.fn_ndx=e;return twve.tcalc.isUserfunction(a,d,-1<f.ndx?f.ndx+1:null)}}},parseSingleTerm:function(a,b,d){var e="",f=null;twve.tcalc.fn[twve.tcalc.fn_ndx].parseSingleTerm?(f=twve.tcalc.fn[twve.tcalc.fn_ndx].parseSingleTerm(a,d))&&(e+=f):e+=f=a;!b&&(f&&twve.tcalc.fn[twve.tcalc.fn_ndx].operator)&&(e+=twve.tcalc.fn[twve.tcalc.fn_ndx].operator);return e},consecutiveCells:function(a){var b=a.split(":");a=twve.tcalc.referenceToIndexCol(b[0]);
var d=twve.tcalc.referenceToIndexRow(b[0]),e=twve.tcalc.referenceToIndexCol(b[1]),b=twve.tcalc.referenceToIndexRow(b[1]);b<d&&(r=d,d=b,b=r);e<a&&(c=a,a=e,e=c);var f=twve.object.create();f.start=twve.object.create();f.start.row=d;f.start.col=a;f.end=twve.object.create();f.end.row=b;f.end.col=e;return f},duplicateFunctionData:function(a){var b="number"==typeof a?twve.tcalc.fn[a]:a;a={};b=b.data?b.data:b;twve.object.copyKeys(a,b,twve.object.keys(b));return a},call_stack:[],pushFunctionCall:function(a){var b=
{ndx:a};void 0!==twve.tcalc.fn[a].data&&(b.data=twve.tcalc.duplicateFunctionData(a));twve.tcalc.call_stack.push(b)},popFunctionCall:function(){var a=twve.tcalc.call_stack.pop();twve.tcalc.fn_ndx=a.ndx;void 0!==a.data&&twve.object.copyKeys(twve.tcalc.fn[a.ndx].data,a.data,twve.object.keys(a.data))},parseConsecutive:function(a){var b,d,e=twve.tcalc.cur_twtable.dom.querySelectorAll("tr"),f=twve.tcalc.consecutiveCells(a),g=twve.tcalc.fn[twve.tcalc.fn_ndx].partialSearch,h=g?[]:"";for(a=f.start.row;a<=
f.end.row;a++){var k=e[a].querySelectorAll("th,td");for(d=f.start.col;d<=f.end.col;d++)if(b=twve.table.getRowCell(k,d))twve.tcalc.pushFunctionCall(twve.tcalc.fn_ndx),b=twve.tcalc.evaluateCell(b,!twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval),twve.tcalc.popFunctionCall(),g?h[h.length]=b:h+=twve.tcalc.parseSingleTerm(b,a==f.end.row&&d==f.end.col)}h&&(g?h=twve.tcalc.parseSingleTerm(h,!0):twve.tcalc.fn[twve.tcalc.fn_ndx].operator&&h.charAt(h.length-1)==twve.tcalc.fn[twve.tcalc.fn_ndx].operator&&(h=h.substring(0,
h.length-1)));return h},allArguments:function(a){var b=a.indexOf("(");if(-1==b)return"";var d=twve.tags.nested.create("(",")").matchedCloseTag(a,twve.position.create(b+1,"("));return 0>d.ndx?"":a.substring(b+1,d.ndx)},splitArguments:function(a){for(var b=twve.tags.nested.create(["(","[","{"],[")","]","}"]),d=twve.position.create(0),e=twve.position.create(0),f=twve.position.create(a.length),g=[];;){d=twve.text.skipToActive(a,",",e,f,b);if(-1==d.ndx)return g[g.length]=a.substring(e.ndx,f.ndx),g;g[g.length]=
a.substring(e.ndx,d.ndx);e.ndx=d.ndx+1}},parseFunction:function(a){var b=twve.tcalc.allArguments(a),d="",e;if(!b){if(twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing&&(e=twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing(a)))d=e;if(twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing&&(e=twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing()))d+=e;if(twve.tcalc.fn_ndx)return d;try{return eval(a)}catch(f){return a}}for(var g=twve.tcalc.fn_ndx,h=0;h<b.length;){var h=h+twve.text.skipStyleText(b,h),k=twve.tcalc.isFunction(b,
h);if(k)var n=twve.tcalc.parseFunction(k),b=b.substring(0,h)+n+b.substring(h+k.length),h=h+n.length;else h++}twve.tcalc.fn_ndx=g;if(twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing&&(e=twve.tcalc.fn[twve.tcalc.fn_ndx].startParsing(a)))d=e;a=-1<a.indexOf("Math.");b=twve.tcalc.splitArguments(b);k=0;for(n=b.length;k<n;k++){var l=b[k].trim();""==l&&(l=b[k]);var s=twve.text.skipStyleText(l);-1<(h=l.indexOf(":",s))&&twve.tcalc.cellReference(l,h+1)?(d+=twve.tcalc.fn[twve.tcalc.fn_ndx].no_deref?twve.tcalc.parseSingleTerm(l,
k==b.length-1):twve.tcalc.parseConsecutive(l),k<b.length-1&&twve.tcalc.fn[twve.tcalc.fn_ndx].operator&&(d+=twve.tcalc.fn[twve.tcalc.fn_ndx].operator)):(twve.tcalc.pushFunctionCall(g),e=twve.tcalc.evaluate(l,s,!twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval,twve.tcalc.fn[twve.tcalc.fn_ndx].no_deref),twve.tcalc.popFunctionCall(),d+=twve.tcalc.parseSingleTerm((s?l.substring(0,s):"")+e,k==b.length-1,s));a&&(k<n-1&&d)&&(d+=",")}if(twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing){if(e=twve.tcalc.fn[twve.tcalc.fn_ndx].stopParsing())d+=
e;twve.tcalc.fn_ndx=g}return d},wrappedReference:function(a,b,d,e){b=b?b:0;if(0>b||b>=a.length||a.charAt(b)!=d)return"";d=a.indexOf(e,b+1);return 0>d?"":a.substring(b,d+1)},exteranlReference:function(a,b){return twve.tcalc.wrappedReference(a,b,"{","}")},tiddlerReference:function(a,b){return twve.tcalc.wrappedReference(a,b,"[","]")},tableReference:function(a,b){var d=b?b:0;if(0>d||d>=a.length)return"";var e=a.charAt(d);if("t"==e||"T"==e){if(e=d+5,"table"==a.substring(d,e).toLowerCase()){var f=twve.tcalc.partialSearch;
twve.tcalc.partialSearch=!0;var g=twve.tcalc.isNumeric(a,e);twve.tcalc.partialSearch=f;if(""==g)return"";e+=g.length;return"!"==a.charAt(e)?a.substring(d,e+1):""}}else return e=twve.tcalc.wrappedReference(a,d,"'","'")||twve.tcalc.wrappedReference(a,d,'"','"'),""==e?"":"!"==a.charAt(d+e.length)?e+"!":"";return""},cellReference:function(a,b){var d=b?b:0;if(0>d||d>=a.length)return"";var e,f="",g="";if("$"==(e=a.charAt(d++)))f=e,e=a.charAt(d++);/[\a-zA-Z]/.test(e)&&(f+=e,e=a.charAt(d++),/[a-zA-Z]/.test(e)&&
(f+=e,e=a.charAt(d++)));if(f&&("$"==e&&(g=e,e=a.charAt(d++)),twve.tcalc.isDigit(e))){for(g+=e;d<a.length&&twve.tcalc.isDigit(e=a.charAt(d++));)g+=e;return f+g}return""},cellContent:function(a,b,d,e){var f=null;"TR"==a.nodeName?f=twve.table.getRowCell(a,b):a.length&&(f="TR"==a[0].nodeName?twve.table.getRowCell(a[twve.tcalc.referenceToIndexRow(b)],twve.tcalc.referenceToIndexCol(b)):a[b]);return(a=f?twve.tcalc.evaluateCell(f,d):null)?a:e&&(e=e.charAt(e.length-1),"+"==e||"-"==e||"*"==e||"/"==e)?"0":""},
evaluate:function(a,b,d,e,f){for(var g="",h="",k,n=b,l,s=twve.tcalc.cur_twtable,p=s&&s.is("table")?s.dom.querySelectorAll("tr"):null,m="",q={"(":0,"[":0,"{":0},u=/[\d]/,t=/[\d]{3}/,v=0;n<a.length;)if(l=twve.tcalc.tiddlerReference(a,n))g+=l,n+=l.length,l=twve.tcalc.tableReference(a,n),g+=l,n+=l.length,l=twve.tcalc.cellReference(a,n),g+=l,n+=l.length;else if(l=twve.tcalc.tableReference(a,n))if(n+=l.length,h+=l,s&&twve.node.matches(s.direct_wrapper.dom,"div.board"))g+=l,l=twve.tcalc.cellReference(a,
n),g+=l,n+=l.length;else{k=l.charAt(0);if("t"==k||"T"==k){k=1*l.substring(5,l.length-1);var x=null;if(s){var w=s.rIndex;if(k==w)continue;var y=s.dIndex;s.rIndex=k;s.dIndex=-1;x=twve.wrapper.renderedElement(s);s.rIndex=w;s.dIndex=y}else twve.tcalc.cur_twwrap&&(w=twve.tcalc.cur_twwrap.findElements(twve.table.twveSelector(null,"table")))&&(x=twve.table.create(w[k]));if(!x)continue}else if('"'==k||"'"==k){g+=l;l=twve.tcalc.cellReference(a,n);g+=l;n+=l.length;continue}e?g+=l:(l=twve.tcalc.cellReference(a,
n))?(h+=l,twve.tcalc.prev_twtable.push(s),twve.tcalc.cur_twtable=x,g+=twve.tcalc.cellContent(x.dom.querySelectorAll("tr"),l,d,g),twve.tcalc.cur_twtable=twve.tcalc.prev_twtable.pop(),n+=l.length):g+=l}else if(l=twve.tcalc.cellReference(a,n))g=p&&!e?g+twve.tcalc.cellContent(p,l,d,g):g+l,n+=l.length;else if(l=twve.tcalc.isFunction(a,n))g+=twve.tcalc.parseFunction(l),d=!twve.tcalc.fn[twve.tcalc.fn_ndx].no_eval,n+=l.length;else if(k=a.charAt(n++),u.test(k))g+=k,v++;else if("."==k)if(u.test(a.charAt(n)))g+=
k;else{m=a.substring(n-1);break}else if(","==k)if(t.test(a.charAt(n)))g+=k;else{m=a.substring(n-1);break}else if("e"==k||"E"==k)if(g)g+=k;else{m=a.substring(n-1);break}else if(":"==k){if(0<v&&(l=a.charAt(n),u.test(l))){g+=k+l;v++;n++;continue}m=a.substring(n-1);break}else if("t"==k||"T"==k)if(l=a.substring(n-1,n+3),"true"==l.toLowerCase())g+=l,n+=3;else{m=a.substring(n-1);break}else if("f"==k||"F"==k)if(l=a.substring(n-1,n+4),"false"==l.toLowerCase())g+=l,n+=4;else{m=a.substring(n-1);break}else if("+"==
k||"-"==k||"*"==k||"/"==k)g+=k;else if("("==k||"["==k||"{"==k)q[k]++,g+=k;else if(")"==k)if(0<q["("])q["("]--,g+=k;else{m=a.substring(n-1);break}else if("]"==k)if(0<q["["])q["["]--,g+=k;else{m=a.substring(n-1);break}else if("}"==k)if(0<q["{"])q["{"]--,g+=k;else{m=a.substring(n-1);break}else if("<"==k||">"==k||"="==k)g+=k;else{m=a.substring(n-1);break}if(!g)return m;if(d&&"-0"!=g&&!twve.tcalc.vector.is(g))try{var z=0<g.indexOf(":")?twve.tcalc.calculateTime(g).normalize():eval(g);f&&f({position:b,expression:h||
g,result:z+""});return z+m}catch(A){config.options.chktwveTcalcDebugMode&&console.log("eval err: "+A+"\n\nexpression="+g)}f&&f({position:b,expression:h||g,result:g});0<twve.text.skipStyleText(g)&&(g="@@"+g+"@@");return g+m},result_being_calculated:"",calculate:function(a,b,d,e,f){if("number"!=typeof b||0>b)b=0;"="!=a.charAt(b)&&(b=twve.tcalc.indexOfExpression(a,b));for(twve.tcalc.result_being_calculated="";-1<b;){a=a.substring(0,b)+twve.tcalc.evaluate(a,b+1,d,e,function(a){f&&(a.position--,a.expression=
"="+a.expression,f(a))});if(!config.options.chktwveTcalcInTextCalc)break;twve.tcalc.result_being_calculated=a;b=twve.tcalc.indexOfExpression(a,b)}return a},calculateTextNode:function(a){var b=a.nodeValue,d=0;b&&twve.tcalc.calculate(b,0,!0,!1,function(b){var f=a.splitText(b.position-d);a=f.splitText(b.expression.length);d=b.position+b.result.length;var g=document.createElement("span");g.className="tcalc";g.appendChild(document.createTextNode(b.result));g.title=b.expression;a.parentNode.replaceChild(g,
f)})},calculateNode:function(a,b){if(3==a.nodeType)twve.tcalc.calculateTextNode(a);else if(!twve.node.matches(a,b))for(var d=a.firstChild;d;)a=d.nextSibling,twve.node.matches(d,b)||twve.tcalc.calculateNode(d,b),d=a},prePrepareElements:null,cur_twwrap:null,prepareElements:function(a){if(!twve.tcalc.prePrepareElements.apply(this,arguments)||!config.options.chktwveTcalcEnabled||!config.options.chktwveTcalcInTextCalc||!a.tiddler)return null;twve.tcalc.cur_twwrap=a;twve.tcalc.calculateNode(a.dom,twve.table.wrapper.getSelector());
twve.tcalc.cur_twwrap=null;return a},preWikify:null,wikify:function(a,b,d){var e=twve.tiddler.focusElem(),f=e?e.getElement():null;if(f&&twve.node.matches(f,"table"))return twve.tcalc.preWikify.call(this,a,b,d);if(twve.node.matches(b,"div.board")){var g=!0;twve.node.matches(f,"th,td")&&(g=!1,twve.tcalc.cells_being_evaluated.push(f));try{var h=twve.tcalc.cur_twtable;!h&&(e&&e.is("table"))&&(twve.tcalc.cur_twtable=e);a=twve.tcalc.calculate(a,0,!0,g)}catch(k){}finally{h||(twve.tcalc.cur_twtable=null),
g||twve.tcalc.cells_being_evaluated.pop()}return twve.tcalc.preWikify.call(this,a,b,d)}b=twve.tcalc.preWikify.apply(this,arguments);if(!twve.node.matches(b,"th,td"))if(b.length){e=0;for(f=b.length;e<f;e++)twve.tcalc.calculateNode(b[e])}else twve.tcalc.calculateNode(b);return b},cells_being_evaluated:[],cyclicReference:function(a){for(var b=0;b<twve.tcalc.cells_being_evaluated.length;b++)if(twve.tcalc.cells_being_evaluated[b]==a)return"Error: "+(b==twve.tcalc.cells_being_evaluated.length-1)?"Self reference.":
"Cyclic reference."},curCellCol:function(){var a=twve.tcalc.cells_being_evaluated;return a&&0<a.length?twve.tcalc.referenceToIndexCol(a[a.length-1].title):-1},curCellRow:function(){var a=twve.tcalc.cells_being_evaluated;return a&&0<a.length?twve.tcalc.referenceToIndexRow(a[a.length-1].title):-1},sortableGridHtml:function(a){return'<a href="#" class="sortheader" onclick="config.macros.sortableGridPlugin.ts_resortTable(this);return false;">'+a+'<span class="sortarrow"> </span></a>'},
cellTitleExpression:function(a){if(!a.title)return-1;var b=a.title.indexOf("\n");if(0>b)return-1;var d=twve.table.cellRowSpan(a);if(1<d)for(var e=2;e<d;e++)b=a.title.indexOf("\n",b+1);return 0>a.title.indexOf("=",b+1)?-1:b+1},nodeText:function(a,b,d){if(!a)return"";if(void 0===b||null===b){if(3==a.nodeType)return a.nodeValue;if("BR"==a.nodeName)return"\n";b="";var e=a.childNodes;if(d){d=0;for(var f=e.length;d<f;d++)3==e[d].nodeType&&(b+=e[d].nodeValue)}else{d=0;for(f=e.length;d<f;d++)b+=twve.tcalc.nodeText(a.childNodes[d])}if(b&&
(config.tableSorting||config.macros.sortableGridPlugin))d=b.indexOf("\u2191"),-1==d&&(d=b.indexOf("\u2193")),config.macros.sortableGridPlugin&&(d-=2),-1<d&&(b=b.substring(0,d));return b}for(d=-1;0<=(d=b.indexOf("\\",d+1));)b=b.substring(0,d)+b.substring(d+1);0==twve.table.cellRowIndex(a)?config.tableSorting?(e=a.querySelector("span"),a.textContent=b,e&&(b=e.innerHTML,d=b.indexOf("\u2191"),-1==d&&(d=b.indexOf("\u2193")),-1<d&&a.appendChild(e))):config.macros.sortableGridPlugin?(e=a.querySelector("a.sortheader span"))?
(a.innerHTML=twve.tcalc.sortableGridHtml(b),a=a.querySelector("a.sortheader span"),a.innerHTML=e.innerHTML,a.setAttribute("sortdir",e.getAttribute("sortdir"))):a.textContent=b:a.textContent=b:3==a.nodeType?a.nodeValue=b:twve.node.wikify(b,a)},evaluateCell:function(a,b,d,e){if(!d&&(d=twve.tcalc.nodeText(a),!d)){if(a.title){var f=twve.tcalc.cellTitleExpression(a);0<f&&(a.title=a.title.substring(0,f-1))}return""}e||(e=twve.tcalc.indexOfExpression(d));var g;if(-1<e)if(a.title?(f=twve.tcalc.cellTitleExpression(a),
a.title=(0<f?a.title.substring(0,f):a.title+"\n")+d):a.title=d,f=twve.tcalc.cyclicReference(a))twve.tcalc.nodeText(a,d+"<br><br>"+a.title+"<br>"+f);else return twve.tcalc.cells_being_evaluated.push(a),d=twve.tcalc.calculate(d,e,b),(g=twve.tcalc.isNumeric(d,0,-1,!0,"",config.options.chktwveTcalcThousandSeparated))||(g=d),twve.tcalc.nodeText(a,g),twve.tcalc.cells_being_evaluated.pop(),g;else if(1==a.childNodes.length&&(g=twve.tcalc.isNumeric(d,0,-1,!0,".",!1))){if(d.trim()!=g.trim())return config.options.chktwveTcalcThousandSeparated&&
(f=d.indexOf(g),twve.tcalc.nodeText(a,d.substring(0,f)+twve.tcalc.separateNumber(g)+d.substring(f+g.length))),g;if(config.options.chktwveTcalcThousandSeparated)twve.tcalc.nodeText(a,twve.tcalc.separateNumber(g));else return g}return d},addOptionsMenu:function(a){return a.addMenu("''twve.tcalc'' Options",config.macros.twveTcalcOptions.handler)},preGetOptionsMenu:null,getOptionsMenu:function(a){var b=twve.tcalc.preGetOptionsMenu.apply(this,arguments);twve.tcalc.addOptionsMenu(b);return b},timer:null,
findTimer:function(a){if(twve.tcalc.timer)for(var b=twve.tcalc.timer.length-1;0<=b;b--){var d=twve.tcalc.timer[b];if(d.table.wrapper_title==a.wrapper_title&&d.table.dIndex==a.dIndex)return d}return null},removeTimer:function(a){if(a=twve.tcalc.findTimer(a))clearInterval(a.pid),twve.tcalc.timer.splice(twve.tcalc.timer.indexOf(a),1);return a},addTimer:function(a,b){twve.tcalc.timer?twve.tcalc.removeTimer(a):twve.tcalc.timer=[];var d=twve.object.create();d.table=a;d.pid=setInterval(a.calculate,b);d.interval=
b;twve.tcalc.timer.push(d);return d},preGetTableMenu:null,getTableMenu:function(){var a=twve.tcalc.preGetTableMenu.apply(this,arguments);twve.tcalc.addOptionsMenu(a);if(!a.findItem("Recalculate")){var b=a.addItem("Recalculate","Recalculate this table");b.submenu=twve.menu.create(b,!0);b.submenu.addItem("now (and stop the timer)").click=function(){var a=twve.tiddler.focusElem();twve.tcalc.removeTimer(a);a.calculate()};b.submenu.addItem("every 1 sec (start the timer)").click=function(){twve.tcalc.addTimer(twve.tiddler.focusElem(),
1E3)}}return a},preTableCreate:null,tableCreate:function(){var a=twve.tcalc.preTableCreate.apply(this,arguments),b=a.startSorting;a.startSorting=function(){return twve.tcalc.block_update=b.apply(this,arguments)};var d=a.refreshSelf;a.refreshSelf=function(){if(a.curNode)d.apply(this,arguments);else{var b=twve.tcalc.removeTimer(a);d.apply(this,arguments);b&&twve.tcalc.addTimer(a,b.interval)}return a};a.cellReferenceTitle=function(a,b,d){for(var e=twve.table.cellSpans(a),f="",s=0;s<e.row;s++){for(var p=
e.col-1;0<=p;p--)f+=twve.tcalc.indexToReferenceCol(d-p)+(b+s),0<p&&(f+=" | ");s<e.row-1&&(f+="\n")}a.title?a.title!=f&&(b=twve.tcalc.cellTitleExpression(a),a.title=f+(0<b?a.title.substring(b-1):"\n"+a.title)):a.title=f};a.referenceToTest=function(a,b){var d=twve.tcalc.indexToReferenceRow(a),e=twve.tcalc.indexToReferenceCol(b);return{ndx:{row:a,col:b},rel:e+d,abs:e+"$"+d}};a.includeCell=function(b,d,e){b=twve.tcalc.splitArguments(b);for(var f=0;f<b.length;f++){var l=twve.tcalc.allArguments(b[f]);if(l){if(a.includeCell(l))return!0}else if(l=
b[f],l=twve.text.removeStyleText(l).trim(),-1<l.indexOf(":")&&(l=twve.tcalc.consecutiveCells(l),l.start.row<=d&&d<=l.end.row&&l.start.col<=e&&e<=l.end.col))return!0}return!1};a.calculateCell=function(a,b,d){try{twve.tcalc.evaluateCell(a,!0,b,d)}catch(e){console.log(e),config.options.chktwveTcalcDebugMode&&twve.tcalc.nodeText(a,twve.tcalc.nodeText(a)+"<br><br>"+b+"<br>"+e)}};a.calculate=function(b,d){if(!config.options.chktwveTcalcEnabled&&!config.options.chktwveTcalcAllTables&&!a.hasClass("spreadsheet"))return!1;
twve.MathJax&&(twve.MathJax.enabled=!1);b||(b=a.tiddler.text);var e=config.options.chktwveTableIncludeCSS;config.options.chktwveTableIncludeCSS=!1;var f=new Date;twve.tcalc.cur_twtable=a;for(var l=twve.table.allCells(b,a.start,a.end),s=a.dom.querySelectorAll("tr"),p=[],m=0,q=s.length;m<q;m++)p[m]=s[m].querySelectorAll("th,td");if(d){for(var u=new Date,m=0,q=s.length;m<q;m++)for(var t=0,v=p[m].length;t<v;t++){var x=p[m][t],w=twve.table.cellColIndex(x),y=l[m][w]?a.getCellText(l[m][w],b):"";y&&(w=twve.tcalc.indexOfExpression(y),
0<=w&&twve.tcalc.nodeText(x,y))}config.options.chktwveTcalcDebugMode&&displayMessage("clear time: "+(new Date-u)+" ms")}f=new Date-f;u=new Date;y=a.getCaption();w=twve.tcalc.indexOfExpression(y);0<=w&&twve.node.wikify(twve.tcalc.calculate(y,w,!0),a.wrappers.cap);m=0;for(q=s.length;m<q;m++){t=0;for(v=p[m].length;t<v;t++)x=p[m][t],w=twve.table.cellColIndex(x),y=l[m][w]?a.getCellText(l[m][w],b):"",a.cellReferenceTitle(x,m,w),y&&(w=twve.tcalc.indexOfExpression(y),0<=w&&a.calculateCell(x,y,w))}u=new Date-
u;twve.tcalc.cur_twtable=null;config.options.chktwveTcalcDebugMode&&(displayMessage("preparation time: "+f+" ms"),displayMessage("calculation time: "+u+" ms"),displayMessage("total time: "+(u+f)+" ms"));config.options.chktwveTableIncludeCSS=e;twve.MathJax&&(twve.MathJax.enabled=!0);return!0};var e=a.initRows;a.initRows=function(){e.apply(this,arguments);config.options.chktwveTcalcEnabled&&(config.options.chktwveTcalcAllTables||a.hasClass("spreadsheet"))&&a.calculate()};var f=a.changed;a.changed=function(b){switch(b.what){case "CAPTION MODIFIED":case "MODIFIED":a.calculate(b.text,
b);break;case "ROW INSERTED":b.text=twve.tcalc.incCellReference(b.text,a.start,a.end,b.where,1);break;case "ROW DELETED":b.text=twve.tcalc.incCellReference(b.text,a.start,a.end,b.where+1,-1);break;case "ROW PASTED":var d=twve.position.create(a.start),e=twve.position.create(a.end);d.ndx=twve.table.rowStarts(b.text,d,e,b.where);e.ndx=twve.table.rowEnds(b.text,d.ndx);b.text=twve.tcalc.incCellReference(b.text,d,e,0,b.where-b.from);break;case "COL INSERTED":b.text=twve.tcalc.incCellReference(b.text,a.start,
a.end,null,null,0,-1,b.where,1);break;case "COL DELETED":b.text=twve.tcalc.incCellReference(b.text,a.start,a.end,null,null,0,-1,b.where+1,-1);break;case "COL PASTED":b.text=twve.tcalc.incCellReference(b.text,a.start,a.end,null,null,b.where,b.where,0,b.where-b.from);break;case "ROW MOVED":b.text=twve.tcalc.updateCellReference(b.text,a.start,a.end,b.from,null,b.to,null,!0);break;case "ROW EXCHANGED":twve.tcalc.startBlockUpdate();b.text=twve.tcalc.updateCellReference(b.text,a.start,a.end,b.from,null,
b.to,null,!0);b.text=twve.tcalc.updateCellReference(b.text,a.start,a.end,b.to,null,b.from,null,!0);b.text=twve.tcalc.endBlockUpdate(b.text,a.start,a.end);break;case "COL EXCHANGED":twve.tcalc.startBlockUpdate();b.text=twve.tcalc.updateCellReference(b.text,a.start,a.end,null,b.from,null,b.to,!0);b.text=twve.tcalc.updateCellReference(b.text,a.start,a.end,null,b.to,null,b.from,!0);b.text=twve.tcalc.endBlockUpdate(b.text,a.start,a.end);break;case "SORTED":b.text=twve.tcalc.endBlockUpdate(b.text,a.start,
a.end);a.calculate(b.text);break;case "TRANSPOSED":b.text=twve.tcalc.transposed(b.text,a.start,a.end)}return f.call(this,b)};a.copyOrCut=function(b,d){twve.node.matches(a.curNode,"th,td")&&(twve.tcalc.copied_from=a.curNode,twve.tcalc.cut_from=d?a.curNode:null)};a.pasteIn=function(b){var d=twve.tcalc.copied_from;if(d){var e=d.title.split("\n"),d=twve.tcalc.referenceToIndexRow(e[0]),f=twve.tcalc.referenceToIndexCol(e[0]),l=a.curNode.title.split("\n"),s=twve.tcalc.referenceToIndexRow(l[0]),l=twve.tcalc.referenceToIndexCol(l[0]);
if(1<e.length){var e=b.value,p,m,q,u,t,v=twve.tcalc.indexOfExpression(e)+1;if(0<v){for(;v<e.length;){if(p=twve.tcalc.tiddlerReference(e,v))v+=p.length;if(p=twve.tcalc.tableReference(e,v))v+=p.length;(p=twve.tcalc.cellReference(e,v))?(q=twve.tcalc.referenceToIndexRow(p),u=twve.tcalc.absoluteRow(p),m=twve.tcalc.referenceToIndexCol(p),t=twve.tcalc.absoluteCol(p),u||(q=s+(q-d)),t||(m=l+(m-f)),m=0<=m?twve.tcalc.indexToReferenceCol(m,t):"{"+m+"}",m+=0<=q?(u?"$":"")+q:"{"+q+"}",e=e.substring(0,v)+m+e.substring(v+
p.length),v+=m.length):v++}b.value=e}}else twve.tcalc.copied_from=null;twve.tcalc.cut_from&&""==twve.tcalc.nodeText(twve.tcalc.cut_from)&&(e=twve.table.create(a.dom),p=a.tiddler.text,p=twve.tcalc.updateCellReference(p,e.start,e.end,d,f,s,l),e.saveText(p));twve.tiddler.previewEditBox(b)}};return a}};
twve.tcalc.vector={is:function(a){if("string"==typeof a){var b=a.indexOf("(");if(-1==b)return"";var d=a.indexOf(")",b+1);return-1==d?"":0<=a.indexOf(",",b+1)?a.substring(b,d+1):""}return a.dot&&a.cross},create:function(a){var b=[];b.dim=function(){return b.length};b.add=function(a){var d=a.length,e=b.length,f;if(e>=d)for(f=0;f<d;f++)b[f]+=a[f];else{for(f=0;f<e;f++)b[f]+=a[f];for(;f<d;f++)b[f]=a[f]}return b};b.subtract=function(a){var d=a.length,e=b.length,f;if(e>=d)for(f=0;f<d;f++)b[f]-=a[f];else{for(f=
0;f<e;f++)b[f]-=a[f];for(;f<d;f++)b[f]=-a[f]}return b};b.multiply=function(a){for(var d=0,e=b.length;d<e;d++)b[d]*=a;return b};b.divideBy=function(a){for(var d=0,e=b.length;d<e;d++)b[d]/=a;return b};b.dot=function(a){for(var d=Math.min(b.length,a.length),e=0,f=0;f<d;f++)e+=b[f]*a[f];return e};b.cross=function(a){var d=b.length;if(d==a.length)switch(d){case 3:return d=twve.tcalc.vector.create(),d[0]=b[1]*a[2]-b[2]*a[1],d[1]=b[2]*a[0]-b[0]*a[2],d[2]=b[0]*a[1]-b[1]*a[0],d}return null};var d=twve.tcalc.vector.is(a);
if("string"==typeof d){a=1;for(var e=0;;){var f=d.indexOf(",",a);if(-1<f)b[e++]=1*d.substring(a,f),a=f+1;else{b[e]=1*d.substring(a,d.length-1);break}}}else if(d){d=0;for(e=a.length;d<e;d++)b[d]=a[d]}return b}};twve.tcalc.parseVector=function(a,b){var d=twve.tcalc.vector.create(b);d&&(null===a.v1?a.v1=d:null===a.v2&&(a.v2=d))};twve.tcalc.addNumeral=function(a,b){if(a){var d=b.charAt(0);a+="+"==d||"-"==d?b:"+"+b}else a=b;return a};
twve.tcalc.getSourceText=function(a){if("string"!=typeof a)return a;switch(a.toLowerCase()){case "this":return a=twve.tcalc.cells_being_evaluated,twve.tcalc.result_being_calculated||twve.tcalc.nodeText(a[a.length-1]);case "caption":case "title":if(twve.tcalc.cur_twtable)return twve.tcalc.cur_twtable.getCaption();default:var b=twve.url.create(a);return b&&b.getTarget()?b.getContent():a}};
twve.tcalc.valueIn=function(a){var b=0;void 0===a.curp?a.curp=0:b="number"==typeof a.curp?a.curp:a.curp.ndx?a.curp.ndx:0;0>b&&(b=0);if("number"!=typeof a.curn||-1>a.curn)a.curn=-1;for(var d=a.txt.length,e="";;){var f=twve.tcalc.partialSearch;for(twve.tcalc.partialSearch=!0;b<d&&!e;)e=twve.tcalc.isNumeric(a.txt,b++);twve.tcalc.partialSearch=f;if(e){b+=e.length-1;if(++a.curn==a.ndx)return void 0!==a.curp.ndx?a.curp.ndx=b:a.curp=b,b=e.length,twve.tcalc.isPartsPerNotation(e,0,b,b-1)||e;e=""}else return""}};
twve.tcalc.matchPreceding=function(a){a.curp=twve.text.indexOf(a.txt,a.preceding,a.curp);if(0>a.curp.ndx)return"";a.curp.ndx+=a.curp.matched.length;var b="";if(a.everything)b=a.txt.substring(a.curp.ndx);else{var d=twve.tcalc.partialSearch;twve.tcalc.partialSearch=!0;b=twve.tcalc.isNumeric(a.txt,a.curp.ndx);twve.tcalc.partialSearch=d;b&&0>b.indexOf("\n")&&(a.curp.ndx+=b.length)}return b};
twve.tcalc.matchFollowing=function(a,b){if(!a.following)return"";for(var d=/\s/,e;d.test(e=a.txt.charAt(a.curp.ndx));)if(a.curp.ndx++,"\n"==e)return null;a.post?a.post.ndx=a.curp.ndx:a.post=twve.position.create(a.curp);a.post=twve.text.indexOf(a.txt,a.following,a.post);if(0>a.post.ndx||!b&&a.post.ndx!=(a.curp.ndx||a.curp))return null;a.curp.ndx=a.post.ndx+a.post.matched.length;return a.post.matched};
twve.tcalc.valueWithText=function(a){var b="";if(a.preceding&&"null"!=a.preceding)for(;;){var d=twve.tcalc.matchPreceding(a);if(0>a.curp.ndx)break;if(d&&"string"==typeof twve.tcalc.matchFollowing(a)){b=d;break}}else{a.ndx=0;for(a.curn=-1;;){d=twve.tcalc.valueIn(a);if(!d)break;if("string"==typeof twve.tcalc.matchFollowing(a)){b=d;break}a.ndx++}}return b};
twve.tcalc.sumWithText=function(a){var b="";if(a.preceding&&"null"!=a.preceding)for(;;){var d=twve.tcalc.matchPreceding(a);if(0>a.curp.ndx)break;d&&"string"==typeof twve.tcalc.matchFollowing(a)&&(b=twve.tcalc.addNumeral(b,d))}else{a.ndx=a.curp.ndx=0;for(a.curn=-1;;){d=twve.tcalc.valueIn(a);if(!d)break;"string"==typeof twve.tcalc.matchFollowing(a)&&(b=twve.tcalc.addNumeral(b,d));a.ndx++}}return b};
twve.tcalc.arrayOrText=function(a){var b=a.length-1;"["==a.charAt(0)&&("]"==a.charAt(b)&&0<=a.indexOf(","))&&(a=a.substring(1,b).split(","));return a};twve.tcalc.parseTrueFalse=function(a){if("false"!=a){if("true"==a)return!0;try{if(a)return"string"==typeof a&&(a=eval(a)),a?!0:!1}catch(b){}}return!1};twve.tcalc.round=function(a,b){if(null===b||0===b)return Math.round(a);var d=Math.pow(10,b);return Math.round(d*a)/d};twve.tcalc.factorial=function(a){var b=1;for(a=Math.round(a);1<a;a--)b*=a;return b};
twve.tcalc.days_in_month=function(a,b){return(new Date(a,b+1,0)).getDate()};twve.tcalc.same_year_month=function(a,b){return a.getFullYear()==b.getFullYear()&&a.getMonth()==b.getMonth()};twve.tcalc.separator_positions=function(a,b,d){for(var e=[],f=twve.position.create(0);;){f=twve.text.indexOf(a,b,f);if(0>f.ndx)break;if(!d||d.call(this,f))e[e.length]=twve.position.create(f);f.ndx++}return e};
twve.tcalc.timestr_after_date=function(a,b){var d=a.indexOf(" ",b);0>d&&(d=a.indexOf("T",b));return d>b?d:-1};twve.tcalc.time_separator=":";
twve.tcalc.parse_time=function(a){var b=twve.tcalc.separator_positions(a,twve.tcalc.date_separator);if(0<b.length&&(b=twve.tcalc.timestr_after_date(a,b[0].ndx),-1<b&&(a=a.substring(b),!a)))return null;var b=twve.tcalc.separator_positions(a,twve.tcalc.time_separator),d=[],e="";switch(b.length){case 2:(e=twve.tcalc.isNumeric(a.substring(0,b[0].ndx)))&&(d[0]=parseFloat(e));(e=twve.tcalc.isNumeric(a.substring(b[0].ndx+1)))&&(d[1]=parseFloat(e));(e=twve.tcalc.isNumeric(a.substring(b[1].ndx+1)))&&(d[2]=
parseFloat(e));break;case 1:(e=twve.tcalc.isNumeric(a.substring(0,b[0].ndx)))&&(d[0]=parseFloat(e));(e=twve.tcalc.isNumeric(a.substring(b[0].ndx+1)))&&(d[1]=parseFloat(e));break;case 0:if(e=twve.tcalc.isNumeric(a))d[0]=parseFloat(e)}return d};twve.tcalc.parse_time_arg=function(a,b){if(!b)return"";var d=b.indexOf("~");0>d?a.time1=twve.tcalc.parse_time(b):(a.time1=twve.tcalc.parse_time(b.substring(0,d)),a.time2=twve.tcalc.parse_time(b.substring(d+1)));return""};
twve.tcalc.calculateTime=function(a){a=twve.tcalc.allArguments(a)||a;var b=twve.tcalc.separator_positions(a,["+","-"],function(b){return":"!=a.charAt(b.ndx-1)}),d=0,e,f=b.length,g=[];for(e=0;e<f;e++)g[e]=twve.tcalc.parse_time(a.substring(d,b[e].ndx)),0<e&&(g[e].operator=b[e-1].matched),d=b[e].ndx+1;g[e]=twve.tcalc.parse_time(a.substring(d));g[e].operator=b[e-1].matched;var h=g[0].slice(),b=h.length;for(e=1;e<=f;e++){d=g[e].length;if(b<d){for(var k=0;k<d-b;k++)h.unshift(0);b=d}for(var n=b-d,k=0;k<
d;k++){var l=g[e].operator?g[e].operator:"+";0>g[e][k]&&("+"==l?l="":(g[e][k]=-g[e][k],l="+"));h[n+k]+=l+g[e][k]}}for(e=0;e<b;e++)try{h[e]=eval(h[e])}catch(s){}h.isPositive=function(){for(var a=0,b=h.length;a<b;a++)if(h[a])return 0<h[a];return!1};h.normalize=function(a,b){var d=h.length;if("number"==typeof a){switch(a){case 1:case 2:b?0>b&&(b=-b):b=60;0<h[a]?h[a]>=b&&(d=Math.round(h[a]/b),h[a]-=d*b,h[a-1]+=d):0>h[a]&&(h[a]<-b&&(d=Math.round(h[a]/b),h[a]-=d*b,h[a-1]+=d),0<h[a-1]&&(h[a-1]--,h[a]+=b));
break;case -1:switch(b.length){case 3:switch(d){case 3:h.normalize(2,b[2]).normalize(1,b[1]).normalize(0,b[0]);break;case 2:h.normalize(1,b[2]).normalize(0,b[1])}break;case 2:switch(d){case 3:if(60==b[0]){h.normalize(2,b[1]).normalize(1,b[0]);break}case 2:h.normalize(1,b[1]).normalize(0,b[0])}}}return h}switch(d){case 2:return h.normalize(1,60);case 3:return h.normalize(2,60).normalize(1,60)}return h};h.format=function(a){return 0<=a?(10<=a?"":"0")+a:-10>=a?""+a:"-0"+-a};h.toString=function(){for(var a=
h.format(h[0]),b=1,d=h.length;b<d;b++)a+=":"+h.format(h[b]);return a};h.getSeconds=function(){var a=h.length,b=h[a-1];switch(a){case 3:b+=3600*h[a-3];case 2:b+=60*h[a-2]}return b};h.getMinutes=function(){var a=h.length,b=0;switch(a){case 3:b+=60*h[a-3];case 2:b+=h[a-2];case 1:b+=h[a-1]/60}return b};h.getHours=function(){var a=h.length,b=0;switch(a){case 3:b+=h[a-3];case 2:b+=h[a-2]/60;case 1:b+=h[a-1]/3600}return b};return h};twve.tcalc.date_separator=["/",".","-"," "];
twve.tcalc.parse_date_format=function(a){if(!a)return null;var b=twve.tcalc.separator_positions(a,twve.tcalc.date_separator),d="",e="",f="",g="";0==b.length?(a={txt:a,following:"\u5e74"},d=twve.tcalc.valueWithText(a),a.following="\u6708",e=twve.tcalc.valueWithText(a),a.following="\u65e5",f=twve.tcalc.valueWithText(a)):(separator=b[0].matched,e=twve.tcalc.timestr_after_date(a,b[0].ndx),-1<e&&(g=a.substring(e),a=a.substring(0,e)),1==b.length?3<b[0].ndx?(d=a.substring(0,b[0].ndx),e=a.substring(b[0].ndx+
1)):3<a.length-b[0].ndx?(e=a.substring(0,b[0].ndx),d=a.substring(b[0].ndx+1)):(e=a.substring(0,b[0].ndx),f=a.substring(b[0].ndx+1)):3<a.length-b[1].ndx?(f=a.substring(0,b[0].ndx),e=a.substring(b[0].ndx+1,b[1].ndx),d=a.substring(b[1].ndx+1)):(d=a.substring(0,b[0].ndx),e=a.substring(b[0].ndx+1,b[1].ndx),f=a.substring(b[1].ndx+1)));return{year:d,month:e,date:f,separator:separator,time:g}};
twve.tcalc.parse_date=function(a){var b=new Date;a=twve.tcalc.parse_date_format(a);if(!a)return b;a.year||(a.year=b.getFullYear());a.month||(a.month=b.getMonth());a.date||(a.date=b.getDate());a.time||(a.time=b.toTimeString());return new Date(a.year+"/"+a.month+"/"+a.date+" "+a.time)};twve.tcalc.parse_date_arg=function(a,b){if(!b)return"";var d=b.indexOf("~");0>d?a.date1=twve.tcalc.parse_date(b):(a.date1=twve.tcalc.parse_date(b.substring(0,d)),a.date2=twve.tcalc.parse_date(b.substring(d+1)));return""};
twve.tcalc.format_date_string=function(a,b){if(!b)return a.toLocaleDateString();var d="",e;b.year&&(e=a.getFullYear(),d+=2==b.year.length?e%100:e);b.month&&(e=a.getMonth()+1,d+=(d?b.separator:"")+(1==b.month.length?e:(10>e?"0":"")+e));b.date&&(e=a.getDate(),d+=(d?b.separator:"")+(1==b.date.length?e:(10>e?"0":"")+e));return d};twve.tcalc.months=function(a,b,d,e,f,g){f||(f=(new Date).getFullYear());a+=b/twve.tcalc.days_in_month(f,a);d+=e/twve.tcalc.days_in_month(f,d);return Math.round(d-a,g)};
twve.tcalc.days=function(a,b,d){function e(a){return 0==a?"00":0<a?10>a?"0"+a:a:-10<a?"-0"+-a:a}var f=(b-a)/1E3,g=f/86400;if("number"==typeof d)return twve.tcalc.round(g,d);g=Math.round(g);1<=Math.abs(g)&&(f-=86400*g);if(0==f)return g+"D";if(60>Math.abs(f))return g+"D 00:00:"+e(f);d=Math.round(f/60);var f=f-60*d,h=Math.round(d/60);d-=60*h;b>a?(0>f&&(f+=60,d-=1),0>d&&(d+=60,h-=1),0>h&&(h+=24,g-=1)):(0<f&&(f-=60,d+=1),0<d&&(d-=60,h+=1),0<h&&(h-=24,g+=1));return g+"D "+e(h)+":"+e(d)+":"+e(f)};
twve.tcalc.functionData={create:function(){var a=twve.object.create();a.clear=function(){a.argCount=0;a.how=null;return a};return a.clear()}};twve.tcalc.arrayData={create:function(){var a=twve.tcalc.functionData.create(),b=a.clear;a.clear=function(){b();a.val=[];return a};a.parseArgument=function(b){var e=b;if(b=twve.tcalc.isNumeric(b))e=1*b;a.val[a.argCount++]=e};return a.clear()}};
twve.tcalc.rowColData={getRange:function(a,b,d){switch(a.how){case "BEFORE":a.first=0;a.last=b;break;case "AFTER":a.first=b+1;a.last=d;break;default:a.first=0,a.last=d}},create:function(){var a=twve.tcalc.functionData.create(),b=a.clear;a.clear=function(){b();a.ndx=null;return a};a.parseArgument=function(b){switch(a.argCount){case 0:if(b&&(a.ndx=b,b=twve.tcalc.isNumeric(b)))a.ndx=1*b;break;case 1:a.how=b.toUpperCase()}a.argCount++};a.getRange=function(b,e){return twve.tcalc.rowColData.getRange(a,
b,e)};return a.clear()}};
twve.tcalc.columnData={columnNumber:function(a){return null===a||"this"==a?twve.tcalc.curCellCol():"number"==typeof a?a:twve.tcalc.referenceToIndexCol(a)},getRange:function(a,b,d){switch(a.how){case "ABOVE":a.first=0;a.last=b;break;case "BELOW":a.first=b+1;a.last=d;break;default:twve.tcalc.rowColData.getRange(a,b,d)}},create:function(){var a=twve.tcalc.rowColData.create(),b=a.parseArgument;a.parseArgument=function(d){b.apply(this,arguments);1==a.argCount&&(a.ndx=twve.tcalc.columnData.columnNumber(a.ndx))};a.getRange=
function(b,e){twve.tcalc.columnData.getRange(a,b,e)};return a}};
twve.tcalc.rowData={rowNumber:function(a){return null===a||"this"==a?twve.tcalc.curCellRow():a},getRange:function(a,b,d){switch(a.how){case "LEFT":a.first=0;a.last=b;break;case "RIGHT":a.first=b+1;a.last=d;break;default:twve.tcalc.rowColData.getRange(a,b,d)}},create:function(){var a=twve.tcalc.rowColData.create(),b=a.parseArgument;a.parseArgument=function(d){b.apply(this,arguments);1==a.argCount&&(a.ndx=twve.tcalc.rowData.rowNumber(a.ndx))};a.getRange=function(b,e){twve.tcalc.rowData.getRange(a,b,
e)};return a}};twve.tcalc.signatureData={create:function(){var a=twve.tcalc.functionData.create(),b=a.clear;a.clear=function(){b();a.txt="";a.preceding="";a.following="";return a};a.parseArgument=function(b){switch(a.argCount){case 0:a.txt=twve.tcalc.getSourceText(b);break;case 1:a.preceding=twve.tcalc.arrayOrText(b);break;case 2:a.following=twve.tcalc.arrayOrText(b);break;case 3:a.how=b.toUpperCase()}a.argCount++};return a.clear()}};
twve.tcalc.fn=[{description:"Get the content of a table cell specified by its row and column indexes.",usage:"=CELL(row,col). For example, =CELL(3,2) or =CELL(3,B) will get the content of the table cell in row 3 (4th row), column 2 (3rd column).",name:"CELL",data:twve.tcalc.arrayData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=twve.tcalc.cur_twtable||twve.tiddler.cur_focus;if(!a||2>this.data.argCount)return null;var b=this.data.val[0];if(0>b)return null;var d=
twve.tcalc.columnData.columnNumber(this.data.val[1]);if(0>d)return null;var e=a.dom.querySelectorAll("tr");return b>=e.length||d>a.maxColumns(e)?null:twve.tcalc.nodeText(twve.table.getRowCell(e[b],d))},parseSingleTerm:function(a){this.data.parseArgument(a)}},{name:"MATH",startParsing:function(a){return a.substring(0,a.indexOf("(")+1)},stopParsing:function(){return")"}},{name:["LENGTH","LEN"],parseSingleTerm:function(a){return a?a.length+"":"0"}},{description:"Extract the left part of a string.",usage:"=LEFT(B3,4) will extract the left 4 characters out of the content in table cell B3.",
name:"LEFT",data:{txt:"",len:0},startParsing:function(){this.data.txt="";this.data.len=0},stopParsing:function(){return this.data.txt.substring(0,this.data.len)},parseSingleTerm:function(a){if(this.data.txt){if(a&&(a=twve.tcalc.isNumeric(a)))this.data.len=1*a}else this.data.txt=twve.tcalc.getSourceText(a)}},{description:"Extract the middle part of a string.",usage:"=MID(B3,start_pos,number_of_char) will extract number_of_char characters, starting from index start_pos, out of the content in table cell B3.",
name:"MID",data:{txt:"",start:-1,len:-1},startParsing:function(){this.data.txt="";this.data.start=this.data.len=-1},stopParsing:function(){return this.data.txt.substring(this.data.start,this.data.start+this.data.len)},parseSingleTerm:function(a){if(this.data.txt){if(a&&(a=twve.tcalc.isNumeric(a)))0>this.data.start?this.data.start=1*a:0>this.data.len&&(this.data.len=1*a)}else this.data.txt=twve.tcalc.getSourceText(a)}},{description:"Extract the right part of a string.",usage:"=RIGHT(B3,4) will extract the right 4 characters out of the content in table cell B3.",
name:"RIGHT",data:{txt:"",len:0},startParsing:function(){this.data.txt="";this.data.len=0},stopParsing:function(){return this.data.txt.substring(this.data.txt.length-this.data.len)},parseSingleTerm:function(a){if(this.data.txt){if(a&&(a=twve.tcalc.isNumeric(a)))this.data.len=1*a}else this.data.txt=twve.tcalc.getSourceText(a)}},{name:"PRODUCT",operator:"*",parseSingleTerm:function(a){return twve.tcalc.isNumeric(a)?a:"0"}},{name:"DOT",data:{v1:null,v2:null},startParsing:function(){this.data.v1=this.data.v2=
null},stopParsing:function(){if(this.data.v1&&this.data.v2)return this.data.v1.dot(this.data.v2)+""},parseSingleTerm:function(a){twve.tcalc.parseVector(this.data,a)}},{name:"CROSS",data:{v1:null,v2:null},startParsing:function(){this.data.v1=this.data.v2=null},stopParsing:function(){if(this.data.v1&&this.data.v2)return this.data.v1.cross(this.data.v2)},parseSingleTerm:function(a){twve.tcalc.parseVector(this.data,a)}},{no_eval:!0,name:"TEXTWITH",data:twve.tcalc.signatureData.create(),textWithText:function(a){var b=
"";a.everything=!0;if(a.preceding&&"null"!=a.preceding){if(b=twve.tcalc.matchPreceding(a))b=a.curp.ndx,a.following||(a.following=["\n"," ",".",","]),twve.tcalc.matchFollowing(a,!0)||(a.post.ndx=a.txt.length),b=a.txt.substring(b,a.post.ndx)}else twve.tcalc.matchFollowing(a,!0)&&(b=a.txt.substring(0,a.post.ndx));return b},startParsing:function(){this.data.clear()},stopParsing:function(){var a="",b={preceding:this.data.preceding,following:this.data.following,curp:twve.position.create(0),curn:-1};if("string"==
typeof this.data.txt)b.txt=this.data.txt,a=this.textWithText(b);else for(var a=[],d=0,e=this.data.txt.length;d<e;d++)b.txt=this.data.txt[d],b.curp.ndx=0,b.curn=-1,a[a.length]=this.textWithText(b)||"0";return a},parseSingleTerm:function(a){this.data.parseArgument(a)}},{name:["VALUEWITH","VALWITH"],partialSearch:!0,data:twve.tcalc.signatureData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a="",b={preceding:this.data.preceding,following:this.data.following,curp:twve.position.create(0),
curn:-1};if("string"==typeof this.data.txt)b.txt=this.data.txt,a=twve.tcalc.valueWithText(b);else for(var a=[],d=0,e=this.data.txt.length;d<e;d++)b.txt=this.data.txt[d],b.curp.ndx=0,b.curn=-1,a[a.length]=twve.tcalc.valueWithText(b)||"0";return a},parseSingleTerm:function(a){this.data.parseArgument(a)}},{name:["VALUE","VAL"],partialSearch:!0,data:twve.tcalc.functionData.create(),startParsing:function(){this.data.clear();this.data.txt="";this.data.ndx=0},stopParsing:function(){if(!this.data.txt)return"0";
if("string"==typeof this.data.txt)return twve.tcalc.valueIn(twve.tcalc.duplicateFunctionData(this));for(var a=[],b={ndx:this.data.ndx},d=0,e=this.data.txt.length;d<e;d++)b.txt=this.data.txt[d],b.curp=0,b.curn=-1,a[a.length]=twve.tcalc.valueIn(b);return a},parseSingleTerm:function(a){switch(this.data.argCount){case 0:this.data.txt=twve.tcalc.getSourceText(a);break;case 1:if(a&&(a=twve.tcalc.isNumeric(a)))this.data.ndx=1*a}this.data.argCount++}},{name:"SUMIN",partialSearch:!0,data:twve.tcalc.functionData.create(),
startParsing:function(){this.data.txt="";this.data.include=[];this.data.exclude=[];return"("},sumInText:function(a){var b="",d=a.include.length;if(d){a.include.sorted||(a.include.sort(function(a,b){return a-b}),a.include.sorted=!0);for(var e=0;e<d;e++){a.ndx=a.include[e];var f=twve.tcalc.valueIn(a);f&&(b=twve.tcalc.addNumeral(b,f))}}else{if((d=a.exclude.length)&&!a.exclude.sorted)a.exclude.sort(function(a,b){return a-b}),a.exclude.sorted=!0;e=0;for(a.ndx=0;;){f=twve.tcalc.valueIn(a);if(!f)break;d&&
a.ndx==a.exclude[e]?e++:b=twve.tcalc.addNumeral(b,f);a.ndx++}}return b},stopParsing:function(){var a="";if("string"==typeof this.data.txt)a=this.sumInText(twve.tcalc.duplicateFunctionData(this));else for(var b={include:this.data.include,exclude:this.data.exclude},d=0,e=this.data.txt.length;d<e;d++){b.txt=this.data.txt[d];b.curp=0;b.curn=-1;var f=this.sumInText(b);f&&(a+="+"+f)}return(a?a:"0")+")"},parseSingleTerm:function(a){if(this.data.txt){var b=a.indexOf("-");-1<b?this.data.exclude[this.data.exclude.length]=
1*a.substring(b+1):this.data.include[this.data.include.length]=1*a}else this.data.txt=twve.tcalc.getSourceText(a)}},{description:"Sum over numerical values in specified cells, with optional signature text (immediately preceding and/or following).",usage:'=SUMWITH(A1[,pre[,post]]) will sum up all the numerical values with an immediately preceding signature "pre" in cell A1.',name:"SUMWITH",partialSearch:!0,data:twve.tcalc.signatureData.create(),startParsing:function(){this.data.clear();return"("},
stopParsing:function(){var a="",b={preceding:this.data.preceding,following:this.data.following,curp:twve.position.create(0),curn:-1};if("string"==typeof this.data.txt)b.txt=this.data.txt,a=twve.tcalc.sumWithText(b);else for(var d=0,e=this.data.txt.length;d<e;d++){b.txt=this.data.txt[d];var f=twve.tcalc.sumWithText(b);f&&(a+=(0<d?"+":"")+f)}return(a?a:"0")+")"},parseSingleTerm:function(a){this.data.parseArgument(a)}},{description:"Sum over numerical values in one column, excluding this cell, with optional signature text (immediately preceding and/or following) and/or range of summation.",
usage:'=SUMCOLWITH([1,[pre[,post[,how]]]]) will sum up all numerical vlaues with an immediately preceding signature "pre" in column 1 (the 2nd column). Current column is assumed if the 1st argument is missing. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.',name:"SUMCOLWITH",data:twve.tcalc.signatureData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.txt;if(0>a)return"0";var b=twve.tcalc.cur_twtable;
if(!b)return"0";var d=b.dom.querySelectorAll("tr");if(a>b.maxColumns(d))return"0";b=twve.tcalc.curCellRow();twve.tcalc.columnData.getRange(this.data,b,d.length);for(var e=0,f={preceding:this.data.preceding,following:this.data.following,curp:twve.position.create(0),curn:-1},g=this.data.first;g<this.data.last;g++)if(g!=b){f.txt=twve.tcalc.cellContent(d[g],a,!0);var h=twve.tcalc.sumWithText(f);h&&(e=g==this.data.first?h:e+("+"+h))}return e+""},parseSingleTerm:function(a){this.data.parseArgument(a);1==
this.data.argCount&&(this.data.txt=twve.tcalc.columnData.columnNumber(this.data.txt||null))}},{description:"Sum over numerical values in one column, excluding this cell, with optional argument spacifying the range of summation.",usage:"=SUMCOL([1[,how]]) or =SUMCOL([B[,how]]) will sum up column 1 or B (the 2nd column). Current column is assumed if the first argument is missing. The 2nd argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.",name:"SUMCOL",data:twve.tcalc.columnData.create(),
startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;null===a&&(this.data.ndx=a=twve.tcalc.columnData.columnNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";var d=b.dom.querySelectorAll("tr");if(a>b.maxColumns(d))return"0";b=twve.tcalc.curCellRow();this.data.getRange(b,d.length);for(var e=0,f=this.data.first;f<this.data.last;f++)if(f!=b){var g=twve.tcalc.cellContent(d[f],a,!0);g&&(g*=1,isNaN(g)||(e+=g))}return e+""},parseSingleTerm:function(a){this.data.parseArgument(a)}},
{description:"Sum over numerical values in one row, excluding this cell, with optional signature text (immediately preceding and/or following) and/or the range of summation.",usage:'=SUMROW([1,[pre[,post[,how]]]]) will sum up all numerical vlaues with an immediately preceding signature "pre" in row 1 (the 2nd row). Current row is assumed if the first argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.',name:"SUMROWWITH",data:twve.tcalc.signatureData.create(),
startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.txt;if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";b=b.dom.querySelectorAll("tr");if(a>=b.length)return"0";a=b[a].querySelectorAll("th,td");b=twve.tcalc.curCellCol();twve.tcalc.rowData.getRange(this.data,b,a.length);for(var d=0,e={preceding:this.data.preceding,following:this.data.following,curp:twve.position.create(0),curn:-1},f=this.data.first;f<this.data.last;f++)if(f!=b){e.txt=twve.tcalc.cellContent(a,
f,!0);var g=twve.tcalc.sumWithText(e);g&&(d=f==this.data.first?g:d+("+"+g))}return d+""},parseSingleTerm:function(a){this.data.parseArgument(a);1==this.data.argCount&&(this.data.txt=twve.tcalc.rowData.rowNumber(this.data.txt||null))}},{description:"Sum over numerical values in one row, excluding this cell, with optional range of summation.",usage:"=SUMROW(1[,how]) will sum up row 1 (the 2nd row). Current row is assumed if the argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.",
name:"SUMROW",data:twve.tcalc.rowData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;null===a&&(this.data.ndx=a=twve.tcalc.rowData.rowNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";b=b.dom.querySelectorAll("tr");if(a>=b.length)return"0";a=b[a].querySelectorAll("th,td");b=twve.tcalc.curCellCol();this.data.getRange(b,a.length);for(var d=0,e=this.data.first;e<this.data.last;e++)if(e!=b){var f=twve.tcalc.cellContent(a,e,!0);f&&
(f*=1,isNaN(f)||(d+=f))}return d+""},parseSingleTerm:function(a){this.data.parseArgument(a)}},{description:"Sum over numerical values in one or more cells.",usage:"=SUM(A1:C3) will sum up cells A1, A2, A3, B1, B2, B3, and C1, C2, C3.",name:"SUM",operator:"+",startParsing:function(){return"("},stopParsing:function(){return")"},parseSingleTerm:function(a){return twve.tcalc.isNumeric(a)?a:"0"}},{name:"COUNTA",description:"Count the number of non-empty values in the specified cells.",usage:"=COUNTA(A3:B6) will count the number of non-empty values in the cells from A3 to B6.",
data:{count:0},startParsing:function(){this.data.count=0},stopParsing:function(){return""+this.data.count},parseSingleTerm:function(a){/\S/.test(a)&&this.data.count++}},{description:"Count the number of non-empty cells in one column, excluding this cell, with optional range of counting.",usage:"=COUNTCOLA([1[,how]]) or =COUNTCOLA([B[,how]]) will count the number of non-empty values in column 1 or B (the 2nd column). Current column is assumed if the 1st argument is omitted. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.",
name:"COUNTCOLA",data:twve.tcalc.columnData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;null===a&&(this.data.ndx=a=twve.tcalc.columnData.columnNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";var d=b.dom.querySelectorAll("tr");if(a>b.maxColumns(d))return"0";b=twve.tcalc.curCellRow();this.data.getRange(b,d.length);for(var e=0,f=this.data.first;f<this.data.last;f++)f!=b&&/\S/.test(twve.tcalc.cellContent(d[f],a,!0))&&e++;return e+
""},parseSingleTerm:function(a){this.data.parseArgument(a)}},{description:"Count the number of numerical values in one column, excluding this cell, with optioanl range of counting.",usage:"=COUNTCOL([1[,how]]) or =COUNTCOL([B[,how]]) will count the number of numerical values in column 1 or B (the 2nd column). Current column is assumed if the 1st argument is missing. The last argument - how - can be ABOVE/BEFORE, BELOW/AFTER, or ALL. If not specified, ALL is assumed.",name:"COUNTCOL",data:twve.tcalc.columnData.create(),
startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;null===a&&(this.data.ndx=a=twve.tcalc.columnData.columnNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";var d=b.dom.querySelectorAll("tr");if(a>b.maxColumns(d))return"0";b=twve.tcalc.curCellRow();this.data.getRange(b,d.length);for(var e=0,f=this.data.first;f<this.data.last;f++)if(f!=b){var g=twve.tcalc.cellContent(d[f],a,!0);g&&(g*=1,isNaN(g)||e++)}return e+""},parseSingleTerm:function(a){this.data.parseArgument(a)}},
{description:"Count the number of non-empty values in one row, excluding this cell, with optional range of counting.",usage:"=COUNTROWA([1[,how]]) will count the number of non-empty values in row 1 (the 2nd row). Current row is assumed if the 1st argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.",name:"COUNTROWA",data:twve.tcalc.rowData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;
null===a&&(this.data.ndx=a=twve.tcalc.rowData.rowNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";b=b.dom.querySelectorAll("tr");if(a>=b.length)return"0";a=b[a].querySelectorAll("th,td");b=twve.tcalc.curCellCol();this.data.getRange(b,a.length);for(var d=0,e=this.data.first;e<this.data.last;e++)e!=b&&/\S/.test(twve.tcalc.cellContent(a,e,!0))&&d++;return d+""},parseSingleTerm:function(a){this.data.parseArgument(a)}},{description:"Count the number of numerical values in one row, excluding this cell, with optional range of counting.",
usage:"=COUNTROW([1[,how]]) will count the number of numerical values in row 1 (the 2nd row). Current row is assumed if the 1st argument is missing. The last argument - how - can be LEFT/BEFORE, RIGHT/AFTER, or ALL. If not specified, ALL is assumed.",name:"COUNTROW",data:twve.tcalc.rowData.create(),startParsing:function(){this.data.clear()},stopParsing:function(){var a=this.data.ndx;null===a&&(this.data.ndx=a=twve.tcalc.rowData.rowNumber(a));if(0>a)return"0";var b=twve.tcalc.cur_twtable;if(!b)return"0";
b=b.dom.querySelectorAll("tr");if(a>=b.length)return"0";a=b[a].querySelectorAll("th,td");b=twve.tcalc.curCellCol();this.data.getRange(b,a.length);for(var d=0,e=this.data.first;e<this.data.last;e++)if(e!=b){var f=twve.tcalc.cellContent(a,e,!0);f&&(f*=1,isNaN(f)||d++)}return d+""},parseSingleTerm:function(a){this.data.parseArgument(a)}},{name:"COUNT",data:{count:0,condition:null},startParsing:function(){this.data.condition=null;this.data.count=0},stopParsing:function(){return""+this.data.count},parseSingleTerm:function(a){if(twve.tcalc.isNumeric(a))this.data.condition?
eval('"'+this.data.condition.replace(/\%/g,a)+'"')&&this.data.count++:this.data.count++;else if(!this.data.condition&&(a=a.trim(),/%\s*[><=!]/.test(a))){var b=a.charAt(0);this.data.condition="'"==b||'"'==b?a.substring(1,a.length-1):a}}},{name:"MAX",data:{max:null},startParsing:function(){this.data.max=null},stopParsing:function(){return this.data.max},parseSingleTerm:function(a){if(a=twve.tcalc.isNumeric(a))null===this.data.max?this.data.max=a:this.data.max<a&&(this.data.max=a)}},{name:"MIN",data:{min:null},
startParsing:function(){this.data.min=null},stopParsing:function(){return this.data.min},parseSingleTerm:function(a){if(a=twve.tcalc.isNumeric(a))null===this.data.min?this.data.min=a:this.data.min>a&&(this.data.min=a)}},{name:["AVERAGE","AVG","MEAN"],operator:"+",data:{count:0},startParsing:function(){this.data.count=0;return"("},stopParsing:function(){return")/"+this.data.count},parseSingleTerm:function(a){return(a=twve.tcalc.isNumeric(a))?(this.data.count++,a):""}},{name:"FACT",data:{num:null},
startParsing:function(){this.data.num=null},stopParsing:function(){return twve.tcalc.factorial(this.data.num)},parseSingleTerm:function(a){if(null==this.data.num&&(a=twve.tcalc.isNumeric(a)))this.data.num=parseInt(a)}},{name:"COMBIN",data:{num:null,chosen:null},startParsing:function(){this.data.num=this.data.chosen=null},stopParsing:function(){return Math.round(twve.tcalc.factorial(this.data.num)/twve.tcalc.factorial(this.data.chosen)/twve.tcalc.factorial(this.data.num-this.data.chosen))},parseSingleTerm:function(a){if(null===
this.data.num){if(a=twve.tcalc.isNumeric(a))this.data.num=parseInt(a)}else if(null===this.data.chosen&&(a=twve.tcalc.isNumeric(a)))this.data.chosen=parseInt(a)}},{name:"THISHOUR",data:{time1:null,time2:null},startParsing:function(){this.data.time1=this.data.time2=null},stopParsing:function(){if(!this.data.time1)return"false";var a=new Date,a=a.getHours()+a.getMinutes()/60,b=this.data.time1[0];if(this.data.time2){var d=this.data.time2[0];return""+(b<=a&&a<d||d<=a&&a<b)}return""+(b==a)},parseSingleTerm:function(a){return twve.tcalc.parse_time_arg(this.data,
a)}},{name:"THISDAYOFWEEK",data:{day_of_week:null},startParsing:function(){this.data.day_of_week=null},stopParsing:function(){return""+("number"==typeof this.data.day_of_week&&this.data.day_of_week==(new Date).getDay())},parseSingleTerm:function(a){if(/[0-7]/.test(a))this.data.day_of_week=1*a;else{a=a.toLowerCase();var b=twve.position.create(0),d="\u65e5\u4e00\u4e8c\u4e09\u56db\u4e94\u516d".split(""),b=twve.text.indexOf(a,d,b);-1<b.ndx?this.data.day_of_week=d.indexOf(b.matched):(d="sun mon tue wed thr thu fri sat".split(" "),
b.ndx=0,b=twve.text.indexOf(a,d,b),-1<b.ndx?(this.data.day_of_week=d.indexOf(b.matched),4<this.data.day_of_week&&this.data.day_of_week--):(d="\u65e5\u6708\u706b\u6c34\u6728\u91d1\u571f".split(""),b.ndx=0,b=twve.text.indexOf(a,d,b),-1<b.ndx&&(this.data.day_of_week=d.indexOf(b.matched))))}}},{name:"THISWEEK",data:{date1:null,date2:null},same_week:function(a,b){var d=a.getDate(),e=b.getDate();if(d==e)return!0;if(7<Math.abs(d-e))return!1;var f=a.getDay(),g=b.getDay();return e>d?g>f:g<f},startParsing:function(){this.data.date1=
this.data.date2=null},stopParsing:function(){if(!this.data.date1)return"false";var a=new Date;return!this.data.date2?!twve.tcalc.same_year_month(this.data.date1,a)?"false":""+this.same_week(this.data.date1,a):twve.tcalc.same_year_month(this.data.date1,a)?""+this.same_week(this.data.date1,a):twve.tcalc.same_year_month(this.data.date2,a)?""+this.same_week(this.data.date2,a):"false"},parseSingleTerm:function(a){return twve.tcalc.parse_date_arg(this.data,a)}},{name:"THISMONTH",data:{date1:null,date2:null},
startParsing:function(){this.data.date1=this.data.date2=null},stopParsing:function(){if(!this.data.date1)return"false";var a=new Date;return!this.data.date2?twve.tcalc.same_year_month(this.data.date1,a)?"true":"false":twve.tcalc.same_year_month(this.data.date1,a)||twve.tcalc.same_year_month(this.data.date2,a)?"true":"false"},parseSingleTerm:function(a){return twve.tcalc.parse_date_arg(this.data,a)}},{name:"DAYS",no_eval:!0,data:{date1:null,date2:null,num_dec:null},startParsing:function(){this.data.date1=
this.data.date2=this.data.num_dec=null},stopParsing:function(){return!this.data.date1||!this.data.date2?null:twve.tcalc.days(this.data.date1,this.data.date2,this.data.num_dec)},parseSingleTerm:function(a){this.data.date1?this.data.date2?this.data.num_dec||(this.data.num_dec=1*a):this.data.date2=twve.tcalc.parse_date(a):this.data.date1=twve.tcalc.parse_date(a)}},{name:"MONTHS",no_eval:!0,data:{date1:null,date2:null,num_dec:null},startParsing:function(){this.data.date1=this.data.date2=this.data.num_dec=
null},stopParsing:function(){if(!this.data.date1||!this.data.date2)return null;var a=[this.data.date1.getFullYear(),this.data.date2.getFullYear()],b=[this.data.date1.getMonth(),this.data.date2.getMonth()],d=[this.data.date1.getDate(),this.data.date2.getDate()];"number"!=typeof this.data.num_dec&&(this.data.num_dec=0);if(a[0]==a[1])return twve.tcalc.months(b[0],d[0],b[1],d[1],a[1],this.data.num_dec);var e=0,f=1;a[1]<a[0]&&(e=1,f=0);a=12*(a[f]-a[e]-1)+twve.tcalc.months(b[e],d[e],11,31,a[e],this.data.num_dec)+
twve.tcalc.months(0,1,b[f],d[f],a[f],this.data.num_dec);return 0==e?a:-a},parseSingleTerm:function(a){this.data.date1?this.data.date2?this.data.num_dec||(this.data.num_dec=1*a):this.data.date2=twve.tcalc.parse_date(a):this.data.date1=twve.tcalc.parse_date(a)}},{name:"DATEADD",no_eval:!0,data:{date1:null,dy:null,dm:null,dd:null,format:""},startParsing:function(){this.data.date1=this.data.dy=this.data.dm=this.data.dd=null;this.data.format=""},stopParsing:function(){var a=this.data.date1||new Date,a=
new Date(a.getFullYear()+this.data.dy,a.getMonth()+this.data.dm,a.getDate()+this.data.dd);return twve.tcalc.format_date_string(a,twve.tcalc.parse_date_format(this.data.format))},parseSingleTerm:function(a){if(this.data.date1){var b=twve.tcalc.isNumeric(a),b=b?1*b:0;null===this.data.dy?this.data.dy=b:null===this.data.dm?this.data.dm=b:null===this.data.dd?this.data.dd=b:this.data.format||(this.data.format=a)}else this.data.date1=twve.tcalc.parse_date(a)}},{name:"DATE",no_eval:!0,data:{y:null,m:null,
d:null,format:""},startParsing:function(){this.data.y=this.data.m=this.data.d=null;this.data.format=""},stopParsing:function(){var a=new Date,a=new Date(this.data.y||today.getFullYear(),this.data.m?this.data.m-1:today.getMonth(),this.data.d||today.getDate());return twve.tcalc.format_date_string(a,twve.tcalc.parse_date_format(this.data.format))},parseSingleTerm:function(a){var b=twve.tcalc.isNumeric(a),b=b?1*b:0;null===this.data.y?this.data.y=b:null===this.data.m?this.data.m=b:null===this.data.d?this.data.d=
b:this.data.foramt||(this.data.format=a)}},{name:"TODAY",no_eval:!0,data:{format:""},startParsing:function(){this.data.format=""},stopParsing:function(){return twve.tcalc.format_date_string(new Date,twve.tcalc.parse_date_format(this.data.format))},parseSingleTerm:function(a){this.data.foramt||(this.data.format=a)}},{name:"ROUND",data:{value:null,num_dec:null},startParsing:function(){this.data.value=this.data.num_dec=null},stopParsing:function(){this.data.value=twve.tcalc.round(this.data.value,this.data.num_dec);
return this.data.value?""+this.data.value:"0"},parseSingleTerm:function(a){if(null===this.data.value)try{this.data.value=eval(a)}catch(b){}else if(null===this.data.num_dec)try{this.data.num_dec=eval(a)}catch(d){}}},{name:"EXP",data:{value:null},startParsing:function(){this.data.value=null},stopParsing:function(){return this.data.value?""+this.data.value:"0"},parseSingleTerm:function(a){null===this.data.value&&(this.data.value=Math.exp(1*a))}},{name:"LN",data:{value:null},startParsing:function(){this.data.value=
null},stopParsing:function(){return this.data.value?""+this.data.value:"0"},parseSingleTerm:function(a){null===this.data.value&&(this.data.value=Math.log(1*a))}},{name:"LOG10",data:{value:null},startParsing:function(){this.data.value=null},stopParsing:function(){return this.data.value?""+this.data.value:"0"},parseSingleTerm:function(a){null===this.data.value&&(this.data.value=Math.log(1*a)/Math.log(10))}},{name:"LOG",data:{argument:null,base:null},startParsing:function(){this.data.argument=this.data.base=
null},stopParsing:function(){null===this.data.base&&(this.data.base=10);var a=Math.log(this.data.argument)/Math.log(this.data.base);return a?""+a:"0"},parseSingleTerm:function(a){null===this.data.argument?this.data.argument=1*a:null===this.data.base&&(this.data.base=1*a)}},{name:"AND",data:{argument:null},startParsing:function(){this.data.argument=[]},stopParsing:function(){var a=this.data.argument.length;if(2>a)return"false";for(var b=0;b<a;b++)if(!this.data.argument[b])return"false";return"true"},
parseSingleTerm:function(a){this.data.argument[this.data.argument.length]=twve.tcalc.parseTrueFalse(a)}},{name:"OR",data:{argument:null},startParsing:function(){this.data.argument=[]},stopParsing:function(){var a=this.data.argument.length;if(0==a)return"false";for(var b=0;b<a;b++)if(this.data.argument[b])return"true";return"false"},parseSingleTerm:function(a){this.data.argument[this.data.argument.length]=twve.tcalc.parseTrueFalse(a)}},{name:"XOR",data:{argument:null},startParsing:function(){this.data.argument=
[]},stopParsing:function(){return 2>this.data.argument.length?"false":this.data.argument[0]?!this.data.argument[1]:this.data.argument[1]},parseSingleTerm:function(a){this.data.argument[this.data.argument.length]=twve.tcalc.parseTrueFalse(a)}},{name:"RANDOM",data:{factor:null},startParsing:function(){this.data.factor=null},stopParsing:function(){var a=Math.random();this.data.factor&&(a*=this.data.factor);return a},parseSingleTerm:function(a){this.data.factor||(this.data.factor=1*a)}},{name:"CONCAT",
no_eval:!0},{name:"ISNUMERIC",no_eval:!0,parseSingleTerm:function(a){return(a=twve.tcalc.isNumeric(a))?a:"false"}},{name:["STDERR","STDEV"],data:{value:null,sum:0},startParsing:function(){this.data.value=[];this.data.sum=0},stopParsing:function(){var a=this.data.value.length;if(0==a)return 0;for(var b=this.data.sum/a,d=0,e=0;e<a;e++)d+=Math.pow(this.data.value[e]-b,2);return Math.sqrt(d/(a-1))},parseSingleTerm:function(a){a=parseFloat(a);this.data.value[this.data.value.length]=a;this.data.sum+=a}},
{name:["COLUMNA","COLA"],no_deref:!0,data:{col:null},startParsing:function(){this.data.col=null},stopParsing:function(){null===this.data.col&&(this.data.col=twve.tcalc.curCellCol());return twve.tcalc.colRef(this.data.col)},parseSingleTerm:function(a){null===this.data.col&&(this.data.col=twve.tcalc.referenceToIndexCol(a))}},{name:["COLUMNS","COLS"],no_eval:!0,no_deref:!0,data:{cols:0},startParsing:function(){this.data.cols=0},stopParsing:function(){return""+this.data.cols},parseSingleTerm:function(a){a=
a.split(":");switch(a.length){case 1:this.data.cols++;break;case 2:var b=twve.tcalc.referenceToIndexCol(a[0]);a=twve.tcalc.referenceToIndexCol(a[1]);this.data.cols+=a-b+1;break;default:throw"Syntax error: "+a;}}},{name:["COLUMN","COL"],no_deref:!0,data:{col:null},startParsing:function(){this.data.col=null},stopParsing:function(){null===this.data.col&&(this.data.col=twve.tcalc.curCellCol());return 0>this.data.col?"":""+this.data.col},parseSingleTerm:function(a){null===this.data.col&&(this.data.col=
twve.tcalc.referenceToIndexCol(a))}},{name:"ROWS",no_eval:!0,no_deref:!0,data:{rows:0},startParsing:function(){this.data.rows=0},stopParsing:function(){return""+this.data.rows},parseSingleTerm:function(a){a=a.split(":");switch(a.length){case 1:this.data.rows++;break;case 2:var b=twve.tcalc.referenceToIndexRow(a[0]);a=twve.tcalc.referenceToIndexRow(a[1]);this.data.rows+=a-b+1;break;default:throw"Syntax error: "+a;}}},{name:"ROW",no_deref:!0,data:{row:null},startParsing:function(){this.data.row=null},
stopParsing:function(){null===this.data.row&&(this.data.row=row=twve.tcalc.curCellRow());return 0>row?"":""+this.data.row},parseSingleTerm:function(a){this.data.row||(this.data.row=twve.tcalc.referenceToIndexRow(a))}},{name:"SQRT",data:{value:null},startParsing:function(){this.data.value=null},stopParsing:function(){return 0==this.data.value?0:0<this.data.value?Math.sqrt(this.data.value):Math.sqrt(-this.data.value)+" i"},parseSingleTerm:function(a){this.data.value=parseFloat(a)}},{name:"IF",data:{args:null},
startParsing:function(){this.data.args=[]},stopParsing:function(){return!0==this.data.args[0]?this.data.args[1]:this.data.args[2]},parseSingleTerm:function(a,b){var d=this.data.args.length;try{switch(d){case 2:case 1:var e=a.substring(0,b);this.data.args[d]=e+eval(a.substring(b));break;case 0:this.data.args[d]=eval(a)}}catch(f){this.data.args[d]=a}}},{name:"COUNTDOWN",data:{start:null,end:null,interval:null,type:null,pid:null},startParsing:function(){this.data.start=this.data.end=this.data.interval=
this.data.type=this.data.pid=null},stopParsing:function(){if(null!==this.data.start){var a="";"number"==this.data.type?((a=a?twve.tcalc.isNumeric(this.data.end):0)?(this.data.end=1*a,1E-5>Math.abs(this.data.end)&&(this.data.end=0)):this.data.end=0,(a=this.data.interval?twve.tcalc.isNumeric(this.data.interval):1)?(this.data.interval=1*a,1E-4>this.data.interval/(this.data.end-this.data.start)&&(this.data.interval=1E-4*(this.data.end-this.data.start))):this.data.interval=1):"date"==this.data.type?(a=
Date.parse(this.data.end),isNaN(a)?this.data.end=new Date:this.data.end=a,a=Date.parse(this.data.interval),isNaN(a)?this.data.interval=1E3:this.data.interval=a):(null===this.data.end&&(this.data.end=""),null===this.data.interval&&(this.data.interval=1))}},parseSingleTerm:function(a){if(!a)return"";if(null===this.data.start){var b=twve.tcalc.isNumeric(a);b?(this.data.start=1*b,this.data.type="number"):(b=Date.parse(a),isNaN(b)?(this.data.start=a,this.data.type="string"):(this.data.start=b,this.data.type=
"date"))}else null===this.data.end?this.data.end=a:null===this.data.interval&&(this.data.interval=a)}}];
//}}}
// @@
This tiddler gives detailed information about the ''twve.wrapper'', an object representing all editable __wrappers__ (containers for part or all of a tiddler's content) in ~TiddlyWiki.