Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
{{{
# a2enmod headers
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin *
</IfModule>
<FilesMatch "\.(html|htm|js|css|vfb|ics)$">
FileETag None
<IfModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</IfModule>
</FilesMatch>
}}}
/***
|''Name''|ActivitiesMacro|
|''Description''|Adds a reminder macro to the current tiddler |
|''Author''|Michael Borck|
|''Version''|0.2.5|
|''Date''|4 Dec 2008|
|''Status''|@@beta@@|
|''Source''|http://schedule.tiddlyspot.com/|
|''Copyright''|2008|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''Feedback''|borck.m@gmail.com|
|''CoreVersion''|2.4|
|''Type''|Macro|
|''Keywords''|timetable, schedule, appointments, hard landscape, macro, reminders|
!Description
This macro adds a reminder to the current tiddler and is an extension/port of the newReminder macro. It provides an alternative tool to construct a reminder. When used in conjunction with the schedule macro the start and end promts are used to schedule the event. If DatePicker is installed it will use that otherwise it will use a custom date picker (the same one provided in newReminder).
!Usage
!Usage
{{{<<addActivity [buttonName]>>>}}}
Where {{{[buttonName]}}} is a name you give to the slider button created.
!!!Code
***/
//{{{
if (!version.extensions.activities) {
version.extensions.activites = {
major: 0,
minor: 2,
revision: 5,
date: new Date(2008, 4, 12),
source: "http://schedule.tiddlyspot.com/"
};
config.macros.addActivity = {
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
if (DatePicker) // Nice calendar popup
{
var format = "DD MMM YYYY";
var dateBox = 'When:<input type="text" size="15" name="dateBox" value="' + new Date().formatString(format) + '">';
}
else // build our own date picker
{
var today = new Date();
dateBox = 'Year: <select name="year"><option value="">Every year' + '</option>';
for (var i = 0; i < 5; i++) {
dateBox += '<option' + ((i == 0) ? ' selected': '') + ' value="' + (today.getFullYear() + i) + '">' + (today.getFullYear() + i) + '</option>';
}
dateBox += '</select> Month:<select name="month">' + '<option value="">Every month</option>';
for (i = 0; i < 12; i++) {
dateBox += '<option' + ((i == today.getMonth()) ? ' selected': '') + ' value="' + (i + 1) + '">' + config.messages.dates.months[i] + '</option>';
}
dateBox += '</select> Day:<select name="day">' + '<option value="">Every day</option>';
for (i = 1; i < 32; i++) {
dateBox += '<option' + ((i == (today.getDate())) ? ' selected': '') + ' value="' + i + '">' + i + '</option>';
}
}
// Create form elements
var start = 'From:<input type="text" size="5" name="start" value="09:00"' + 'onfocus="this.select();">';
var end = 'to <input type="text" size="5" name="end" value="09:00"' + 'onfocus="this.select();">';
var desc = 'What:<input type="text" size="41" name="title" ' + 'value="Please enter a description" onfocus="this.select();">';
var btn = '<input type="button" value="add" ' + 'onclick="config.macros.addActivity.addEventToTiddler(this.form)">';
var once = '<input type="radio" name="repeat" value="once" checked> Once';
var daily = '<input type="radio" name="repeat" value="daily"> Daily';
var weekly = '<input type="radio" name="repeat" value="weekly"> Weekly';
var twoWeeks = '<input type="radio" name="repeat" value="twoWeeks"> Every 2 weeks';
var fourWeeks = '<input type="radio" name="repeat" value="fourWeeks"> Every 4 weeks';
var monthly = '<input type="radio" name="repeat" value="monthly"> Monthly';
var yearly = '<input type="radio" name="repeat" value="yearly"> Yearly';
var hidden = 'Hide: <input type="checkbox" name="hidden" value="hidden" checked>';
var frequency = '<div>How often:' + once + daily + weekly + twoWeeks + fourWeeks + monthly + yearly + '</div>';
// Construct form
var formstring = '<br><html><form>' + desc + '<br><br>' + dateBox + ' ' + start + ' ' + end + '<br><br>' + frequency + '<br>' + hidden + '<br>' + btn + '</form></html>';
var btnName = params[0] || "Add Activity";
var panel = config.macros.slider.createSlider(place, null, btnName, "Open a form to add a new hidden reminder to this tiddler");
wikify(formstring, panel, null, store.getTiddler(params[1]));
// Form built, now attach calendar popup
if (DatePicker) {
var cb = function(el, objDate) {
el.value = objDate.formatString(format);
};
var box = document.getElementsByName("dateBox")[0];
DatePicker.create(box, new Date(), cb);
}
}
};
config.macros.addActivity.addEventToTiddler = function(form) {
// Determine where to write the macro
var title = window.story.findContainingTiddler(form).id.substr(7);
var tiddler = store.getTiddler(title);
// Determine options for reminder
var eventDate = new Date(form.dateBox.value);
var year = eventDate.getFullYear() || "";
var month = eventDate.getMonth() ? (eventDate.getMonth() + 1) : "";
var day = eventDate.getDate() || "";
var frequency = "once";
for (i = 0; i < form.repeat.length; i++) {
if (form.repeat[i].checked) {
frequency = form.repeat[i].value;
}
}
var recurring = "";
switch (frequency) {
case "once":
break;
case "daily":
recurring = 1;
break;
case "weekly":
recurring = 7;
break;
case "twoWeeks":
recurring = 14;
break;
case "fourWeeks":
recurring = 28;
break;
case "monthly":
month = "";
break
case "yearly":
recurring = 365;
break
}
// Build reminder macro
var txt = '\n<<reminder ';
txt += year ? 'year:' + year + ' ': "";
txt += month ? 'month:' + month + ' ': "";
txt += day ? 'day:' + day + ' ': "";
txt += recurring ? 'recurdays:' + recurring + ' ': "";
txt += form.start.value ? 'title:"' + form.start.value: ""
txt += form.start.value && form.end.value ? '-' + form.end.value: "";
txt += form.title.value ? ' ' + form.title.value + '" ': "";
txt += form.hidden.checked ? ' hidden ': "";
txt += ' >>';
// Write the macro and refresh tiddler
tiddler.set(null, tiddler.text + txt);
window.story.refreshTiddler(title, 1, true);
store.setDirty(true);
};
}
//}}}
<<showtoc>>
<<EncryptionDecryptAll "Decrypt all" "Decrypt all tiddlers">>
!Meetings
<<calendar thismonth meetingPrefix:"[x(cancelled)] Cancelled [x(doNotPublish)] Do not publish [x(alarm:display:off)] Hide notification [x(alarm:off)] No alarm" name:meetings tag:"meeting AND NOT cancelled AND NOT attachment AND NOT Trash" tag-task:"task" tag-meeting:"meeting alarm alarm:sound alarm:vibrate alarm:-15...5">>
<<matchTags popup "label:Meetings" "prompt:Meeting list" sort:+title meeting AND NOT attachment AND NOT Trash>>
<<showReminders format:"DIFF: TITLE" leadtime:21 limit tag:"meeting AND NOT cancelled AND NOT attachment AND NOT Trash">>
!Tasks
<<calendar thismonth name:tasks tag:"task AND NOT done AND NOT attachment AND NOT Trash" tag-task:"task" tag-meeting:"meeting">>
<<showReminders format:"DIFF: TITLE" leadtime:21 limit tag:"task AND NOT done AND NOT attachment AND NOT Trash">>
<<newTiddler
label:"New task"
prompt:"Create a new task"
title:{{new Date().formatString(getTaskDateFomat()) + ' : '}}
text:{{"@@background-color:#ffff00;[x(done)] ''Done ''@@\n\n\<\<reminder " + ((function () {var date = new Date(); var timezoneHours = (date.getTimezoneOffset() / 60) | 0; var timezoneMinutes = Math.abs(date.getTimezoneOffset() - (timezoneHours * 60)); var timezone = (Math.abs(timezoneHours) < 10 ? "0" : "") + Math.abs(timezoneHours) + (timezoneMinutes < 10 ? "0" : "") + timezoneMinutes; return "year:" + date.getFullYear() + " month:" + (date.getMonth() + 1) + " day:" + date.getDate() + " tag:\"timezone:UTC" + (date.getTimezoneOffset() <= 0 ? "+" : "-") + timezone + "\"";})()) + " function:config.macros.ics.formatTitle() messagefunction:config.macros.reminder.replaceTiddlerName() messagefunction:config.macros.reminder.replaceReminderDateTime() messagefunction:config.macros.reminder.replaceTiddlerFormatting() title:\"@@background-color:" + config.macros.date.remindersbg + ";color:" + config.macros.date.reminderstxt + ";priority:" + config.macros.date.reminderspriority + ";\\\"\\\"\\\"TIDDLERNAME\\\"\\\"\\\"@@ \\<\\<goToTiddler label:> title:{" + "{'REMINDERDATE : TIDDLERNAMEESC'}" + "} text:{" + "{'@@background-color:#ffff00;[x(done)] \\'\\'Done \\'\\'@@\\n\\n'}" + "} tag:task tag:[[TIDDLERFULLNAME]] focus:text\\>\\>\"\>\>\n\n"}}
focus:"title"
tag:{{new Date().formatString(getTaskDateFomat())}}
tag:"task"
>>
<<list filter "[tag[task AND NOT done AND NOT attachment AND NOT Trash]]">>
<<matchTags popup "label:Completed tasks" "prompt:Completed task list" sort:+title task AND done AND NOT attachment AND NOT Trash>>
<script>config.macros.fileDrop.icalendartag = "agenda";</script>/%
reminderstyle:normal:background-color:#c0ffee;color:#000000;priority:1;
reminderstyle:important:background-color:#ff0000;color:#ffffff;priority:1000;
<<reminder year:0 month:0 day:0 "function:config.macros.ics.create('agenda')" leadtime:7 tag:"meeting alarm alarm:vibrate alarm:-15...5 ics%UID%" "title:@@background-color:#83b8c2;color:#000000;priority:20; %START% - %END% : \"\"\"%SUMMARY%\"\"\"@@ \<\<goToTiddler label:> prompt:{{'%DESCRIPTION%'}} title:{{'%DATE% %START% - %END% : ' + '%SUMMARY%'}} text:{{'[x(cancelled)] Cancelled [x(doNotPublish)] Do not publish [x(alarm:display:off)] Hide notification [x(alarm:off)] No alarm\n\n%DESCRIPTION%'}} tag:meeting tag:alarm tag:[[alarm:sound]] tag:[[alarm:vibrate]] tag:[[alarm:-15...5]] tag:[[%ICS_TIDDLER_TITLE%]] tag:{{'ics' + '%UID%'}} focus:text\>\>">>
%/
text/plain
.txt .text .js .vbs .asp .cgi .pl
----
text/html
.htm .html .hta .htx .mht
----
text/comma-separated-values
.csv
----
text/javascript
.js
----
text/css
.css
----
text/xml
.xml .xsl .xslt
----
image/gif
.gif
----
image/jpeg
.jpg .jpe .jpeg
----
image/png
.png
----
image/bmp
.bmp
----
image/tiff
.tif .tiff
----
audio/basic
.au .snd
----
audio/wav
.wav
----
audio/x-pn-realaudio
.ra .rm .ram
----
audio/x-midi
.mid .midi
----
audio/mp3
.mp3
----
audio/m3u
.m3u
----
video/x-ms-asf
.asf
----
video/avi
.avi
----
video/mpeg
.mpg .mpeg
----
video/quicktime
.qt .mov .qtvr
----
application/pdf
.pdf
----
application/rtf
.rtf
----
application/postscript
.ai .eps .ps
----
application/wordperfect
.wpd
----
application/mswrite
.wri
----
application/msexcel
.xls .xls3 .xls4 .xls5 .xlw
----
application/msword
.doc
----
application/mspowerpoint
.ppt .pps
----
application/x-director
.swa
----
application/x-shockwave-flash
.swf
----
application/x-zip-compressed
.zip
----
application/x-gzip
.gz
----
application/x-rar-compressed
.rar
----
application/octet-stream
.com .exe .dll .ocx
----
application/java-archive
.jar
/***
|Name|AttachFilePlugin|
|Source|http://www.TiddlyTools.com/#AttachFilePlugin|
|Documentation|http://www.TiddlyTools.com/#AttachFilePluginInfo|
|Version|4.0.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|AttachFilePluginFormatters, AttachFileMIMETypes|
|Description|Store binary files as base64-encoded tiddlers with fallback links for separate local and/or remote file storage|
Store or link binary files (such as jpg, gif, pdf or even mp3) within your TiddlyWiki document and then use them as images or links from within your tiddler content.
> Important note: As of version 3.6.0, in order to //render// images and other binary attachments created with this plugin, you must also install [[AttachFilePluginFormatters]], which extends the behavior of the TiddlyWiki core formatters for embedded images ({{{[img[tooltip|image]]}}}), linked embedded images ({{{[img[tooltip|image][link]]}}}), and external/"pretty" links ({{{[[label|link]]}}}), so that these formatter will process references to attachment tiddlers as if a normal file reference had been provided. |
!!!!!Documentation
>see [[AttachFilePluginInfo]]
!!!!!Inline interface (live)
>see [[AttachFile]] (shadow tiddler)
><<tiddler AttachFile>>
!!!!!Revisions
<<<
2011.02.14 4.0.1 fix OSX error: use picker.file.path
2009.06.04 4.0.0 changed attachment storage format to use //sections// instead of embedded substring markers.
|please see [[AttachFilePluginInfo]] for additional revision details|
2005.07.20 1.0.0 Initial Release
<<<
!!!!!Code
***/
// // version
//{{{
version.extensions.AttachFilePlugin= {major: 4, minor: 0, revision: 1, date: new Date(2011,2,14)};
// shadow tiddler
config.shadowTiddlers.AttachFile="<<attach inline>>";
// add 'attach' backstage task (insert before built-in 'importTask')
if (config.tasks) { // for TW2.2b or above
config.tasks.attachTask = {
text: "attach",
tooltip: "Attach a binary file as a tiddler",
content: "<<attach inline>>"
}
config.backstageTasks.splice(config.backstageTasks.indexOf("importTask"),0,"attachTask");
}
config.macros.attach = {
// // lingo
//{{{
label: "attach file",
tooltip: "Attach a file to this document",
linkTooltip: "Attachment: ",
typeList: "AttachFileMIMETypes",
titlePrompt: " enter tiddler title...",
MIMEPrompt: "<option value=''>select MIME type...</option><option value='editlist'>[edit list...]</option>",
localPrompt: " enter local path/filename...",
URLPrompt: " enter remote URL...",
tiddlerErr: "Please enter a tiddler title",
sourceErr: "Please enter a source path/filename",
storageErr: "Please select a storage method: embedded, local or remote",
MIMEErr: "Unrecognized file format. Please select a MIME type",
localErr: "Please enter a local path/filename",
URLErr: "Please enter a remote URL",
fileErr: "Invalid path/file or file not found",
tiddlerFormat: '!usage\n{{{%0}}}\n%0\n!notes\n%1\n!type\n%2\n!file\n%3\n!url\n%4\n!data\n%5\n',
//}}}
// // macro definition
//{{{
handler:
function(place,macroName,params) {
if (params && !params[0])
{ createTiddlyButton(place,this.label,this.tooltip,this.toggleAttachPanel); return; }
var id=params.shift();
this.createAttachPanel(place,id+"_attachPanel",params);
document.getElementById(id+"_attachPanel").style.position="static";
document.getElementById(id+"_attachPanel").style.display="block";
},
//}}}
//{{{
createAttachPanel:
function(place,panel_id,params) {
if (!panel_id || !panel_id.length) var panel_id="_attachPanel";
// remove existing panel (if any)
var panel=document.getElementById(panel_id); if (panel) panel.parentNode.removeChild(panel);
// set styles for this panel
setStylesheet(this.css,"attachPanel");
// create new panel
var title=""; if (params && params[0]) title=params.shift();
var types=this.MIMEPrompt+this.formatListOptions(store.getTiddlerText(this.typeList)); // get MIME types
panel=createTiddlyElement(place,"span",panel_id,"attachPanel",null);
var html=this.html.replace(/%id%/g,panel_id);
html=html.replace(/%title%/g,title);
html=html.replace(/%disabled%/g,title.length?"disabled":"");
html=html.replace(/%IEdisabled%/g,config.browser.isIE?"disabled":"");
html=html.replace(/%types%/g,types);
panel.innerHTML=html;
if (config.browser.isGecko) { // FF3 FIXUP
document.getElementById("attachSource").style.display="none";
document.getElementById("attachFixPanel").style.display="block";
}
return panel;
},
//}}}
//{{{
toggleAttachPanel:
function (e) {
if (!e) var e = window.event;
var parent=resolveTarget(e).parentNode;
var panel = document.getElementById("_attachPanel");
if (panel==undefined || panel.parentNode!=parent)
panel=config.macros.attach.createAttachPanel(parent,"_attachPanel");
var isOpen = panel.style.display=="block";
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
else
panel.style.display = isOpen ? "none" : "block" ;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return(false);
},
//}}}
//{{{
formatListOptions:
function(text) {
if (!text || !text.trim().length) return "";
// get MIME list content from text
var parts=text.split("\n----\n");
var out="";
for (var p=0; p<parts.length; p++) {
var lines=parts[p].split("\n");
var label=lines.shift(); // 1st line=display text
var value=lines.shift(); // 2nd line=item value
out +='<option value="%1">%0</option>'.format([label,value]);
}
return out;
},
//}}}
// // interface definition
//{{{
css:
".attachPanel { display: none; position:absolute; z-index:10; width:35em; right:105%; top:0em;\
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em; text-align:left }\
.attachPanel form { display:inline;border:0;padding:0;margin:0; }\
.attachPanel select { width:99%;margin:0px;font-size:8pt;line-height:110%;}\
.attachPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
.attachPanel textarea { width:98%;margin:0px;height:2em;font-size:8pt;line-height:110%}\
.attachPanel table { width:100%;border:0;margin:0;padding:0;color:inherit; }\
.attachPanel tbody, .attachPanel tr, .attachPanel td { border:0;margin:0;padding:0;color:#000; }\
.attachPanel .box { border:1px solid black; padding:.3em; margin:.3em 0px; background:#f8f8f8; \
-moz-border-radius:5px;-webkit-border-radius:5px; }\
.attachPanel .chk { width:auto;border:0; }\
.attachPanel .btn { width:auto; }\
.attachPanel .btn2 { width:49%; }\
",
//}}}
//{{{
html:
'<form>\
attach from source file\
<input type="file" id="attachSource" name="source" size="56"\
onChange="config.macros.attach.onChangeSource(this)">\
<div id="attachFixPanel" style="display:none"><!-- FF3 FIXUP -->\
<input type="text" id="attachFixSource" style="width:90%"\
title="Enter a path/file to attach"\
onChange="config.macros.attach.onChangeSource(this);">\
<input type="button" style="width:7%" value="..."\
title="Enter a path/file to attach"\
onClick="config.macros.attach.askForFilename(document.getElementById(\'attachFixSource\'));">\
</div><!--end FF3 FIXUP-->\
<div class="box">\
<table style="border:0"><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
embed data <input type=checkbox class=chk name="useData" %IEdisabled% \
onclick="if (!this.form.MIMEType.value.length)\
this.form.MIMEType.selectedIndex=this.checked?1:0; "> \
</td><td style="border:0">\
<select size=1 name="MIMEType" \
onchange="this.title=this.value; if (this.value==\'editlist\')\
{ this.selectedIndex=this.form.useData.checked?1:0; story.displayTiddler(null,config.macros.attach.typeList,2); return; }">\
<option value=""></option>\
%types%\
</select>\
</td></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
local link <input type=checkbox class=chk name="useLocal"\
onclick="this.form.local.value=this.form.local.defaultValue=this.checked?config.macros.attach.localPrompt:\'\';"> \
</td><td style="border:0">\
<input type=text name="local" size=15 autocomplete=off value=""\
onchange="this.form.useLocal.checked=this.value.length" \
onkeyup="this.form.useLocal.checked=this.value.length" \
onfocus="if (!this.value.length) this.value=config.macros.attach.localPrompt; this.select()">\
</td></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
remote link <input type=checkbox class=chk name="useURL"\
onclick="this.form.URL.value=this.form.URL.defaultValue=this.checked?config.macros.attach.URLPrompt:\'\';\"> \
</td><td style="border:0">\
<input type=text name="URL" size=15 autocomplete=off value=""\
onfocus="if (!this.value.length) this.value=config.macros.attach.URLPrompt; this.select()"\
onchange="this.form.useURL.checked=this.value.length;"\
onkeyup="this.form.useURL.checked=this.value.length;">\
</td></tr></table>\
</div>\
<table style="border:0"><tr style="border:0"><td style="border:0;text-align:right;vertical-align:top;width:1%;white-space:nowrap">\
notes \
</td><td style="border:0" colspan=2>\
<textarea name="notes" style="width:98%;height:3.5em;margin-bottom:2px"></textarea>\
</td><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
attach as \
</td><td style="border:0" colspan=2>\
<input type=text name="tiddlertitle" size=15 autocomplete=off value="%title%"\
onkeyup="if (!this.value.length) { this.value=config.macros.attach.titlePrompt; this.select(); }"\
onfocus="if (!this.value.length) this.value=config.macros.attach.titlePrompt; this.select()" %disabled%>\
</td></tr></tr><tr style="border:0"><td style="border:0;text-align:right;width:1%;white-space:nowrap">\
add tags \
</td><td style="border:0">\
<input type=text name="tags" size=15 autocomplete=off value="" onfocus="this.select()">\
</td><td style="width:40%;text-align:right;border:0">\
<input type=button class=btn2 value="attach"\
onclick="config.macros.attach.onClickAttach(this)"><!--\
--><input type=button class=btn2 value="close"\
onclick="var panel=document.getElementById(\'%id%\'); if (panel) panel.parentNode.removeChild(panel);">\
</td></tr></table>\
</form>',
//}}}
// // control processing
//{{{
onChangeSource:
function(here) {
var form=here.form;
var list=form.MIMEType;
var theFilename = here.value;
var theExtension = theFilename.substr(theFilename.lastIndexOf('.')).toLowerCase();
// if theFilename is in current document folder, remove path prefix and use relative reference
var h=document.location.href; folder=getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf("/")+1)));
if (theFilename.substr(0,folder.length)==folder) theFilename='./'+theFilename.substr(folder.length);
else theFilename='file:///'+theFilename; // otherwise, use absolute reference
theFilename=theFilename.replace(/\\/g,"/"); // fixup: change \ to /
form.useLocal.checked = true;
form.local.value = theFilename;
form.useData.checked = !form.useData.disabled;
list.selectedIndex=1;
for (var i=0; i<list.options.length; i++) // find matching MIME type
if (list.options[i].value.indexOf(theExtension)!=-1) { list.selectedIndex = i; break; }
if (!form.tiddlertitle.disabled)
form.tiddlertitle.value=theFilename.substr(theFilename.lastIndexOf('/')+1); // get tiddlername from filename
},
//}}}
//{{{
onClickAttach:
function (here) {
clearMessage();
// get input values
var form=here.form;
var src=form.source; if (config.browser.isGecko) src=document.getElementById("attachFixSource");
src=src.value!=src.defaultValue?src.value:"";
var when=(new Date()).formatString(config.macros.timeline.dateFormat);
var title=form.tiddlertitle.value;
var local = form.local.value!=form.local.defaultValue?form.local.value:"";
var url = form.URL.value!=form.URL.defaultValue?form.URL.value:"";
var notes = form.notes.value;
var tags = "attachment excludeMissing "+form.tags.value;
var useData=form.useData.checked;
var useLocal=form.useLocal.checked;
var useURL=form.useURL.checked;
var mimetype = form.MIMEType.value.length?form.MIMEType.options[form.MIMEType.selectedIndex].text:"";
// validate checkboxes and get filename
if (useData) {
if (src.length) { if (!theLocation) var theLocation=src; }
else { alert(this.sourceErr); src.focus(); return false; }
}
if (useLocal) {
if (local.length) { if (!theLocation) var theLocation = local; }
else { alert(this.localErr); form.local.focus(); return false; }
}
if (useURL) {
if (url.length) { if (!theLocation) var theLocation = url; }
else { alert(this.URLErr); form.URL.focus(); return false; }
}
if (!(useData||useLocal||useURL))
{ form.useData.focus(); alert(this.storageErr); return false; }
if (!theLocation)
{ src.focus(); alert(this.sourceErr); return false; }
if (!title || !title.trim().length || title==this.titlePrompt)
{ form.tiddlertitle.focus(); alert(this.tiddlerErr); return false; }
// if not already selected, determine MIME type based on filename extension (if any)
if (useData && !mimetype.length && theLocation.lastIndexOf('.')!=-1) {
var theExt = theLocation.substr(theLocation.lastIndexOf('.')).toLowerCase();
var theList=form.MIMEType;
for (var i=0; i<theList.options.length; i++)
if (theList.options[i].value.indexOf(theExt)!=-1)
{ var mimetype=theList.options[i].text; theList.selectedIndex=i; break; }
}
// attach the file
return this.createAttachmentTiddler(src, when, notes, tags, title,
useData, useLocal, useURL, local, url, mimetype);
},
getMIMEType:
function(src,def) {
var ext = src.substr(src.lastIndexOf('.')).toLowerCase();
var list=store.getTiddlerText(this.typeList);
if (!list || !list.trim().length) return def;
// get MIME list content from tiddler
var parts=list.split("\n----\n");
for (var p=0; p<parts.length; p++) {
var lines=parts[p].split("\n");
var mime=lines.shift(); // 1st line=MIME type
var match=lines.shift(); // 2nd line=matching extensions
if (match.indexOf(ext)!=-1) return mime;
}
return def;
},
createAttachmentTiddler:
function (src, when, notes, tags, title, useData, useLocal, useURL, local, url, mimetype, noshow) {
if (useData) { // encode the data
if (!mimetype.length) {
alert(this.MIMEErr);
form.MIMEType.selectedIndex=1; form.MIMEType.focus();
return false;
}
var d = this.readFile(src); if (!d) { return false; }
displayMessage('encoding '+src);
var encoded = this.encodeBase64(d);
displayMessage('file size='+d.length+' bytes, encoded size='+encoded.length+' bytes');
}
var usage=(mimetype.substr(0,5)=="image"?'[img[%0]]':'[[%0|%0]]').format([title]);
var theText=this.tiddlerFormat.format([
usage, notes.length?notes:'//none//', mimetype,
useLocal?local.replace(/\\/g,'/'):'', useURL?url:'',
useData?('data:'+mimetype+';base64,'+encoded):'' ]);
store.saveTiddler(title,title,theText,config.options.txtUserName,new Date(),tags);
var panel=document.getElementById("attachPanel"); if (panel) panel.style.display="none";
if (!noshow) { story.displayTiddler(null,title); story.refreshTiddler(title,null,true); }
displayMessage('attached "'+title+'"');
return true;
},
//}}}
// // base64 conversion
//{{{
encodeBase64:
function (d) {
if (!d) return null;
// encode as base64
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var out="";
var chr1,chr2,chr3="";
var enc1,enc2,enc3,enc4="";
for (var count=0,i=0; i<d.length; ) {
chr1=d.charCodeAt(i++);
chr2=d.charCodeAt(i++);
chr3=d.charCodeAt(i++);
enc1=chr1 >> 2;
enc2=((chr1 & 3) << 4) | (chr2 >> 4);
enc3=((chr2 & 15) << 2) | (chr3 >> 6);
enc4=chr3 & 63;
if (isNaN(chr2)) enc3=enc4=64;
else if (isNaN(chr3)) enc4=64;
out+=keyStr.charAt(enc1)+keyStr.charAt(enc2)+keyStr.charAt(enc3)+keyStr.charAt(enc4);
chr1=chr2=chr3=enc1=enc2=enc3=enc4="";
}
return out;
},
decodeBase64: function(input) {
var out="";
var chr1,chr2,chr3;
var enc1,enc2,enc3,enc4;
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
input=input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1=keyStr.indexOf(input.charAt(i++));
enc2=keyStr.indexOf(input.charAt(i++));
enc3=keyStr.indexOf(input.charAt(i++));
enc4=keyStr.indexOf(input.charAt(i++));
chr1=(enc1 << 2) | (enc2 >> 4);
chr2=((enc2 & 15) << 4) | (enc3 >> 2);
chr3=((enc3 & 3) << 6) | enc4;
out=out+String.fromCharCode(chr1);
if (enc3!=64) out=out+String.fromCharCode(chr2);
if (enc4!=64) out=out+String.fromCharCode(chr3);
} while (i<input.length);
return out;
},
//}}}
// // I/O functions
//{{{
readFile: // read local BINARY file data
function(filePath) {
if(!window.Components) { return null; }
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { alert("access denied: "+filePath); return null; }
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(filePath); } catch(e) { alert("cannot read file - invalid path: "+filePath); return null; }
if (!file.exists()) { alert("cannot read file - not found: "+filePath); return null; }
var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
inputStream.init(file, 0x01, 00004, null);
var bInputStream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
bInputStream.setInputStream(inputStream);
return(bInputStream.readBytes(inputStream.available()));
},
//}}}
//{{{
writeFile:
function(filepath,data) {
// TBD: decode base64 and write BINARY data to specified local path/filename
return(false);
},
//}}}
//{{{
askForFilename: // for FF3 fixup
function(target) {
var msg=config.messages.selectFile;
if (target && target.title) msg=target.title; // use target field tooltip (if any) as dialog prompt text
// get local path for current document
var path=getLocalPath(document.location.href);
var p=path.lastIndexOf("/"); if (p==-1) p=path.lastIndexOf("\\"); // Unix or Windows
if (p!=-1) path=path.substr(0,p+1); // remove filename, leave trailing slash
var file=""
var result=window.mozAskForFilename(msg,path,file,true); // FF3 FIXUP ONLY
if (target && result.length) // set target field and trigger handling
{ target.value=result; target.onchange(); }
return result;
}
};
//}}}
//{{{
if (window.mozAskForFilename===undefined) { // also defined by CoreTweaks (for ticket #604)
window.mozAskForFilename=function(msg,path,file,mustExist) {
if(!window.Components) return false;
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, mustExist?nsIFilePicker.modeOpen:nsIFilePicker.modeSave);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension='';
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel)
var result=picker.file.path;
}
catch(ex) { displayMessage(ex.toString()); }
return result;
}
}
//}}}
/***
|Name|AttachFilePluginFormatters|
|Source|http://www.TiddlyTools.com/#AttachFilePluginFormatters|
|Version|4.0.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1.3|
|Type|plugin|
|Description|run-time library for displaying attachment tiddlers|
Runtime processing for //rendering// attachment tiddlers created by [[AttachFilePlugin]]. Attachment tiddlers are tagged with<<tag attachment>>and contain binary file content (e.g., jpg, gif, pdf, mp3, etc.) that has been stored directly as base64 text-encoded data or can be loaded from external files stored on a local filesystem or remote web server. Note: after creating new attachment tiddlers, you can remove [[AttachFilePlugin]], as long as you retain //this// tiddler (so that images can be rendered later on).
!!!!!Formatters
<<<
This plugin extends the behavior of the following TiddlyWiki core "wikify()" formatters:
* embedded images: {{{[img[tooltip|image]]}}}
* linked embedded images: {{{[img[tooltip|image][link]]}}}
* external/"pretty" links: {{{[[label|link]]}}}
''Please refer to AttachFilePlugin (source: http://www.TiddlyTools.com/#AttachFilePlugin) for additional information.''
<<<
!!!!!Revisions
<<<
2009.10.10 [4.0.1] in fileExists(), check for IE to avoid hanging Chrome during startup
2009.06.04 [4.0.0] changed attachment storage format to use //sections// instead of embedded substring markers.
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info
2007.12.04 [*.*.*] update for TW2.3.0: replaced deprecated core functions, regexps, and macros
2007.10.29 [3.7.0] more code reduction: removed upload handling from AttachFilePlugin (saves ~7K!)
2007.10.28 [3.6.0] removed duplicate formatter code from AttachFilePlugin (saves ~10K!) and updated documentation accordingly. This plugin ([[AttachFilePluginFormatters]]) is now //''required''// in order to display attached images/binary files within tiddler content.
2006.05.20 [3.4.0] through 2007.03.01 [3.5.3] sync with AttachFilePlugin
2006.05.13 [3.2.0] created from AttachFilePlugin v3.2.0
<<<
!!!!!Code
***/
// // version
//{{{
version.extensions.AttachFilePluginFormatters= {major: 4, minor: 0, revision: 1, date: new Date(2009,10,10)};
//}}}
//{{{
if (config.macros.attach==undefined) config.macros.attach= { };
//}}}
//{{{
if (config.macros.attach.isAttachment==undefined) config.macros.attach.isAttachment=function (title) {
var tiddler = store.getTiddler(title);
if (tiddler==undefined || tiddler.tags==undefined) return false;
return (tiddler.tags.indexOf("attachment")!=-1);
}
//}}}
//{{{
// test for local file existence - returns true/false without visible error display
if (config.macros.attach.fileExists==undefined) config.macros.attach.fileExists=function(f) {
if(window.Components) { // MOZ
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { return false; } // security access denied
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(f); }
catch(e) { return false; } // invalid directory
return file.exists();
}
else if (config.browser.isIE) { // IE
var fso = new ActiveXObject("Scripting.FileSystemObject");
return fso.FileExists(f);
}
else return true; // other browsers: assume file exists
}
//}}}
//{{{
if (config.macros.attach.getAttachment==undefined) config.macros.attach.getAttachment=function(title) {
// extract embedded data, local and remote links (if any)
var text=store.getTiddlerText(title,'');
var embedded=store.getTiddlerText(title+'##data','').trim();
var locallink=store.getTiddlerText(title+'##file','').trim();
var remotelink=store.getTiddlerText(title+'##url','').trim();
// backward-compatibility for older attachments (pre 4.0.0)
var startmarker="---BEGIN_DATA---\n";
var endmarker="\n---END_DATA---";
var pos=0; var endpos=0;
if ((pos=text.indexOf(startmarker))!=-1 && (endpos=text.indexOf(endmarker))!=-1)
embedded="data:"+(text.substring(pos+startmarker.length,endpos)).replace(/\n/g,'');
if ((pos=text.indexOf("/%LOCAL_LINK%/"))!=-1)
locallink=text.substring(text.indexOf("|",pos)+1,text.indexOf("]]",pos));
if ((pos=text.indexOf("/%REMOTE_LINK%/"))!=-1)
remotelink=text.substring(text.indexOf("|",pos)+1,text.indexOf("]]",pos));
// if there is a data: URI defined (not supported by IE)
if (embedded.length && !config.browser.isIE) return embedded;
// document is being served remotely... use remote URL (if any) (avoids security alert)
if (remotelink.length && document.location.protocol!="file:")
return remotelink;
// local link only... return link without checking file existence (avoids security alert)
if (locallink.length && !remotelink.length)
return locallink;
// local link, check for file exist... use local link if found
if (locallink.length) {
locallink=locallink.replace(/^\.[\/\\]/,''); // strip leading './' or '.\' (if any)
if (this.fileExists(getLocalPath(locallink))) return locallink;
// maybe local link is relative... add path from current document and try again
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\");
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
if (this.fileExists(getLocalPath(pathPrefix+locallink))) return locallink;
}
// no embedded data, no local (or not found), fallback to remote URL (if any)
if (remotelink.length) return remotelink;
// attachment URL doesn't resolve, just return input as is
return title;
}
//}}}
//{{{
if (config.macros.attach.init_formatters==undefined) config.macros.attach.init_formatters=function() {
if (this.initialized) return;
// find the formatter for "image" and replace the handler
for (var i=0; i<config.formatters.length && config.formatters[i].name!="image"; i++);
if (i<config.formatters.length) config.formatters[i].handler=function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) // Simple bracketted link
{
var e = w.output;
if(lookaheadMatch[5])
{
var link = lookaheadMatch[5];
// ELS -------------
var external=config.formatterHelpers.isExternalLink(link);
if (external)
{
if (config.macros.attach.isAttachment(link))
{
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);
// ELS -------------
addClass(e,"imageLink");
}
var img = createTiddlyElement(e,"img");
if(lookaheadMatch[1])
img.align = "left";
else if(lookaheadMatch[2])
img.align = "right";
if(lookaheadMatch[3])
img.title = lookaheadMatch[3];
img.src = lookaheadMatch[4];
// ELS -------------
if (config.macros.attach.isAttachment(lookaheadMatch[4]))
img.src=config.macros.attach.getAttachment(lookaheadMatch[4]);
// ELS -------------
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
//}}}
//{{{
// find the formatter for "prettyLink" and replace the handler
for (var i=0; i<config.formatters.length && config.formatters[i].name!="prettyLink"; i++);
if (i<config.formatters.length) {
config.formatters[i].handler=function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e;
var text = lookaheadMatch[1];
if(lookaheadMatch[3]) {
// Pretty bracketted link
var link = lookaheadMatch[3];
if (config.macros.attach.isAttachment(link)) {
e = createExternalLink(w.output,link);
e.href=config.macros.attach.getAttachment(link);
e.title=config.macros.attach.linkTooltip+link;
}
else e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link))
? createExternalLink(w.output,link)
: createTiddlyLink(w.output,link,false,null,w.isStatic);
} else {
e = createTiddlyLink(w.output,text,false,null,w.isStatic);
}
createTiddlyText(e,text);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
} // if "prettyLink" formatter found
this.initialized=true;
}
//}}}
//{{{
config.macros.attach.init_formatters(); // load time init
//}}}
//{{{
if (TiddlyWiki.prototype.coreGetRecursiveTiddlerText==undefined) {
TiddlyWiki.prototype.coreGetRecursiveTiddlerText = TiddlyWiki.prototype.getRecursiveTiddlerText;
TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth) {
return config.macros.attach.isAttachment(title)?
config.macros.attach.getAttachment(title):this.coreGetRecursiveTiddlerText.apply(this,arguments);
}
}
//}}}
//{{{
if (config.macros.attach && config.macros.attach.init_formatters) {
var code = eval("config.macros.attach.init_formatters").toString();
var newCode = null;
var startPosition = code.indexOf("!lookaheadMatch[2]");
if (startPosition > -1) {
var startPosition2 = code.indexOf(';', startPosition);
if (startPosition2 > -1) {
newCode = code.substring(0, startPosition2 + 1)
+ " if (link && (link.indexOf('data:') == 0)) { e.download = text; e.title = text; } "
+ code.substring(startPosition2 + 1);
code = newCode;
}
startPosition2 = code.lastIndexOf('e.title', startPosition);
if (startPosition2 > -1) {
startPosition2 = code.indexOf(';', startPosition2);
if (startPosition2 > -1) {
newCode = code.substring(0, startPosition2 + 1)
+ " if (e.href && (e.href.indexOf('data:') == 0)) { e.download = text; e.title = text; } "
+ code.substring(startPosition2 + 1);
code = newCode;
}
}
}
if (newCode != null) {
eval("config.macros.attach.init_formatters = function init_formatters" + newCode.substring(newCode.indexOf("(")));
config.macros.attach.initialized = false;
config.macros.attach.init_formatters();
}
}
//}}}
/***
|Name|AttachFilePluginOverride|
|License|[[TW Notes License]]|
|Requires|[[AttachFilePlugin]]|
!!!!!Code
***/
//{{{
(function () {
var attachFileMIMETypes = store.getTiddlerText("AttachFileMIMETypes");
if (attachFileMIMETypes) {
config.shadowTiddlers.FileMIMETypes = attachFileMIMETypes.replace(".text .js", ".text").replace(".js", ".js, .mjs").replace(".tiff", ".tiff\n----\nimage/svg+xml\n.svg");
}
})();
//}}}
//{{{
(function () {
var code = config.macros.attach.createAttachmentTiddler.toString();
code = code.replace("store.saveTiddler(title", "store.getTiddlerText(title); var tiddler = store.fetchTiddler(title); if (tiddler) { tiddler.set(undefined, theText, config.options.txtUserName, new Date()); }; store.saveTiddler(tiddler ? tiddler : title");
code = code.replace("var panel", "if (!tiddler) store.fetchTiddler(title).creator = config.options.txtUserName; var panel");
eval("config.macros.attach.createAttachmentTiddler = function readFile" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
var code = config.macros.attach.decodeBase64.toString();
var newCode = null;
var startPosition = code.indexOf("{");
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1) + ' var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; ' + code.substring(startPosition + 1);
code = newCode;
}
if (newCode != null) {
eval("config.macros.attach.decodeBase64 = function decodeBase64" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
(function () {
var code = config.macros.attach.readFile.toString();
var newCode = null;
var startPosition = code.indexOf("{");
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1) + " if (filePath.fileData !== undefined) return filePath.fileData; " + code.substring(startPosition + 1);
code = newCode;
}
if (newCode != null) {
eval("config.macros.attach.readFile = function readFile" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
if ((typeof netscape === "undefined") || (!netscape.security || !netscape.security.PrivilegeManager || !netscape.security.PrivilegeManager.enablePrivilege)) {
config.macros.attach.onClickAttach = function (here) {
clearMessage();
// get input values
var form=here.form;
var src=form.source.files[0].name;
var when=(new Date()).formatString(config.macros.timeline.dateFormat);
var title=form.tiddlertitle.value;
var local = form.local.value!=form.local.defaultValue?form.local.value:"";
var url = form.URL.value!=form.URL.defaultValue?form.URL.value:"";
var notes = form.notes.value;
var tags = "attachment excludeMissing "+form.tags.value;
var useData=form.useData.checked;
var useLocal=form.useLocal.checked;
var useURL=form.useURL.checked;
var mimetype = form.MIMEType.value.length?form.MIMEType.options[form.MIMEType.selectedIndex].text:"";
// validate checkboxes and get filename
if (useData) {
if (src.length) { if (!theLocation) var theLocation=src; }
else { alert(this.sourceErr); src.focus(); return false; }
}
if (useLocal) {
if (local.length) { if (!theLocation) var theLocation = local; }
else { alert(this.localErr); form.local.focus(); return false; }
}
if (useURL) {
if (url.length) { if (!theLocation) var theLocation = url; }
else { alert(this.URLErr); form.URL.focus(); return false; }
}
if (!(useData||useLocal||useURL))
{ form.useData.focus(); alert(this.storageErr); return false; }
if (!theLocation)
{ src.focus(); alert(this.sourceErr); return false; }
if (!title || !title.trim().length || title==this.titlePrompt)
{ form.tiddlertitle.focus(); alert(this.tiddlerErr); return false; }
// if not already selected, determine MIME type based on filename extension (if any)
if (useData && !mimetype.length && theLocation.lastIndexOf('.')!=-1) {
var theExt = theLocation.substr(theLocation.lastIndexOf('.')).toLowerCase();
var theList=form.MIMEType;
for (var i=0; i<theList.options.length; i++)
if (theList.options[i].value.indexOf(theExt)!=-1)
{ var mimetype=theList.options[i].text; theList.selectedIndex=i; break; }
}
// attach the file
var reader = new FileReader;
reader.onload = function (e) {
var loadedfilesrc = {};
loadedfilesrc.fileData = e.target.result;
config.macros.attach.createAttachmentTiddler(loadedfilesrc, when, notes, tags, title, useData, useLocal, useURL, local, url, mimetype);
};
reader.readAsBinaryString(form.source.files[0]);
return true;
}
var code = config.macros.attach.createAttachPanel.toString();
var newCode = null;
var startPosition = code.indexOf("config.browser.isGecko");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "false" + code.substring(startPosition + "config.browser.isGecko".length);
code = newCode;
}
if (newCode != null) {
eval("config.macros.attach.createAttachPanel = function createAttachPanel" + newCode.substring(newCode.indexOf("(")));
}
}
//}}}
//{{{
config.macros.attach.typeList = "FileMIMETypes";
//}}}
// // http://www.antiyes.com/jquery-blink-plugin
//{{{
(function ($)
{
$.fn.blink = function(options)
{
var defaults = { delay:500 };
var options = $.extend(defaults, options);
return this.each(function()
{
var obj = $(this);
setInterval(function()
{
if($(obj).css("visibility") == "visible")
{
$(obj).css('visibility','hidden');
}
else
{
$(obj).css('visibility','visible');
}
}, options.delay);
});
}
}(jQuery));
//}}}
//{{{
config.formatters[config.formatters.length] = {
name: "blink",
match: "<[Bb][Ll][Ii][Nn][Kk]>",
termRegExp: /(<\/[Bb][Ll][Ii][Nn][Kk]>)/mg,
handler: function(w) {
var blinkElement = createTiddlyElement(w.output, "span");
w.subWikifyTerm(blinkElement, this.termRegExp);
jQuery(blinkElement).blink();
}
};
//}}}
/***
|Name|BookmarksPlugin|
|License|[[TW Notes License]]|
|Requires|[[jstree.js]]|
!!!!!Code
***/
//{{{
if (config.options.txtBookmarksPluginDblClickDelay === undefined) {
config.options.txtBookmarksPluginDblClickDelay = "500";
}
merge(config.optionsDesc, {
txtBookmarksPluginDblClickDelay: "The double-click delay for displaying bookmark information instead of following the link"
});
//}}}
//{{{
setStylesheet(".bookmarks-plugin .jstree-themeicon-custom {background-size: contain !important}", "BookmarksPluginStyleSheet");
//}}}
//{{{
config.formatters[config.formatters.length] = {
name: "bookmarks",
match: "\\<bookmarks",
lookahead: "\\<bookmarks(?: folder=\\\"([^\\\"]*)\\\")?\\>((?:.|\\n)*?)\\</bookmarks\\>",
defaulticon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJub25lIiBkPSJNMCAwaDI0djI0SDBWMHoiLz48cGF0aCBkPSJNMTIgMkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnpNNCAxMmMwLS42MS4wOC0xLjIxLjIxLTEuNzhMOC45OSAxNXYxYzAgMS4xLjkgMiAyIDJ2MS45M0M3LjA2IDE5LjQzIDQgMTYuMDcgNCAxMnptMTMuODkgNS40Yy0uMjYtLjgxLTEtMS40LTEuOS0xLjRoLTF2LTNjMC0uNTUtLjQ1LTEtMS0xaC02di0yaDJjLjU1IDAgMS0uNDUgMS0xVjdoMmMxLjEgMCAyLS45IDItMnYtLjQxQzE3LjkyIDUuNzcgMjAgOC42NSAyMCAxMmMwIDIuMDgtLjgxIDMuOTgtMi4xMSA1LjR6Ii8+PC9zdmc+",
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 rootFolder = lookaheadMatch[1];
var bookmarks = jQuery(jQuery.parseHTML(lookaheadMatch[2]));
var icon = this.defaulticon;
var tree = rootFolder ? [{text: rootFolder, children: []}] : [];
var processBookmarks = function (tree, bookmarks) {
bookmarks.children("DT").each(function (index) {
var tag = jQuery(this);
var DL = tag.children("DL").first();
if (DL.length) {
var folder = {text: tag.children("H3").text(), children: []};
tree.push(folder);
processBookmarks(folder.children, DL);
} else {
tag.children("A, HR").each(function () {
var item = jQuery(this);
if (item.prop("tagName") === "HR") {
tree.push({separator: true});
} else {
var bookmark = {text: item.text()};
bookmark.icon = item.attr("icon") ? item.attr("icon") : icon;
if (item.attr("href")) {
bookmark.a_attr = {title: item.attr("href"), href: item.attr("href"), target: "_blank"};
}
tree.push(bookmark);
}
});
}
});
};
processBookmarks(rootFolder ? tree[0].children : tree, bookmarks.closest("DL").first());
var searchElement = jQuery("<input class='env-dashboard-search' style='margin:0em auto 1em auto; padding:4px; border-radius:4px; border:1px solid silver;' type='text' value=''></input>");
var bookmarksElement = jQuery("<div class='bookmarks-plugin'></div>");
jQuery(w.output).append(searchElement).append(bookmarksElement);
var timer = 0;
var clicked = false;
var onClick = function (e) {
var reference = jQuery.jstree.reference(this);
if (reference) {
if (clicked) {
var node = reference.get_node(e.target);
if (node && node.a_attr && node.a_attr.href && (node.a_attr.href != "#")) {
clearTimeout(timer);
clicked = false;
displayMessage(node.text);
displayMessage(node.a_attr.href);
}
} else {
var delay = jQuery.isNumeric(config.options.txtBookmarksPluginDblClickDelay) ? parseInt(config.options.txtBookmarksPluginDblClickDelay) : 500;
var node = reference.get_node(e.target);
if (node && node.a_attr && node.a_attr.href && (node.a_attr.href != "#")) {
clicked = true;
timer = setTimeout(function () {
window.open(node.a_attr.href, node.a_attr.target);
clicked = false;
}, delay);
}
}
}
};
bookmarksElement.on("dblclick.jstree", function (e) {
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
onClick(e);
});
bookmarksElement.on("click.jstree", onClick);
bookmarksElement.jstree({core: {data: tree}, plugins: ["search", "separator"], search: {fuzzy: "normalized"}});
searchElement.keypress(function (e) {
var keycode = e.keyCode || e.which;
if (keycode == 13) {
e.preventDefault();
var searchValue = searchElement.val();
bookmarksElement.jstree(true).search(searchValue);
}
});
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
};
//}}}
//{{{
jQuery.jstree.plugins.separator = function (options, parent) {
this.redraw_node = function (node, deep, is_callback, force_render) {
var obj = this.get_node(node);
node = parent.redraw_node.apply(this, arguments);
if (obj && obj.original && obj.original.separator) {
jQuery(node).empty();
}
return node;
};
};
//}}}
/***
|Name|CalculatorPlugin|
|License|[[TW Notes License]]|
|Requires|[[jQueryCalculator]]|
!!!!!Code
***/
//{{{
if (config.options.txtCalculatorLayout === undefined) {
config.options.txtCalculatorLayout = "advanced";
}
if (config.options.chkCalculatorShowFormula === undefined) {
config.options.chkCalculatorShowFormula = true;
}
if (config.options.chkCalculatorVibrate === undefined) {
config.options.chkCalculatorVibrate = false;
}
if (config.options.txtCalculatorKeyHighlightDelay === undefined) {
config.options.txtCalculatorKeyHighlightDelay = "";
}
merge(config.optionsDesc, {
txtCalculatorLayout: "The default layout of the calculator",
chkCalculatorShowFormula: "Display the formula as it's entered within the calculator",
chkCalculatorVibrate : "Vibrate the device when pressing calculator keys",
txtCalculatorKeyHighlightDelay: "The time in milliseconds that a calculator key remains highlighted after being clicked (blank to disable)"
});
//}}}
//{{{
config.macros.calc = {
layouts: {
advanced: {
style: "width:355px; max-scale:1.5",
layout: [
"@U CECABS BBBOBDBH",
"DGRDPI_ MC_ _7_8_9_/opcp",
"SNCSTN_ MR_ _4_5_6_*_%md",
"ASACAT_ MS_ _1_2_3_-1Xfl",
"SQXYEX_ M+_ _0+-_._+_=RN",
"SRLGLN_ M-_ _A_B_C_D_E_F"
]
}
},
keyDefs: {},
// http://stackoverflow.com/questions/15454183/how-to-make-a-function-that-computes-the-factorial-for-numbers-with-decimals
gamma_g: 7,
gamma_C: [0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716 * Math.pow(10, -6), 1.5056327351493116 * Math.pow(10, -7)],
gamma: function (z) {
var g = config.macros.calc.gamma_g;
var C = config.macros.calc.gamma_C;
var gamma = config.macros.calc.gamma;
if (z < 0.5) {
return Math.PI / (Math.sin(Math.PI * z) * gamma(1 - z));
} else {
z -= 1;
var x = C[0];
for (var i = 1; i < g + 2; i++) {
x += C[i] / (z + i);
}
var t = z + g + 0.5;
return Math.sqrt(2 * Math.PI) * Math.pow(t, (z + 0.5)) * Math.exp(-t) * x;
}
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var cmc = config.macros.calc;
var btnName = null;
var layout = null;
var showFormula = config.options.chkCalculatorShowFormula;
var useAction = "copy";
var parsedParams = paramString.parseParams("anon", null, true, false, false);
for (var i = 0; i < parsedParams.length; i++) {
if (parsedParams[i]) {
if ((btnName === null) && (parsedParams[i].name == "anon")) {
btnName = parsedParams[i].value;
} else if ((layout === null) && (parsedParams[i].name == "layout")) {
layout = cmc.layouts[parsedParams[i].value];
} else if (parsedParams[i].name == "showFormula") {
showFormula = ("" + parsedParams[i].value).toLowerCase() != "false";
} else if (parsedParams[i].name == "use") {
useAction = ("" + parsedParams[i].value).toLowerCase();
}
}
}
btnName = (btnName === null) ? "Calculator" : btnName;
layout = layout ? layout : ((cmc.layouts && config.options.txtCalculatorLayout) ? cmc.layouts[config.options.txtCalculatorLayout] : null);
if (!layout && cmc.layouts) {
for (var l in cmc.layouts) {
layout = cmc.layouts[l];
if (layout) {
break;
}
}
}
if (layout) {
eval(store.getTiddlerText('jQueryCalculator##init'));
var panel = config.macros.slider.createSlider(place, "config.macros.calc:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), btnName);
var calculator = createTiddlyElement(panel, "div", null, null, null, (layout && layout.style) ? {style: layout.style} : null);
var squareRoot = jQuery.calculator._keyDefs["SR"];
jQuery.calculator.addKeyDef("SR", squareRoot[0], squareRoot[1], squareRoot[2], squareRoot[3], squareRoot[4], "q");
jQuery.calculator._keyCodes[13] = "_=";
var factorial = function (inst) {
inst.curValue = cmc.gamma(inst.curValue + 1);
}
jQuery.calculator.addKeyDef("fl", "n!", jQuery.calculator.unary, factorial, "fn factorial", "FACTORIAL", "!");
setStylesheet("\
.calculator-modulus {\
font-size: 70%;\
}", "CalculatorPlugin");
var modulus = function (inst) {
inst.curValue = inst.prevValue % inst.curValue;
}
jQuery.calculator.addKeyDef("md", "mod", jQuery.calculator.binary, modulus, "arith modulus", "MODULUS", "|");
jQuery.calculator._keyDefs["md"][7] = 20;
var openParenthesis = function (inst) {
var stacks = inst._stacks;
if ((stacks.sequence.length > 1) && (stacks.sequence[stacks.sequence.length - 2][1] != jQuery.calculator.binary) && (stacks.sequence[stacks.sequence.length - 2][0] != "(")) {
var calculation = stacks.calculation[stacks.calculation.length - 1];
calculation.number[calculation.number.length] = inst.curValue;
calculation.operator[calculation.operator.length] = jQuery.calculator._keyDefs["_*"];
}
stacks.calculation[inst._stacks.calculation.length] = {number: [], operator: []};
inst._pendingOp = jQuery.calculator._noOp
inst._newValue = true;
}
var closeParenthesis = function (inst) {
var stacks = inst._stacks;
var calculation = stacks.calculation[stacks.calculation.length - 1];
if ((stacks.sequence.length > 1) && (stacks.sequence[stacks.sequence.length - 2][1] == jQuery.calculator.binary)) {
calculation.operator.pop();
}
calculation.number[calculation.number.length] = inst.curValue;
while (calculation.operator.length) {
inst.curValue = calculation.number.pop();
inst.prevValue = calculation.number.pop();
inst._pendingOp = calculation.operator.pop()[2];
inst._pendingOp(inst);
inst.curValue = (inst.options.base == 10 ? inst.curValue : Math.floor(inst.curValue));
calculation.number[calculation.number.length] = inst.curValue;
}
if (stacks.calculation.length > 1) {
stacks.calculation.pop();
} else {
calculation.number.pop();
}
}
jQuery.calculator.addKeyDef("op", "(", jQuery.calculator.unary, openParenthesis, "arith parenthesis", "OPENPARENTHESIS", "(");
jQuery.calculator.addKeyDef("cp", ")", jQuery.calculator.unary, closeParenthesis, "arith parenthesis", "CLOSEPARENTHESIS", ")");
var use = function (inst) {
var input = "";
if (inst.options.use === "copy-result") {
input = jQuery.calculator._setDisplay(inst);
} else {
var sequence = inst._stacks.sequence;
for (var i = 0; i < sequence.length; i++) {
if (sequence[i][0] != "#use") {
if (input.length && ((sequence[i][1] != jQuery.calculator.digit) || (sequence[i - 1][1] != jQuery.calculator.digit))) {
input = input + " ";
}
input = input + (sequence[i][0].charAt(0) == "#" ? inst.options[sequence[i][0].substr(1) + "Text"] : (sequence[i][0] === "." ? inst.options.decimalChar : sequence[i][0]));
}
}
if ((sequence.length > 1) && (sequence[sequence.length - 2][0] == "=")) {
input = input + " " + jQuery.calculator._setDisplay(inst);
}
}
if ((inst.options.use === "tiddler") && inst.options.tiddler) {
var tiddler = inst.options.tiddler;
if (input) {
var tiddlerPosition = inst.options.tiddlerPosition;
var tiddlerText = store.getTiddlerText(tiddler.title);
if (tiddlerPosition > -1) {
tiddler.text = tiddlerText.substring(0, tiddlerPosition) + input + "\n" + tiddler.text.substring(tiddlerPosition);
} else {
tiddler.text = tiddlerText + "\n" + input;
}
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddler.title, null, true);
}
} else if (inst.options.use !== "tiddler") {
var clip = document.createElement("div");
clip.style = "position: absolute; left: -1000px; top: -1000px";
jQuery(clip).text(input);
document.body.appendChild(clip);
if (window.getSelection) {
window.getSelection().removeAllRanges();
var range = document.createRange();
range.selectNode(clip);
window.getSelection().addRange(range);
} else if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(clip);
range.select().createTextRange();
}
try {
if (!document.execCommand("copy")) {
displayMessage("Unable to copy");
} else {
displayMessage(((inst.options.use === "copy-result") ? "Result" : "Formula") + " copied");
}
} catch (e) {
displayMessage("Unable to copy: " + e);
}
document.body.removeChild(clip);
}
}
jQuery.calculator.addKeyDef("@U", "#use", jQuery.calculator.control, use, "use", "USE", "U");
if (cmc.keyDefs) {
for (var code in cmc.keyDefs) {
var keyDef = cmc.keyDefs[code];
jQuery.calculator.addKeyDef(code, keyDef[0], jQuery.calculator[keyDef[1]], keyDef[2], keyDef[3], keyDef[4], keyDef[5], keyDef[6]);
jQuery.calculator._keyDefs[code][7] = keyDef[7];
}
}
var options = {
keyHighlightDelay: config.options.txtCalculatorKeyHighlightDelay,
layout: layout ? layout.layout : null,
showFormula: showFormula,
use: useAction,
tiddler: tiddler,
tiddlerPosition: wikifier ? wikifier.matchStart : -1,
vibrate: (config.options.chkCalculatorVibrate === true) ? 50 : null
};
if (layout) {
var excludedOptions = ["layout", "showFormula", "style"];
for (var k in layout) {
if (excludedOptions.indexOf(k) === -1) {
options[k] = layout[k];
}
}
}
jQuery(calculator).calculator(options);
var calculatorWidth = jQuery(calculator).width();
calculatorWidth = calculatorWidth ? calculatorWidth : jQuery(panel).width();
var calculatorHeight = jQuery(panel).height();
calculatorHeight = calculatorHeight ? calculatorHeight : jQuery(panel).height();
var scaleCalculator = function() {
var maxScaleMatch = (layout && layout.style) ? layout.style.match(/max\-scale\s*:\s*(\d*\.?\d*)/i) : null;
if (maxScaleMatch && (maxScaleMatch.length == 2)) {
var scale = maxScaleMatch[1];
var width = jQuery(jQuery(place).parent()).width();
var height = jQuery(jQuery(place).parent()).height();
scale = (calculatorWidth * scale > width) ? width / calculatorWidth : scale;
scale = (scale < 1) ? 1 : scale;
jQuery(calculator).css({
"min-height": (calculatorHeight * scale) + "px",
"-moz-transform-origin": "0 0",
"-moz-transform": "scale(" + ((function () { var match = window.navigator.userAgent.match(/Firefox\/([0-9]+)\./); return match ? parseInt(match[1]) : 0; })() < 126 ? scale : 1) + ")",
"-o-transform": "scale(" + scale + ")",
"zoom": scale
});
}
};
scaleCalculator();
jQuery(window).resize(scaleCalculator);
}
}
}
//}}}
//{{{
config.macros.calc.layouts["conversion"] = {
style: config.macros.calc.layouts["advanced"].style,
layout: [
"@U CECABS BBBOBDBH",
"iMImfc_ MC_ _7_8_9_/opcp",
"mkfmic_ MR_ _4_5_6_*_%md",
"tkpkog_ MS_ _1_2_3_-1Xfl",
"glcmmf_ M+_ _0+-_._+_=RN",
"SQXYSR_ M-_ _A_B_C_D_E_F"
]
};
setStylesheet("\
.calculator-conversion {\
font-size: 60%;\
line-height: 80%;\
}\
.calculator-conversion-control {\
width: 28px !important;\
}\
.calculator-conversion-active {\
border: 2px inset #fff;\
}", "CalculatorPluginConversion");
config.macros.calc.conversion = {
styleMetric: function (inst) {
return config.macros.calc.conversion.style(inst, true);
},
styleImperial: function (inst) {
return config.macros.calc.conversion.style(inst, false);
},
style: function (inst, metricButton) {
inst.options.convertToMetric = inst.options.convertToMetric === undefined ? true : inst.options.convertToMetric;
if (metricButton) {
return inst.options.convertToMetric ? "conversion-control conversion-active" : "conversion-control";
} else {
return inst.options.convertToMetric ? "conversion-control" : "conversion-control conversion-active";
}
}
};
config.macros.calc.keyDefs["iM"] = ["→", "control", function (inst) { inst._stacks.sequence.pop(); inst.options.convertToMetric = true; jQuery.calculator._updateCalculator(inst); }, config.macros.calc.conversion.styleMetric, "CONVERTMETRIC"];
config.macros.calc.keyDefs["Im"] = ["←", "control", function (inst) { inst._stacks.sequence.pop(); inst.options.convertToMetric = false; jQuery.calculator._updateCalculator(inst); }, config.macros.calc.conversion.styleImperial, "CONVERTIMPERIAL"];
// Area
config.macros.calc.conversion.convertSquareFeetSquareMeters = function (value, toMetric) {
return Math.pow(config.macros.calc.conversion.convertFeetMeters(Math.sqrt(value), toMetric), 2);
}
config.macros.calc.keyDefs["mf"] = ["ft²<br>↔<br>m²", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "ft\u00B2\u2192m\u00B2" : "m\u00B2\u2192ft\u00B2"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["Mf"] = ["ft\u00B2\u2192m\u00B2", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertSquareFeetSquareMeters(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["mF"] = ["m\u00B2\u2192ft\u00B2", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertSquareFeetSquareMeters(inst.curValue, false); }, "conversion"];
// Length
config.macros.calc.conversion.convertFeetMeters = function (value, toMetric) {
if (toMetric) {
return value / 3.280839895;
} else {
return value * 3.280839895;
}
}
config.macros.calc.keyDefs["fm"] = ["ft<br>↔<br>m", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "ft\u2192m" : "m\u2192ft"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["fM"] = ["ft\u2192m", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertFeetMeters(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Fm"] = ["m\u2192ft", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertFeetMeters(inst.curValue, false); }, "conversion"];
config.macros.calc.conversion.convertInchesCentimeters = function (value, toMetric) {
if (toMetric) {
return value * 2.54;
} else {
return value / 2.54;
}
}
config.macros.calc.keyDefs["ic"] = ["in<br>↔<br>cm", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "in\u2192cm" : "cm\u2192in"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["iC"] = ["in\u2192cm", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertInchesCentimeters(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Ic"] = ["cm\u2192in", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertInchesCentimeters(inst.curValue, false); }, "conversion"];
config.macros.calc.conversion.convertMilesKilometers = function (value, toMetric) {
if (toMetric) {
return value * 1.609344;
} else {
return value / 1.609344;
}
}
config.macros.calc.keyDefs["mk"] = ["mile<br>↔<br>km", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "mile\u2192km" : "km\u2192mile"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["mK"] = ["mile\u2192km", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertMilesKilometers(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Mk"] = ["km\u2192mile", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertMilesKilometers(inst.curValue, false); }, "conversion"];
// Quantity
config.macros.calc.conversion.convertCupsMilliliters = function (value, toMetric) {
if (toMetric) {
return value * 250;
} else {
return value / 250;
}
}
config.macros.calc.keyDefs["cm"] = ["cup<br>↔<br>ml", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "cup\u2192ml" : "ml\u2192cup"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["cM"] = ["cup\u2192ml", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertCupsMilliliters(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Cm"] = ["ml\u2192cup", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertCupsMilliliters(inst.curValue, false); }, "conversion"];
config.macros.calc.conversion.convertImperialGallonsLiters = function (value, toMetric) {
if (toMetric) {
return value * 4.54609188;
} else {
return value / 4.54609188;
}
}
config.macros.calc.conversion.convertUSGallonsLiters = function (value, toMetric) {
if (toMetric) {
return value * 3.78541178;
} else {
return value / 3.78541178;
}
}
config.macros.calc.keyDefs["gl"] = ["gal<br>↔<br>l", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "us gal\u2192l" : "l\u2192us gal"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["gL"] = ["us gal\u2192l", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertUSGallonsLiters(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Gl"] = ["l\u2192us gal", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertUSGallonsLiters(inst.curValue, false); }, "conversion"];
// Temperature
config.macros.calc.conversion.convertFahrenheitCelsius = function (value, toMetric) {
if (toMetric) {
return (value - 32) * 5/9;
} else {
return value * 9/5 + 32;
}
}
config.macros.calc.keyDefs["fc"] = ["°F<br>↔<br>°C", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "\u00B0F\u2192\u00B0C" : "\u00B0C\u2192\u00B0F"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["fC"] = ["\u00B0F\u2192\u00B0C", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertFahrenheitCelsius(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Fc"] = ["\u00B0C\u2192\u00B0F", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertFahrenheitCelsius(inst.curValue, false); }, "conversion"];
// Weight
config.macros.calc.conversion.convertOuncesGrams = function (value, toMetric) {
if (toMetric) {
return value * 28.349523;
} else {
return value / 28.349523;
}
}
config.macros.calc.keyDefs["og"] = ["oz<br>↔<br>g", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "oz\u2192g" : "g\u2192oz"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["oG"] = ["oz\u2192g", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertOuncesGrams(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Og"] = ["g\u2192oz", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertOuncesGrams(inst.curValue, false); }, "conversion"];
config.macros.calc.conversion.convertPoundsKilograms = function (value, toMetric) {
if (toMetric) {
return value / 2.2;
} else {
return value * 2.2;
}
}
config.macros.calc.keyDefs["pk"] = ["lb<br>↔<br>kg", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "lb\u2192kg" : "kg\u2192lb"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["pK"] = ["lb\u2192kg", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertPoundsKilograms(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Pk"] = ["kg\u2192lb", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertPoundsKilograms(inst.curValue, false); }, "conversion"];
config.macros.calc.conversion.convertUKTonsKilograms = function (value, toMetric) {
if (toMetric) {
return value * 1016.0469088;
} else {
return value / 1016.0469088;
}
}
config.macros.calc.conversion.convertUSTonsKilograms = function (value, toMetric) {
if (toMetric) {
return value * 907.18474;
} else {
return value / 907.18474;
}
}
config.macros.calc.keyDefs["tk"] = ["ton<br>↔<br>kg", "unary", function (inst) { inst._stacks.sequence.pop(); jQuery.calculator._calculate(inst, inst.options.convertToMetric ? "us ton\u2192kg" : "kg\u2192us ton"); inst._disableFormulaUpdate = true; }, "conversion"];
config.macros.calc.keyDefs["tK"] = ["us ton\u2192kg", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertUSTonsKilograms(inst.curValue, true); }, "conversion"];
config.macros.calc.keyDefs["Tk"] = ["kg\u2192us ton", "unary", function (inst) { inst.curValue = config.macros.calc.conversion.convertUSTonsKilograms(inst.curValue, false); }, "conversion"];
//}}}
/***
|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]]
*[[ReminderMacros|http://remindermacros.tiddlyspot.com/]]
!!!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');
//}}}
// // override cookie settings for CalendarPlugin:
//{{{
config.options.txtCalFirstDay=6;
config.options.txtCalStartOfWeekend=5;
//}}}
// // override internal default settings for CalendarPlugin:
//{{{
config.macros.calendar.journalDateFmt="DDD MMM 0DD YYYY";
//}}}
/***
|Name|CalendarPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[CalendarPlugin]] [[iCalendarPlugin]] [[ScheduleOverride]]|
!!!!!Code
***/
//{{{
config.options.chkDisplayWeekNumbers = true;
//}}}
//{{{
merge(config.shadowTiddlers,{
SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "YYYY-0MM-0DD" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb" "Change TiddlyWiki advanced options">>'
});
//}}}
//{{{
config.macros.calendar.monthnames = ['01','02','03','04','05','06','07','08','09','10','11','12'];
//}}}
//{{{
config.macros.calendar.weekNumberCalculation = function (date, firstDay) {
if (!(!isNaN(parseInt(firstDay)) && isFinite(firstDay))) {
firstDay = (!isNaN(parseInt(config.options.txtCalFirstDay)) && isFinite(config.options.txtCalFirstDay)) ? ((parseInt(config.options.txtCalFirstDay) + 1) % 7) : 0;
}
var firstWeekDate = new Date(date.getTime());
firstWeekDate.setDate(firstWeekDate.getDate() - (firstWeekDate.getDay() - firstDay >= 0 ? firstWeekDate.getDay() - firstDay : 7 + firstWeekDate.getDay() - firstDay));
var firstYearWeekDate = new Date(firstWeekDate.getTime());
firstYearWeekDate.setMonth(0);
firstYearWeekDate.setDate(1);
firstYearWeekDate.setDate(firstYearWeekDate.getDate() + (4 - firstYearWeekDate.getDay() >= 0 ? 4 - firstYearWeekDate.getDay() : 11 - firstYearWeekDate.getDay()));
firstYearWeekDate.setDate(firstYearWeekDate.getDate() - (4 - firstDay >= 0 ? 4 - firstDay : 11 - firstDay));
var weekNumber = (((firstWeekDate.getTime() - firstYearWeekDate.getTime()) / 1000 / 60 / 60 / 24 / 7) | 0) + 1;
return ((weekNumber > 52) && (config.macros.calendar.weekNumberCalculation(new Date(firstWeekDate.getTime() + (1000 * 60 * 60 * 24 * 7)), firstDay) == 2)) ? 1 : weekNumber;
}
//}}}
//{{{
config.macros.calendar.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var today = new Date();
var year = today.getFullYear();
var container = {};
container.params = {};
params = [];
container.nofullcalendar = config.macros.calendar.nofullcalendar;
var parsedParams = paramString.parseParams("anon", null, true, false, false);
for (var i = 0; i < parsedParams.length; i++) {
if (parsedParams[i]) {
if (parsedParams[i].name == "anon") {
if (parsedParams[i].value == "nofullcalendar") {
container.nofullcalendar = true;
} else {
params[params.length] = parsedParams[i].value;
}
} else {
if (parsedParams[i].name == "tag") {
container.filter = parsedParams[i].value;
} else if (parsedParams[i].name == "tag-task") {
container.task = parsedParams[i].value;
} else if (parsedParams[i].name == "tag-meeting") {
container.meeting = parsedParams[i].value;
} else if (parsedParams[i].name == "name") {
container.name = parsedParams[i].value;
} else if ((parsedParams[i].name !== undefined) && (parsedParams[i].value !== undefined)) {
container.params[parsedParams[i].name] = parsedParams[i].value;
}
}
}
}
container.tiddlerName = tiddler.title;
container.name = container.name ? container.name : "calendar";
var calendarDateCache = config.macros.calendar.calendarDateCache;
if (!calendarDateCache) {
calendarDateCache = {};
config.macros.calendar.calendarDateCache = calendarDateCache;
}
var calendarDateTiddlerCache = calendarDateCache[container.tiddlerName];
if (!calendarDateTiddlerCache) {
calendarDateTiddlerCache = {};
calendarDateCache[container.tiddlerName] = calendarDateTiddlerCache;
}
// 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;
var date=1;
if (params[0] == 'thismonth') {
month=today.getMonth();
date = today.getDate();
} else if (params[0] == 'lastmonth') {
month = today.getMonth()-1; if (month==-1) { month=11; year--; }
} else if (params[0] == 'nextmonth') {
month = today.getMonth()+1; if (month>11) { month=0; year++; }
} else if (params[0]&&'+-'.indexOf(params[0].substr(0,1))!=-1) {
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;
date = (month == today.getMonth()) ? today.getDate() : date;
}
}
if (month!=-1) {
var fullCalendarInit = store.getTiddlerText('FullCalendar##init');
if (!container.nofullcalendar && fullCalendarInit) {
eval(fullCalendarInit);
calendarDateTiddlerCache[container.name] = calendarDateTiddlerCache[container.name] ? calendarDateTiddlerCache[container.name] : new Date(year, month, date, 0, 0);
config.macros.calendar.createFullCalendar(place, container);
} else {
var calendar = createTiddlyElement(place, 'table', null, 'calendar', null);
var tbody = createTiddlyElement(calendar, 'tbody');
tbody.tags = container;
var cal = tbody;
calendarDateTiddlerCache[container.name] = calendarDateTiddlerCache[container.name] ? calendarDateTiddlerCache[container.name] : new Date(year, month, 1, 0, 0);
cacheReminders(calendarDateTiddlerCache[container.name], 31, cal.tags);
createCalendarOneMonth(tbody, calendarDateTiddlerCache[container.name].getFullYear(), calendarDateTiddlerCache[container.name].getMonth());
window.reminderCacheForCalendar = null;
}
} else {
var calendar = createTiddlyElement(place, 'table', null, 'calendar', null);
var tbody = createTiddlyElement(calendar, 'tbody');
tbody.tags = container;
var cal = tbody;
calendarDateTiddlerCache[container.name] = calendarDateTiddlerCache[container.name] ? calendarDateTiddlerCache[container.name] : new Date(year, 0, 1, 0, 0);
cacheReminders(calendarDateTiddlerCache[container.name], 366, cal.tags);
createCalendarYear(tbody, calendarDateTiddlerCache[container.name].getFullYear());
window.reminderCacheForCalendar = null;
}
}
//}}}
//{{{
config.macros.calendar.createFullCalendar = function (place, tags) {
var lumdiff = function (rgb1, rgb2) {
var colorToIntArray = function (color) {
if ((color.indexOf('#') == 0) && ((color.length == 4) || (color.length == 7))) {
var r = (color.length == 4) ? color.substring(1, 2) : color.substring(1, 3);
r = parseInt((color.length == 4) ? "" + r + "" + r : r, 16);
var g = (color.length == 4) ? color.substring(2, 3) : color.substring(3, 5);
g = parseInt((color.length == 4) ? "" + g + "" + g : g, 16);
var b = (color.length == 4) ? color.substring(3, 4) : color.substring(5, 7);
b = parseInt((color.length == 4) ? "" + b + "" + b : b, 16);
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
return [r, g, b];
}
}
}
var RGB1 = colorToIntArray(rgb1);
var RGB2 = colorToIntArray(rgb2);
if (RGB1 && RGB2) {
// The returned value should be bigger than 5 for best readability.
// http://www.splitbrain.org/blog/2008-09/18-calculating_color_contrast_with_php
// http://stackoverflow.com/questions/1331591/given-a-background-color-black-or-white-text
var L1 = 0.2126 * Math.pow(RGB1[0] / 255, 2.2)
+ 0.7152 * Math.pow(RGB1[1] / 255, 2.2)
+ 0.0722 * Math.pow(RGB1[2] / 255, 2.2);
var L2 = 0.2126 * Math.pow(RGB2[0] / 255, 2.2)
+ 0.7152 * Math.pow(RGB2[1] / 255, 2.2)
+ 0.0722 * Math.pow(RGB2[2] / 255, 2.2);
if (L1 > L2) {
return (L1 + 0.05) / (L2 + 0.05);
} else {
return (L2 + 0.05) / (L1 + 0.05);
}
}
return 0;
}
var determineEvents = function (start, end, callback) {
var reminderEvents = new ScheduleReminderEvents();
reminderEvents.determineEvents(start, tags.filter, [0, 42], /[0-9]?[0-9]:[0-9]?[0-9]\s*-\s*[0-9]?[0-9]:[0-9]?[0-9]\s*:/);
var events = [];
for (var t = 0; t < reminderEvents.list.length; t++) {
var reminder = reminderEvents.list[t].reminder;
if ((reminder.params.year !== 0) || (reminder.params.month !== 0) || (reminder.params.day !== 0)) {
reminder["params"]["format"] = "TITLE";
var title = getReminderMessageForDisplay(reminder["diff"], reminder["params"], reminder["matchedDate"], reminder["tiddler"]);
var goToTiddlerMacro = null;
var goToTiddlerLengh = "<<goToTiddler".length;
var goToTiddlerMacroIndex = title.indexOf("<<goToTiddler");
goToTiddlerLengh = (goToTiddlerMacroIndex == -1) ? "<<newTiddler".length : goToTiddlerLengh;
goToTiddlerMacroIndex = (goToTiddlerMacroIndex == -1) ? title.indexOf("<<newTiddler") : goToTiddlerMacroIndex;
if (goToTiddlerMacroIndex > -1) {
goToTiddlerMacro = title.substring(goToTiddlerMacroIndex, title.indexOf(">>", goToTiddlerMacroIndex) + ">>".length);
title = title.substring(0, goToTiddlerMacroIndex) + title.substring(title.indexOf(">>", goToTiddlerMacroIndex) + ">>".length);
}
var backgroundColor = "#DDDDDD";
var borderColor = "#DDDDDD";
var textColor = "#000000";
var fontWeight = null;
var titleStyleMatches = (new RegExp("(@@)(.*)", "g")).exec(title);
if (titleStyleMatches && (titleStyleMatches.length > 1) && (titleStyleMatches[1] == "@@")) {
titleStyleMatches.source = title;
titleStyleMatches.nextMatch = titleStyleMatches.index + titleStyleMatches[1].length;
var styles = config.formatterHelpers.inlineCssHelper(titleStyleMatches);
for (var i = 0; i < styles.length; i++) {
if (styles[i].style == "backgroundColor") {
var color = styles[i].value;
backgroundColor = color;
borderColor = color;
textColor = (lumdiff(color, "#FFFFFF") < lumdiff(color, "#000000")) ? "#000000" : "#FFFFFF";
}
}
for (var i = 0; i < styles.length; i++) {
if (styles[i].style == "color") {
textColor = styles[i].value;
}
if (styles[i].style == "fontWeight") {
fontWeight = styles[i].value;
}
}
}
var txt = "";
if (title) {
var wikifier = new Wikifier(title, formatter, null, null);
var e = createTiddlyElement(document.body, "div");
var jqe = jQuery(e);
jqe.css("display", "none");
wikifier.subWikify(e);
txt = jqe.text();
removeNode(e);
} else {
txt = reminder.tiddler;
}
var date = new Date(reminder['matchedDate'].getFullYear(), reminder['matchedDate'].getMonth(), reminder['matchedDate'].getDate(), 0, 0, 0, 0);
var start = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
var end = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
var allDay = !reminderEvents.list[t].start || !reminderEvents.list[t].end;
if (!allDay) {
start.setHours(Number(reminderEvents.list[t].start.substring(0, reminderEvents.list[t].start.indexOf(":"))));
start.setMinutes(Number(reminderEvents.list[t].start.substring(reminderEvents.list[t].start.indexOf(":") + 1)));
end.setHours(Number(reminderEvents.list[t].end.substring(0, reminderEvents.list[t].end.indexOf(":"))));
end.setMinutes(Number(reminderEvents.list[t].end.substring(reminderEvents.list[t].end.indexOf(":") + 1)));
var timeMatches = (new RegExp("^(\\s*[0-9]?[0-9]:[0-9]?[0-9]\\s*-\\s*[0-9]?[0-9]:[0-9]?[0-9]\\s*:\\s*)")).exec(txt);
txt = (timeMatches && (timeMatches.length > 0)) ? txt.substring(timeMatches[0].length) : txt;
}
var editable = !reminder.tags || (reminder.tags.indexOf("readOnlyReminder") == -1);
editable = editable && (reminder["params"]["year"] !== undefined) && (reminder["params"]["month"] !== undefined) && ((reminder["params"]["day"] !== undefined) || (reminder["params"]["date"] !== undefined));
editable = editable && (reminder["params"]["rrule"] === undefined);
editable = editable && (reminder["params"]["recuryears"] === undefined);
editable = editable && (reminder["params"]["recurmonths"] === undefined);
editable = editable && (reminder["params"]["recurdays"] === undefined);
editable = editable && (reminder["macroIndex"] !== undefined) && (reminder["macroText"] !== undefined);
events[events.length] = {
title: txt,
start: start,
end: end,
allDay: allDay,
editable: editable,
backgroundColor: backgroundColor,
borderColor: borderColor,
textColor: textColor,
fontWeight: fontWeight,
initialStart: new Date(start.getTime()),
initialEnd: new Date(end.getTime()),
initialAllDay: allDay,
tiddler: reminder.tiddler,
tiddlerInstance: (reminder.tiddler != null) ? store.fetchTiddler(reminder.tiddler) : null,
reminderMacroIndex: reminder["macroIndex"],
reminderMacroText: reminder["macroText"],
goToTiddlerMacro: goToTiddlerMacro
};
}
}
callback(events);
};
var startYear = null;
var startMonth = null;
var startDate = null;
var viewName = null;
var cachedDate = config.macros.calendar.calendarDateCache[tags.tiddlerName][tags.name];
if (cachedDate) {
startYear = cachedDate.getFullYear();
startMonth = cachedDate.getMonth();
startDate = cachedDate.getDate();
viewName = config.macros.calendar.calendarDateCache[tags.tiddlerName][tags.name].viewName;
} else {
var currentDate = new Date();
startYear = currentDate.getFullYear();
startMonth = currentDate.getMonth();
startDate = currentDate.getDate();
}
viewName = viewName ? viewName : "month";
var determineTiddlerTitleAndParams = function (event) {
var tiddlerTitleAndParams = {title: event.tiddler, params: []};
if (event.goToTiddlerMacro) {
var macroFormatter = null;
for (var i = 0; i < config.formatters.length; i++) {
if (config.formatters[i].name == "macro") {
macroFormatter = config.formatters[i];
break;
}
}
if (macroFormatter) {
macroFormatter.lookaheadRegExp.lastIndex = 0;
var matches = macroFormatter.lookaheadRegExp.exec(event.goToTiddlerMacro);
if (matches.length > 2) {
var params = matches[2].parseParams("anon", null, true, false, false);
if (params) {
tiddlerTitleAndParams.params = params;
var title = params[1] && params[1].name == "anon" ? params[1].value : "";
title = getParam(params, "title", title);
tiddlerTitleAndParams.title = (title && (title != "")) ? title : tiddlerTitleAndParams.title;
}
}
}
}
return tiddlerTitleAndParams;
};
var eventModified = function (event, revertFunc, jsEvent, ui, view) {
var reminderUpdated = false;
if ((event.allDay === event.initialAllDay) && ((event.tiddler != null) || (event.tiddlerInstance != null)) && (event.reminderMacroIndex != null) && (event.reminderMacroText != null)) {
var tiddler = event.tiddlerInstance || store.getTiddler(event.tiddler);
var tiddlerText = tiddler ? tiddler.text : null;
if ((tiddlerText != null) && tiddlerText.substring(event.reminderMacroIndex, event.reminderMacroIndex + event.reminderMacroText.length) == event.reminderMacroText) {
var formattedOldDate = event.initialStart.formatString(config.macros.calendar.journalDateFmt);
var formattedNewDate = event.start.formatString(config.macros.calendar.journalDateFmt);
var oldTiddlerTitle = tiddler.title;
var newTiddlerTitle = oldTiddlerTitle.replace(new RegExp(formattedOldDate, "gi"), formattedNewDate);
var timezoneHours = (event.start.getTimezoneOffset() / 60) | 0;
var timezoneMinutes = Math.abs(event.start.getTimezoneOffset() - (timezoneHours * 60));
var timezone = (Math.abs(timezoneHours) < 10 ? "0" : "") + Math.abs(timezoneHours) + (timezoneMinutes < 10 ? "0" : "") + timezoneMinutes;
var newReminder = event.reminderMacroText.replace(/year:[0-9]*/, "year:" + event.start.getFullYear()).replace(/month:[0-9]*/, "month:" + (event.start.getMonth() + 1)).replace(/(day|date):[0-9]*/, "$1:" + event.start.getDate()).replace(/tag:.timezone:UTC.[0-9]*./, "tag:\"timezone:UTC" + (event.start.getTimezoneOffset() <= 0 ? "+" : "-") + timezone + "\"");
if (!event.allDay) {
var time = event.initialStart;
var oldStartTime = (time.getHours() < 10 ? '0' : '') + time.getHours() + ':' + (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
time = event.initialAllDay ? event.initialStart : event.initialEnd;
var oldEndTime = (time.getHours() < 10 ? '0' : '') + time.getHours() + ':' + (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
time = event.start;
var newStartTime = (time.getHours() < 10 ? '0' : '') + time.getHours() + ':' + (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
var newEndDate = event.allDay ? event.start : event.end;
var newEndTime = "23:59";
if ((time.getFullYear() == newEndDate.getFullYear()) && (time.getMonth() == newEndDate.getMonth()) && (time.getDate() == newEndDate.getDate())) {
time = newEndDate;
newEndTime = (time.getHours() < 10 ? '0' : '') + time.getHours() + ':' + (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
}
var startTimePosition = newReminder.indexOf(oldStartTime);
var endTimePosition = newReminder.indexOf(oldEndTime, (startTimePosition == -1) ? 0 : startTimePosition + oldStartTime.length);
if (startTimePosition > -1) {
newReminder = newReminder.substring(0, startTimePosition) + newStartTime + newReminder.substring(startTimePosition + oldStartTime.length);
}
if (endTimePosition > -1) {
newReminder = newReminder.substring(0, endTimePosition) + newEndTime + newReminder.substring(endTimePosition + oldEndTime.length);
}
}
tiddlerText = tiddlerText.substring(0, event.reminderMacroIndex) + newReminder + tiddlerText.substring(event.reminderMacroIndex + event.reminderMacroText.length);
var newTags = !tiddler.tags ? [] : tiddler.tags.slice();
if (newTags.indexOf(formattedOldDate) > -1) {
newTags[newTags.indexOf(formattedOldDate)] = formattedNewDate;
}
store.saveTiddler(oldTiddlerTitle, newTiddlerTitle, tiddlerText, config.options.txtUserName, new Date(), newTags, tiddler.fields);
store.setDirty(true);
story.refreshTiddler(newTiddlerTitle, null, true);
//story.refreshTiddler(tags.tiddlerName, null, true);
if (config.options.chkUpdateTaggedTiddlersOnSaveChanges && config.commands.saveTiddler.updateTaggedTiddlers) {
config.commands.saveTiddler.updateTaggedTiddlers.update(oldTiddlerTitle, newTiddlerTitle);
}
fullCalendarConfig.calendarInstance.fullCalendar('refetchEvents');
reminderUpdated = true;
}
}
if (!reminderUpdated) {
revertFunc();
}
};
var fullCalendarId = "fc_" + (new Date()).getTime() + "_" + Math.round((Math.random() * 10000));
createTiddlyElement(place, 'div', fullCalendarId, 'fullcalendar');
var fullCalendarConfig = {
year: startYear,
month: startMonth,
date: startDate,
defaultView: viewName,
header: {
left: 'prevYear,prev,next,nextYear today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
allDayText: 'Tasks',
axisFormat: 'HH:mm',
timeFormat: 'HH:mm{ - HH:mm}',
slotMinutes: 15,
weekNumbers: config.options.chkDisplayWeekNumbers,
weekNumberCalculation: function (date) {
return config.macros.calendar.weekNumberCalculation(date, this.firstDay);
},
firstDay: (!isNaN(parseInt(config.options.txtCalFirstDay)) && isFinite(config.options.txtCalFirstDay)) ? ((parseInt(config.options.txtCalFirstDay) + 1) % 7) : 0,
selectable: true,
selectHelper: true,
select: function (start, end, allDay, jsEvent) {
Popup.remove();
var title = prompt(fullCalendarConfig["newEventPromptTitle"] ? fullCalendarConfig["newEventPromptTitle"] : 'Title');
fullCalendarConfig.calendarInstance.fullCalendar('unselect');
if (title) {
var date = start;
var timezoneHours = (date.getTimezoneOffset() / 60) | 0;
var timezoneMinutes = Math.abs(date.getTimezoneOffset() - (timezoneHours * 60));
var timezone = (Math.abs(timezoneHours) < 10 ? "0" : "") + Math.abs(timezoneHours) + (timezoneMinutes < 10 ? "0" : "") + timezoneMinutes;
var formattedDate = date.formatString(config.macros.calendar.journalDateFmt);
title = formattedDate + " : " + title;
var newTiddlerTags = [];
newTiddlerTags[newTiddlerTags.length] = formattedDate;
var eventTags = allDay ? tags.task : tags.meeting;
if (!eventTags) {
newTiddlerTags[newTiddlerTags.length] = allDay ? "task" : "meeting";
} else {
var tagList = eventTags.readBracketedList();
for (var i = 0; i < tagList.length; i++) {
newTiddlerTags[newTiddlerTags.length] = tagList[i];
}
}
var time = "";
if (!allDay) {
time = (start.getHours() < 10 ? '0' : '') + start.getHours() + ':' + (start.getMinutes() < 10 ? '0' : '') + start.getMinutes();
time = time + " - ";
if ((start.getFullYear() == end.getFullYear()) && (start.getMonth() == end.getMonth()) && (start.getDate() == end.getDate())) {
time = time + (end.getHours() < 10 ? '0' : '') + end.getHours() + ':' + (end.getMinutes() < 10 ? '0' : '') + end.getMinutes();
} else {
time = time + "23:59";
}
time = time + " : ";
}
var remindersFormat = fullCalendarConfig["remindersFormat"];
var goToTaskLabel = ((fullCalendarConfig["goToTaskLabel"] !== undefined) && (fullCalendarConfig["goToTaskLabel"] !== null)) ? fullCalendarConfig["goToTaskLabel"] : ">";
var goToMeetingLabel = ((fullCalendarConfig["goToMeetingLabel"] !== undefined) && (fullCalendarConfig["goToMeetingLabel"] !== null)) ? fullCalendarConfig["goToMeetingLabel"] : ">";
var taskPrefix = ((fullCalendarConfig["taskPrefix"] !== undefined) && (fullCalendarConfig["taskPrefix"] !== null)) ? fullCalendarConfig["taskPrefix"] : "@@background-color:#ffff00;[x(done)] ''Done ''@@";
var meetingPrefix = ((fullCalendarConfig["meetingPrefix"] !== undefined) && (fullCalendarConfig["meetingPrefix"] !== null)) ? fullCalendarConfig["meetingPrefix"] : "[x(cancelled)] Cancelled [x(doNotPublish)] Do not publish";
var rem = '<' + '<reminder year:%2 month:%1 day:%0 tag:"timezone:UTC%3" function:config.macros.ics.formatTitle() messagefunction:config.macros.reminder.replaceTiddlerName() messagefunction:config.macros.reminder.replaceReminderDateTime() messagefunction:config.macros.reminder.replaceTiddlerFormatting() ' + (remindersFormat ? 'format:"' + remindersFormat + '" ' : '') + 'title:"@@background-color:' + (allDay ? config.macros.date.remindersbg : config.macros.date.eventbg) + ';color:' + (allDay ? config.macros.date.reminderstxt : config.macros.date.eventtxt) + ';priority:' + (allDay ? config.macros.date.reminderspriority : config.macros.date.eventpriority) + ';' + time + '\\"\\"\\"TIDDLERNAME\\"\\"\\"@@ \\<\\<goToTiddler label:' + (allDay ? goToTaskLabel : goToMeetingLabel) + ' title:{{\'REMINDERDATE : TIDDLERNAMEESC\'}} ' + (allDay ? (taskPrefix ? "text:{{'" + taskPrefix.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n") + "\\n\\n'}} " : "") : (meetingPrefix ? "text:{{'" + meetingPrefix.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n") + "\\n\\n'}} " : "")) + 'tag:' + (allDay ? 'task' : 'meeting') + ' tag:[[TIDDLERFULLNAME]] focus:text\\>\\>">>';
rem = rem.format([date.getDate(),date.getMonth()+1,date.getFullYear(),(date.getTimezoneOffset() <= 0 ? "+" : "-") + timezone]);
var text = (allDay ? (taskPrefix ? taskPrefix + "\n\n" : "") : (meetingPrefix ? meetingPrefix + "\n\n" : "")) + rem + "\n\n";
if (store.getTiddler(title) || readOnly) {
story.displayTiddler(resolveTarget(jsEvent), title, null, true, null, null, false);
} else {
story.displayTiddler(resolveTarget(jsEvent), title, DEFAULT_EDIT_TEMPLATE, false, null, null, false);
if (!store.isShadowTiddler(title)) {
story.addCustomFields(story.getTiddler(title), String.encodeHashMap(config.defaultCustomFields));
}
if (text !== undefined) {
story.getTiddlerField(title, "text").value = text.format([title]);
}
for (var t = 0; t < newTiddlerTags.length; t++) {
story.setTiddlerTag(title, newTiddlerTags[t], 1);
}
story.focusTiddler(title, "text");
}
}
},
editable: false,
events: determineEvents,
viewRender: function (view, element) {
var cacheName = tags.tiddlerName;
var calendarName = tags.name;
var cachedDate = new Date();
cachedDate = (cachedDate.getTime() >= view.start.getTime()) && (cachedDate.getTime() <= view.end.getTime()) ? cachedDate : view.start;
config.macros.calendar.calendarDateCache[cacheName][calendarName] = cachedDate;
config.macros.calendar.calendarDateCache[cacheName][calendarName].viewName = view.name;
},
eventClick: function (calEvent, jsEvent, view) {
if (calEvent.popupTimerId) {
window.clearTimeout(calEvent.popupTimerId);
calEvent.popupTimerId = null;
}
Popup.remove();
var tiddlerTitleAndParams = determineTiddlerTitleAndParams(calEvent);
var title = tiddlerTitleAndParams.title;
if (title) {
var tiddler = store.getTiddler(title);
if (tiddler || readOnly) {
story.displayTiddler(resolveTarget(jsEvent), title, null, true, null, null, false);
} else {
story.displayTiddler(resolveTarget(jsEvent), title, DEFAULT_EDIT_TEMPLATE, false, null, null, false);
if (!store.isShadowTiddler(title)) {
story.addCustomFields(story.getTiddler(title), String.encodeHashMap(config.defaultCustomFields));
}
var params = tiddlerTitleAndParams.params;
var text = getParam(params, "text");
if (text !== undefined) {
story.getTiddlerField(title, "text").value = text.format([title]);
}
for (var t = 1; t < params.length; t++) {
if ((params[t].name == "anon" && t != 1) || (params[t].name == "tag")) {
story.setTiddlerTag(title, params[t].value, 1);
}
}
story.focusTiddler(title, "text");
}
}
return false;
},
eventDragStart: function (event, jsEvent, ui, view) {
event.preventPopup = true;
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
Popup.remove();
},
eventDragStop: function (event, jsEvent, ui, view) {
event.preventPopup = false;
},
eventDrop: function (event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view) {
event.preventPopup = false;
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
Popup.remove();
eventModified(event, revertFunc, jsEvent, ui, view);
},
eventResizeStart: function (event, jsEvent, ui, view) {
event.preventPopup = true;
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
Popup.remove();
},
eventResizeStop: function (event, jsEvent, ui, view) {
event.preventPopup = false;
},
eventResize: function (event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
event.preventPopup = false;
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
Popup.remove();
eventModified(event, revertFunc, jsEvent, ui, view);
},
eventMouseover: function (event, jsEvent, view) {
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
if (!event.preventPopup) {
var title = determineTiddlerTitleAndParams(event).title;
event.popupTimerId = config.macros.tiddlerPreviewPopup.showDelayed(jsEvent, title, store.tiddlerExists(title) ? title : null, 1000);
}
},
eventMouseout: function (event, jsEvent, view) {
if (event.popupTimerId) {
window.clearTimeout(event.popupTimerId);
event.popupTimerId = null;
}
},
};
var isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
if (tags.params) {
for (var optionName in tags.params) {
if (isNumber(tags.params[optionName])) {
fullCalendarConfig[optionName] = parseFloat(tags.params[optionName]);
} else {
var value = tags.params[optionName];
try {
eval("value = " + tags.params[optionName]);
if (typeof value == "boolean") {
fullCalendarConfig[optionName] = value;
} else if (typeof value == "function") {
fullCalendarConfig[optionName] = value;
} else if (value instanceof Array) {
fullCalendarConfig[optionName] = value;
} else if ((value != null) && (typeof value === 'object')) {
fullCalendarConfig[optionName] = value;
} else {
value = tags.params[optionName];
}
} catch (e) {}
fullCalendarConfig[optionName] = value;
}
}
}
fullCalendarConfig.calendarInstance = jQuery('#' + fullCalendarId).fullCalendar(fullCalendarConfig);
fullCalendarConfig.fullCalendar = fullCalendarConfig.calendarInstance.data("fullCalendar");
fullCalendarConfig.fullCalendarId = fullCalendarId;
fullCalendarConfig.calendarInstance.find("table.fc-header").find("span.fc-button").on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
var isCalendarDateEditing = false;
var applyEditedDate = function (apply) {
if (isCalendarDateEditing) {
isCalendarDateEditing = false;
var fields = jQuery(this).find("span.fc-header-title").find("span");
if (apply) {
var date = fullCalendarConfig.fullCalendar.getDate();
var year = fields.find("input").eq(0).val();
var month = parseInt(fields.find("select").val());
var day = fields.find("input").eq(1).val();
year = jQuery.isNumeric(year) ? parseInt(year) : date.getFullYear();
if (jQuery.isNumeric(day)) {
day = parseInt(day);
} else if (fields.find("input").eq(1).length || ((year === date.getFullYear()) && (month === date.getMonth()))) {
day = date.getDate();
} else {
day = 1;
}
fullCalendarConfig.fullCalendar.gotoDate(year, month, day);
}
fields.remove();
var view = fullCalendarConfig.fullCalendar.getView();
jQuery("<h2></h2>").appendTo(jQuery(this).find("span.fc-header-title")).html(fullCalendarConfig.fullCalendar.formatDates(
(view.name === "month") ? fullCalendarConfig.fullCalendar.getDate() : view.visStart,
jQuery.fullCalendar.addDays(jQuery.fullCalendar.cloneDate(view.visEnd), -1),
view.opt("titleFormat")));
}
};
fullCalendarConfig.calendarInstance.find("table.fc-header").find("span.fc-header-title").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
}).on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
if (!isCalendarDateEditing) {
isCalendarDateEditing = true;
jQuery(this).find("h2").remove();
var date = fullCalendarConfig.fullCalendar.getDate();
var fields = (fullCalendarConfig.fullCalendar.getView().name === "month")
? jQuery("<span><select></select> <input style='margin:0em auto 1em auto; padding:4px; border-radius:4px; border:1px solid silver;' type='text' size='4' maxlength='4'></span>").appendTo(this)
: jQuery("<span><input style='margin:0em auto 1em auto; padding:4px; border-radius:4px; border:1px solid silver;' type='text' size='4' maxlength='4'> - <select></select> - <input style='margin:0em auto 1em auto; padding:4px; border-radius:4px; border:1px solid silver;' type='text' size='2' maxlength='2'></span>").appendTo(this);
var monthList = fields.find("select");
var monthNames = fullCalendarConfig.fullCalendar.options.monthNames;
for (var i = 0; i < monthNames.length; i++) {
jQuery("<option value='" + i + "'></option>").appendTo(monthList).text(monthNames[i]);
}
monthList.val(date.getMonth());
fields.find("input").eq(0).val(date.getFullYear());
fields.find("input").eq(1).val(date.getDate());
fields.find("select, input").on("keyup", function (e) {
if (e.key === "Enter") {
e.preventDefault();
e.stopPropagation();
applyEditedDate.call(jQuery(this).closest("table.fc-header"), true);
} else if (e.key === "Escape") {
e.preventDefault();
e.stopPropagation();
applyEditedDate.call(jQuery(this).closest("table.fc-header"), false);
}
});
monthList.trigger("focus");
}
});
fullCalendarConfig.calendarInstance.find("table.fc-header").on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
}).on("click", function (e) {
applyEditedDate.call(this, true);
});
};
//}}}
//{{{
function cacheReminders(date, leadtime, tags)
{
if (window.findTiddlersWithReminders == null) return;
window.reminderCacheForCalendar = {};
var leadtimeHash = [];
leadtimeHash [0] = 0;
leadtimeHash [1] = leadtime;
var t = findTiddlersWithReminders(date, leadtimeHash, tags ? tags.filter : null, 1);
var re = null;
var fm = null;
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.
var reminder = window.reminderCacheForCalendar[t[i]['matchedDate']];
var title = null;
var cacheNewReminder = !reminder || (reminder.indexOf("@@") == -1);
if (!cacheNewReminder) {
re = new RegExp('priority\\s*:\\s*(\\d*)', 'mg');
var newPriority = null;
t[i]["params"]["format"] = "TITLE";
title = getReminderMessageForDisplay(t[i]["diff"], t[i]["params"], t[i]["matchedDate"], t[i]["tiddler"]);
fm = re.exec(title);
if (fm && (fm[1] != null)) {
var pa=fm[1].readMacroParams();
if (!isNaN(pa[0])) {
newPriority = parseInt(pa[0]);
}
}
if (newPriority != null) {
re = new RegExp('priority\\s*:\\s*(\\d*)', 'mg');
var oldPriority = null;
fm = re.exec(reminder);
if (fm && (fm[1] != null)) {
var pa=fm[1].readMacroParams();
if (!isNaN(pa[0])) {
oldPriority = parseInt(pa[0]);
}
}
if (oldPriority == null) {
cacheNewReminder = true;
} else {
cacheNewReminder = newPriority > oldPriority;
}
}
}
if (cacheNewReminder) {
if (title == null) {
t[i]["params"]["format"] = "TITLE";
title = getReminderMessageForDisplay(t[i]["diff"], t[i]["params"], t[i]["matchedDate"], t[i]["tiddler"]);
}
window.reminderCacheForCalendar[t[i]['matchedDate']] = 'reminder:' + title;
}
}
}
//}}}
//{{{
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 monthNavigationHandler = function(newyear, newmon) {
removeChildren(cal);
if (config.macros.calendar.calendarDateCache && config.macros.calendar.calendarDateCache[cal.tags.tiddlerName]) {
config.macros.calendar.calendarDateCache[cal.tags.tiddlerName][cal.tags.name] = new Date(newyear, newmon , 1, 0, 0);
}
cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31, cal.tags);
createCalendarOneMonth(cal, newyear, newmon);
return false;
};
var backMonHandler = function() {
var newyear = year;
var newmon = mon-1;
if(newmon == -1) { newmon = 11; newyear = parseInt(newyear)-1;}
return monthNavigationHandler(newyear, newmon); // consume click
};
createTiddlyButton(back, '<', 'Previous month', backMonHandler);
month = createTiddlyElement(row, 'td', null, 'calendarMonthname')
month.setAttribute('colSpan', config.options.chkDisplayWeekNumbers?6:5);//wn**
var yearMonth = "" + year +"-" + (mon < 9 ? "0" : "") + (mon + 1);
var monthTitleLink = createTiddlyLink(month, "" + year, false);
monthTitleLink.appendChild(document.createTextNode("" + year));
monthTitleLink.title = yearMonth;
monthTitleLink.style.fontStyle = "normal";
monthTitleLink.onclick = function (e) {
var p = Popup.create(this); if (!p) return false;
if (!readOnly || store.tiddlerExists(name)) {
createTiddlyLink(createTiddlyElement(p, 'li'), yearMonth, true);
} else {
createTiddlyText(createTiddlyElement(p, 'li'), yearMonth);
}
for (var i = year - 2; i < year + 3; i++) {
var link = createTiddlyLink(createTiddlyElement(p, 'li'), "" + i, true);
link.style.fontStyle = "normal";
link.selectedYear = i;
link.onclick = function (e) {
monthNavigationHandler(this.selectedYear, mon);
Popup.remove();
e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); return false;
};
}
Popup.show(); e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); return false;
};
month.appendChild(document.createTextNode("-"));
monthTitleLink = createTiddlyLink(month, config.macros.calendar.monthnames[mon], false);
monthTitleLink.appendChild(document.createTextNode((mon < 9 ? "0" : "") + (mon + 1)));
monthTitleLink.title = yearMonth;
monthTitleLink.style.fontStyle = "normal";
monthTitleLink.onclick = function (e) {
var p = Popup.create(this); if (!p) return false;
if (!readOnly || store.tiddlerExists(name)) {
createTiddlyLink(createTiddlyElement(p, 'li'), yearMonth, true);
} else {
createTiddlyText(createTiddlyElement(p, 'li'), yearMonth);
}
for (var i = 0; i < config.macros.calendar.monthnames.length; i++) {
var link = createTiddlyLink(createTiddlyElement(p, 'li'), (i < 9 ? "0" : "") + (i + 1), true);
link.style.fontStyle = "normal";
link.selectedMonth = i;
link.onclick = function (e) {
monthNavigationHandler(year, this.selectedMonth);
Popup.remove();
e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); return false;
};
}
Popup.show(); e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); return false;
};
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;}
return monthNavigationHandler(newyear, newmon); // 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 createCalendarDays(row, col, first, max, year, mon) {
var i;
if (config.options.chkDisplayWeekNumbers){
if (first<=max) {
var ww = new Date(year,mon,first);
var weekNumber = config.macros.calendar.weekNumberCalculation(ww);
var td=createTiddlyElement(row, 'td');//wn**
var txtWeekNumberLinkFormat = config.options.txtWeekNumberLinkFormat;
txtWeekNumberLinkFormat = txtWeekNumberLinkFormat.replace(/0WW/g,String.zeroPad(weekNumber,2));
txtWeekNumberLinkFormat = txtWeekNumberLinkFormat.replace(/WW/g,weekNumber);
var link=createTiddlyLink(td,ww.formatString(txtWeekNumberLinkFormat),false);
var txtWeekNumberDisplayFormat = row.cal.tags.params["weekNumberTitle"] ? row.cal.tags.params["weekNumberTitle"] + "0WW" : config.options.txtWeekNumberDisplayFormat;
txtWeekNumberDisplayFormat = txtWeekNumberDisplayFormat.replace(/0WW/g,String.zeroPad(weekNumber,2));
txtWeekNumberDisplayFormat = txtWeekNumberDisplayFormat.replace(/WW/g,weekNumber);
link.appendChild(document.createTextNode(
ww.formatString(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, row.cal ? row.cal.tags : null);
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++;
}
}
//}}}
//{{{
(function () {
var insertedCode = [
' if (config.macros.calendar.calendarDateCache && config.macros.calendar.calendarDateCache[calendar.tags.tiddlerName]) config.macros.calendar.calendarDateCache[calendar.tags.tiddlerName][calendar.tags.name] = new Date(parseInt(year)-1, 0, 1, 0, 0); cacheReminders(new Date(parseInt(year)-1, 0, 1, 0, 0), 366, calendar.tags); ',
' if (config.macros.calendar.calendarDateCache && config.macros.calendar.calendarDateCache[calendar.tags.tiddlerName]) config.macros.calendar.calendarDateCache[calendar.tags.tiddlerName][calendar.tags.name] = new Date(parseInt(year)+1, 0, 1, 0, 0); cacheReminders(new Date(parseInt(year)+1, 0, 1, 0, 0), 366, calendar.tags); '
];
var code = eval("createCalendarYear").toString();
var newCode = null;
var startPosition = 0;
for (var i = 0; i < 2; i++) {
startPosition = code.indexOf("removeChildren", startPosition);
if (startPosition > -1) {
startPosition = code.indexOf(");", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 2) + insertedCode[i] + code.substring(startPosition + 2);
code = newCode;
}
}
}
if (newCode != null) {
eval("createCalendarYear = function createCalendarYear" + code.substring(newCode.indexOf("(")));
}
var code = eval("createCalendarDayRows").toString();
var newCode = null;
var startPosition = 0;
for (var i = 0; i < 1; i++) {
startPosition = code.indexOf("createCalendarDays", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' row.cal = cal; ' + code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval("createCalendarDayRows = function createCalendarDayRows" + code.substring(newCode.indexOf("(")));
}
var code = eval("createCalendarDayRowsSingle").toString();
var newCode = null;
var startPosition = 0;
for (var i = 0; i < 1; i++) {
startPosition = code.indexOf("createCalendarDays", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' row.cal = cal; ' + code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval("createCalendarDayRowsSingle = function createCalendarDayRowsSingle" + code.substring(newCode.indexOf("(")));
}
})();
//}}}
/***
|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|CheckboxPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[CheckboxPlugin]]|
!!!!!Code
***/
//{{{
config.macros.checkbox.onClickCheckbox = function(event) {
var wasDirty = store.isDirty();
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.init && 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);
t.modifier = config.options.txtUserName;
t.modified = new Date();
if (!story.isDirty(t.title)) story.refreshTiddler(t.title,null,true);
store.setDirty(true);
}
}
if (this.field && this.tiddler) {
if (store.tiddlerExists(this.tiddler)) {
// set the field value in the target tiddler
store.setValue(this.tiddler,this.field,this.checked?"true":"false");
var t = store.getTiddler(this.tiddler);
if (t) {
t.modifier = config.options.txtUserName;
t.modified = new Date();
}
// DEBUG: displayMessage(this.field+"@"+this.tiddler+" is "+this.checked);
}
}
if (this.tag && this.tiddler) {
if (store.tiddlerExists(this.tiddler)) {
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); t.modifier = config.options.txtUserName; t.modified = new Date(); store.setDirty(true); }
if (!this.checked && tagged) { t.tags.splice(t.tags.indexOf(this.tag),1); t.modifier = config.options.txtUserName; t.modified = new Date(); 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
if (store.getTiddler(this.tiddler)) 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
if (this.srctid && !wasDirty && store.isDirty()) {
story.refreshTiddler(store.getTiddler(this.srctid).title,null,true);
}
return true;
};
//}}}
//{{{
(function () {
var formatter = null;
for (var i = 0; i < config.formatters.length; i++) {
if (config.formatters[i].name == "checkbox") {
formatter = config.formatters[i];
break;
}
}
if (formatter) {
var code = formatter.handler.toString();
var newCode = null;
var startPosition = code.indexOf("tid.getAttribute");
if (startPosition > -1) {
startPosition = code.indexOf(';', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1) + " tid = (w.tiddler && w.tiddler.title) ? w.tiddler.title : tid;" + code.substring(startPosition + 1);
code = newCode;
}
}
if (newCode != null) {
eval("formatter.handler = function handler" + newCode.substring(newCode.indexOf("(")));
}
}
})();
//}}}
//{{{
(function () {
var code = eval("config.macros.checkbox.create").toString();
code = code.replace('if (pos==-1) var pos=target.indexOf(":");', '');
eval("config.macros.checkbox.create = function create" + code.substring(code.indexOf("(")));
})();
//}}}
<<EncryptionDecryptAll "decrypt all" "Decrypt all Tiddlers.">> <<saveChanges>>
-------
<html>title (regular expression): <input type="text" id="tiddler_cleanup_title" /></html>
<html>from: <input type="text" id="tiddler_cleanup_date_from" /> until: <input type="text" id="tiddler_cleanup_date_until" /></html>
<html>tags: <input type="text" id="tiddler_cleanup_tags" /></html>
<html>characters - more than: <input type="text" id="tiddler_cleanup_characters_from" /> less than: <input type="text" id="tiddler_cleanup_characters_until" /></html>
<script>
var date = new Date();
$("tiddler_cleanup_title").value = (window.cached_tiddler_cleanup_title !== undefined) ? window.cached_tiddler_cleanup_title : "^(?!" + date.getFullYear() + "-)";
var formatDateString = function(date) {
return date.getFullYear() + "-"
+ (date.getMonth() < 9 ? "0" : "") + (date.getMonth() + 1) + "-"
+ (date.getDate() < 10 ? "0" : "") + date.getDate() + " "
+ (date.getHours() < 10 ? "0" : "") + date.getHours() + ":"
+ (date.getMinutes() < 10 ? "0" : "") + date.getMinutes();
}
if ($("tiddler_cleanup_date_until").value == "") {
$("tiddler_cleanup_date_until").value = (window.cached_tiddler_cleanup_date_until !== undefined) ? window.cached_tiddler_cleanup_date_until : formatDateString(date);
}
date.setFullYear(date.getFullYear() - 1);
if ($("tiddler_cleanup_date_from").value == "") {
$("tiddler_cleanup_date_from").value = (window.cached_tiddler_cleanup_date_from !== undefined) ? window.cached_tiddler_cleanup_date_from : formatDateString(date);
}
$("tiddler_cleanup_tags").value = (window.cached_tiddler_cleanup_tags !== undefined) ? window.cached_tiddler_cleanup_tags : "NOT systemConfig AND NOT AddressBook";
$("tiddler_cleanup_characters_from").value = (window.cached_tiddler_cleanup_characters_from !== undefined) ? window.cached_tiddler_cleanup_characters_from : "";
$("tiddler_cleanup_characters_until").value = (window.cached_tiddler_cleanup_characters_until !== undefined) ? window.cached_tiddler_cleanup_characters_until : "";
</script>
<script label="Find">
window.cached_tiddler_cleanup_title = $("tiddler_cleanup_title").value;
window.cached_tiddler_cleanup_date_from = $("tiddler_cleanup_date_from").value;
window.cached_tiddler_cleanup_date_until = $("tiddler_cleanup_date_until").value;
window.cached_tiddler_cleanup_tags = $("tiddler_cleanup_tags").value;
window.cached_tiddler_cleanup_characters_from = $("tiddler_cleanup_characters_from").value;
window.cached_tiddler_cleanup_characters_until = $("tiddler_cleanup_characters_until").value;
if (window.tiddler_cleanup_selection_find_node && jQuery(window.tiddler_cleanup_selection_find_node).next("span")) {
jQuery(window.tiddler_cleanup_selection_find_node).next("span").remove();
}
if (window.tiddler_cleanup_selection_find_node) {
delete window.tiddler_cleanup_selection_find_node;
}
var parseDateString = function(date) {
var dateMatch = date.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/);
return (dateMatch) ? new Date(dateMatch[1], dateMatch[2] - 1, dateMatch[3], dateMatch[4], dateMatch[5], 0, 0).getTime() : null;
}
var titleRegExp = ($("tiddler_cleanup_title").value == "") ? null : new RegExp($("tiddler_cleanup_title").value);
var dateFrom = parseDateString ($("tiddler_cleanup_date_from").value);
var dateUntil = parseDateString ($("tiddler_cleanup_date_until").value);
var charactersFrom = (isNaN($("tiddler_cleanup_characters_from").value) || ($("tiddler_cleanup_characters_from").value == "")) ? null : Number($("tiddler_cleanup_characters_from").value);
var charactersUntil = (isNaN($("tiddler_cleanup_characters_until").value) || ($("tiddler_cleanup_characters_until").value == "")) ? null : Number($("tiddler_cleanup_characters_until").value);
var tiddlers = [];
window.tiddler_cleanup_selection = [];
var list = ($("tiddler_cleanup_tags").value == "") ? store.getTiddlers() : store.getMatchingTiddlers($("tiddler_cleanup_tags").value);
for (var i = 0; i < list.length; i++) {
var tiddlerDate = list[i].modified ? Number(list[i].modified) : Number(list[i].created);
if ((!dateFrom || (tiddlerDate >= dateFrom)) && (!dateUntil || (tiddlerDate < dateUntil)) && (!titleRegExp || titleRegExp.test(list[i].title))) {
var tiddlerCharacters = list[i].text ? list[i].text.length : 0;
if (((charactersFrom === null) || (tiddlerCharacters >= charactersFrom)) && ((charactersUntil === null) || (tiddlerCharacters < charactersUntil))) {
if ((list[i].title != "Trash") && (!list[i].fields['server.host'] || (list[i].fields['server.host'] == "")) && (!list[i].tags || (
(list[i].tags.indexOf(config.macros.emptyTrash.tag) == -1)
))) {
tiddlers.push(list[i]);
window.tiddler_cleanup_selection.pushUnique(escape(list[i].title));
}
}
}
}
tiddlers.sort(function (a, b) { return config.macros.collator.compare(a.title, b.title); });
window.tiddler_cleanup_selection_find_node = place;
var out = "\n";
for (var i = 0; i < tiddlers.length; i++) {
var tags = "";
var tagList = tiddlers[i].tags;
if (tagList && tagList.length) {
tags = " @@''tags:'' ";
for (var j = 0; j < tagList.length; j++) {
tags = tags + ((j == 0) ? " " : ", ");
if (store.getTiddler(tagList[j])) {
tags = tags + "[[" + tagList[j] + "]]";
} else {
tags = tags + tagList[j];
}
}
tags = tags + "@@";
}
var referringTiddlers = "";
var referringTiddlerList = store.getReferringTiddlers(tiddlers[i].title);
if (referringTiddlerList.length > 0) {
referringTiddlers = " @@background-color:#c0c0c0;''referred by:''";
for (var j = 0; j < referringTiddlerList.length; j++) {
referringTiddlers = referringTiddlers + ((j == 0) ? "" : ",") + " [[" + referringTiddlerList[j].title + "]]";
}
referringTiddlers = referringTiddlers + "@@";
}
if (!store.tiddlersUpdated) {
store.updateTiddlers();
}
var linkedTiddlers = "";
var tiddlersLinks = tiddlers[i].links;
if (tiddlersLinks && tiddlersLinks.length > 0) {
linkedTiddlers = " @@background-color:#babb1e;''links:''";
for (var j = 0; j < tiddlersLinks.length; j++) {
linkedTiddlers = linkedTiddlers + ((j == 0) ? "" : ",") + " [[" + tiddlersLinks[j] + "]]";
}
linkedTiddlers = linkedTiddlers + "@@";
}
out = out +"\n<html><input type='checkbox' checked='checked' class='tiddler_cleanup_checkbox' onclick='this.checked ? window.tiddler_cleanup_selection.pushUnique(\"" + escape(tiddlers[i].title) + "\") : window.tiddler_cleanup_selection.remove(\"" + escape(tiddlers[i].title) + "\")' /></html> <html><span class='tiddlerlink'>" + tiddlers[i].title.replace(/</g, "&lt;").replace(/</g, "<") + "</span></html>" + tags + " @@background-color:#c0ffee;''characters:'' " + (tiddlers[i].text ? tiddlers[i].text.length : 0) + "@@" + referringTiddlers + linkedTiddlers;
}
var listPlace = jQuery("<span></span>");
wikify(out, listPlace[0]);
listPlace.find("span.tiddlerlink").each(function () {
var tiddlerLink = jQuery(this);
var link = tiddlerLink.text();
tiddlerLink.empty();
createTiddlyText(createTiddlyLink(tiddlerLink[0], link, false, null, false, null), link);
});
listPlace.insertAfter(place);
</script>
-------
<script label="Find plugin info">
window.cached_tiddler_cleanup_date_from = $("tiddler_cleanup_date_from").value;
window.cached_tiddler_cleanup_date_until = $("tiddler_cleanup_date_until").value;
window.cached_tiddler_cleanup_tags = $("tiddler_cleanup_tags").value;
window.cached_tiddler_cleanup_characters_from = $("tiddler_cleanup_characters_from").value;
window.cached_tiddler_cleanup_characters_until = $("tiddler_cleanup_characters_until").value;
if (window.tiddler_cleanup_selection_find_node && jQuery(window.tiddler_cleanup_selection_find_node).next("span")) {
jQuery(window.tiddler_cleanup_selection_find_node).next("span").remove();
}
if (window.tiddler_cleanup_selection_find_node) {
delete window.tiddler_cleanup_selection_find_node;
}
var parseDateString = function(date) {
var dateMatch = date.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/);
return (dateMatch) ? new Date(dateMatch[1], dateMatch[2] - 1, dateMatch[3], dateMatch[4], dateMatch[5], 0, 0).getTime() : null;
}
var dateFrom = parseDateString ($("tiddler_cleanup_date_from").value);
var dateUntil = parseDateString ($("tiddler_cleanup_date_until").value);
var tiddlers = [];
window.tiddler_cleanup_selection = [];
var list = store.getTiddlers();
for (var i = 0; i < list.length; i++) {
if ((list[i].title != "Trash") && (list[i].fields['server.host'] && (list[i].fields['server.host'].indexOf("-ext.html") == list[i].fields['server.host'].length - "-ext.html".length)) && (!list[i].tags || (
(list[i].tags.indexOf(config.macros.emptyTrash.tag) == -1)
))) {
tiddlers.push(list[i]);
window.tiddler_cleanup_selection.pushUnique(escape(list[i].title));
}
}
tiddlers.sort(function (a, b) { return config.macros.collator.compare(a.title, b.title); });
window.tiddler_cleanup_selection_find_node = place; //.parentNode;
var out = "\n";
for (var i = 0; i < tiddlers.length; i++) {
out = out +"\n<html><input type='checkbox' checked='checked' class='tiddler_cleanup_checkbox' onclick='this.checked ? window.tiddler_cleanup_selection.pushUnique(\"" + escape(tiddlers[i].title) + "\") : window.tiddler_cleanup_selection.remove(\"" + escape(tiddlers[i].title) + "\")' /></html> <html><span class='tiddlerlink'>" + tiddlers[i].title.replace(/</g, "&lt;").replace(/</g, "<") + "</span></html>" + (tiddlers[i].getTags() ? " @@''tags:'' " + tiddlers[i].getTags() + "@@" : "") + " @@background-color:#c0ffee;''characters:'' " + (tiddlers[i].text ? tiddlers[i].text.length : 0) + "@@";
}
var listPlace = jQuery("<span></span>");
wikify(out, listPlace[0]);
listPlace.find("span.tiddlerlink").each(function () {
var tiddlerLink = jQuery(this);
var link = tiddlerLink.text();
tiddlerLink.empty();
createTiddlyText(createTiddlyLink(tiddlerLink[0], link, false, null, false, null), link);
});
listPlace.insertAfter(place);
</script>
-------
<script label="Select all">
jQuery(".tiddler_cleanup_checkbox").each(function() {
this.checked = true;
this.onclick();
});
</script>
<script label="Select none">
jQuery(".tiddler_cleanup_checkbox").each(function() {
this.checked = false;
this.onclick();
});
</script>
<script label="Send selected to trash">
if (window.tiddler_cleanup_selection) {
for (var i = 0; i < window.tiddler_cleanup_selection.length - 1; i++) {
var title = unescape(window.tiddler_cleanup_selection[i]);
if (store.tiddlerExists(title)) {
var tiddler = store.getTiddler(title);
if (tiddler.tags.indexOf(config.macros.emptyTrash.tag) == -1) tiddler.tags[tiddler.tags.length] = config.macros.emptyTrash.tag;
if (tiddler.tags.indexOf('excludeLists') == -1) tiddler.tags[tiddler.tags.length] = 'excludeLists';
if (tiddler.tags.indexOf('excludeMissing') == -1) tiddler.tags[tiddler.tags.length] = 'excludeMissing';
if (tiddler.tags.indexOf('excludeSearch') == -1) tiddler.tags[tiddler.tags.length] = 'excludeSearch';
if (tiddler.isTagged('systemConfig')) store.setTiddlerTag(title, 1, 'systemConfigDisable');
story.closeTiddler(title, true);
}
}
if (window.tiddler_cleanup_selection.length > 0) {
config.commands.deleteTiddler.sendToTrash(unescape(window.tiddler_cleanup_selection[window.tiddler_cleanup_selection.length - 1]));
}
delete window.tiddler_cleanup_selection;
if (window.tiddler_cleanup_selection_find_node && jQuery(window.tiddler_cleanup_selection_find_node).next("span")) {
jQuery(window.tiddler_cleanup_selection_find_node).next("span").remove();
}
if (window.tiddler_cleanup_selection_find_node) {
delete window.tiddler_cleanup_selection_find_node;
}
}
</script>
[[Trash]]
/***
|Name|CollatorPlugin|
|Source|https://stackoverflow.com/a/18391901|
|Documentation|https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript|
|License|[[Apache License 2.0|https://www.apache.org/licenses/LICENSE-2.0]]|
!!!!!Code
***/
//{{{
config.macros.collator = {
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
defaultDiacriticsRemovalMap: [
{'base':'A', 'letters':'\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F'},
{'base':'AA','letters':'\uA732'},
{'base':'AE','letters':'\u00C6\u01FC\u01E2'},
{'base':'AO','letters':'\uA734'},
{'base':'AU','letters':'\uA736'},
{'base':'AV','letters':'\uA738\uA73A'},
{'base':'AY','letters':'\uA73C'},
{'base':'B', 'letters':'\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181'},
{'base':'C', 'letters':'\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E'},
{'base':'D', 'letters':'\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779\u00D0'},
{'base':'DZ','letters':'\u01F1\u01C4'},
{'base':'Dz','letters':'\u01F2\u01C5'},
{'base':'E', 'letters':'\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E'},
{'base':'F', 'letters':'\u0046\u24BB\uFF26\u1E1E\u0191\uA77B'},
{'base':'G', 'letters':'\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'},
{'base':'H', 'letters':'\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D'},
{'base':'I', 'letters':'\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'},
{'base':'J', 'letters':'\u004A\u24BF\uFF2A\u0134\u0248'},
{'base':'K', 'letters':'\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2'},
{'base':'L', 'letters':'\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'},
{'base':'LJ','letters':'\u01C7'},
{'base':'Lj','letters':'\u01C8'},
{'base':'M', 'letters':'\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C'},
{'base':'N', 'letters':'\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'},
{'base':'NJ','letters':'\u01CA'},
{'base':'Nj','letters':'\u01CB'},
{'base':'O', 'letters':'\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C'},
{'base':'OI','letters':'\u01A2'},
{'base':'OO','letters':'\uA74E'},
{'base':'OU','letters':'\u0222'},
{'base':'OE','letters':'\u008C\u0152'},
{'base':'oe','letters':'\u009C\u0153'},
{'base':'P', 'letters':'\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754'},
{'base':'Q', 'letters':'\u0051\u24C6\uFF31\uA756\uA758\u024A'},
{'base':'R', 'letters':'\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'},
{'base':'S', 'letters':'\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'},
{'base':'T', 'letters':'\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'},
{'base':'TZ','letters':'\uA728'},
{'base':'U', 'letters':'\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244'},
{'base':'V', 'letters':'\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245'},
{'base':'VY','letters':'\uA760'},
{'base':'W', 'letters':'\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72'},
{'base':'X', 'letters':'\u0058\u24CD\uFF38\u1E8A\u1E8C'},
{'base':'Y', 'letters':'\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'},
{'base':'Z', 'letters':'\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762'},
{'base':'a', 'letters':'\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250'},
{'base':'aa','letters':'\uA733'},
{'base':'ae','letters':'\u00E6\u01FD\u01E3'},
{'base':'ao','letters':'\uA735'},
{'base':'au','letters':'\uA737'},
{'base':'av','letters':'\uA739\uA73B'},
{'base':'ay','letters':'\uA73D'},
{'base':'b', 'letters':'\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253'},
{'base':'c', 'letters':'\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184'},
{'base':'d', 'letters':'\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A'},
{'base':'dz','letters':'\u01F3\u01C6'},
{'base':'e', 'letters':'\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD'},
{'base':'f', 'letters':'\u0066\u24D5\uFF46\u1E1F\u0192\uA77C'},
{'base':'g', 'letters':'\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'},
{'base':'h', 'letters':'\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'},
{'base':'hv','letters':'\u0195'},
{'base':'i', 'letters':'\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'},
{'base':'j', 'letters':'\u006A\u24D9\uFF4A\u0135\u01F0\u0249'},
{'base':'k', 'letters':'\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3'},
{'base':'l', 'letters':'\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'},
{'base':'lj','letters':'\u01C9'},
{'base':'m', 'letters':'\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F'},
{'base':'n', 'letters':'\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'},
{'base':'nj','letters':'\u01CC'},
{'base':'o', 'letters':'\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275'},
{'base':'oi','letters':'\u01A3'},
{'base':'ou','letters':'\u0223'},
{'base':'oo','letters':'\uA74F'},
{'base':'p','letters':'\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755'},
{'base':'q','letters':'\u0071\u24E0\uFF51\u024B\uA757\uA759'},
{'base':'r','letters':'\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'},
{'base':'s','letters':'\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'},
{'base':'t','letters':'\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'},
{'base':'tz','letters':'\uA729'},
{'base':'u','letters': '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289'},
{'base':'v','letters':'\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C'},
{'base':'vy','letters':'\uA761'},
{'base':'w','letters':'\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73'},
{'base':'x','letters':'\u0078\u24E7\uFF58\u1E8B\u1E8D'},
{'base':'y','letters':'\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'},
{'base':'z','letters':'\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763'},
{'base':"'",'letters':'\u0060\u00B4\u2018\u2019'},
{'base':'"','letters':'\u00AB\u00BB\u201C\u201D'},
],
diacriticsMap: {},
diacriticsOneCharMap: {},
// "what?" version ... http://jsperf.com/diacritics/12
removeDiacritics: function (str, oneChar) {
var cmc = config.macros.collator;
return !str ? str : str.replace(/[^\u0000-\u007E]/g, function (a) {
return (oneChar ? cmc.diacriticsOneCharMap[a] : cmc.diacriticsMap[a]) || a;
});
},
toCollationKey: function (source, oneChar) {
return !source ? source : config.macros.collator.removeDiacritics(source, oneChar).toLowerCase();
},
compare: function (a, b) {
if (a == b) {
return 0;
}
return (config.macros.collator.toCollationKey(a) < config.macros.collator.toCollationKey(b)) ? -1 : 1;
},
indexOf: function (source, key, fromIndex) {
var cmc = config.macros.collator;
fromIndex = fromIndex || 0;
if (source && key && (cmc.toCollationKey(source).indexOf(cmc.toCollationKey(key), fromIndex) > -1)) {
return cmc.toCollationKey(source, true).indexOf(cmc.toCollationKey(key, true), fromIndex);
}
return -1;
},
lastIndexOf: function (source, key, fromIndex) {
var cmc = config.macros.collator;
fromIndex = fromIndex || 0;
if (source && key && (cmc.toCollationKey(source).lastIndexOf(cmc.toCollationKey(key), fromIndex) > -1)) {
return cmc.toCollationKey(source, true).lastIndexOf(cmc.toCollationKey(key, true), fromIndex);
}
return -1;
}
};
//}}}
//{{{
(function () {
var cmc = config.macros.collator;
var defaultDiacriticsRemovalMap = cmc.defaultDiacriticsRemovalMap;
var diacriticsMap = {};
for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
var letters = defaultDiacriticsRemovalMap[i].letters;
for (var j = 0; j < letters.length; j++){
diacriticsMap[letters[j]] = defaultDiacriticsRemovalMap[i].base;
}
}
cmc.diacriticsMap = diacriticsMap;
var diacriticsOneCharMap = {};
for (var i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
var letters = defaultDiacriticsRemovalMap[i].letters;
for (var j = 0; j < letters.length; j++){
diacriticsOneCharMap[letters[j]] = defaultDiacriticsRemovalMap[i].base.substring(0, 1);
}
}
cmc.diacriticsOneCharMap = diacriticsOneCharMap;
})();
//}}}
/***
|Name|CompareTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#CompareTiddlersPlugin|
|Version|1.1.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|show color-coded differences between two selected tiddlers|
!!!!!Usage
<<<
Display a form that lets you select and compare any two tiddlers:
{{{
<<compareTiddlers>>
}}}
To filter the lists of tiddlers by tags, include an optional tagvalue (or tag expression) parameter:
{{{
<<compareTiddlers "tagValue">>
OR
<<compareTiddlers "boolean tag expression">> (requires MatchTagsPlugin)
}}}
<<<
!!!!!Example
<<<
{{{<<compareTiddlers>>}}}
{{smallform small{<<compareTiddlers>>}}}
<<<
!!!!!Revisions
<<<
2009.07.25 [1.1.0] added optional tag filter param
2007.10.15 [1.0.0] converted from inline script to true plugin
2006.12.27 [0.0.0] inline script. {{{diff()}}} and {{{diffString()}}} functions written by Bradley Meck.
<<<
!!!!!Code
***/
//{{{
version.extensions.CompareTiddlersPlugin= {major: 1, minor: 1, revision: 0, date: new Date(2009,7,25)};
//}}}
//{{{
config.shadowTiddlers.CompareTiddlers='<<compareTiddlers>>';
//}}}
/***
//{{{
!html
<form><!--
--><input type=hidden name=filter value=''><!--
--><select name=list1 size=1 style='width:30%'
onchange='config.macros.compareTiddlers.pick(this,this.form.view1,this.form.edit1,this.form.text1)'></select><!--
--><input type=button name=view1 style='width:10%' value='view' disabled
onclick='if (this.form.list1.value.length)
story.displayTiddler(story.findContainingTiddler(this),this.form.list1.value)'><!--
--><input type=button name=edit1 style='width:10%' value='edit' disabled
onclick='if (this.form.list1.value.length)
story.displayTiddler(story.findContainingTiddler(this),this.form.list1.value,DEFAULT_EDIT_TEMPLATE)'><!--
--><select name=list2 size=1 style='width:30%'
onchange='config.macros.compareTiddlers.pick(this,this.form.view2,this.form.edit2,this.form.text2)'></select><!--
--><input type=button name=view2 style='width:10%' value='view' disabled
onclick='if (this.form.list2.value.length)
story.displayTiddler(story.findContainingTiddler(this),this.form.list2.value)'><!--
--><input type=button name=edit2 style='width:10%' value='edit' disabled
onclick='if (this.form.list2.value.length)
story.displayTiddler(story.findContainingTiddler(this),this.form.list2.value,DEFAULT_EDIT_TEMPLATE)'><br><!--
--><nobr><!--
--><textarea name=text1 style='width:49.5%;display:none' rows='10' readonly></textarea><!--
--><textarea name=text2 style='width:49.5%;display:none' rows='10' readonly></textarea><!--
--></nobr><!--
--><div style='float:left'><!--
-->Additions are shown in <span style='color:green'>GREEN</span>, <!--
-->deletions are shown in <span style='color:red'>RED</span><!--
--></div><!--
--><div style='text-align:right'><!--
--><input type=button name=compare style='width:10%' value='compare' disabled
onclick='config.macros.compareTiddlers.compare(this.form,this.form.nextSibling)'><!--
--><input type=button name=done style='width:10%' value='done' disabled
onclick='config.macros.compareTiddlers.reset(this.form,this.form.nextSibling)'><!--
--></div><!--
--></form><div class='compareTiddlersResults'>contents to be replaced by results of comparison</div>
!end
//}}}
***/
//{{{
config.macros.compareTiddlers= {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
setStylesheet(this.css,'CompareTiddlersStyles');
var out=createTiddlyElement(place,'span');
out.innerHTML=store.getTiddlerText('CompareTiddlersPlugin##html');
var form=out.getElementsByTagName('form')[0];
var target=form.nextSibling;
this.reset(form,target,params[0]);
},
css: '.compareTiddlersResults \
{ display:none;clear:both;margin-top:1em;border:1px solid;-moz-border-radius:1em;-webkit-border-radius:1em;padding:1em;white-space:normal; }',
reset: function(f,target,filter) {
if (f.filter.value.length) var filter=f.filter.value;
if (filter) var tids=store.filterTiddlers('[tag['+filter+']]')
else var tids=store.getTiddlers('title','excludeLists');
f.text1.style.display='none'; f.text1.value='';
while (f.list1.options[0]) f.list1.options[0]=null;
f.list1.options[0]=new Option('select a tiddler...','',false,false);
for (i=0; i<tids.length; i++)
f.list1.options[f.list1.length]=new Option(tids[i].title,tids[i].title,false,false);
f.text2.style.display='none'; f.text2.value='';
while (f.list2.options[0]) f.list2.options[0]=null;
f.list2.options[0]=new Option('select a tiddler...','',false,false);
for (i=0; i<tids.length; i++)
f.list2.options[f.list2.length]=new Option(tids[i].title,tids[i].title,false,false);
f.view1.disabled=f.view2.disabled=f.edit1.disabled=f.edit2.disabled=f.compare.disabled=f.done.disabled=true;
f.filter.value=filter;
target.style.display='none';
removeChildren(target);
},
pick: function(list,view,edit,text) {
var f=list.form;
view.disabled=edit.disabled=f.done.disabled=!list.value.length;
f.compare.disabled=!f.list1.value.length||!f.list2.value.length;
if (!list.value.length) return;
f.text1.style.display=f.text2.style.display='inline';
text.value=store.getTiddlerText(list.value);
},
compare: function(f,target) {
if (!f.list1.value.length) { f.list1.focus(); return alert('select a tiddler'); }
var t1=store.getTiddlerText(f.list1.value); if (!t1) { displayMessage(f.list1.value+' not found');return false; }
if (!f.list2.value.length) { f.list2.focus(); return alert('select a tiddler'); }
var t2=store.getTiddlerText(f.list2.value); if (!t2) { displayMessage(f.list2.value+' not found');return false; }
var out=this.diffString(t1,t2); if (!out || !out.length) out='no differences';
removeChildren(target);
target.innerHTML=out;
target.style.display='block';
f.done.disabled=false;
},
//}}}
//{{{
diffString: function( o, n ) {
// This function was written by Bradley Meck
// returns difference between old and new text, color-formatted additions and deletions
if (o==n) return ""; // simple check, saves time if true
var error = 5;
var reg = new RegExp( "\\n|(?:.{0,"+error+"})", "g" );
var oarr = o.match( reg ); // dices text into chunks
var narr = n.match( reg );
var out = this.diff(oarr,narr); // compare the word arrays
var str = ""; // construct output
for (i=0; i<out.length; i++) {
switch (out[i].change) {
case "ADDED":
str+="<span style='color:green'>";
str+=narr.slice(out[i].index,out[i].index+out[i].length).join("");
str+="</span> ";
break;
case "DELETED":
str+="<span style='color:red'>";
str+=oarr.slice(out[i].index,out[i].index+out[i].length).join("");
str+="</span> ";
break;
default:
str+="<span>";
str+=oarr.slice(out[i].index,out[i].index+out[i].length).join("");
str+="</span> ";
break;
}
}
return str;
},
diff: function( oldArray, newArray ) {
// This function was written by Bradley Meck
// finds the differences between one set of objects and another.
// The objects do not need to be Strings. It outputs an array of objects with the properties value and change.
// This function is pretty hefty but appears to be rather light for a diff and tops out at O(N^2) for absolute worst cast scenario.
var newElementHash = { };
for( var i = 0; i < newArray.length; i++ ) {
if( ! newElementHash [ newArray [ i ] ] ) {
newElementHash [ newArray [ i ] ] = [ ];
}
newElementHash [ newArray [ i ] ].push( i );
}
var substringTable = [ ];
for( var i = 0; i < oldArray.length; i++ ) {
if(newElementHash [ oldArray [ i ] ] ) {
var locations = newElementHash [ oldArray [ i ] ] ;
for( var j = 0; j < locations.length; j++){
var length = 1;
while( i + length < oldArray.length && locations [ j ] + length < newArray.length
&& oldArray [ i + length ] == newArray [ locations [ j ] + length ] ){
length++;
}
substringTable.push( {
oldArrayIndex : i,
newArrayIndex : locations [ j ],
matchLength : length
} );
}
}
}
substringTable.sort( function( a, b ) {
if ( a.matchLength > b.matchLength /* a is less than b by some ordering criterion */ ) {
return -1;
}
if ( a.matchLength < b.matchLength /* a is greater than b by the ordering criterion */ ) {
return 1;
}
// a must be equal to b
return 0
} );
//displayMessage( substringTable.toSource( ) );
for( var i = 0; i < substringTable.length; i++) {
for( var j = 0; j < i; j++) {
var oldDelta = substringTable [ i ].oldArrayIndex + substringTable [ i ].matchLength - 1 - substringTable [ j ].oldArrayIndex;
var newDelta = substringTable [ i ].newArrayIndex + substringTable [ i ].matchLength - 1 - substringTable [ j ].newArrayIndex;
//displayMessage( "oldDelta ::: " + oldDelta );
//displayMessage( "newDelta ::: " + newDelta );
//displayMessage( "matchLength ::: " + substringTable [ j ].matchLength );
if( ( oldDelta >= 0 && oldDelta <= substringTable [ j ].matchLength )
|| ( newDelta >= 0 && newDelta <= substringTable [ j ].matchLength )
|| ( oldDelta < 0 && newDelta > 0 )
|| ( oldDelta > 0 && newDelta < 0 ) ) {
substringTable.splice( i, 1 );
i--;
break;
}
}
}
//displayMessage( substringTable.toSource( ) );
substringTable.sort( function( a, b ) {
if ( a.oldArrayIndex < b.oldArrayIndex /* a is less than b by some ordering criterion */ ) {
return -1;
}
if ( a.oldArrayIndex > b.oldArrayIndex /* a is greater than b by the ordering criterion */ ) {
return 1;
}
// a must be equal to b
return 0
} );
//displayMessage( substringTable.toSource( ) );
var oldArrayIndex = 0;
var newArrayIndex = 0;
var results = [ ];
for( var i = 0; i < substringTable.length; i++ ) {
if( oldArrayIndex != substringTable [ i ].oldArrayIndex ) {
results.push( {
change : "DELETED",
length : substringTable [ i ].oldArrayIndex - oldArrayIndex,
index : oldArrayIndex
} );
}
if( newArrayIndex != substringTable [ i ].newArrayIndex ) {
results.push( {
change : "ADDED",
length : substringTable [ i ].newArrayIndex - newArrayIndex,
index : newArrayIndex
} );
}
results.push( {
change : "STAYED",
length : substringTable [ i ].matchLength,
index : substringTable [ i ].oldArrayIndex
} );
oldArrayIndex = substringTable [ i ].oldArrayIndex + substringTable [ i ].matchLength;
newArrayIndex = substringTable [ i ].newArrayIndex + substringTable [ i ].matchLength;
}
if( oldArrayIndex != oldArray.length ) {
results.push( {
change : "DELETED",
length : oldArray.length - oldArrayIndex,
index : oldArrayIndex
} );
}
if( newArrayIndex != newArray.length ) {
results.push( {
change : "ADDED",
length : newArray.length - newArrayIndex,
index : newArrayIndex
} );
}
return results;
}
}
//}}}
<<twab>> <<twab "tags:AddressBook Encrypt(contacts)" new encrypted contact>>
<<list filter "[tag[AddressBook AND NOT project AND NOT Trash]]">>
<html>
<style>
.rolodex table {
border: 0px solid;
}
.rolodex tr, .rolodex td {
border: 0px solid;
}
</style>
</html>
<<tabs addressBookTabs
Overview "Email Addresses, Homepage URL, etc." TwabTabParts/tab1form
Home "Home Address/Phone, etc." TwabTabParts/tab2form
Business "Business Address/Phone, etc." TwabTabParts/tab3form
Misc "Notes, Birthday, SSN, etc." TwabTabParts/tab4form
>>
/***
|Name|CookieSaverPlugin|
|Source|http://www.TiddlyTools.com/#CookieSaverPlugin|
|Version|1.1.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|automatically save TiddlyWiki cookie options to [[CookieJar]] tiddler for portable settings|
!!!!!Usage
<<<
Whenever TiddlyWiki option settings are changed, a browser-based cookie value is added, removed, or changed. Each time this occurs, the CookieSaverPlugin generates an equivalent ''portable cookie'': a single line of javascript code that assigns a stored value directly to the specific TiddlyWiki internal config.options.* variable corresponding to the browser cookie with the same name.
The portable cookies are automatically written into a tiddler named [[CookieJar]] that is tagged with<<tag systemConfig>>so that their values will be applied as soon as the document is saved and reloaded. You can change or delete an individual portable cookie by editing the contents of the [[CookieJar]] and removing the appropriate line of javascript from the tiddler source code. Note: editing the portable cookie definitions does not alter the values of any corresponding browser cookies, nor does it update the internal value that is in use within the current TiddlyWiki document session. Changes made directly to the [[CookieJar]] are only applied after saving and reloading the document. In any case, whenever a browser cookie value is updated, any modifications you made to the equivalent portable cookie are immediately rewritten to reflect the current browser cookie value.
Browser cookies are, obviously, stored with your browser... and are kept separate from the document itself. In contrast, because your portable cookies are stored in a [[CookieJar]] within the document, they remain part of that document.
When the document is copied and shared with others, each copy includes the [[CookieJar]] containing //your// stored portable cookies. Fortunately, CookieSaverPlugin can generate and maintain several separate sets of portable cookies in the same [[CookieJar]] tiddler, where each set is associated with a different TiddlyWiki username. As long as other readers have not chosen the same username, your portable cookie values will not be automatically applied when they are reading the document. Rather, as they interact with the document, a new set of portable cookies, associated with //their// username, will be automatically added to the [[CookieJar]].
In addition to tracking and applying separate portable cookies for each user, CookieSaverPlugin can also be configured so that sensitive data (such as internal URLs, email addresses, login IDs and passwords, etc.) will never be inadvertently stored in the [[CookieJar]]. To achieve this, you can selectively prevent specific cookienames from being used as portable cookies by placing a special javascript function definition in a tiddler named [[CookieSaverPluginConfig]], tagged with 'systemConfig':
{{{
config.macros.cookieSaver.allowPortableCookie=function(name){
if ( ... some test using 'name' ...) return false;
if ( ... another test with 'name' ...) return true;
etc.
return true; // default=allow
}
}}}
The allowPortableCookie() function offers a flexible method for plugin developers and other technically skilled document authors to implement their own custom-defined, application-specific cookie data protection by applying sophisticated logic for deciding which cookies should be allowed or blocked based on variety of different conditions. The basic operation of this function is to accept a cookie name as text input, apply some tests based on that cookie name (combined with any other useful criteria), and then return //true// if saving the portable cookie should be permitted, or //false// if the cookie should be excluded from the [[CookieJar]].
Unfortunately, although the technical expertise needed to write this test function is relatively minor, the level of programming ability that is needed can nonetheless be beyond the skills that many people possess. To help address this, CookieSaverPlugin also supports an alternative syntax that allows you to define a simple array of cookie names that is used by the plugin to automatically block the indicated names from being included as portable cookies in the [[CookieJar]]. The array definition syntax looks like this:
{{{
// define a complete set of blocked cookie names
config.macros.cookieSaver.blockedCookies=['cookie','cookie','cookie',etc...];
}}}
or
{{{
// add individual cookies names to the current set of blocked cookies
config.macros.cookieSaver.blockedCookies.push('cookie');
config.macros.cookieSaver.blockedCookies.push('cookie');
etc...
}}}
Note: the allowPortableCookie() function and the blockedCookies[] array are only used to limit the creation of portable cookies within the [[CookieJar]], and are //not// applied when creating normal browser cookies. Thus, regardless of whether or not a given portable cookie has been excluded or permitted, all the usual TiddlyWiki settings and internal state data can still be saved as secure, private, local browser cookies that are never made visible to others, even when the document is shared.
<<<
!!!!!Configuration
<<<
<<option chkPortableCookies>> allow ~CookieSaver to store //''portable cookies''// in [[CookieJar]] tiddler
<<option chkMonitorCookieJar>> monitor ~CookieSaver activity (show messages whenever [[CookieJar]] is updated)
<<option chkCookieJarAddToAdvancedOptions>> display [[CookieJar]] in [[AdvancedOptions]]
^^//note: changing this setting does not take effect until you reload the document//^^
<<<
!!!!!Revisions
<<<
2009.08.05 [1.1.0] changed CookieJar output format to support odd symbols in option names (e.g. '@')
2008.09.11 [1.0.2] automatically add portable cookies header to existing CookieJar (if any). Also, added chkMonitorCookieJar option to display CookieJar activity messages
2008.09.10 [1.0.1] documentation, code cleanup, improvements in 'allowPortableCookie()' function handling
2008.09.09 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.CookieSaverPlugin= {major: 1, minor: 1, revision: 0, date: new Date(2009,8,5)};
config.macros.cookieSaver = {
target:
config.options.txtCookieJar||"CookieJar",
init: function() {
if (config.options.chkPortableCookies===undefined)
config.options.chkPortableCookies=false;
if (config.options.txtCookieJar===undefined)
config.options.txtCookieJar=this.target;
if (config.options.chkCookieJarAddToAdvancedOptions===undefined)
config.options.chkCookieJarAddToAdvancedOptions=true;
if (config.options.chkCookieJarAddToAdvancedOptions)
config.shadowTiddlers.AdvancedOptions+="\n!!%0\n><<tiddler [[%0]]>>".format([this.target]);
if (config.options.chkMonitorCookieJar===undefined)
config.options.chkMonitorCookieJar=false;
// add empty Portable Cookies section to shadow CookieJar
var h="/***\n<<tiddler CookieManager>>\n***/\n";
var t=(config.shadowTiddlers[this.target]||"").replace(new RegExp(h.replace(/\*/g,'\\*'),''),'')
config.shadowTiddlers[this.target]=this.header+this.footer+t;
// add empty Portable Cookies section to real CookieJar (if one exists)
if (store.tiddlerExists(this.target) && !readOnly) {
var tid=this.get(this.target);
var t=tid.text;
if (t.indexOf(this.header)==-1){
t=this.header+this.footer+t.replace(new RegExp(h.replace(/\*/g,'\\*'),''),'');
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(tid.title,tid.title,t,who,when,tid.tags,tid.fields);
displayMessage("CookieSaver: added 'Portable Cookies' section to CookieJar");
}
}
// add "cookies" backstage task
if (config.tasks && !config.tasks.cookies) { // for TW2.2b3 or above
config.tasks.cookies = {
text: "cookies",
tooltip: "manage cookie-based option settings",
content: "{{groupbox{<<tiddler CookieManager>><<tiddler [[%0]]>>}}}".format([this.target])
}
config.backstageTasks.push("cookies");
}
},
header:
"/***\n<<tiddler CookieManager>>\n***/\n"
+"/***\n"
+"!!![[Portable cookies:|CookieSaverPlugin]] "
+"{{fine{<<option chkPortableCookies>>enable <<option chkMonitorCookieJar>>monitor}}}\n"
+"^^This section is ''//__automatically maintained__//'' by [[CookieSaverPlugin]]. "
+"To block specific cookies, see [[CookieSaverPluginConfig]].^^\n"
+"***/\n",
startUser:
"//{{{\n"
+"if (config.options.txtUserName==\"%0\" && config.options.chkPortableCookies) {",
endUser:
"\n}\n//}}}\n",
footer:
"// // /% end portable cookies %/\n",
get: function(tid) { // create or retrieve tiddler
if (story.isDirty(tid)) return null; // tiddler is being hand-edited... leave it alone.
var text=config.shadowTiddlers[this.target];
var who=config.options.txtUserName;
var when=new Date();
var tags=['systemConfig'];
return store.getTiddler(tid)||store.saveTiddler(tid,tid,text,who,when,tags,{});
},
format: function(name) {
if (name.substr(0,3)=='chk')
return '\tconfig.options["'+name+'"]='+(config.options[name]?'true;':'false;');
return '\tconfig.options["'+name+'"]="'+config.options[name]+'";';
},
blockedCookies: [],
allowPortableCookie: function(name) {
return true;
},
set: function(name) {
if (!name||!name.trim().length) return;
if (name=='txtUserName' || this.blockedCookies.contains(name) || !this.allowPortableCookie(name)) {
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: blocked '"+name+"'");
return false; // don't save excluded cookies
}
var tid=this.get(this.target);
if (!tid) return false; // if no tiddler... do nothing
var t=tid.text;
if (t.indexOf(this.header)==-1) { // re-add Portable Cookies section if it was deleted by hand edit
var h="/***\n<<tiddler CookieManager>>\n***/\n";
t=this.header+this.footer+t.replace(new RegExp(h.replace(/\*/g,'\\*'),''),'');
}
var who=config.options.txtUserName;
var when=new Date();
var startmark=this.startUser.format([who]);
var endmark=this.endUser;
var startpos=t.indexOf(startmark);
if (startpos==-1) { // insert new user (just before footer)
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: added new user '"+who+"'");
var addpos=t.indexOf(this.footer); if (addpos==-1) addpos=t.length;
t=t.substr(0,addpos)+startmark+endmark+t.substr(addpos);
startpos=addpos;
}
startpos+=startmark.length;
var endpos=t.indexOf(endmark,startpos);
var pre=t.substr(0,startpos);
var lines=t.substring(startpos,endpos).split('\n');
var post=t.substr(endpos);
var code=this.format(name);
var match='\tconfig.options["'+name+'"]=';
var found=false; var changed=false;
for (var i=0; i<lines.length; i++) { // find and replace existing setting
if (lines[i].substr(0,match.length)==match) {
found=true;
if (changed=lines[i]!=code) lines[i]=code; // replace value
if (config.options.chkMonitorCookieJar && !startingUp && changed)
displayMessage("CookieJar: "+code);
}
}
if (!found && code.length) { // OR, add new setting
lines[lines.length]=code;
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: "+code);
}
if (found && !changed) return; // don't alter tiddler unless necessary
t=pre+lines.join('\n')+post;
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(this.target,this.target,t,who,when,tid.tags,tid.fields);
story.refreshTiddler(this.target,null,true);
},
remove: function(name) {
if (!name||!name.trim().length) return;
var who=config.options.txtUserName;
var when=new Date();
var tid=store.getTiddler(this.target);
if (!tid) return false; // if no tiddler... do nothing
var t=tid.text;
var who=config.options.txtUserName
var startmark=this.startUser.format([who]);
var endmark=this.endUser;
var startpos=t.indexOf(startmark);
if (startpos==-1) return false; // no such user... do nothing
startpos+=startmark.length;
var endpos=t.indexOf(endmark,startpos);
var pre=t.substr(0,startpos);
var lines=t.substring(startpos,endpos).split('\n');
var post=t.substr(endpos);
var match='\tconfig.options["'+name+'"]';
var found=false; var changed=false;
for (var i=0; i<lines.length; i++) { // find and remove setting
if (lines[i].substr(0,match.length)==match) {
lines.splice(i,1);
changed=true;
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: deleted '"+name+"'");
break;
}
}
if (!changed) return; // not found... do nothing
t=pre+lines.join('\n')+post;
if (lines.length==1) { // no cookies left, remove user
t=pre.substr(0,pre.length-startmark.length)+post.substr(endmark.length);
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: removed user '"+who+"'");
}
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(this.target,this.target,t,who,when,tid.tags,tid.fields);
story.refreshTiddler(this.target,null,true);
}
}
//}}}
//{{{
// Hijack saveOptionCookie() to add CookieSaver processing
config.macros.cookieSaver.saveOptionCookie=saveOptionCookie;
window.saveOptionCookie=function(name)
{
config.macros.cookieSaver.saveOptionCookie.apply(this,arguments);
if (!readOnly && (config.options.chkPortableCookies || name=="chkPortableCookies"))
config.macros.cookieSaver.set(name);
}
// 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=/;';
}
}
// ... and then hijack it to also remove any corresponding PortableCookie
config.macros.cookieSaver.removeCookie=removeCookie;
window.removeCookie=function(name)
{
if (config.options.chkPortableCookies && !readOnly)
config.macros.cookieSaver.remove(name);
config.macros.cookieSaver.removeCookie.apply(this,arguments);
}
//}}}
/***
|Name|CookieSaverPluginConfig|
|Source|http://www.TiddlyTools.com/#CookieSaverPluginConfig|
|Requires|CookieSaverPlugin|
|Description|custom settings for [[CookieSaverPlugin]]|
!!!!!Portable Cookie Configuration:
***/
// // <<option chkPortableCookies>> allow ~CookieSaver to store //''portable cookies''// in [[CookieJar]] tiddler
// // <<option chkMonitorCookieJar>> monitor ~CookieSaver activity (show messages whenever [[CookieJar]] is updated)
//{{{
// default to these settings:
// config.options.chkPortableCookies=false; // when FALSE this blocks ALL portable cookies
// config.options.chkMonitorCookieJar=false;
//}}}
// // Individual cookie names can be added to the {{{config.macros.cookieSaver.blockedCookies}}} array to prevent them from being stored in the [[CookieJar]].
//{{{
var bc=config.macros.cookieSaver.blockedCookies;
bc.push("chkBackstage"); // core
bc.push("txtMainTab"); // TiddlyWiki: SideBarTabs
bc.push("txtTOCSortBy"); // TiddlyTools: TableOfContentsPlugin
bc.push("txtCatalogTab"); // TiddlyTools: CatalogTabs
bc.push("txtUploadFilename"); // BidiX: UploadPlugin
bc.push("txtUploadDir"); // BidiX: UploadPlugin
bc.push("pasPassword"); // BidiX: UploadPlugin
bc.push("pasUploadPassword"); // BidiX: UploadPlugin
//}}}
// // You can also define a javascript test function that determines whether or not any particular cookie name should be stored in the [[CookieJar]]. The following function should return FALSE if the portable cookie should be blocked, or TRUE if the cookie should be allowed:
//{{{
config.macros.cookieSaver.allowPortableCookie=function(name) {
// add tests based on specific cookie names and runtime conditions
if (name.substr(0,9)=="chkSlider") return false; // NestedSlidersPlugin
if (name.substr(0,13)=="txtFirstVisit") return false; // VisitCounter
if (name.substr(0,12)=="txtLastVisit") return false; // VisitCounter
if (name.substr(0,13)=="txtVisitCount") return false; // VisitCounter
return true;
}
//}}}
/***
|Name|CookieSaverPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[CookieSaverPlugin]] [[CookieSaverPluginConfig]]|
!!!!!Code
***/
//{{{
config.shadowTiddlers.BlockedCookieJarRegExp =
"/^config\\.macros\\.calc:.*$/,\n" +
"/^config\\.macros\\.email:.*$/,\n" +
"/^config\\.macros\\.reciprocalTags:.*$/,\n" +
"/^config\\.macros\\.relatedTiddlers:.*$/,\n"
//}}}
//{{{
config.options.chkPortableCookies = (config.options.chkPortableCookies === undefined) ? true : config.options.chkPortableCookies;
config.macros.cookieSaver.startUser = "//{{{\nif (true) {";
config.macros.cookieSaver.target = config.options.txtCookieJar || "zCookieJar";
//}}}
//{{{
config.macros.cookieSaver.allowedCookies = (config.macros.cookieSaver.allowedCookies === undefined) ? ["txtMainTab", "chkSliderOptionsPanel"] : config.macros.cookieSaver.allowedCookies;
for (var i = 0; i < config.macros.cookieSaver.allowedCookies.length; i++) {
var index = config.macros.cookieSaver.blockedCookies.indexOf(config.macros.cookieSaver.allowedCookies[i]);
if (index > -1) {
config.macros.cookieSaver.blockedCookies.splice(index, 1);
}
}
config.macros.cookieSaver.allowPortableCookieOverride_base = config.macros.cookieSaver.allowPortableCookie;
config.macros.cookieSaver.allowPortableCookie = function (name) {
if (config.macros.cookieSaver.allowedCookies.indexOf(name) > -1) {
return true;
}
var blockedCookieJarRegExpText = store.getTiddlerText('BlockedCookieJarRegExp');
if (blockedCookieJarRegExpText != config.macros.cookieSaver.blockedCookieJarRegExpText) {
try {
eval("config.macros.cookieSaver.blockedCookieJarRegExp = [" + blockedCookieJarRegExpText + "]");
config.macros.cookieSaver.blockedCookieJarRegExpText = blockedCookieJarRegExpText;
} catch (e) {}
}
config.macros.cookieSaver.blockedCookieJarRegExp = config.macros.cookieSaver.blockedCookieJarRegExp ? config.macros.cookieSaver.blockedCookieJarRegExp : [];
var blockCookie = false;
for (var r = 0; r < config.macros.cookieSaver.blockedCookieJarRegExp.length; r++) {
if (name.match(config.macros.cookieSaver.blockedCookieJarRegExp[r])) {
blockCookie = true;
break;
}
}
if (blockCookie) {
return false;
}
return config.macros.cookieSaver.allowPortableCookieOverride_base(name);
}
//}}}
//{{{
config.macros.cookieSaver.saveOptionCookie=saveCookie;
window.saveCookie = window.saveOptionCookie
config.macros.cookieSaver.set_base = config.macros.cookieSaver.set;
config.macros.cookieSaver.set = function (name) {
var dirty = store.isDirty();
config.macros.cookieSaver.set_base(name);
store.setDirty(dirty);
}
config.macros.cookieSaver.remove_base = config.macros.cookieSaver.remove;
config.macros.cookieSaver.remove = function (name) {
var dirty = store.isDirty();
config.macros.cookieSaver.remove_base(name);
store.setDirty(dirty);
}
//}}}
//{{{
config.macros.cookieSaver.format_base = config.macros.cookieSaver.format;
config.macros.cookieSaver.format = function (name) {
var code = config.macros.cookieSaver.format_base(name);
var start = code.indexOf(']="');
if (start > -1) {
start += ']="'.length;
var end = code.lastIndexOf('"');
var value = config.options[name];
if ((typeof value === "boolean") || (jQuery.isNumeric(value) && (typeof value !== "string"))) {
code = code.substring(0, start - 1) + code.substring(start, end) + code.substring(end + 1);
} else {
code = code.substring(0, start) + code.substring(start, end).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + code.substring(end);
}
}
return code;
}
//}}}
/***
|Name|CopyLinePlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
config.macros.copyline = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
if (wikifier && tiddler) {
params = paramString.parseParams("anon", null, true, false, false);
var label = getParam(params, "label", "copy");
var prompt = getParam(params, "prompt", "Copy tiddler lines");
var accessKey = getParam(params, "accessKey", null);
var isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
var offset = getParam(params, "offset", -1);
offset = isNumber(offset) ? offset | 0 : -1;
var count = getParam(params, "count", 1);
count = isNumber(count) ? count | 0 : 1;
if ((offset != 0) && (count > 0)) {
var macroPosition = (offset < 0) ? wikifier.matchStart : wikifier.nextMatch;
var copyText = function () {
var textStart = -1;
var textEnd = -1;
if (offset < 0) {
textEnd = macroPosition + 1;
for (var i = 0; (i > offset) && (textEnd > -1); i--) {
textEnd = tiddler.text.lastIndexOf("\n", textEnd - 1);
}
if (textEnd > -1) {
textStart = tiddler.text.lastIndexOf("\n", textEnd - 1);
textStart = textStart > -1 ? textStart + 1 : 0;
}
} else {
offset--;
textStart = macroPosition;
for (var i = 0; (i < offset) && (textStart > -1); i++) {
textStart = tiddler.text.indexOf("\n", textStart + 1);
}
if (textStart > -1) {
textEnd = tiddler.text.indexOf("\n", textStart + 1);
textEnd = textEnd > -1 ? textEnd : tiddler.text.length;
}
}
if ((textStart > -1) && (textEnd > -1)) {
if (tiddler.text.charAt(textEnd) == "\n") {
for (var i = 0; i < count - 1; i++) {
textEnd = tiddler.text.indexOf("\n", textEnd + 1);
if (textEnd < 0) {
textEnd = tiddler.text.length;
break;
}
}
}
var copiedText = tiddler.text.substring(textStart, textEnd);
if ((offset < 0) && (copiedText.length > 0) && (copiedText.charAt(copiedText.length - 1) != "\n")) {
copiedText = copiedText + "\n";
}
tiddler.text = tiddler.text.substring(0, macroPosition) + copiedText + tiddler.text.substring(macroPosition);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddler.title, null, true);
}
};
createTiddlyButton(place, label, prompt, copyText, null, null, accessKey);
}
}
}
}
//}}}
/***
|Name|CopyTiddlerPlugin|
|Source|http://www.TiddlyTools.com/#CopyTiddlerPlugin|
|Version|3.2.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.3|
|Type|plugin|
|Description|Quickly create a copy of any existing tiddler|
!!!Usage
<<<
The plugin automatically updates the default (shadow) ToolbarCommands definitions to insert the ''copyTiddler'' command, which will appear as ''copy'' when a tiddler is rendered. If you are already using customized toolbar definitions, you will need to manually add the ''copyTiddler'' toolbar command to your existing ToolbarCommands tiddler, e.g.:
{{{
|EditToolbar|... copyTiddler ... |
}}}
When the ''copy'' command is selected, a new tiddler is created containing an exact copy of the current text/tags/fields, using a title of "{{{TiddlerName (n)}}}", where ''(n)'' is the next available number (starting with 1, of course). If you copy while //editing// a tiddler, the current values displayed in the editor are used (including any changes you may have already made to those values), and the new tiddler is immediately opened for editing.
The plugin also provides a macro that allows you to embed a ''copy'' command directly in specific tiddler content:
{{{
<<copyTiddler TidderName label:"..." prompt:"...">>
}}}
where
* ''TiddlerName'' (optional)<br>specifies the //source// tiddler to be copied. If omitted, the current containing tiddler (if any) will be copied.
* ''label:"..."'' (optional)<br>specifies text to use for the embedded link (default="copy TiddlerName")
* ''prompt:"..."'' (optional)<br>specifies mouseover 'tooltip' help text for link
//Note: to use non-default label/prompt values with the current containing tiddler, use "" for the TiddlerName//
<<<
!!!Configuration
<<<
<<option chkCopyTiddlerDate>> use date/time from existing tiddler (otherwise, use current date/time)
{{{<<option chkCopyTiddlerDate>>}}}
<<<
!!!Revisions
<<<
2010.11.30 3.2.6 use story.getTiddler()
2009.06.08 3.2.5 added option to use timestamp from source tiddler
2009.03.09 3.2.4 fixed IE-specific syntax error
2009.03.02 3.2.3 refactored code (again) to restore use of config.commands.copyTiddler.* custom settings
2009.02.13 3.2.2 in click(), fix calls to displayTiddler() to use current tiddlerElem and use getTiddlerText() to permit copying of shadow tiddler content
2009.01.30 3.2.1 fixed handling for copying field values when in edit mode
2009.01.23 3.2.0 refactored code and added {{{<<copyTiddler TiddlerName>>}}} macro
2008.12.18 3.1.4 corrected code for finding next (n) value when 'sparse' handling is in effect
2008.11.14 3.1.3 added optional 'sparse' setting (avoids 'filling in' missing numbers that may have been previously deleted)
2008.11.14 3.1.2 added optional 'zeroPad' setting
2008.11.14 3.1.1 moved hard-coded '(n)' regex into 'suffixPattern' object property so it can be customized
2008.09.26 3.1.0 changed new title generation to use '(n)' suffix instead of 'Copy of' prefix
2008.05.20 3.0.3 in handler, when copying from VIEW mode, create duplicate array from existing tags array before saving new tiddler.
2007.12.19 3.0.2 in handler, when copying from VIEW mode, duplicate custom fields before saving new tiddler.
2007.09.26 3.0.1 in handler, use findContainingTiddler(src) to get tiddlerElem (and title). Allows 'copy' command to find correct tiddler when transcluded using {{{<<tiddler>>}}} macro or enhanced toolbar inclusion (see [[CoreTweaks]])
2007.06.28 3.0.0 complete re-write to handle custom fields and alternative view/edit templates
2007.05.17 2.1.2 use store.getTiddlerText() to retrieve tiddler content, so that SHADOW tiddlers can be copied correctly when in VIEW mode
2007.04.01 2.1.1 in copyTiddler.handler(), fix check for editor fields by ensuring that found field actually has edit=='text' attribute
2007.02.05 2.1.0 in copyTiddler.handler(), if editor fields (textfield and/or tagsfield) can't be found (i.e., tiddler is in VIEW mode, not EDIT mode), then get text/tags values from stored tiddler instead of active editor fields. Allows use of COPY toolbar directly from VIEW mode
2006.12.12 2.0.0 completely rewritten so plugin just creates a new tiddler EDITOR with a copy of the current tiddler EDITOR contents, instead of creating the new tiddler in the STORE by copying the current tiddler values from the STORE.
2005.xx.xx 1.0.0 original version by Tim Morgan
<<<
!!!Code
***/
//{{{
version.extensions.CopyTiddlerPlugin= {major: 3, minor: 2, revision: 6, date: new Date(2010,11,30)};
// automatically tweak shadow EditTemplate to add 'copyTiddler' toolbar command (following 'cancelTiddler')
config.shadowTiddlers.ToolbarCommands=config.shadowTiddlers.ToolbarCommands.replace(/cancelTiddler/,'cancelTiddler copyTiddler');
if (config.options.chkCopyTiddlerDate===undefined) config.options.chkCopyTiddlerDate=false;
config.commands.copyTiddler = {
text: 'copy',
hideReadOnly: true,
tooltip: 'Make a copy of this tiddler',
notitle: 'this tiddler',
prefix: '',
suffixText: ' (%0)',
suffixPattern: / \(([0-9]+)\)$/,
zeroPad: 0,
sparse: false,
handler: function(event,src,title)
{ return config.commands.copyTiddler.click(src,event); },
click: function(here,ev) {
var tiddlerElem=story.findContainingTiddler(here);
var template=tiddlerElem?tiddlerElem.getAttribute('template'):null;
var title=here.getAttribute('from');
if (!title || !title.length) {
if (!tiddlerElem) return false;
else title=tiddlerElem.getAttribute('tiddler');
}
var root=title.replace(this.suffixPattern,''); // title without suffix
// find last matching title
var last=title;
if (this.sparse) { // don't fill-in holes... really find LAST matching title
var tids=store.getTiddlers('title','excludeLists');
for (var t=0; t<tids.length; t++) if (tids[t].title.startsWith(root)) last=tids[t].title;
}
// get next number (increment from last matching title)
var n=1; var match=this.suffixPattern.exec(last); if (match) n=parseInt(match[1])+1;
var newTitle=this.prefix+root+this.suffixText.format([String.zeroPad(n,this.zeroPad)]);
// if not sparse mode, find the next hole to fill in...
while (store.tiddlerExists(newTitle)||story.getTiddler(newTitle))
{ n++; newTitle=this.prefix+root+this.suffixText.format([String.zeroPad(n,this.zeroPad)]); }
if (!story.isDirty(title)) { // if tiddler is not being EDITED
// duplicate stored tiddler (if any)
var text=store.getTiddlerText(title,'');
var who=config.options.txtUserName;
var when=new Date();
var newtags=[]; var newfields={};
var tid=store.getTiddler(title); if (tid) {
if (config.options.chkCopyTiddlerDate) var when=tid.modified;
for (var t=0; t<tid.tags.length; t++) newtags.push(tid.tags[t]);
store.forEachField(tid,function(t,f,v){newfields[f]=v;},true);
}
store.saveTiddler(newTitle,newTitle,text,who,when,newtags,newfields,true);
story.displayTiddler(tiddlerElem,newTitle,template);
} else {
story.displayTiddler(tiddlerElem,newTitle,template);
var fields=config.commands.copyTiddler.gatherFields(tiddlerElem); // get current editor fields
var newTiddlerElem=story.getTiddler(newTitle);
for (var f=0; f<fields.length; f++) { // set fields in new editor
if (fields[f].name=='title') fields[f].value=newTitle; // rename title in new tiddler
var fieldElem=config.commands.copyTiddler.findField(newTiddlerElem,fields[f].name);
if (fieldElem) {
if (fieldElem.getAttribute('type')=='checkbox')
fieldElem.checked=fields[f].value;
else
fieldElem.value=fields[f].value;
}
}
}
story.focusTiddler(newTitle,'title');
return false;
},
findField: function(tiddlerElem,field) {
var inputs=tiddlerElem.getElementsByTagName('input');
for (var i=0; i<inputs.length; i++) {
if (inputs[i].getAttribute('type')=='checkbox' && inputs[i].field == field) return inputs[i];
if (inputs[i].getAttribute('type')=='text' && inputs[i].getAttribute('edit') == field) return inputs[i];
}
var tas=tiddlerElem.getElementsByTagName('textarea');
for (var i=0; i<tas.length; i++) if (tas[i].getAttribute('edit') == field) return tas[i];
var sels=tiddlerElem.getElementsByTagName('select');
for (var i=0; i<sels.length; i++) if (sels[i].getAttribute('edit') == field) return sels[i];
return null;
},
gatherFields: function(tiddlerElem) { // get field names and values from current tiddler editor
var fields=[];
// get checkboxes and edit fields
var inputs=tiddlerElem.getElementsByTagName('input');
for (var i=0; i<inputs.length; i++) {
if (inputs[i].getAttribute('type')=='checkbox')
if (inputs[i].field) fields.push({name:inputs[i].field,value:inputs[i].checked});
if (inputs[i].getAttribute('type')=='text')
if (inputs[i].getAttribute('edit')) fields.push({name:inputs[i].getAttribute('edit'),value:inputs[i].value});
}
// get textareas (multi-line edit fields)
var tas=tiddlerElem.getElementsByTagName('textarea');
for (var i=0; i<tas.length; i++)
if (tas[i].getAttribute('edit')) fields.push({name:tas[i].getAttribute('edit'),value:tas[i].value});
// get selection lists (droplist or listbox)
var sels=tiddlerElem.getElementsByTagName('select');
for (var i=0; i<sels.length; i++)
if (sels[i].getAttribute('edit')) fields.push({name:sels[i].getAttribute('edit'),value:sels[i].value});
return fields;
}
};
//}}}
// // MACRO DEFINITION
//{{{
config.macros.copyTiddler = {
label: 'copy',
prompt: 'Make a copy of %0',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var title=params.shift();
params=paramString.parseParams('anon',null,true,false,false);
var label =getParam(params,'label',this.label+(title?' '+title:''));
var prompt =getParam(params,'prompt',this.prompt).format([title||this.notitle]);
var b=createTiddlyButton(place,label,prompt,
function(ev){return config.commands.copyTiddler.click(this,ev)});
b.setAttribute('from',title||'');
}
};
//}}}
/***
|Name|CopyToClipboardPlugin|
|Documentation|https://css-tricks.com/native-browser-copy-clipboard <br> https://stackoverflow.com/questions/36639681/how-to-copy-text-from-a-div-to-clipboard|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.txtCopyToClipboardMultiClickDelay === undefined) {
config.options.txtCopyToClipboardMultiClickDelay = "500";
}
if (config.options.txtCopyToClipboardDefaultLabel === undefined) {
config.options.txtCopyToClipboardDefaultLabel = "click-to-copy";
}
if (config.options.txtCopyToClipboardDefaultTitle === undefined) {
config.options.txtCopyToClipboardDefaultTitle = "click to copy";
}
merge(config.optionsDesc, {
txtCopyToClipboardMultiClickDelay: "The delay within which multiple clicks on a <clip></clip> are considered to be the same click",
txtCopyToClipboardDefaultLabel: 'The <clip label=""></clip> label to use when an empty one is specified',
txtCopyToClipboardDefaultTitle: 'The <clip title=""></clip> title (tooltip) to use when an empty one is specified'
});
//}}}
//{{{
config.macros.copyToClipboard = {
clipId: 0,
formatter: {
name: "clip",
match: "\\<clip",
lookahead: "\\<clip(?: label=\\\"([^\\\"]*)\\\")?(?: title=\\\"([^\\\"]*)\\\")?\\>((?:.|\\n)*?)\\</clip\\>",
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 delay = jQuery.isNumeric(config.options.txtCopyToClipboardMultiClickDelay) ? parseInt(config.options.txtCopyToClipboardMultiClickDelay) : 500;
var label = (lookaheadMatch[1] === "") ? config.options.txtCopyToClipboardDefaultLabel : lookaheadMatch[1];
var title = (lookaheadMatch[2] === "") ? config.options.txtCopyToClipboardDefaultTitle : lookaheadMatch[2];
var text = lookaheadMatch[3];
var clipId = "clipid_" + config.macros.copyToClipboard.clipId++;
var clipElement = createTiddlyElement(w.output, "span", clipId);
clipElement.style = "cursor: pointer";
jQuery(clipElement).data("clipid", clipId);
if (title) {
clipElement.title = wikifyPlainText(title, 0, w.tiddler);
}
if (label) {
wikify(label, clipElement, w.highlightRegExp, w.tiddler);
} else {
jQuery(clipElement).data("clicks", 0);
wikify(text, clipElement, w.highlightRegExp, w.tiddler);
}
var copy = function (clicks) {
var clipContainer = document.createElement("div");
clipContainer.style = "position: absolute; left: -1000px; top: -1000px";
var clip = null;
if (label) {
clip = clipContainer;
jQuery(clip).data("clipid", clipId);
jQuery(clip).data("clicks", clicks);
wikify(text, clip, w.highlightRegExp, w.tiddler);
} else {
clip = jQuery("#" + clipId).clone().appendTo(clipContainer)[0];
}
document.body.appendChild(clipContainer);
var copyError = null;
if (!jQuery(clip).is(":empty")) {
if (window.getSelection) {
window.getSelection().removeAllRanges();
var range = document.createRange();
range.selectNodeContents(clip);
window.getSelection().addRange(range);
} else if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(clip);
range.select().createTextRange();
}
try {
if (!document.execCommand("copy")) {
copyError = "Unable to copy";
} else {
if (window.getSelection) {
window.getSelection().removeAllRanges();
var range = document.createRange();
range.selectNodeContents(document.getElementById(clipId));
window.getSelection().addRange(range);
} else if (document.selection) {
var range = document.body.createTextRange();
range.moveToElementText(document.getElementById(clipId));
range.select().createTextRange();
}
}
} catch (e) {
copyError = "Unable to copy: " + e;
}
}
document.body.removeChild(clipContainer);
if (copyError) {
alert(copyError);
}
};
var timer = null;
var clearTimer = function () {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
};
var clicks = 0;
var copied = false;
clipElement.addEventListener("dblclick", function (event) {
event.cancelBubble = true;
if (event.stopPropagation) {
event.stopPropagation();
}
});
["mousedown", "touchstart"].forEach(function (type) {
clipElement.addEventListener(type, function (event) {
clearTimer();
copied = false;
});
});
["mouseup", "touchend", "touchcancel"].forEach(function (type) {
clipElement.addEventListener(type, function (event) {
if ((event.type !== "mouseup") || (event.button === 0)) {
event.preventDefault();
clearTimer();
if (!copied) {
if (label) {
clicks++;
timer = setTimeout(function () {
var totalClicks = clicks;
clicks = 0;
timer = null;
copied = true;
copy(totalClicks);
}, delay);
} else {
copied = true;
copy(1);
}
}
}
});
});
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
}
};
//}}}
//{{{
config.formatters[config.formatters.length] = config.macros.copyToClipboard.formatter;
//}}}
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.7 (2012-04-19)|
|''Summary:''|Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Twitter:''|[[@abego|https://twitter.com/#!/abego]]|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''License:''|[[BSD open source license|http://www.abego-software.de/legal/apl-v10.html]]|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).
Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers.
''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.
''Access and Modify Tiddler Data''
You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields.
These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//
''Data Representation in a Tiddler''
The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]].
//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}
The data section is not displayed when viewing the tiddler (see also "The showData Macro").
Beside the data section a tiddler may have all kind of other content.
Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.
''Saving Changes''
The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.
''Notifications''
No notifications are sent when a tiddler's data value is changed through the "setData" methods.
''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.
''The showData Macro''
By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:
''Syntax:''
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
!Revision history
* v1.0.7 (2012-04-19)
** Bugfix: showData macro fails in TW 2.6.5 when tiddler title contains spaces (Thanks to MattShanks for reporting.)
** remove warnings
* v1.0.6 (2006-08-26)
** Removed misleading comment
* v1.0.5 (2006-02-27) (Internal Release Only)
** Internal
*** Make "JSLint" conform
* v1.0.4 (2006-02-05)
** Bugfix: showData fails in TiddlyWiki 2.0
* v1.0.3 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.2 (2005-12-22)
** Enhancements:
*** Handle texts "<data>" or "</data>" more robust when used in a tiddler text or as a field value.
*** Improved (JSON) error messages.
** Bugs fixed:
*** References are not updated when using the DataTiddler.
*** Changes to compound objects are not always saved.
*** "~</data>" is not rendered correctly (expected "</data>")
* v1.0.1 (2005-12-13)
** Features:
*** The showData macro supports an optional "tiddlername" argument to specify the tiddler containing the data to be displayed
** Bugs fixed:
*** A script immediately following a data section is deleted when the data is changed. (Thanks to GeoffS for reporting.)
* v1.0.0 (2005-12-12)
** initial version
!Source Code
***/
//{{{
//============================================================================
//============================================================================
// DataTiddlerPlugin
//============================================================================
//============================================================================
// Ensure that the DataTiddler Plugin is only installed once.
//
if (!version.extensions.DataTiddlerPlugin) {
version.extensions.DataTiddlerPlugin = {
major: 1, minor: 0, revision: 7,
date: new Date(2012, 3, 19),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"
};
// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window;
if (!TiddlyWiki.prototype.getTiddler) {
TiddlyWiki.prototype.getTiddler = function(title) {
var t = this.tiddlers[title];
return (t !== undefined && t instanceof Tiddler) ? t : null;
};
}
//============================================================================
// DataTiddler Class
//============================================================================
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
function DataTiddler() {
}
DataTiddler = {
// Function to stringify a JavaScript value, producing the text for the data section content.
// (Must match the implementation of DataTiddler.parse.)
//
stringify : null,
// Function to parse the text for the data section content, producing a JavaScript value.
// (Must match the implementation of DataTiddler.stringify.)
//
parse : null
};
// Ensure access for IE
window.DataTiddler = DataTiddler;
// ---------------------------------------------------------------------------
// Data Accessor and Mutator
// ---------------------------------------------------------------------------
// Returns the value of the given data field of the tiddler.
// When no such field is defined or its value is undefined
// the defaultValue is returned.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.getData = function(tiddler, field, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataValue(t, field, defaultValue);
};
// Sets the value of the given data field of the tiddler to
// the value. When the value is equal to the defaultValue
// no value is set (and the field is removed)
//
// Changing data of a tiddler will not trigger notifications.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.setData = function(tiddler, field, value, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler+ "("+t+")";
}
DataTiddler.setTiddlerDataValue(t, field, value, defaultValue);
};
// Returns the data object of the tiddler, with a property for every field.
//
// The properties of the returned data object may only be read and
// not be modified. To modify the data use DataTiddler.setData(...)
// or the corresponding Tiddler method.
//
// If no data section is defined a new (empty) object is returned.
//
// @param tiddler either a tiddler name or a Tiddler
//
DataTiddler.getDataObject = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataObject(t);
};
// Returns the text of the content of the data section of the tiddler.
//
// When no data section is defined for the tiddler null is returned
//
// @param tiddler either a tiddler name or a Tiddler
// @return [may be null]
//
DataTiddler.getDataText = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.readDataSectionText(t);
};
// ---------------------------------------------------------------------------
// Internal helper methods (must not be used by code from outside this plugin)
// ---------------------------------------------------------------------------
// Internal.
//
// The original JSONError is not very user friendly,
// especially it does not define a toString() method
// Therefore we extend it here.
//
DataTiddler.extendJSONError = function(ex) {
if (ex.name == 'JSONError') {
ex.toString = function() {
return ex.name + ": "+ex.message+" ("+ex.text+")";
};
}
return ex;
};
// Internal.
//
// @param t a Tiddler
//
DataTiddler.getTiddlerDataObject = function(t) {
if (t.dataObject === undefined) {
var data = DataTiddler.readData(t);
t.dataObject = (data) ? data : {};
}
return t.dataObject;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.getTiddlerDataValue = function(tiddler, field, defaultValue) {
var value = DataTiddler.getTiddlerDataObject(tiddler)[field];
return (value === undefined) ? defaultValue : value;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.setTiddlerDataValue = function(tiddler, field, value, defaultValue) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
var oldValue = data[field];
if (value == defaultValue) {
if (oldValue !== undefined) {
delete data[field];
DataTiddler.save(tiddler);
}
return;
}
data[field] = value;
DataTiddler.save(tiddler);
};
// Internal.
//
// Reads the data section from the tiddler's content and returns its text
// (as a String).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readDataSectionText = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
if (matches === null || !matches[2]) {
return null;
}
return matches[2];
};
// Internal.
//
// Reads the data section from the tiddler's content and returns it
// (as an internalized object).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readData = function(tiddler) {
var text = DataTiddler.readDataSectionText(tiddler);
try {
return text ? DataTiddler.parse(text) : null;
} catch(ex) {
throw DataTiddler.extendJSONError(ex);
}
};
// Internal.
//
// Returns the serialized text of the data of the given tiddler, as it
// should be stored in the data section.
//
// @param tiddler a Tiddler
//
DataTiddler.getDataTextOfTiddler = function(tiddler) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
return DataTiddler.stringify(data);
};
// Internal.
//
DataTiddler.indexOfNonEscapedText = function(s, subString, startIndex) {
var index = s.indexOf(subString, startIndex);
while ((index > 0) && (s[index-1] == '~')) {
index = s.indexOf(subString, index+1);
}
return index;
};
// Internal.
//
DataTiddler.getDataSectionInfo = function(text) {
// Special care must be taken to handle "<data>" and "</data>" texts inside
// a data section.
// Also take care not to use an escaped <data> (i.e. "~<data>") as the start
// of a data section. (Same for </data>)
// NOTE: we are explicitly searching for a data section that contains a JSON
// string, i.e. framed with braces. This way we are little bit more robust in
// case the tiddler contains unescaped texts "<data>" or "</data>". This must
// be changed when using a different stringifier.
var startTagText = "<data>{";
var endTagText = "}</data>";
// Find the first not escaped "<data>".
var startDataTagIndex = DataTiddler.indexOfNonEscapedText(text, startTagText, 0);
if (startDataTagIndex < 0) {
return null;
}
// Find the *last* not escaped "</data>".
var endDataTagIndex = text.indexOf(endTagText, startDataTagIndex);
if (endDataTagIndex < 0) {
return null;
}
var nextEndDataTagIndex;
while ((nextEndDataTagIndex = text.indexOf(endTagText, endDataTagIndex+1)) >= 0) {
endDataTagIndex = nextEndDataTagIndex;
}
return {
prefixEnd: startDataTagIndex,
dataStart: startDataTagIndex+(startTagText.length)-1,
dataEnd: endDataTagIndex,
suffixStart: endDataTagIndex+(endTagText.length)
};
};
// Internal.
//
// Returns the "matches" of a content of a DataTiddler on the
// "data" regular expression. Return null when no data is defined
// in the tiddler content.
//
// Group 1: text before data section (prefix)
// Group 2: content of data section
// Group 3: text behind data section (suffix)
//
// @param tiddler a Tiddler
// @return [may be null] null when the tiddler contains no data section, otherwise see above.
//
DataTiddler.getDataTiddlerMatches = function(tiddler) {
var text = tiddler.text;
var info = DataTiddler.getDataSectionInfo(text);
if (!info) {
return null;
}
var prefix = text.substr(0,info.prefixEnd);
var data = text.substr(info.dataStart, info.dataEnd-info.dataStart+1);
var suffix = text.substr(info.suffixStart);
return [text, prefix, data, suffix];
};
// Internal.
//
// Saves the data in a <data> block of the given tiddler (as a minor change).
//
// The "chkAutoSave" and "chkForceMinorUpdate" options are respected.
// I.e. the TiddlyWiki *file* is only saved when AutoSave is on.
//
// Notifications are not send.
//
// This method should only be called when the data really has changed.
//
// @param tiddler
// the tiddler to be saved.
//
DataTiddler.save = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
var prefix;
var suffix;
if (matches === null) {
prefix = tiddler.text;
suffix = "";
} else {
prefix = matches[1];
suffix = matches[3];
}
var dataText = DataTiddler.getDataTextOfTiddler(tiddler);
var newText =
(dataText !== null)
? prefix + "<data>" + dataText + "</data>" + suffix
: prefix + suffix;
if (newText != tiddler.text) {
// make the change in the tiddlers text
// ... see DataTiddler.MyTiddlerChangedFunction
tiddler.isDataTiddlerChange = true;
// ... do the action change
tiddler.set(
tiddler.title,
newText,
config.options.txtUserName,
config.options.chkForceMinorUpdate? undefined : new Date(),
tiddler.tags);
// ... see DataTiddler.MyTiddlerChangedFunction
delete tiddler.isDataTiddlerChange;
// Mark the store as dirty.
store.dirty = true;
// AutoSave if option is selected
if(config.options.chkAutoSave) {
saveChanges();
}
}
};
// Internal.
//
DataTiddler.MyTiddlerChangedFunction = function() {
// Remove the data object from the tiddler when the tiddler is changed
// by code other than DataTiddler code.
//
// This is necessary since the data object is just a "cached version"
// of the data defined in the data section of the tiddler and the
// "external" change may have changed the content of the data section.
// Thus we are not sure if the data object reflects the data section
// contents.
//
// By deleting the data object we ensure that the data object is
// reconstructed the next time it is needed, with the data defined by
// the data section in the tiddler's text.
// To indicate that a change is a "DataTiddler change" a temporary
// property "isDataTiddlerChange" is added to the tiddler.
if (this.dataObject && !this.isDataTiddlerChange) {
delete this.dataObject;
}
// call the original code.
DataTiddler.originalTiddlerChangedFunction.apply(this, arguments);
};
//============================================================================
// Formatters
//============================================================================
// This formatter ensures that "~<data>" is rendered as "<data>". This is used to
// escape the "<data>" of a data section, just in case someone really wants to use
// "<data>" as a text in a tiddler and not start a data section.
//
// Same for </data>.
//
config.formatters.push( {
name: "data-escape",
match: "~<\\/?data>",
handler: function(w) {
w.outputText(w.output,w.matchStart + 1,w.nextMatch);
}
} );
// This formatter ensures that <data>...</data> sections are not rendered.
//
config.formatters.push( {
name: "data",
match: "<data>",
handler: function(w) {
var info = DataTiddler.getDataSectionInfo(w.source);
if (info && info.prefixEnd == w.matchStart) {
w.nextMatch = info.suffixStart;
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
} );
//============================================================================
// Tiddler Class Extension
//============================================================================
// "Hijack" the changed method ---------------------------------------------------
DataTiddler.originalTiddlerChangedFunction = Tiddler.prototype.changed;
Tiddler.prototype.changed = DataTiddler.MyTiddlerChangedFunction;
// Define accessor methods -------------------------------------------------------
// Returns the value of the given data field of the tiddler. When no such field
// is defined or its value is undefined the defaultValue is returned.
//
// When field is undefined (or null) the data object is returned. (See
// DataTiddler.getDataObject.)
//
// @param field [may be null, undefined]
// @param defaultValue [may be null, undefined]
// @return [may be null, undefined]
//
Tiddler.prototype.data = function(field, defaultValue) {
return (field)
? DataTiddler.getTiddlerDataValue(this, field, defaultValue)
: DataTiddler.getTiddlerDataObject(this);
};
// Sets the value of the given data field of the tiddler to the value. When the
// value is equal to the defaultValue no value is set (and the field is removed).
//
// @param value [may be null, undefined]
// @param defaultValue [may be null, undefined]
//
Tiddler.prototype.setData = function(field, value, defaultValue) {
DataTiddler.setTiddlerDataValue(this, field, value, defaultValue);
};
//============================================================================
// showData Macro
//============================================================================
config.macros.showData = {
// Standard Properties
label: "showData",
prompt: "Display the values stored in the data section of the tiddler"
};
config.macros.showData.handler = function(place,macroName,params) {
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the optional "JSON"
var showInJSONFormat = false;
if ((i < params.length) && params[i] == "JSON") {
i++;
showInJSONFormat = true;
}
var tiddlerName = story.findContainingTiddler(place).getAttribute("tiddler");
if (i < params.length) {
tiddlerName = params[i];
i++;
}
// --- Processing ------------------------------------------
try {
if (showInJSONFormat) {
this.renderDataInJSONFormat(place, tiddlerName);
} else {
this.renderDataAsTable(place, tiddlerName);
}
} catch (e) {
this.createErrorElement(place, e);
}
};
config.macros.showData.renderDataInJSONFormat = function(place,tiddlerName) {
var text = DataTiddler.getDataText(tiddlerName);
if (text) {
createTiddlyElement(place,"pre",null,null,text);
}
};
config.macros.showData.renderDataAsTable = function(place,tiddlerName) {
var text = "|!Name|!Value|\n";
var data = DataTiddler.getDataObject(tiddlerName);
if (data) {
for (var i in data) {
var value = data[i];
text += "|"+i+"|"+DataTiddler.stringify(value)+"|\n";
}
}
wikify(text, place);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.showData.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"showDataError","<<showData ...>>: "+message);
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".showDataError{color: #ffffff;background-color: #880000;}",
"showData");
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... TiddlyWiki Core
/*global createTiddlyElement, saveChanges, store, story, wikify */
// ... DataTiddler
/*global DataTiddler */
// ... JSON
/*global JSON */
/***
!JSON Code, used to serialize the data
***/
/*
Copyright (c) 2005 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
The global object JSON contains two methods.
JSON.stringify(value) takes a JavaScript value and produces a JSON text.
The value must not be cyclical.
JSON.parse(text) takes a JSON text and produces a JavaScript value. It will
throw a 'JSONError' exception if there is an error.
*/
var JSON = {
copyright: '(c)2005 JSON.org',
license: 'http://www.crockford.com/JSON/license.html',
/*
Stringify a JavaScript value, producing a JSON text.
*/
stringify: function (v) {
var a = [];
/*
Emit a string.
*/
function e(s) {
a[a.length] = s;
}
/*
Convert a value.
*/
function g(x) {
var c, i = undefined, l, v;
switch (typeof x) {
case 'object':
if (x) {
if (x instanceof Array) {
e('[');
l = a.length;
for (i = 0; i < x.length; i += 1) {
v = x[i];
if (typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(v);
}
}
e(']');
return;
} else if (typeof x.toString != 'undefined') {
e('{');
l = a.length;
for (i in x) {
v = x[i];
if (x.hasOwnProperty(i) &&
typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(i);
e(':');
g(v);
}
}
return e('}');
}
}
e('null');
return;
case 'number':
e(isFinite(x) ? +x : 'null');
return;
case 'string':
l = x.length;
e('"');
for (i = 0; i < l; i += 1) {
c = x.charAt(i);
if (c >= ' ') {
if (c == '\\' || c == '"') {
e('\\');
}
e(c);
} else {
switch (c) {
case '\b':
e('\\b');
break;
case '\f':
e('\\f');
break;
case '\n':
e('\\n');
break;
case '\r':
e('\\r');
break;
case '\t':
e('\\t');
break;
default:
c = c.charCodeAt();
e('\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16));
}
}
}
e('"');
return;
case 'boolean':
e(String(x));
return;
default:
e('null');
return;
}
}
g(v);
return a.join('');
},
/*
Parse a JSON text, producing a JavaScript value.
*/
parse: function (text) {
var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,
token = undefined,
operator = undefined;
function error(m, t) {
throw {
name: 'JSONError',
message: m,
text: t || operator || token
};
}
function next(b) {
if (b && b != operator) {
error("Expected '" + b + "'");
}
if (text) {
var t = p.exec(text);
if (t) {
if (t[2]) {
token = null;
operator = t[2];
} else {
operator = null;
try {
token = eval(t[1]);
} catch (e) {
error("Bad token", t[1]);
}
}
text = text.substring(t[0].length);
} else {
error("Unrecognized token", text);
}
} else {
token = operator = undefined;
}
}
function val() {
var k, o;
switch (operator) {
case '{':
next('{');
o = {};
if (operator != '}') {
for (;;) {
if (operator || typeof token != 'string') {
error("Missing key");
}
k = token;
next();
next(':');
o[k] = val();
if (operator != ',') {
break;
}
next(',');
}
}
next('}');
return o;
case '[':
next('[');
o = [];
if (operator != ']') {
for (;;) {
o.push(val());
if (operator != ',') {
break;
}
next(',');
}
}
next(']');
return o;
default:
if (operator !== null) {
error("Missing value");
}
k = token;
next();
return k;
}
}
next();
return val();
}
};
/***
!Setup the data serialization
***/
DataTiddler.format = "JSON";
DataTiddler.stringify = JSON.stringify;
DataTiddler.parse = JSON.parse;
//}}}
/***
|Name|DataTiddlerPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[DataTiddlerPlugin]] [[json2.js]]|
!!!!!Code
***/
//{{{
window.JSON = window.JSON2;
//}}}
/***
|''Name:''|DatePickerLibrary|
|''Description:''|DatePicker library for use with macros|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Code Repository:''|http://svn.tiddlywiki.org/Trunk/contributors/SaqImtiaz/libraries/DatePicker.js|
|''Version:''|0.9|
|''Date:''|06/04/2007|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.3.0|
***/
// /%
//!BEGIN-PLUGIN-CODE
//{{{
function $id(n) {
return document.getElementById(n);
}
DatePicker = {
days : ['S','M','T','W','T','F','S'],
cells : new Array(42),
setup : function(){
var cte = createTiddlyElement;
var table = this.table = cte(null,"table","datePickerTable");
table.style.display = 'none';
document.body.appendChild(table);
var thead = cte(table,"thead");
var hRow = cte(thead,"tr");
hRow.onclick = stopEvent;
cte(hRow,"td",null,"datePickerNav","<<").onclick = DatePicker.prevm;
cte(hRow,"td","datePickerMNS",null,null,{colSpan:"5"});
cte(hRow,"td",null,"datePickerNav",">>",{align:"right"}).onclick=DatePicker.nextm;
var tbody = cte(table,"tbody","datePickerTableBody");
var dayRow = cte(tbody,"tr",null,"datePickerDaysHeader");
for (var i=0; i<this.days.length; i++){
cte(dayRow,"td",null,null,this.days[i]);
}
},
show : function(el,dateObj,cb) {
var me = DatePicker;
var now = me.now = new Date();
if (!dateObj)
dateObj = now;
me.root = el;
if (cb)
me.root.datePickerCallback = cb;
me.scc = { m : now.getMonth(), y : now.getFullYear(), d : now.getDate() };
Popup.place(el,me.table,{x:0,y:1});
var cur = [dateObj.getDate(),dateObj.getMonth()+1,dateObj.getFullYear()];
me.cc = { m : cur[1]-1, y : cur[2] };
me.sd = { m : cur[1]-1, y : cur[2], d : cur[0] };
me.fillCalendar(cur[0],me.scc.d,cur[1]-1,cur[2]);
},
fillCalendar : function(hd,today,cm,cy) {
var me = DatePicker;
var sd = me.now.getDate();
var td = new Date(cy,cm,1)
var cd = td.getDay();
$id('datePickerMNS').innerHTML = td.formatString('MMM YYYY')
var tbody = $id('datePickerTableBody');
removeChildren(tbody);
var days = (new Date(cy, cm+1, 0)).getDate();
var day = 1;
for (var j=1;j<=6;j++) { //rows
var row = createTiddlyElement(tbody,"tr",null,"datePickerDayRow");
for (var t=1; t<=7; t++) { //cells
var d = 7 * (j-1) - (-t); //id key
if ( (d >= (cd -(-1))) && (d<=cd-(-(days))) ) {
var dip = ( ((d-cd < sd) && (cm == me.scc.m) && (cy == me.scc.y)) || (cm < me.scc.m && cy == me.scc.y) || (cy < me.scc.y) );
var htd = ( (hd != '') && (d-cd == hd) );
var hToday = ( (today != '') && (d-cd == today) && cy == me.scc.y && cm == me.scc.m );
if (htd)
_class = 'highlightedDate';
else if (dip)
_class = 'oldDate';
else if (hToday && ! htd)
_class = 'todayDate';
else
_class = 'defaultDate';
var cell = createTiddlyElement(row,"td","datePickerDay"+d,_class,d-cd);
cell.onmouseover = function(e){addClass(this,'tdover');};
cell.onmouseout = function(e){removeClass(this,'tdover');};
cell.onclick = me.selectDate;
me.cells[d] = new Date(cy,cm,d-cd);
}
else {
var cell = createTiddlyElement(row,"td","datePickerDay"+d,"emptyDate");
}
day++;
}
if(day > days + cd)
break;
}
},
nextm : function() {
var me = DatePicker;
me.cc.m += 1;
if (me.cc.m >= 12) {
me.cc.m = 0;
me.cc.y++;
}
me.fillCalendar(me.getDayStatus(me.cc.m,me.cc.y),me.scc.d,me.cc.m,me.cc.y);
return false;
},
prevm : function() {
var me = DatePicker;
me.cc.m -= 1;
if (me.cc.m < 0) {
me.cc.m = 11;
me.cc.y--;
}
me.fillCalendar(me.getDayStatus(me.cc.m,me.cc.y),me.scc.d,me.cc.m,me.cc.y);
return false;
},
getDayStatus : function(ccm,ccy){
return (ccy == this.sd.y && ccm == this.sd.m)? this.sd.d : '';
},
selectDate : function(ev){
var e = ev ? ev : window.event;
var me = DatePicker;
var date = me.cells[resolveTarget(e).id.substring(13,resolveTarget(e).id.length)];
if (me.root.datePickerCallback && typeof me.root.datePickerCallback == 'function')
me.root.datePickerCallback(me.root,date);
$id('datePickerTable').style.display = 'none';
return false;
},
onclick : function(ev){
$id("datePickerTable").style.display = 'none';
return false;
},
create : function(el,dateObj,cb){
el.onclick = el.onfocus = function(e){DatePicker.show(el,dateObj,cb);stopEvent(e)};
},
css: "table#datePickerTable td.datePickerNav {\n"+
" cursor:pointer;\n"+
"}\n"+
"\n"+
".datePickerDaysHeader td {\n"+
" text-align:center;\n"+
" background:#ABABAB;\n"+
" font:12px Arial;\n"+
"}\n"+
"\n"+
".datePickerDayRow td {\n"+
" width:18px;\n"+
" height:18px;\n"+
"}\n"+
"\n"+
"td#datePickerMNS, td.datePickerNav {\n"+
" font:bold 13px Arial;\n"+
"}\n"+
"\n"+
"table#datePickerTable {\n"+
" position:absolute;\n"+
" border-collapse:collapse;\n"+
" background:#FFFFFF;\n"+
" border:1px solid #ABABAB;\n"+
" display:none; \n"+
"}\n"+
"\n"+
"table#datePickerTable td{\n"+
" padding: 3px;\n"+
"}\n"+
"\n"+
"td#datePickerMNS {\n"+
" text-align: center;\n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td {\n"+
" background-color : #C4D3EA;\n"+
" cursor : pointer;\n"+
" border : 1px solid #6487AE;\n"+
" text-align : center;\n"+
" font : 10px Arial;\n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td.defaultDate {\n"+
" color : #333333; \n"+
" text-decoration : none; \n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td.emptyDate {\n"+
" cursor:default; \n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td.oldDate {\n"+
" color : #ABABAB;\n"+
" text-decoration : line-through;\n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td.highlightedDate {\n"+
" background : #FFF799;\n"+
" font-weight : bold;\n"+
" color : #333333;\n"+
"}\n"+
"\n"+
"tr.datePickerDayRow td.todayDate {\n"+
" font-weight : bold;\n"+
" color : red;\n"+
"}\n"+
"\n"+
"table#datePickerTable tr.datePickerDayRow td.tdover {\n"+
" background:#fc6;\n"+
"}",
init : function(){
this.setup();
addEvent(document,'click',DatePicker.onclick);
config.shadowTiddlers['StyleSheetDatePicker'] = this.css;
if(store)
store.addNotification('StyleSheetDatePicker',refreshStyles);
}
};
DatePicker.init();
//}}}
//!END-PLUGIN-CODE
// %/
/***
|Name|[[DatePlugin]]|
|Source|http://www.TiddlyTools.com/#DatePlugin|
|Documentation|http://www.TiddlyTools.com/#DatePluginInfo|
|Version|2.7.3|
|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
<<<
2011.04.23 2.7.3 added config.macros.date.tipformat for custom mouseover tooltip and config.macros.date.leadtime for custom reminder leadtime (default=90 days)
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: 3, date: new Date(2011,4,23)};
config.macros.date = {
format: 'YYYY.0MM.0DD', // default date display format
linkformat: 'YYYY.0MM.0DD', // 'dated tiddler' link format
tipformat: 'YYYY.0MM.0DD', // 'dated tiddler' link tooltip format
leadtime: 31, // find reminders up to 31 days from now
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);
var tip=date.formatString(config.macros.date.tipformat);
// 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 = tip;
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,config.macros.date.leadtime); // create a 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,config.macros.date.leadtime],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.7.3|
|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
config.macros.date.tipformat="YYYY.0MM.0DD"; // 'dated tiddler' tooltip format
//}}}
// // reminder lead time
//{{{
config.macros.date.leadtime=31; // find reminders up to 31 days from now
//}}}
// // 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);
}
//}}}
//{{{
config.options.chkDatePopupHideCreated = true;
config.options.chkDatePopupHideChanged = true;
config.options.chkDatePopupHideTagged = false;
config.macros.date.linkedbg = "";
config.macros.date.reminderstxt = "#000000";
config.macros.date.reminderspriority = "1";
config.macros.date.eventbg = "#83b8c2";
config.macros.date.eventtxt = "#000000";
config.macros.date.eventpriority = "20";
config.macros.date.birthdaylabel = "Birthday : ";
config.macros.date.birthdaybg = "#ffff00";
config.macros.date.birthdaytxt = "#000000";
config.macros.date.birthdaypriority = "10";
//}}}
//{{{
config.macros.date.holidays = [];
//}}}
//{{{
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 (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 (isToday(date)&&(cmd.todaybg!=""))
{ place.style.background = cmd.todaybg; }
if (isHoliday(date)&&(cmd.holidaybg!=""))
{ place.style.background = cmd.holidaybg; }
var reminder = hasReminders(date);
if (reminder) {
var wikifier = new Wikifier(reminder, formatter, null, null);
var e = createTiddlyElement(document.body, "div");
var jqe = jQuery(e);
jqe.css("display", "none");
wikifier.subWikify(e);
var style = jqe.children().attr("style");
removeNode(e);
if (style && (style != "")) {
jQuery(place).attr("style", style);
jQuery(link).attr("style", style);
} else if (cmd.remindersbg != "") {
place.style.background = cmd.remindersbg;
}
}
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 (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);
}
//}}}
/***
|Name|DatePluginOverride|
|License|[[TW Notes License]]|
|Requires|[[DatePlugin]] [[iCalendarPlugin]] [[ReminderMacrosOverride]]|
!!!!!Code
***/
//{{{
window.getTaskDateFomat = function() {
var dateFormat = 'YYYY-0MM-0DD';
// From CalendarPlugin config.macros.calendar.handler
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]) dateFormat = pa[0]; }
return dateFormat;
}
//}}}
//{{{
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));
var rem;
var cmd;
var date = this.date;
var timezoneHours = (date.getTimezoneOffset() / 60) | 0;
var timezoneMinutes = Math.abs(date.getTimezoneOffset() - (timezoneHours * 60));
var timezone = (Math.abs(timezoneHours) < 10 ? "0" : "") + Math.abs(timezoneHours) + (timezoneMinutes < 10 ? "0" : "") + timezoneMinutes;
var newTiddlerTags = 'tag:"task"';
if (this.tags && this.tags.task) {
newTiddlerTags = '';
var tagList = this.tags.task.readBracketedList();
for (var i = 0; i < tagList.length; i++) {
newTiddlerTags = newTiddlerTags + ' tag:"' + tagList[i] + '"';
}
}
var remindersFormat = (this.tags && this.tags.params) ? this.tags.params["remindersFormat"] : null;
remindersFormat = remindersFormat ? remindersFormat.replace(/\\/g, "\\\\") : remindersFormat;
var newTaskLabel = (this.tags && this.tags.params && (this.tags.params["newTaskLabel"] !== undefined) && (this.tags.params["newTaskLabel"] !== null)) ? this.tags.params["newTaskLabel"] : "new task...";
var newTaskPrompt = (this.tags && this.tags.params && (this.tags.params["newTaskPrompt"] !== undefined) && (this.tags.params["newTaskPrompt"] !== null)) ? this.tags.params["newTaskPrompt"] : "add a task to '%0'";
var goToTaskLabel = (this.tags && this.tags.params && (this.tags.params["goToTaskLabel"] !== undefined) && (this.tags.params["goToTaskLabel"] !== null)) ? this.tags.params["goToTaskLabel"] : ">";
var taskPrefix = (this.tags && this.tags.params && (this.tags.params["taskPrefix"] !== undefined) && (this.tags.params["taskPrefix"] !== null)) ? this.tags.params["taskPrefix"] : "@@background-color:#ffff00;[x(done)] ''Done ''@@";
taskPrefix = taskPrefix ? taskPrefix.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n") : taskPrefix;
rem='\\<\\<reminder year:%2 month:%1 day:%0 tag:\\"timezone:UTC%3\\" function:config.macros.ics.formatTitle() messagefunction:config.macros.reminder.replaceTiddlerName() messagefunction:config.macros.reminder.replaceReminderDateTime() messagefunction:config.macros.reminder.replaceTiddlerFormatting() ' + (remindersFormat ? 'format:\\"' + remindersFormat + '\\" ' : '') + 'title:\\"@@background-color:' + config.macros.date.remindersbg + ';color:' + config.macros.date.reminderstxt + ';priority:' + config.macros.date.reminderspriority + ';\\\\\\"\\\\\\"\\\\\\"TIDDLERNAME\\\\\\"\\\\\\"\\\\\\"@@ \\\\<\\\\<goToTiddler label:' + goToTaskLabel + ' title:\\{\\{\\\'REMINDERDATE : TIDDLERNAMEESC\\\'\\}\\} ' + (taskPrefix ? 'text:\\{\\{\\\'' + taskPrefix.replace(/\\/g, "\\\\") + '\\\\n\\\\n\\\'\\}\\} ' : '') + 'tag:task tag:[[TIDDLERFULLNAME]] focus:text\\\\>\\\\>\\"\\>\\>';
rem=rem.format([this.date.getDate(),this.date.getMonth()+1,this.date.getFullYear(),(date.getTimezoneOffset() <= 0 ? "+" : "-") + timezone]);
cmd="<<newTiddler label:[[" + newTaskLabel + "]] prompt:[[" + newTaskPrompt + "]]"
+" title:[[%0 : ]] text:{{\"" + (taskPrefix ? taskPrefix + "\\n\\n" : "") + "%1\\n\\n\"}} tag:%0 " + newTiddlerTags + " >>";
wikify(cmd.format([this.date.formatString(this.linkformat),rem]),
createTiddlyElement(p,'li'));
var newTiddlerTags = 'tag:"meeting"';
if (this.tags && this.tags.meeting) {
newTiddlerTags = '';
var tagList = this.tags.meeting.readBracketedList();
for (var i = 0; i < tagList.length; i++) {
newTiddlerTags = newTiddlerTags + ' tag:"' + tagList[i] + '"';
}
}
var newMeetingLabel = (this.tags && this.tags.params && this.tags.params["newMeetingLabel"]) ? this.tags.params["newMeetingLabel"] : "new meeting...";
var newMeetingPrompt = (this.tags && this.tags.params && this.tags.params["newMeetingPrompt"]) ? this.tags.params["newMeetingPrompt"] : "add a meeting to '%0'";
var goToMeetingLabel = (this.tags && this.tags.params && this.tags.params["goToMeetingLabel"]) ? this.tags.params["goToMeetingLabel"] : ">";
var meetingPrefix = (this.tags && this.tags.params && (this.tags.params["meetingPrefix"] !== undefined) && (this.tags.params["meetingPrefix"] !== null)) ? this.tags.params["meetingPrefix"] : "[x(cancelled)] Cancelled [x(doNotPublish)] Do not publish";
meetingPrefix = meetingPrefix ? meetingPrefix.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n") : meetingPrefix;
rem='\\<\\<reminder year:%2 month:%1 day:%0 tag:\\"timezone:UTC%3\\" function:config.macros.ics.formatTitle() messagefunction:config.macros.reminder.replaceTiddlerName() messagefunction:config.macros.reminder.replaceReminderDateTime() messagefunction:config.macros.reminder.replaceTiddlerFormatting() ' + (remindersFormat ? 'format:\\"' + remindersFormat + '\\" ' : '') + 'title:\\"@@background-color:' + config.macros.date.eventbg + ';color:' + config.macros.date.eventtxt + ';priority:' + config.macros.date.eventpriority + ';09:00 - 09:00 : \\\\\\"\\\\\\"\\\\\\"TIDDLERNAME\\\\\\"\\\\\\"\\\\\\"@@ \\\\<\\\\<goToTiddler label:' + goToMeetingLabel + ' title:\\{\\{\\\'REMINDERDATE : TIDDLERNAMEESC\\\'\\}\\} ' + (meetingPrefix ? 'text:\\{\\{\\\'' + meetingPrefix.replace(/\\/g, "\\\\") + '\\\\n\\\\n\\\'\\}\\} ' : '') + 'tag:meeting tag:[[TIDDLERFULLNAME]] focus:text\\\\>\\\\>\\"\\>\\>';
rem=rem.format([this.date.getDate(),this.date.getMonth()+1,this.date.getFullYear(),(date.getTimezoneOffset() <= 0 ? "+" : "-") + timezone]);
cmd="<<newTiddler label:[[" + newMeetingLabel + "]] prompt:[[" + newMeetingPrompt + "]]"
+" title:[[%0 : ]] text:{{\"" + (meetingPrefix ? meetingPrefix + "\\n\\n" : "") + "%1\\n\\n\"}} tag:%0 " + newTiddlerTags + " >>";
wikify(cmd.format([this.date.formatString(this.linkformat),rem]),
createTiddlyElement(p,'li'));
addCreatedsToPopup(p,this.date,this.format,this.linkformat);
addModifiedsToPopup(p,this.date,this.format,this.linkformat);
addTaggedToPopup(p,this.date,this.linkformat,this.tags);
addRemindersToPopup(p,this.date,this.linkformat,this.tags);
Popup.show(); e.cancelBubble=true; if(e.stopPropagation)e.stopPropagation(); return false;
}
//}}}
//{{{
function addRemindersToPopup(p,when,format,tags)
{
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],tags ? tags.filter : null,1);
createTiddlyElement(p,'div',null,null,((tags && tags.params && tags.params["remindersLabel"]) ? tags.params["remindersLabel"] : 'reminders') + ' ('+(reminders.length||((tags && tags.params && tags.params["noRemindersLabel"]) ? tags.params["noRemindersLabel"] : 'none'))+')');
for(var t=0; t<reminders.length; t++) {
var link;
reminders[t]["params"]["format"] = (tags && tags.params && tags.params["remindersFormat"]) ? tags.params["remindersFormat"] : "DIFF - TITLE";
var title = getReminderMessageForDisplay(reminders[t]["diff"], reminders[t]["params"], reminders[t]["matchedDate"], reminders[t]["tiddler"]);
var goToTiddlerLengh = "<<goToTiddler".length;
var goToTiddlerMacroIndex = title.indexOf("<<goToTiddler");
goToTiddlerLengh = (goToTiddlerMacroIndex == -1) ? "<<newTiddler".length : goToTiddlerLengh;
goToTiddlerMacroIndex = (goToTiddlerMacroIndex == -1) ? title.indexOf("<<newTiddler") : goToTiddlerMacroIndex;
if (goToTiddlerMacroIndex > -1) {
link = title.substring(goToTiddlerMacroIndex + goToTiddlerLengh, title.indexOf(">>", goToTiddlerMacroIndex) + ">>".length);
} else {
link = createTiddlyLink(createTiddlyElement(p,'li'),reminders[t].tiddler,false);
}
var txt="";
if (title) {
var wikifier = new Wikifier(title, formatter, null, null);
var e = createTiddlyElement(document.body, "div");
var jqe = jQuery(e);
jqe.css("display", "none");
wikifier.subWikify(e);
txt = jqe.text();
removeNode(e);
} else {
txt = reminders[t].tiddler;
}
if (goToTiddlerMacroIndex > -1) {
wikify("<<goToTiddler " + ("label:[[%0]]").format(indent + txt) + link, createTiddlyElement(p,'li'));
} else {
link.appendChild(document.createTextNode(indent+txt));
}
}
return;
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.getFullYear()]);
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'));
}
//}}}
//{{{
var code = eval("showDate").toString();
var newCode = null;
var startPosition = 0;
for (var i = 0; i < 1; i++) {
startPosition = code.indexOf("link.linkformat", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' link.tags = tags; ' + code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval("showDate = function showDate(place,date,mode,format,linkformat,autostyle,weekend,tags" + code.substring(newCode.indexOf(")")));
}
var code = eval("addTaggedToPopup").toString();
var newCode = null;
var startPosition = 0;
for (var i = 0; i < 1; i++) {
startPosition = code.indexOf("getTaggedTiddlers", startPosition);
if (startPosition > -1) {
var endPosition = code.indexOf(");", startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + "getMatchingTiddlers(when.formatString(format) + (tags && tags.filter ? ' AND (' + tags.filter + ')' : '')" + code.substring(endPosition);
code = newCode;
}
}
}
var startPosition = code.indexOf("createTiddlyElement", startPosition);
if (startPosition > -1) {
var endPosition = code.indexOf(";", startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + "createTiddlyElement(p,'div',null,null,((tags && tags.params && tags.params['taggedLabel']) ? tags.params['taggedLabel'] : 'tagged') + ' ('+tagged.length+')')" + code.substring(endPosition);
code = newCode;
}
}
if (newCode != null) {
eval("addTaggedToPopup = function addTaggedToPopup(p,when,format,tags" + newCode.substring(newCode.indexOf(")")));
}
//}}}
[[DateTime Data]]: delete or rename the tiddler to clear all settings
<html>
<table border="0" cellspacing="0" cellpadding="0" align="center" style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse; text-align: center;">
<tr style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenyear" size="4" maxlength="4" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">-</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenmonth" size="3" maxlength="3" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">-</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenday" size="3" maxlength="3" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenhours" size="3" maxlength="3" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">:</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenminutes" size="3" maxlength="3" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">:</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenseconds" size="3" maxlength="3" /></td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">.</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddenmilliseconds" size="4" maxlength="4" /></td>
</tr>
<tr style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">YYYY</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">M</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">D</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">H</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">M</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">S</td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"> </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">millisec</td>
</tr>
</table>
<br />
<table border="0" cellspacing="0" cellpadding="0" style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">
<tr style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">Time Zone Offset: </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><input type="text" id="overriddentimezoneoffset" size="4" maxlength="4" /> minutes (UTC-5 would be 300)</td>
</tr>
<tr style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;">Time Zone: </td>
<td style="border: 0px; padding: 0px; border-spacing: 0; border-collapse: collapse;"><select id="overriddentimezone">
<option value=""></option>
<option value="ET">Eastern Time (US & Canada)</option>
</select></td>
</tr>
</table>
</html>
<script>
var dateTimeTiddlerName = "DateTime Data";
var value = store.getValue(dateTimeTiddlerName, "overriddenyear");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenyear").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenmonth");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenmonth").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenday");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenday").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenhours");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenhours").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenminutes");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenminutes").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenseconds");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenseconds").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddenmilliseconds");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddenmilliseconds").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddentimezoneoffset");
if (!isNaN(parseFloat(value)) && isFinite(value)) $("overriddentimezoneoffset").value = value;
value = store.getValue(dateTimeTiddlerName, "overriddentimezone");
if (value) jQuery("#overriddentimezone").val(value);
</script>
<script label="set">
store.suspendNotifications();
var dateTimeTiddlerName = "DateTime Data";
if (!store.getTiddler(dateTimeTiddlerName)) {
store.saveTiddler(dateTimeTiddlerName, dateTimeTiddlerName, "", config.options.txtUserName, new Date(), []);
}
var value = $("overriddenyear").value;
store.setValue(dateTimeTiddlerName, "overriddenyear", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Math.abs(Number(value)) | 0) : "");
value = $("overriddenmonth").value;
store.setValue(dateTimeTiddlerName, "overriddenmonth", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddenday").value;
store.setValue(dateTimeTiddlerName, "overriddenday", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddenhours").value;
store.setValue(dateTimeTiddlerName, "overriddenhours", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddenminutes").value;
store.setValue(dateTimeTiddlerName, "overriddenminutes", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddenseconds").value;
store.setValue(dateTimeTiddlerName, "overriddenseconds", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddenmilliseconds").value;
store.setValue(dateTimeTiddlerName, "overriddenmilliseconds", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = $("overriddentimezoneoffset").value;
store.setValue(dateTimeTiddlerName, "overriddentimezoneoffset", (!isNaN(parseFloat(value)) && isFinite(value)) ? "" + (Number(value) | 0) : "");
value = jQuery("#overriddentimezone").val();
store.setValue(dateTimeTiddlerName, "overriddentimezone", "" + value);
store.resumeNotifications();
story.refreshTiddler("Date and Time Settings", null, true);
store.notifyAll();
displayMessage("The date and time settings have been applied");
</script>
/***
|Name|DcTableOfContentsPlugin|
|Author|[[Doug Compton|http://www.zagware.com/tw/plugins.html#DcTableOfContentsPlugin]]|
|Contributors|[[Lewcid|http://lewcid.org]], [[FND|http://devpad.tiddlyspot.com]], [[ELS|http://www.tiddlytools.com]]|
|Source|[[FND's DevPad|http://devpad.tiddlyspot.com#DcTableOfContentsPlugin]]|
|Version|0.4.1|
|~CoreVersion|2.2|
<<showtoc>>
!Description
This macro will insert a table of contents reflecting the headings that are used in a tiddler and will be automatically updated when you make changes. Each item in the table of contents can be clicked on to jump to that heading. It can be used either inside of select tiddlers or inside a system wide template.
A parameter can be used to show the table of contents of a seperate tiddler, <<showtoc tiddlerTitle>>
It will also place a link beside each header which will jump the screen to the top of the current tiddler. This will only be displayed if the current tiddler is using the <<showtoc>> macro.
The appearance of the table of contents and the link to jump to the top can be modified using CSS. An example of this is given below.
!Usage
!!Only in select tiddlers
The table of contents above is an example of how to use this macro in a tiddler. Just insert <<showtoc>> in a tiddler on a line by itself.
It can also display the table of contents of another tiddler by using the macro with a parameter, <<showtoc tiddlerTitle>>
!!On every tiddler
It can also be used in a template to have it show on every tiddler. An example ViewTemplate is shown below.
//{{{
<div class='toolbar' macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'>Created <span macro='view created date DD-MM-YY'></span>, updated <span macro='view modified date DD-MM-YY'></span></div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class="toc" macro='showtoc'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
//}}}
!Examples
If you had a tiddler with the following headings:
{{{
!Heading1a
!!Heading2a
!!Heading2b
!!!Heading3
!Heading1b
}}}
this table of contents would be automatically generated:
* Heading1a
** Heading2a
** Heading2b
*** Heading3
* Heading1b
!Changing how it looks
To modifiy the appearance, you can use CSS similiar to the below.
//{{{
.dcTOC ul {
color: red;
list-style-type: lower-roman;
}
.dcTOC a {
color: green;
border: none;
}
.dcTOC a:hover {
background: white;
border: solid 1px;
}
.dcTOCTop {
font-size: 2em;
color: green;
}
//}}}
!Revision History
!!v0.1.0 (2006-04-07)
* initial release
!!v0.2.0 (2006-04-10)
* added the [top] link on headings to jump to the top of the current tiddler
* appearance can now be customized using CSS
* all event handlers now return false
!!v0.3.0 (2006-04-12)
* added the ability to show the table of contents of a seperate tiddler
* fixed an error when a heading had a ~WikiLink in it
!!v0.3.5 (2007-10-16)
* updated formatter object for compatibility with TiddlyWiki v2.2 (by Lewcid)
!!v0.4.0 (2007-11-14)
* added toggle button for collapsing/expanding table of contents element
* refactored documentation
!To Do
* code sanitizing/rewrite
* documentation refactoring
* use shadow tiddler for styles
!Code
***/
//{{{
version.extensions.DcTableOfContentsPlugin= {
major: 0, minor: 4, revision: 0,
type: "macro",
source: "http://devpad.tiddlyspot.com#DcTableOfContentsPlugin"
};
// Replace heading formatter with our own
for (var n=0; n<config.formatters.length; n++) {
var format = config.formatters[n];
if (format.name == 'heading') {
format.handler = function(w) {
// following two lines is the default handler
var e = createTiddlyElement(w.output, "h" + w.matchLength);
w.subWikifyTerm(e, this.termRegExp); //updated for TW 2.2+
// Only show [top] if current tiddler is using showtoc
if (w.tiddler && w.tiddler.isTOCInTiddler == 1) {
// Create a container for the default CSS values
var c = createTiddlyElement(e, "div");
c.setAttribute("style", "font-size: 0.5em; color: blue;");
// Create the link to jump to the top
createTiddlyButton(c, " [top]", "Go to top of tiddler", window.scrollToTop, "dcTOCTop", null, null);
}
}
break;
}
}
config.macros.showtoc = {
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
var text = "";
var title = "";
var myTiddler = null;
// Did they pass in a tiddler?
if (params.length) {
title = params[0];
myTiddler = store.getTiddler(title);
} else {
myTiddler = tiddler;
}
if (myTiddler == null) {
wikify("ERROR: Could not find " + title, place);
return;
}
var lines = myTiddler .text.split("\n");
myTiddler.isTOCInTiddler = 1;
// Create a parent container so the TOC can be customized using CSS
var r = createTiddlyElement(place, "div", null, "dcTOC");
// create toggle button
createTiddlyButton(r, "toggle", "show/collapse table of contents",
function() { config.macros.showtoc.toggleElement(this.nextSibling); },
"toggleButton")
// Create a container so the TOC can be customized using CSS
var c = createTiddlyElement(r, "div");
if (lines != null) {
for (var x=0; x<lines.length; x++) {
var line = lines[x];
if (line.substr(0,1) == "!") {
// Find first non ! char
for (var i=0; i<line.length; i++) {
if (line.substr(i, 1) != "!") {
break;
}
}
var desc = line.substring(i);
// Remove WikiLinks
desc = desc.replace(/\[\[/g, "");
desc = desc.replace(/\]\]/g, "");
text += line.substr(0, i).replace(/[!]/g, '*');
text += '<html><a href="javascript:;" onClick="window.scrollToHeading(\'' + title + '\', \'' + desc+ '\', event)">' + desc+ '</a></html>\n';
}
}
}
wikify(text, c);
}
}
config.macros.showtoc.toggleElement = function(e) {
if(e) {
if(e.style.display != "none") {
e.style.display = "none";
} else {
e.style.display = "";
}
}
};
window.scrollToTop = function(evt) {
if (! evt)
var evt = window.event;
var target = resolveTarget(evt);
var tiddler = story.findContainingTiddler(target);
if (! tiddler)
return false;
window.scrollTo(0, ensureVisible(tiddler));
return false;
};
window.scrollToHeading = function(title, anchorName, evt) {
var tiddler = null;
if (! evt)
var evt = window.event;
if (title) {
story.displayTiddler(store.getTiddler(title), title, null, false);
tiddler = document.getElementById(story.idPrefix + title);
} else {
var target = resolveTarget(evt);
tiddler = story.findContainingTiddler(target);
}
if (tiddler == null)
return false;
var children1 = tiddler.getElementsByTagName("h1");
var children2 = tiddler.getElementsByTagName("h2");
var children3 = tiddler.getElementsByTagName("h3");
var children4 = tiddler.getElementsByTagName("h4");
var children5 = tiddler.getElementsByTagName("h5");
var children = new Array();
children = children.concat(children1, children2, children3, children4, children5);
for (var i = 0; i < children.length; i++) {
for (var j = 0; j < children[i].length; j++) {
var heading = children[i][j].innerHTML;
// Remove all HTML tags
while (heading.indexOf("<") >= 0) {
heading = heading.substring(0, heading.indexOf("<")) + heading.substring(heading.indexOf(">") + 1);
}
// Cut off the code added in showtoc for TOP
heading = heading.substr(0, heading.length-6);
if (heading == anchorName) {
var y = findPosY(children[i][j]);
window.scrollTo(0,y);
return false;
}
}
}
return false
};
//}}}
/***
|Name|DcTableOfContentsPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[DcTableOfContentsPlugin]]|
!!!!!Code
***/
//{{{
config.macros.showtoc.escape = function (value) {
if (value) {
value = value.replace(/&/g, "&").replace(/'/g, "'").replace(/"/g, """).replace(/&/g, "&");
}
return value;
};
config.macros.showtoc.unescape = function (value) {
if (value) {
value = value.replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
}
return value;
};
config.macros.showtoc.nodeListArrayToArray = function (nodeListArray) {
var array = []
if (nodeListArray) {
for (var i = 0; i < nodeListArray.length; i++) {
for (var j = 0; j < nodeListArray[i].length; j++) {
array[array.length] = nodeListArray[i][j];
}
}
}
return array;
};
config.macros.showtoc.documentPositionComparator = function (a, b) {
// https://stackoverflow.com/questions/31991235/sort-elements-by-document-order-in-javascript/31992057
if (a === b) {
return 0;
}
var position = a.compareDocumentPosition(b);
if (position & Node.DOCUMENT_POSITION_FOLLOWING || position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
return -1;
} else if (position & Node.DOCUMENT_POSITION_PRECEDING || position & Node.DOCUMENT_POSITION_CONTAINS) {
return 1;
} else {
return 0;
}
};
//}}}
//{{{
(function () {
var code = eval("window.scrollToHeading").toString();
code = code.replace(/(while)([\s\S]*?)(\+.*?;)([\s\S]*?\})([\s\S]*?;)/, "if$2;$4");
code = code.replace(/var/, "title = config.macros.showtoc.unescape(title);\n\tanchorName = config.macros.showtoc.unescape(anchorName);\n\n\tvar");
code = code.replace(/var/, "var anchorIndex = 0;\n\tanchorName.replace(/^(.*?)(\\[)(\\d*)(\\])$/, function (m, p1, p2, p3, p4) {\n\t\tanchorName = p1;\n\t\tanchorIndex = jQuery.isNumeric(p3) ? parseInt(p3) : anchorIndex;\n\t});\n\n\tvar");
code = code.replace(/(children.concat.*?;)([\s\S]*?\{)([\s\S]*?\{)([\s\S]*?return.*?;[\s\S]*?\})([\s\S]*?\})/, "$1\n\tchildren = config.macros.showtoc.nodeListArrayToArray(children).sort(config.macros.showtoc.documentPositionComparator);$2$4");
code = code.replace(/children\[i\]\[j\]/g, "children[i]");
code = code.replace(/(for \(var i)/, "var currentAnchorIndex = -1;\n\t$1");
code = code.replace(/(if \(heading == anchorName)/, "currentAnchorIndex += (heading == anchorName) ? 1 : 0;\n\t\t\t$1 && currentAnchorIndex === anchorIndex");
eval("window.scrollToHeading = function scrollToHeading" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
for (var n=0; n<config.formatters.length; n++) {
var format = config.formatters[n];
if (format.name == 'heading') {
var code = format.handler.toString();
code = code.replace(/(createTiddlyButton.*?)(\[top\])(.*?,)(.*?,)/, "$1[^]$3 null,");
eval("format.handler = function formatHandler" + code.substring(code.indexOf("(")));
break;
}
}
})();
//}}}
//{{{
(function () {
var code = eval("config.macros.showtoc.handler").toString();
code = code.replace(/(createTiddlyButton.*?)(toggle)(.*?,)(.*?,)/, "$1+/-$3 null,");
code = code.replace(/(var desc.*?;)/, '$1 if (desc.indexOf("~") == 0) continue;');
code = code.replace(/(myTiddler.*?;)/, "$1\n\t\tvar anchorIndexes = {};");
code = code.replace(/(.replace\(\/\\\]\\\]\/g.*?;)/, "$1\n\n\t\t\t\t\tanchorIndexes[desc] = (anchorIndexes[desc] == null) ? 0 : anchorIndexes[desc] + 1;");
code = code.replace(/(window\.scrollToHeading\()(.*?)(title)(.*?)(desc)(.*?)(, event\))/, "$1$2config.macros.showtoc.escape(title)$4config.macros.showtoc.escape(desc) + '[' + anchorIndexes[desc] + ']' $6$7");
eval("config.macros.showtoc.handler = function showtocHandler" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name|DevicePlugin|
|License|[[TW Notes License]]|
|Requires|[[json2.js]]|
<<device keepScreenOn>>
<<device nativeLogging>> <<device dumpLogs>> <<device clearLogs>>
<<device permissions>>
!!!!!Code
***/
//{{{
if (config.options.txtDevicePluginDialogUrls === undefined) {
config.options.txtDevicePluginDialogUrls = "";
}
if (config.options.txtDevicePluginLogDumpTiddler === undefined) {
config.options.txtDevicePluginLogDumpTiddler = "";
}
merge(config.optionsDesc, {
txtDevicePluginDialogUrls: "The bracketed list of the prefixes of URLs to be opened in an application dialog instead of natively (reload required after modification)",
txtDevicePluginLogDumpTiddler: "The name of the tiddler into which device logs are to be dumped (blank to dump logs to screen only)"
});
//}}}
//{{{
config.macros.device = {
supported: typeof Device !== "undefined",
init: function () {
if (config.macros.device.supported && jQuery.isFunction(Device.setDialogUrls) && config.options.txtDevicePluginDialogUrls) {
Device.setDialogUrls(config.options.txtDevicePluginDialogUrls.readBracketedList());
}
},
scheduledWakeupTags: {
list: [],
map: {}
},
setScheduledWakeupTag: function (tag, tagIndex) {
tagIndex = Number.isInteger(tagIndex) ? tagIndex : -1;
var cmd = config.macros.device;
if ((tag != null) && (tagIndex > -1)) {
cmd.scheduledWakeupTags.list[tagIndex] = tag;
cmd.scheduledWakeupTags.map[tag] = tagIndex;
} else if ((tag != null) && (tagIndex < 0)) {
if (cmd.scheduledWakeupTags.map[tag] != null) {
tagIndex = cmd.scheduledWakeupTags.map[tag];
} else {
var i = 0;
while (tagIndex < 0) {
if (cmd.scheduledWakeupTags.list[i] == null) {
tagIndex = i;
cmd.scheduledWakeupTags.list[tagIndex] = tag;
cmd.scheduledWakeupTags.map[tag] = tagIndex;
} else {
i++;
}
}
}
} else if ((tag == null) && (tagIndex > -1)) {
if (cmd.scheduledWakeupTags.list[tagIndex] != null) {
if (cmd.scheduledWakeupTags.map[cmd.scheduledWakeupTags.list[tagIndex]] != null) {
delete cmd.scheduledWakeupTags.map[cmd.scheduledWakeupTags.list[tagIndex]];
}
cmd.scheduledWakeupTags.list[tagIndex] = null;
}
tagIndex = -1;
}
return tagIndex;
},
scheduledWakeupListeners: [],
notifyScheduledWakeup: function (tagIndex) {
var cmd = config.macros.device;
for (var i = 0; i < cmd.scheduledWakeupListeners.length; i++) {
if (cmd.scheduledWakeupListeners[i] && jQuery.isFunction(cmd.scheduledWakeupListeners[i].scheduledDeviceWakeup)) {
cmd.scheduledWakeupListeners[i].scheduledDeviceWakeup(cmd.scheduledWakeupTags.list[tagIndex]);
}
}
},
scheduleWakeup: function (wakeupInMillis, keepAwakeMillis, tag, useAlarmClock) {
var tagIndex = config.macros.device.setScheduledWakeupTag(tag);
Device.scheduleWakeup(wakeupInMillis, keepAwakeMillis, tagIndex, useAlarmClock);
},
cancelWakeup: function (tag) {
var tagIndex = config.macros.device.setScheduledWakeupTag(tag);
Device.cancelWakeup(tagIndex);
},
HTML5DownloadSaveFile: (config.macros.timestampedDownloadSaver && config.macros.timestampedDownloadSaver.HTML5DownloadSaveFile)
? config.macros.timestampedDownloadSaver.HTML5DownloadSaveFile
: window.HTML5DownloadSaveFile,
saveFile: function (filePath, content) {
config.saveByDownload = true;
var slashpos = filePath.lastIndexOf("/");
slashpos = (slashpos === -1) ? filePath.lastIndexOf("\\") : slashpos;
return Device.saveFile(filePath.substr(slashpos + 1), content, true);
},
console: console,
log: function (level, tag, msg) {
var cmd = config.macros.device;
if (cmd.supported) {
Device.log(level, tag, msg);
} else {
var d = new Date();
d = d.getFullYear() + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" + ("0" + d.getDate()).slice(-2) + " " + ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2) + ":" + ("0" + d.getSeconds()).slice(-2) + "." + ("00" + d.getMilliseconds()).slice(-3);
(cmd.log.console[level] || cmd.console.log).call(cmd.console, d + " " + tag + ": " + msg)
}
},
handlers: {},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var cmd = config.macros.device;
var initCheckbox = function (checkbox, handler) {
if (cmd.supported) {
checkbox.children("input").click(handler);
} else {
checkbox.children("input").attr("disabled", "disabled");
checkbox.children("input").attr("readonly", "readonly");
checkbox.css("cursor", "not-allowed");
checkbox.attr("title", "unsupported device");
}
return checkbox;
};
var initButton = function (parent, text, tooltip, action) {
var button;
var buttonAction = (!cmd.supported || !action) ? null : function () {
var result = action.call(this);
if (result != null) {
var resultPlace = button.nextAll("br").eq(0).next("span.buttonActionResult");
if (!resultPlace.length) {
resultPlace = jQuery("<span class='buttonActionResult'></span>").insertAfter(button.nextAll("br").eq(0));
}
resultPlace.empty();
wikify(result, resultPlace[0]);
}
};
button = jQuery(createTiddlyButton(parent, text, tooltip, buttonAction));
if (!cmd.supported) {
button.css("cursor", "not-allowed");
button.attr("title", "unsupported device");
}
return button;
};
if (cmd.handlers[params[0]]) {
cmd.handlers[params[0]].apply(this, arguments);
} else if (params[0] === "keepScreenOn") {
initCheckbox(jQuery("<label><input type='checkbox' /> keep screen on</label>").appendTo(place), function () {
Device.keepScreenOn(jQuery(this).prop("checked"));
}).children("input").prop("checked", cmd.supported && Device.isScreenKeptOn());
} else if (params[0] === "nativeLogging") {
initCheckbox(jQuery("<label><input type='checkbox' /> use native logging</label>").appendTo(place), function () {
Device.useNativeLogging(jQuery(this).prop("checked"));
}).children("input").prop("checked", cmd.supported && Device.isNativeLoggingUsed());
} else if (params[0] === "dumpLogs") {
initButton(place, "dump logs", "Display system logs", function () {
var result = '{{{\n' + Device.dumpLogs(null) + '\n}}}';
var logTiddler = config.options.txtDevicePluginLogDumpTiddler;
if (logTiddler) {
if (store.tiddlerExists(logTiddler)) {
logTiddler = store.getTiddler(logTiddler);
logTiddler.modifier = config.options.txtUserName;
logTiddler.modified = new Date();
} else {
var created = new Date();
logTiddler = store.saveTiddler(logTiddler, logTiddler, "", config.options.txtUserName, created, null, null, false, created, config.options.txtUserName);
}
logTiddler.text = result;
store.setDirty(true);
result = "\n[[" + logTiddler.title + "]]\n" + result;
} else if (config.macros.copyToClipboard) {
result = '<clip title="">' + result + '</clip>';
}
return result;
});
} else if (params[0] === "clearLogs") {
initButton(place, "clear logs", "Clear system logs", function () {
if (confirm("Clear system logs? This cannot be undone.")) {
var result = Device.clearLogs();
if (!result) {
displayMessage("logs cleared");
} else {
result = '{{{\n' + result + '\n}}}';
if (config.macros.copyToClipboard) {
result = '<clip title="">' + result + '</clip>';
}
return result;
}
}
});
} else if (params[0] === "permissions") {
initButton(place, "permissions", "List application permissions", function () {
var result = Device.listPermissions();
try {
result = '{{{\n' + JSON2.stringify(JSON.parse(result), null, "\t") + '\n}}}';
} catch (e) {
result = '{{{\n' + result + '\n}}}\n';
result += '{{{\n' + JSON2.stringify(e, null, "\t") + '\n}}}';
}
if (config.macros.copyToClipboard) {
result = '<clip title="">' + result + '</clip>';
}
return result;
});
}
}
};
//}}}
//{{{
(function () {
var cmd = config.macros.device;
cmd.log.VERBOSE = 2;
cmd.log.DEBUG = 3;
cmd.log.INFO = 4;
cmd.log.WARN = 5;
cmd.log.ERROR = 6;
cmd.log.console = [];
cmd.log.console[cmd.log.VERBOSE] = cmd.console.debug;
cmd.log.console[cmd.log.DEBUG] = cmd.console.debug;
cmd.log.console[cmd.log.INFO] = cmd.console.info;
cmd.log.console[cmd.log.WARN] = cmd.console.warn;
cmd.log.console[cmd.log.ERROR] = cmd.console.error;
if (cmd.supported) {
console = {
log: function () { try { cmd.log(cmd.log.INFO, "window.console", JSON2.stringify(arguments)) } catch (e) { cmd.console.log(e, arguments) } },
error: function () { try { cmd.log(cmd.log.ERROR, "window.console", JSON2.stringify(arguments)) } catch (e) { cmd.console.log(e, arguments) } },
warn: function () { try { cmd.log(cmd.log.WARN, "window.console", JSON2.stringify(arguments)) } catch (e) { cmd.console.log(e, arguments) } },
info: function () { try { cmd.log(cmd.log.INFO, "window.console", JSON2.stringify(arguments)) } catch (e) { cmd.console.log(e, arguments) } },
debug: function () { try { cmd.log(cmd.log.DEBUG, "window.console", JSON2.stringify(arguments)) } catch (e) { cmd.console.log(e, arguments) } }
}
}
if (cmd.supported) {
if (jQuery.isFunction(Device.getClipboardText)) {
if (!window.navigator) {
window.navigator = {};
}
if (!window.navigator.clipboard) {
window.navigator.clipboard = {};
}
window.navigator.clipboard.readText = function () {
return new Promise(function (resolve, reject) {
resolve(Device.getClipboardText());
});
};
}
if (jQuery.isFunction(Device.notify) && jQuery.isFunction(Device.cancelNotification) && jQuery.isFunction(Device.checkPermission)) {
window.Notification = function (title, options) {
var notification = this;
window.Notification.instances.push(notification);
notification.index = window.Notification.instances.length - 1;
notification.title = title;
notification.options = options || {};
setTimeout(function () {
Device.notify(notification.index, notification.options.tag, title, notification.options.body, notification.options.vibrate);
if (jQuery.isFunction(notification.onshow)) {
notification.onshow();
}
}, 1);
};
window.Notification.instances = [];
window.Notification.notifyInstanceClosed = function (index) {
if (window.Notification.instances[index]) {
if (jQuery.isFunction(window.Notification.instances[index].onclose)) {
window.Notification.instances[index].onclose();
}
window.Notification.instances[index] = null;
}
};
window.Notification.prototype.close = function () {
Device.cancelNotification(this.options.tag);
window.Notification.notifyInstanceClosed(this.index);
};
window.Notification.permission = "default";
window.Notification.requestPermission = function (callback) {
if (Device.checkPermission("NOTIFICATIONS")) {
window.Notification.permission = "granted";
} else {
window.Notification.permission = "denied";
}
if (callback) {
callback(window.Notification.permission);
} else {
return new Promise(function (resolve, reject) {
resolve(window.Notification.permission);
});
}
};
}
if (jQuery.isFunction(Device.getLoadedContentPath) && (typeof tw !== "undefined") && tw.io && tw.io.getOriginalLocalPath) {
tw.io.getOriginalLocalPath = function () {
var path = Device.getLoadedContentPath();
try {
path = decodeURIComponent(path);
} catch (e) {}
if (path.indexOf("raw:/") > -1) {
path = getLocalPath("file:///" + path.substring(path.indexOf("raw:/") + "raw:/".length));
} else if (config.options.txtSavingFixFileName) {
path = "file:///" + config.options.txtSavingFixFileName;
} else {
path = Device.getLoadedContentPath();
}
return path;
};
}
if (jQuery.isFunction(Device.saveFile)) {
if (config.macros.timestampedDownloadSaver) {
config.macros.timestampedDownloadSaver.HTML5DownloadSaveFile = cmd.saveFile;
} else {
window.HTML5DownloadSaveFile = cmd.saveFile;
}
}
}
})();
//}}}
/***
|Name|DiffFormatPlugin|
|License|[[TW Notes License]]|
|Requires|[[CopyToClipboardPlugin]] [[diff2html.js]] [[SnapshotPlugin]]|
!!!!!Code
***/
//{{{
config.macros.diff = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var parsedParams = paramString.parseParams("anon", null, true, false, false);
var title = null;
var filter = (parsedParams.length && parsedParams[0].filter) ? parsedParams[0].filter[0] : null;
if (filter) {
var related = null;
if (filter.indexOf("guid:") == 0) {
filter = filter.substring("guid:".length);
try {
filter = eval(filter);
} catch (e) {}
var storeTiddlers = store.getTiddlers();
for (var i = 0; i < storeTiddlers.length; i++) {
if (storeTiddlers[i].fields && storeTiddlers[i].fields["guid"]) {
if (filter.test) {
if (filter.test(storeTiddlers[i].fields["guid"])) {
title = storeTiddlers[i].title;
break;
}
} else if (storeTiddlers[i].fields["guid"].indexOf(filter) > -1) {
title = storeTiddlers[i].title;
break;
}
}
}
} else {
if (filter.indexOf("tagged:") == 0) {
related = tiddler ? store.getTaggedTiddlers(tiddler.title) : related;
filter = filter.substring("tagged:".length);
} else if (filter.indexOf("tag:") == 0) {
related = tiddler ? tiddler.tags : related;
filter = filter.substring("tag:".length);
}
related = related ? related : store.getTiddlers();
try {
filter = eval(filter);
} catch (e) {}
for (var i = 0; i < (related ? related.length : 0); i++) {
var currentTitle = related[i].title ? related[i].title : related[i];
if (filter.test) {
if (filter.test(currentTitle)) {
title = currentTitle;
break;
}
} else if (currentTitle.indexOf(filter) > -1) {
title = currentTitle;
break;
}
}
}
}
var processors = parsedParams.length ? parsedParams[0].processor : null;
var sourceReference = (parsedParams.length && parsedParams[0].source) ? parsedParams[0].source[0] : null;
var source = null;
if (title !== null) {
store.getTiddlerText(title);
source = (sourceReference !== null) ? store.getTiddlerText(title + sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(title + (processors ? "" : "##diff"));
}
if (source === null) {
var tiddlerTitle = tiddler ? tiddler.title : "";
source = (sourceReference !== null) ? store.getTiddlerText(sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + ((sourceReference !== null) ? sourceReference : (processors ? "" : "##diff")));
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + (processors ? "" : "##diff"));
}
if (processors) {
for (var i = 0; i < processors.length; i++) {
try {
eval(processors[i]);
} catch (e) {
throw "processing error: " + e;
}
}
}
if (source) {
source = source.trim();
source = ((source.indexOf("%/") > -1) && (source.lastIndexOf("%/") === source.length - "%/".length))
? source.substring(0, source.lastIndexOf("%/")).trim()
: source;
var diff = jQuery(
Diff2Html.getPrettyHtml(source, {
inputFormat: "diff",
showFiles: true,
matching: "lines",
outputFormat: "side-by-side"
}
));
diff.find(".d2h-diff-table").find("td.d2h-emptyplaceholder").empty().html(" ");
diff.find(".d2h-diff-table").find("span.d2h-code-line-ctn").each(function (index, element) {
var code = jQuery(element);
code.text(code.text().replace(/ /g, " "));
});
diff.find("div.d2h-file-wrapper").each(function (index, element) {
var file = jQuery(element);
var files = [];
var copyCode
= 'var id = "' + file.attr('id') + '"; '
+ 'var result = jQuery("<div></div>").append(jQuery("#" + id).clone()); '
+ 'result.find("span.delete-line").remove(); '
+ 'result.find("div.d2h-file-header").children("span")[1].remove(); '
+ 'var i = 1; while (jQuery("#diff-" + i).length) i++; result.attr("id", "diff-" + i); '
+ 'result.children().removeAttr("id"); '
+ 'result.find("div.d2h-file-wrapper").css("width", "max-content"); '
+ 'result.find("div.d2h-files-diff").css("white-space", "nowrap").css("height", "auto"); '
+ 'result.find("div.d2h-file-side-diff").css("width", "auto").css("overflow", "visible"); '
+ 'result.find("td.d2h-code-side-linenumber").css("position", "static"); '
+ 'result.find("div.d2h-code-side-line").css("padding", "0 0.5em"); '
+ 'result.find("span.d2h-code-line-ctn").each(function (index, element) { var code = jQuery(element); code.text(code.text().replace(/^\\s+/, function (m) { return m.replace(/\\s/g, String.fromCharCode(160)); })); }); '
+ 'result = jQuery("<div></div>").css("width", "100%").css("overflow", "scroll").append(result); '
+ 'result.prepend("<span><strong>DOM ID:</strong> diff-" + i + "<span>"); '
+ 'return "<nowiki><html>" + jQuery("<div></div>").append(result).html() + "</html></nowiki>";'
var copy = jQuery("<span></span>");
wikify('<clip label="" title=""><script>' + copyCode + '</' + 'script></clip>', copy[0]);
file.find("div.d2h-file-header").append(copy);
file.find("div.d2h-files-diff").find(".d2h-file-side-diff").each(function (index, element) {
var fileIndex = index;
files[fileIndex] = {rows: [], colours: []};
jQuery(element).find(".d2h-diff-table").find("tr").each(function (index, element) {
var rowIndex = index;
files[fileIndex].rows[rowIndex] = jQuery(element);
files[fileIndex].colours[rowIndex] = "";
var deleteLine = jQuery("<span class='delete-line' style='float: left'>x</span>");
deleteLine.on("click", function () {
for (var i = 0; i < files.length; i++) {
files[i].rows[rowIndex].remove();
}
});
files[fileIndex].rows[rowIndex].find(".d2h-code-side-linenumber").prepend(deleteLine);
files[fileIndex].rows[rowIndex].on("click", function () {
var colour = files[fileIndex].colours[rowIndex] ? "" : "yellow";
for (var i = 0; i < files.length; i++) {
files[i].colours[rowIndex] = colour;
files[i].rows[rowIndex].find("td").each(function (index, element) {
jQuery(this).css("background-color", colour);
jQuery(this).find("div.d2h-code-side-line").css("background-color", colour);
});
}
});
});
});
});
var diffid = parsedParams.length ? parsedParams[0].diffid : null;
var style = parsedParams.length ? parsedParams[0].style : null;
jQuery("<div" + (diffid ? " id='" + diffid + "'" : "") + (style ? " style='" + style + "'" : "") + "></div>").append(diff).appendTo(place);
}
}
}
//}}}
TiddlyWiki FireFox TiddlyTools TiddlyTech HowTo $1
/***
|Name|DisableWikiLinksPlugin|
|Source|http://www.TiddlyTools.com/#DisableWikiLinksPlugin|
|Version|1.6.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|selectively disable TiddlyWiki's automatic ~WikiWord linking behavior|
This plugin allows you to disable TiddlyWiki's automatic ~WikiWord linking behavior, so that WikiWords embedded in tiddler content will be rendered as regular text, instead of being automatically converted to tiddler links. To create a tiddler link when automatic linking is disabled, you must enclose the link text within {{{[[...]]}}}.
!!!!!Usage
<<<
You can block automatic WikiWord linking behavior for any specific tiddler by ''tagging it with<<tag excludeWikiWords>>'' (see configuration below) or, check a plugin option to disable automatic WikiWord links to non-existing tiddler titles, while still linking WikiWords that correspond to existing tiddlers titles or shadow tiddler titles. You can also block specific selected WikiWords from being automatically linked by listing them in [[DisableWikiLinksList]] (see configuration below), separated by whitespace. This tiddler is optional and, when present, causes the listed words to always be excluded, even if automatic linking of other WikiWords is being permitted.
Note: WikiWords contained in default ''shadow'' tiddlers will be automatically linked unless you select an additional checkbox option lets you disable these automatic links as well, though this is not recommended, since it can make it more difficult to access some TiddlyWiki standard default content (such as AdvancedOptions or SideBarTabs)
<<<
!!!!!Configuration
<<<
<<option chkDisableWikiLinks>> Disable ALL automatic WikiWord tiddler links
<<option chkAllowLinksFromShadowTiddlers>> ... except for WikiWords //contained in// shadow tiddlers
<<option chkDisableNonExistingWikiLinks>> Disable automatic WikiWord links for non-existing tiddlers
Disable automatic WikiWord links for words listed in: <<option txtDisableWikiLinksList>>
Disable automatic WikiWord links for tiddlers tagged with: <<option txtDisableWikiLinksTag>>
<<<
!!!!!Revisions
<<<
2008.07.22 [1.6.0] hijack tiddler changed() method to filter disabled wiki words from internal links[] array (so they won't appear in the missing tiddlers list)
2007.06.09 [1.5.0] added configurable txtDisableWikiLinksTag (default value: "excludeWikiWords") to allows selective disabling of automatic WikiWord links for any tiddler tagged with that value.
2006.12.31 [1.4.0] in formatter, test for chkDisableNonExistingWikiLinks
2006.12.09 [1.3.0] in formatter, test for excluded wiki words specified in DisableWikiLinksList
2006.12.09 [1.2.2] fix logic in autoLinkWikiWords() (was allowing links TO shadow tiddlers, even when chkDisableWikiLinks is TRUE).
2006.12.09 [1.2.1] revised logic for handling links in shadow content
2006.12.08 [1.2.0] added hijack of Tiddler.prototype.autoLinkWikiWords so regular (non-bracketed) WikiWords won't be added to the missing list
2006.05.24 [1.1.0] added option to NOT bypass automatic wikiword links when displaying default shadow content (default is to auto-link shadow content)
2006.02.05 [1.0.1] wrapped wikifier hijack in init function to eliminate globals and avoid FireFox 1.5.0.1 crash bug when referencing globals
2005.12.09 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.DisableWikiLinksPlugin= {major: 1, minor: 6, revision: 0, date: new Date(2008,7,22)};
if (config.options.chkDisableNonExistingWikiLinks==undefined) config.options.chkDisableNonExistingWikiLinks= false;
if (config.options.chkDisableWikiLinks==undefined) config.options.chkDisableWikiLinks=false;
if (config.options.txtDisableWikiLinksList==undefined) config.options.txtDisableWikiLinksList="DisableWikiLinksList";
if (config.options.chkAllowLinksFromShadowTiddlers==undefined) config.options.chkAllowLinksFromShadowTiddlers=true;
if (config.options.txtDisableWikiLinksTag==undefined) config.options.txtDisableWikiLinksTag="excludeWikiWords";
// find the formatter for wikiLink and replace handler with 'pass-thru' rendering
initDisableWikiLinksFormatter();
function initDisableWikiLinksFormatter() {
for (var i=0; i<config.formatters.length && config.formatters[i].name!="wikiLink"; i++);
config.formatters[i].coreHandler=config.formatters[i].handler;
config.formatters[i].handler=function(w) {
// supress any leading "~" (if present)
var skip=(w.matchText.substr(0,1)==config.textPrimitives.unWikiLink)?1:0;
var title=w.matchText.substr(skip);
var exists=store.tiddlerExists(title);
var inShadow=w.tiddler && store.isShadowTiddler(w.tiddler.title);
// check for excluded Tiddler
if (w.tiddler && w.tiddler.isTagged(config.options.txtDisableWikiLinksTag))
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// check for specific excluded wiki words
var t=store.getTiddlerText(config.options.txtDisableWikiLinksList);
if (t && t.length && t.indexOf(w.matchText)!=-1)
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// if not disabling links from shadows (default setting)
if (config.options.chkAllowLinksFromShadowTiddlers && inShadow)
return this.coreHandler(w);
// check for non-existing non-shadow tiddler
if (config.options.chkDisableNonExistingWikiLinks && !exists)
{ w.outputText(w.output,w.matchStart+skip,w.nextMatch); return; }
// if not enabled, just do standard WikiWord link formatting
if (!config.options.chkDisableWikiLinks)
return this.coreHandler(w);
// just return text without linking
w.outputText(w.output,w.matchStart+skip,w.nextMatch)
}
}
Tiddler.prototype.coreAutoLinkWikiWords = Tiddler.prototype.autoLinkWikiWords;
Tiddler.prototype.autoLinkWikiWords = function()
{
// if all automatic links are not disabled, just return results from core function
if (!config.options.chkDisableWikiLinks)
return this.coreAutoLinkWikiWords.apply(this,arguments);
return false;
}
Tiddler.prototype.disableWikiLinks_changed = Tiddler.prototype.changed;
Tiddler.prototype.changed = function()
{
this.disableWikiLinks_changed.apply(this,arguments);
// remove excluded wiki words from links array
var t=store.getTiddlerText(config.options.txtDisableWikiLinksList,"").readBracketedList();
if (t.length) for (var i=0; i<t.length; i++)
if (this.links.contains(t[i]))
this.links.splice(this.links.indexOf(t[i]),1);
};
//}}}
//{{{
for (var i = 0; i < config.formatters.length; i++) {
if (config.formatters[i].name == "image") {
config.textPrimitives.imageLink = config.formatters[i].lookaheadRegExp.source;
break;
}
}
config.textPrimitives.tiddlerForcedLinkAndImageRegExp = config.textPrimitives.imageLink ? new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")|(?:" +
config.textPrimitives.imageLink + ")","mg") : config.textPrimitives.tiddlerForcedLinkRegExp;
config.textPrimitives.tiddlerAnyLinkAndImageRegExp = config.textPrimitives.imageLink ? new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")|(?:" +
config.textPrimitives.imageLink + ")","mg") : config.textPrimitives.tiddlerAnyLinkRegExp;
//}}}
//{{{
var code = eval(((typeof DataTiddler != "undefined") && DataTiddler.originalTiddlerChangedFunction) ? "DataTiddler.originalTiddlerChangedFunction" : "Tiddler.prototype.disableWikiLinks_changed").toString();
var newCode = null;
var startPosition = code.indexOf("config.textPrimitives.tiddlerAnyLinkRegExp");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "config.textPrimitives.tiddlerAnyLinkAndImageRegExp" + code.substring(startPosition + "config.textPrimitives.tiddlerAnyLinkRegExp".length);
code = newCode;
}
var startPosition = code.indexOf("config.textPrimitives.tiddlerForcedLinkRegExp");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "config.textPrimitives.tiddlerForcedLinkAndImageRegExp" + code.substring(startPosition + "config.textPrimitives.tiddlerForcedLinkRegExp".length);
code = newCode;
}
var startPosition = code.lastIndexOf("else ");
if (startPosition > -1) {
startPosition = code.indexOf("tiddlerLinkRegExp", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition)
+ " else if (formatMatch[8 - t]) { "
+ "if (!config.formatterHelpers.isExternalLink(formatMatch[8 - t])) { "
+ "this.links.pushUnique(formatMatch[8 - t]); "
+ "} "
+ "if (formatMatch[9 - t] && !config.formatterHelpers.isExternalLink(formatMatch[9 - t])) { "
+ "this.links.pushUnique(formatMatch[9 - t]); "
+ "} "
+ "} "
+ code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval((((typeof DataTiddler != "undefined") && DataTiddler.originalTiddlerChangedFunction) ? "DataTiddler.originalTiddlerChangedFunction = function originalTiddlerChangedFunction" : "Tiddler.prototype.disableWikiLinks_changed = function disableWikiLinks_changed") + code.substring(newCode.indexOf("(")));
}
//}}}
/***
|Name|EPUBPlugin|
|License|[[TW Notes License]]|
|Requires|[[CollatorPlugin]] [[JSZip]]|
!!!!!Code
***/
//{{{
config.macros.epub = {
readers: {},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var parsedParams = paramString.parseParams("anon", null, true, false, false);
var title = null;
var filter = (parsedParams.length && parsedParams[0].filter) ? parsedParams[0].filter[0] : null;
if (filter) {
var related = null;
if (filter.indexOf("guid:") == 0) {
filter = filter.substring("guid:".length);
try {
filter = eval(filter);
} catch (e) {}
var storeTiddlers = store.getTiddlers();
for (var i = 0; i < storeTiddlers.length; i++) {
if (storeTiddlers[i].fields && storeTiddlers[i].fields["guid"]) {
if (filter.test) {
if (filter.test(storeTiddlers[i].fields["guid"])) {
title = storeTiddlers[i].title;
break;
}
} else if (storeTiddlers[i].fields["guid"].indexOf(filter) > -1) {
title = storeTiddlers[i].title;
break;
}
}
}
} else {
if (filter.indexOf("tagged:") == 0) {
related = tiddler ? store.getTaggedTiddlers(tiddler.title) : related;
filter = filter.substring("tagged:".length);
} else if (filter.indexOf("tag:") == 0) {
related = tiddler ? tiddler.tags : related;
filter = filter.substring("tag:".length);
}
related = related ? related : store.getTiddlers();
try {
filter = eval(filter);
} catch (e) {}
for (var i = 0; i < (related ? related.length : 0); i++) {
var currentTitle = related[i].title ? related[i].title : related[i];
if (filter.test) {
if (filter.test(currentTitle)) {
title = currentTitle;
break;
}
} else if (currentTitle.indexOf(filter) > -1) {
title = currentTitle;
break;
}
}
}
}
var processors = parsedParams.length ? parsedParams[0].processor : null;
var sourceReference = (parsedParams.length && parsedParams[0].source) ? parsedParams[0].source[0] : null;
var source = null;
if (title !== null) {
store.getTiddlerText(title);
source = (sourceReference !== null) ? store.getTiddlerText(title + sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(title + (processors ? "" : "##epub"));
}
if (source === null) {
var tiddlerTitle = tiddler ? tiddler.title : "";
source = (sourceReference !== null) ? store.getTiddlerText(sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + ((sourceReference !== null) ? sourceReference : (processors ? "" : "##epub")));
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + (processors ? "" : "##epub"));
}
if (processors) {
for (var i = 0; i < processors.length; i++) {
try {
eval(processors[i]);
} catch (e) {
throw "processing error: " + e;
}
}
}
if (source) {
source = source.trim();
source = ((source.indexOf("%/") > -1) && (source.lastIndexOf("%/") === source.length - "%/".length))
? source.substring(0, source.lastIndexOf("%/")).trim()
: source;
source = (source.indexOf(",") > -1) ? source.substring(source.indexOf(",") + 1) : source;
var epubReaderKey = tiddler.fields.guid;
var TOP = " " + epubReaderKey + "-TOP ";
var TOCENTRY_START_SPINE_SECTION_INDEX_POS = 0;
var TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS = 1;
var TOCENTRY_END_SPINE_SECTION_INDEX_POS = 2;
var TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS = 3;
var TOCENTRY_TOTAL_PARAGRAPHS_POS = 4;
var localStorageBaseKey = epubReaderKey;
var localStorageBookmarksKey = localStorageBaseKey ? "epub:" + localStorageBaseKey + ":bookmarks" : null;
var localStorageSectionKey = localStorageBaseKey ? "epub:" + localStorageBaseKey + ":section" : null;
var localStorageParagraphKey = localStorageBaseKey ? "epub:" + localStorageBaseKey + ":paragraph" : null;
var localStoragePositionKey = localStorageBaseKey ? "epub:" + localStorageBaseKey + ":position" : null;
if (!("epubfontsize" in tiddler.fields) && parsedParams.length && parsedParams[0].fontsize) {
tiddler.fields.epubfontsize = parsedParams[0].fontsize[0];
}
if (!("epubsection" in tiddler.fields) && parsedParams.length && parsedParams[0].section) {
tiddler.fields.epubsection = parsedParams[0].section[0];
if (localStorageBaseKey) {
localStorage.removeItem(localStorageBookmarksKey);
localStorage.removeItem(localStorageSectionKey);
localStorage.removeItem(localStorageParagraphKey);
localStorage.removeItem(localStoragePositionKey);
}
tiddler.fields.epubposition = "";
}
if (!("epubparagraph" in tiddler.fields) && parsedParams.length && parsedParams[0].paragraph) {
tiddler.fields.epubparagraph = parsedParams[0].paragraph[0];
if (localStorageBaseKey) {
localStorage.removeItem(localStorageBookmarksKey);
localStorage.removeItem(localStorageSectionKey);
localStorage.removeItem(localStorageParagraphKey);
localStorage.removeItem(localStoragePositionKey);
}
tiddler.fields.epubposition = "";
}
if ((localStorageSectionKey && localStorage.getItem(localStorageSectionKey) != null) && (localStorage.getItem(localStorageSectionKey) !== tiddler.fields.epubsection)
|| (localStorageParagraphKey && localStorage.getItem(localStorageParagraphKey) != null) && (localStorage.getItem(localStorageParagraphKey) !== tiddler.fields.epubparagraph)) {
if (localStoragePositionKey) {
localStorage.removeItem(localStoragePositionKey);
}
tiddler.fields.epubposition = "";
if (confirm("Resume to non-saved location?")) {
tiddler.fields.epubsection = localStorage.getItem(localStorageSectionKey);
tiddler.fields.epubparagraph = localStorage.getItem(localStorageParagraphKey);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
}
}
var localStoragePosition = localStoragePositionKey ? localStorage.getItem(localStoragePositionKey) : null;
localStoragePosition = ((localStoragePosition === tiddler.fields.epubposition) && jQuery.isNumeric(localStoragePosition)) ? parseFloat(localStoragePosition) : 0
if (localStorageBookmarksKey && (localStorage.getItem(localStorageBookmarksKey) != null) && (localStorage.getItem(localStorageBookmarksKey) !== tiddler.fields.epubbookmarks)) {
if (confirm("Restore non-saved bookmarks?")) {
tiddler.fields.epubbookmarks = localStorage.getItem(localStorageBookmarksKey);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
} else {
if (tiddler.fields.epubbookmarks == null) {
localStorage.removeItem(localStorageBookmarksKey);
} else {
localStorage.setItem(localStorageBookmarksKey, tiddler.fields.epubbookmarks);
}
}
}
var resolvePath = function (path, basePath) {
if ((basePath != null) && (basePath.indexOf("/") > -1)) {
basePath = basePath.substring(0, basePath.lastIndexOf("/") + 1);
path = basePath + path;
}
while (path.indexOf("/../") > -1) {
var index = path.indexOf("/../");
if (path.lastIndexOf("/", index - 1) > -1) {
path = path.substring(0, path.lastIndexOf("/", index - 1)) + "/" + path.substring(index + "/../".length);
} else {
path = path.substring(index + "/../".length);
}
}
return path;
};
var resolveItem = function (items, path, basePath) {
var item = items[path];
if (!item) {
path = resolvePath(path, basePath);
item = items[path] || items[decodeURIComponent(path)];
}
return item;
};
var loadContent = function (ePub, paths, items, loadCssRefs) {
return new Promise(function (resolve, reject) {
items = items || {};
if (ePub && ePub.zip && paths && paths.length) {
(function next(loop) {
if (loop.i >= loop.max) {
loop.end();
} else if (items[paths[loop.i].path] && ("content" in items[paths[loop.i].path])) {
loop.i++;
next(loop);
} else {
var file = ePub.zip.file(resolvePath((ePub.folder || "") + paths[loop.i].path));
file = file || ePub.zip.file(resolvePath(decodeURIComponent((ePub.folder || "") + paths[loop.i].path)));
if (file) {
file.async(paths[loop.i].type).then(
function (content) {
items[paths[loop.i].path] = items[paths[loop.i].path] || {};
items[paths[loop.i].path].folder = (paths[loop.i].path.indexOf("/") > -1)
? paths[loop.i].path.substring(0, paths[loop.i].path.lastIndexOf("/") + 1)
: "";
items[paths[loop.i].path].content = paths[loop.i].prefix + content;
if (loadCssRefs && (paths[loop.i].type === "string") && items[paths[loop.i].path].type && (items[paths[loop.i].path].type.toLowerCase().indexOf("css") > -1)) {
var cssRefPaths = [];
content.replace(/url\(['"]?(.*?)['"]?\)/gi, function (match, p1) {
var item = resolveItem(items, p1, paths[loop.i].path);
if (item) {
cssRefPaths.push({type: "base64", prefix: "data:" + item.type + ";base64,", path: item.path});
}
});
if (!cssRefPaths.length) {
loop.i++;
next(loop);
} else {
loadContent(ePub, cssRefPaths, items, true).then(function () {
items[paths[loop.i].path].content = paths[loop.i].prefix + content.replace(/url\(['"]?(.*?)['"]?\)/gi, function (match, p1) {
var item = resolveItem(items, p1, paths[loop.i].path);
if (item && item.content) {
return 'url("' + item.content + '")';
} else {
return match;
}
});
loop.i++;
next(loop);
});
}
} else {
loop.i++;
next(loop);
}
},
function (reason) {
if (items[paths[loop.i].path]) {
items[paths[loop.i].path].content = null;
} else {
items[paths[loop.i].path] = {content: null};
}
console.log(JSON.stringify(paths[loop.i]), reason);
loop.i++;
next(loop);
}
).catch(function (error) {
if (items[paths[loop.i].path]) {
items[paths[loop.i].path].content = null;
} else {
items[paths[loop.i].path] = {content: null};
}
console.log(JSON.stringify(paths[loop.i]), error);
loop.i++;
next(loop);
});
} else {
if (items[paths[loop.i].path]) {
items[paths[loop.i].path].content = null;
} else {
items[paths[loop.i].path] = {content: null};
}
loop.i++;
next(loop);
}
}
})({i: 0, max: paths.length, end: function () {
resolve({ePub: ePub, paths: paths, items: items});
}});
} else {
resolve({ePub: ePub, paths: paths, items: items});
}
});
};
var updateBookmarkPanel;
var search;
var searchResultPanel;
var originSection = tiddler.fields.epubsection;
var originParagraph = tiddler.fields.epubparagraph;
var originPosition = tiddler.fields.epubposition;
var updateLocation = function (section, paragraph, position) {
if ((section != null) && (paragraph != null)) {
section = "" + section;
paragraph = "" + paragraph;
position = (position == null) ? null : "" + position;
if ((section !== tiddler.fields.epubsection) || (paragraph !== tiddler.fields.epubparagraph) || ((position != null) && (position !== tiddler.fields.epubposition))) {
var previousLocation = {section: tiddler.fields.epubsection, paragraph: tiddler.fields.epubparagraph, position: tiddler.fields.epubposition};
tiddler.fields.epubsection = section;
tiddler.fields.epubparagraph = paragraph;
tiddler.fields.epubposition = jQuery.isNumeric(position) ? position : "";
localStoragePosition = jQuery.isNumeric(position) ? parseFloat(position) : 0;
if (localStorageBaseKey) {
localStorage.setItem(localStorageSectionKey, section);
localStorage.setItem(localStorageParagraphKey, paragraph);
if (jQuery.isNumeric(position)) {
localStorage.setItem(localStoragePositionKey, position);
} else {
localStorage.removeItem(localStoragePositionKey);
}
}
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
return previousLocation;
}
}
return null;
};
var paragraphIndexVersion = 3;
var paragraphElements = "a,br,div,h1,h2,h3,h4,h5,h6,p,pagebreak,span";
var filterParagraphs = function (section, callback) {
if (section) {
var paragraphTypes = {
a: {prefix: "a", id: true, counter: 0},
br: {prefix: "b", id: true, counter: 0},
div: {prefix: "d", id: false, counter: 0},
h1: {prefix: "ha", id: false, counter: 0},
h2: {prefix: "hb", id: false, counter: 0},
h3: {prefix: "hc", id: false, counter: 0},
h4: {prefix: "hd", id: false, counter: 0},
h5: {prefix: "he", id: false, counter: 0},
h6: {prefix: "hf", id: false, counter: 0},
p: {prefix: "", id: false, counter: 0},
pagebreak: {prefix: "pb", id: true, counter: 0},
span: {prefix: "s", id: true, counter: 0}
};
section.find(paragraphElements).each(function () {
var paragraph = jQuery(this);
var paragraphType = paragraphTypes[paragraph.prop("tagName").toLowerCase()];
if (paragraphType && (!paragraphType.id || (paragraph.attr("id") != null))) {
callback(paragraph, paragraphType);
}
});
}
};
var indexParagraphs = function (section) {
var paragraphIds = {};
var paragraphNos = [];
var paragraphs = [];
if (section) {
var paragraphCounter = 0;
filterParagraphs(section, function (paragraph, paragraphType) {
var paragraphNo = paragraphType.prefix + paragraphType.counter;
paragraphNos.push(paragraphNo);
paragraph.attr("pno", paragraphNo);
paragraphs.push(paragraph);
if (paragraph.attr("id") != null) {
paragraphIds[paragraph.attr("id")] = paragraphNo;
}
paragraphType.counter++;
paragraphCounter++;
});
}
return {paragraphIds: paragraphIds, paragraphNos: paragraphNos, paragraphs: paragraphs};
};
var loadReader = function (ePub, path, paragraphId) {
var readerLoaded = false;
if (!jQuery.isNumeric(tiddler.fields.epubfontsize)) {
tiddler.fields.epubfontsize = "1.00";
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
}
var fontSize = parseFloat(tiddler.fields.epubfontsize);
if (fontSize <= 0) {
fontSize = 1.00;
tiddler.fields.epubfontsize = "" + fontSize;
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
}
var readerPlace = jQuery(place).children(".epubreader");
var controlButtons = jQuery(place).children(".control");
var tocPanel = jQuery(place).find("span.toc");
if (!readerPlace.length) {
controlButtons = jQuery(
'<div class="control" style="float: right">' +
'<span class="back" style="display: none;"><button type="button" class="back" style="padding: 5px;" title="Back">←</button>' +
' </span>' +
'<button type="button" class="bookmark" style="padding: 5px;" title="Bookmark top paragraph">🔖</button>' +
' ' +
' ' +
'<button type="button" class="decrease" style="padding: 5px;" title="Decrease font size">–</button>' +
' ' +
'<button type="button" class="increase" style="padding: 5px;" title="Increase font size">+</button>' +
'</div>').appendTo(place);
jQuery('<br>').appendTo(place);
controlButtons.find("button").on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
controlButtons.find("button.bookmark").on("click", function () {
tiddler.fields.epubbookmarks = tiddler.fields.epubbookmarks || "[]";
try {
var bookmarks = JSON.parse(tiddler.fields.epubbookmarks);
var max = 0;
for (var i = 0; i < bookmarks.length; i++) {
if (bookmarks[i].label && /^bookmark [0-9]+$/.test(bookmarks[i].label)) {
var index = parseInt(bookmarks[i].label.substring(bookmarks[i].label.indexOf(" ") + 1));
max = (index > max) ? index : max;
}
}
max++;
bookmarks.push({label: "bookmark " + max, section: tiddler.fields.epubsection, paragraph: tiddler.fields.epubparagraph});
tiddler.fields.epubbookmarks = JSON.stringify(bookmarks);
if (localStorageBookmarksKey) {
localStorage.setItem(localStorageBookmarksKey, tiddler.fields.epubbookmarks);
}
bookmarks[bookmarks.length - 1].position = tiddler.fields.epubposition;
if (ePub.bookmarks) {
ePub.bookmarks.push(bookmarks[bookmarks.length - 1]);
} else {
ePub.bookmarks = bookmarks;
}
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
updateBookmarkPanel();
} catch (e) {
console.log(e);
displayMessage("Unable to process bookmarks");
}
});
controlButtons.find("button.back").on("click", function () {
var previousLocation = updateLocation(originSection, originParagraph, originPosition);
if (previousLocation != null) {
ePub.removeEventHandlers();
var destinationSection = originSection;
originSection = previousLocation.section;
originParagraph = previousLocation.paragraph;
originPosition = previousLocation.position;
loadReader(ePub, destinationSection, null);
} else {
controlButtons.find("span.back").hide();
}
});
var contentPanel = jQuery(config.macros.slider.createSlider(jQuery('<div style="padding-bottom: 5px;"></div>').appendTo(place)[0], "config.macros.epub:content:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), "Content", "Display content items"));
contentPanel = jQuery('<div style="padding-left: 10px;"></div>').appendTo(contentPanel);
tocPanel = jQuery(config.macros.slider.createSlider(jQuery('<div style="padding-top: 5px;"></div>').appendTo(contentPanel)[0], "config.macros.epub:toc:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), "Table of Contents", "Display table of contents"));
tocPanel = jQuery('<span class="fontsize toc" style="font-size: ' + fontSize + 'em; line-height: ' + (fontSize * 1.25) + 'em;"></span>').appendTo(tocPanel);
jQuery("<p><strong></strong> <i class='globalprogress'></i></p>").appendTo(tocPanel).find("strong").text(ePub.title || "eBook");
if (ePub.nav) {
var buildToc = function (nav, tocPlace, hide) {
if (nav.length) {
tocPlace = jQuery("<ul></ul>").appendTo(tocPlace);
if (hide) {
tocPlace.hide();
}
for (var i = 0; i < nav.length; i++) {
var tocItem = jQuery("<li><span><span></span> <i class='tocprogress " + nav[i].id + "'></i></span></li>").appendTo(tocPlace);
tocItem.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
tocItem.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
jQuery(this).children("ul").toggle();
});
(function () {
var tocItemContent = tocItem.children("span");
if (nav[i].nav.length) {
tocItemContent.children("span").text(nav[i].label);
tocItemContent.children("span").css("cursor", "pointer");
} else {
jQuery("<i></i>").appendTo(tocItemContent.children("span")).text(nav[i].label);
}
if (nav[i].section) {
tocItemContent.append(" <a style='cursor: pointer;'>→</a>");
var navPoint = nav[i];
tocItemContent.find("a").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
tocItemContent.closest("span.toc").find("li").children("span.highlight").removeClass("highlight");
tocItemContent.addClass("highlight");
ePub.removeEventHandlers();
originSection = tiddler.fields.epubsection;
originParagraph = tiddler.fields.epubparagraph;
originPosition = tiddler.fields.epubposition;
localStoragePosition = 0;
loadReader(ePub, navPoint.section, navPoint.paragraphId);
});
}
})();
buildToc(nav[i].nav, tocItem, true);
}
}
};
buildToc(ePub.nav, tocPanel);
}
var bookmarkPanel = jQuery(config.macros.slider.createSlider(jQuery('<div style="padding-top: 5px;"></div>').appendTo(contentPanel)[0], "config.macros.epub:bookmarks:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), "Bookmarks", "Display bookmarks"));
bookmarkPanel = jQuery('<span class="fontsize bookmarks" style="font-size: ' + fontSize + 'em; line-height: ' + (fontSize * 1.25) + 'em;"></span>').appendTo(bookmarkPanel);
updateBookmarkPanel = function () {
bookmarkPanel.empty();
if (tiddler.fields.epubbookmarks && tiddler.fields.epubbookmarks.trim()) {
try {
var bookmarks = JSON.parse(tiddler.fields.epubbookmarks);
if (bookmarks.length) {
ePub.bookmarks = ePub.bookmarks || JSON.parse(tiddler.fields.epubbookmarks);
var ePubBookmarks = ePub.bookmarks;
var bookmarkPlace = jQuery("<ul></ul>").appendTo(bookmarkPanel);
for (var i = 0; i < bookmarks.length; i++) {
var bookmarkItem = jQuery("<li><span><span style='cursor: pointer;' title='Delete bookmark'>🗑</span> " + ((i === 0) ? "<span style='font-family: monospace;'> </span>" : "<span class='up' style='cursor: pointer; font-family: monospace;' title='Move up'>↑</span>") + " <span style='cursor: pointer;' contenteditable='true' title='Edit bookmark label'></span> <a style='cursor: pointer;'>→</a></span></li>").appendTo(bookmarkPlace);
bookmarkItem.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
(function () {
var index = i;
var bookmarkItemContent = bookmarkItem.children("span");
var updateBookmarks = function () {
tiddler.fields.epubbookmarks = JSON.stringify(bookmarks);
if (localStorageBookmarksKey) {
localStorage.setItem(localStorageBookmarksKey, tiddler.fields.epubbookmarks);
}
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
updateBookmarkPanel();
};
bookmarkItemContent.children("span").eq(0).on("click", function () {
if (confirm("Delete: " + bookmarks[index].label)) {
bookmarks.splice(index, 1);
ePubBookmarks.splice(index, 1);
updateBookmarks();
}
});
bookmarkItemContent.children("span.up").on("click", function () {
var bookmark = bookmarks[index];
bookmarks.splice(index, 1);
bookmarks.splice(index - 1, 0, bookmark);
bookmark = ePubBookmarks[index];
ePubBookmarks.splice(index, 1);
ePubBookmarks.splice(index - 1, 0, bookmark);
updateBookmarks();
});
var updateBookmarkLabel = function (label) {
var text = label.text();
if ((text !== bookmarks[index].label) && text.length) {
bookmarks[index].label = text;
ePubBookmarks[index].label = text;
updateBookmarks();
} else {
label.text(bookmarks[index].label);
}
};
bookmarkItemContent.children("span").eq(2).text(bookmarks[index].label);
bookmarkItemContent.children("span").eq(2).on("blur", function () {
updateBookmarkLabel(jQuery(this));
}).keypress(function (e) {
var keycode = e.keyCode || e.which;
if (keycode == 13) {
e.preventDefault();
updateBookmarkLabel(jQuery(this));
}
});
bookmarkItemContent.find("a").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
bookmarkItemContent.closest("span.bookmarks").find("li").children("span.highlight").removeClass("highlight");
bookmarkItemContent.addClass("highlight");
var previousLocation = updateLocation(bookmarks[index].section, bookmarks[index].paragraph, ePubBookmarks[index].position);
if (previousLocation != null) {
controlButtons.find("span.back").show();
ePub.removeEventHandlers();
originSection = previousLocation.section;
originParagraph = previousLocation.paragraph;
originPosition = previousLocation.position;
loadReader(ePub, bookmarks[index].section, null);
}
});
})();
}
}
} catch (e) {
console.log(e);
displayMessage("Unable to process bookmarks");
}
}
};
updateBookmarkPanel();
var searchPanel = jQuery(config.macros.slider.createSlider(jQuery('<div style="padding-top: 5px;"></div>').appendTo(contentPanel)[0], "config.macros.epub:search:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), "Search", "Find in document"));
searchPanel.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
var searchField = jQuery('<div style="padding: 5px;"><input style="margin:0em auto 1em auto; padding:4px; border-radius:4px; border:1px solid silver;" type="text" value=""></input> <span style="cursor: pointer; font-size: 1.5em;"> 🔍 </span></div>').appendTo(searchPanel);
searchResultPanel = jQuery('<span class="fontsize search" style="font-size: ' + fontSize + 'em; line-height: ' + (fontSize * 1.25) + 'em;"></span>').appendTo(searchPanel);
var previousSearchTerm = null;
searchField.children("span").on("click", function () {
var term = jQuery(this).siblings("input").val();
if (term !== previousSearchTerm) {
previousSearchTerm = term;
search(term);
}
});
searchField.children("input").keypress(function (e) {
var keycode = e.keyCode || e.which;
if (keycode == 13) {
e.preventDefault();
var term = jQuery(this).val();
if (term !== previousSearchTerm) {
previousSearchTerm = term;
search(term);
}
}
});
readerPlace = jQuery('<span class="epubreader epubreader-' + epubReaderKey + '"></span>').appendTo(place);
readerPlace.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
}
search = function (term) {
searchResultPanel.empty();
if (term && ePub.items && ePub.spine) {
term = term.replace(/\s+/g, " ");
var resourcePaths = [];
for (var i = 0; i < ePub.spine.length; i++) {
resourcePaths.push({type: "string", prefix: "", path: ePub.spine[i]});
}
loadContent(ePub, resourcePaths, ePub.items).then(function () {
var searchResults = [];
for (var i = 0; i < ePub.spine.length; i++) {
if (ePub.items[ePub.spine[i]].content) {
var section = jQuery("<span>" + ePub.items[ePub.spine[i]].content + "</span>");
var indexedParagraphs = indexParagraphs(section);
for (var j = 0; j < indexedParagraphs.paragraphs.length; j++) {
var paragraph = indexedParagraphs.paragraphs[j].clone();
filterParagraphs(paragraph, function (paragraph) {
paragraph.remove();
});
var text = paragraph.text();
text = text.replace(/\s+/g, " ");
var index = -1;
while ((index = config.macros.collator.indexOf(text, term, index)) > -1) {
var start = index;
while ((start > 0) && !(/\s/.test(text.charAt(start - 1)))) {
start--;
}
while ((start > 0) && (/\s/.test(text.charAt(start - 1)))) {
start--;
}
while ((start > 0) && !(/\s/.test(text.charAt(start - 1)))) {
start--;
}
var end = index + term.length - 1;
while ((end < text.length - 1) && !(/\s/.test(text.charAt(end + 1)))) {
end++;
}
while ((end < text.length - 1) && (/\s/.test(text.charAt(end + 1)))) {
end++;
}
while ((end < text.length - 1) && !(/\s/.test(text.charAt(end + 1)))) {
end++;
}
var label = ((start > 0) ? "... " : "") + text.substring(start, end + 1).trim() + ((end < text.length - 1) ? " ..." : "");
searchResults.push({label: label, section: ePub.spine[i], paragraph: indexedParagraphs.paragraphNos[j]});
index += term.length;
}
}
}
}
if (searchResults.length) {
var searchResultPlace = jQuery("<ul></ul>").appendTo(searchResultPanel);
for (var i = 0; i < searchResults.length; i++) {
var searchResultItem = jQuery("<li><span><span></span> <a style='cursor: pointer;'>→</a></span></li>").appendTo(searchResultPlace);
searchResultItem.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
(function () {
var index = i;
var searchResultItemContent = searchResultItem.children("span");
searchResultItemContent.children("span").text(searchResults[index].label);
searchResultItemContent.find("a").on("click", function (e) {
e.preventDefault();
e.stopPropagation();
searchResultItemContent.closest("span.search").find("li").children("span.highlight").removeClass("highlight");
searchResultItemContent.addClass("highlight");
var previousLocation = updateLocation(searchResults[index].section, searchResults[index].paragraph);
if (previousLocation != null) {
controlButtons.find("span.back").show();
ePub.removeEventHandlers();
originSection = previousLocation.section;
originParagraph = previousLocation.paragraph;
originPosition = previousLocation.position;
loadReader(ePub, searchResults[index].section, null);
}
});
})();
}
}
});
}
};
readerPlace.empty();
var determineReaderHeight = function () {
var readerHeight = jQuery(window).height();
try {
readerHeight = (jQuery(window).height() - jQuery(place).offset().top + (jQuery(place).closest(".tiddler").offset().top - jQuery(place).closest("#displayArea").offset().top - jQuery(place).closest("#displayArea").children("#topMenu").first().height()));
} catch (e) {}
return readerHeight;
};
var reader = jQuery('<div class="fontsize" style="font-size: ' + fontSize + 'em; line-height: ' + (fontSize * 1.25) + 'em; position: relative; width: 100%; max-height: ' + determineReaderHeight() + 'px; overflow-y: scroll;"></div>').appendTo(readerPlace);
var sectionParagraphIds = {};
var sectionParagraphNos = {};
var paragraphs = [];
var paragraphNos = [];
var currentParagraph = null;
var currentParagraphPosition = -1;
var determineCurrentParagraph = function (index) {
var positionChanged = false;
var i = -1;
if (Number.isInteger(index)) {
i = index;
if (localStoragePositionKey) {
localStorage.removeItem(localStoragePositionKey);
}
tiddler.fields.epubposition = "";
positionChanged = true;
} else {
if ((currentParagraph == null) || ((currentParagraph >= 0) && (currentParagraph < paragraphs.length) && (currentParagraphPosition !== paragraphs[currentParagraph].position().top))) {
positionChanged = true;
}
if (positionChanged) {
for (i = 0; i < paragraphs.length; i++) {
if ((paragraphs[i].is(":visible") && (paragraphs[i].position().top >= 0)) || (i === paragraphs.length - 1)) {
if ((paragraphs[i].position().top > 1) && (i > 0) && paragraphs[i - 1].is(":visible")) {
i -= 1;
}
tiddler.fields.epubposition = "" + paragraphs[i].position().top;
if (localStoragePositionKey) {
localStorage.setItem(localStoragePositionKey, tiddler.fields.epubposition);
}
break;
}
}
}
}
i = (i < 0) ? 0 : i;
i = (i >= paragraphs.length) ? paragraphs.length - 1 : i;
if (positionChanged && (currentParagraph !== i)) {
currentParagraph = i;
var section = paragraphs[currentParagraph].closest("span.epubsection").attr("section");
var paragraph = paragraphs[currentParagraph].attr("pno");
if ((section != null) && (paragraph != null)) {
var sectionIndex = ePub.spine.indexOf(section);
var paragraphIndex = (sectionParagraphNos[section] && (sectionParagraphNos[section].indexOf(paragraph) > -1)) ? sectionParagraphNos[section].indexOf(paragraph) : 0;
var progress = ePub.progress.data;
var paragraphProgress = 0;
for (var j = 0; j < sectionIndex; j++) {
paragraphProgress += progress.paragraphs[j];
}
paragraphProgress += paragraphIndex;
tocPanel.find("i.globalprogress").text("(" + Math.round(paragraphProgress / ePub.progress.data.total * 100).toFixed(0) + "%)");
tocPanel.find("i.tocprogress").each(function () {
var tocProgress = jQuery(this);
var classes = tocProgress.attr("class").split(/\s+/);
for (var i = 0; i < classes.length; i++) {
var toc = progress.toc[classes[i]];
if (toc) {
if (toc.length
&& (((sectionIndex > toc[TOCENTRY_START_SPINE_SECTION_INDEX_POS]) && (sectionIndex < toc[TOCENTRY_END_SPINE_SECTION_INDEX_POS]))
|| ((sectionIndex === toc[TOCENTRY_START_SPINE_SECTION_INDEX_POS]) && (paragraphIndex >= toc[TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS]) && ((sectionIndex < toc[TOCENTRY_END_SPINE_SECTION_INDEX_POS]) || ((sectionIndex === toc[TOCENTRY_END_SPINE_SECTION_INDEX_POS]) && (paragraphIndex < toc[TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS]))))
|| ((sectionIndex === toc[TOCENTRY_END_SPINE_SECTION_INDEX_POS]) && (paragraphIndex < toc[TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS]) && ((sectionIndex > toc[TOCENTRY_START_SPINE_SECTION_INDEX_POS]) || ((sectionIndex === toc[TOCENTRY_START_SPINE_SECTION_INDEX_POS]) && (paragraphIndex > toc[TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS])))))) {
var paragraphProgress = 0;
for (var j = toc[TOCENTRY_START_SPINE_SECTION_INDEX_POS]; j < sectionIndex; j++) {
paragraphProgress += progress.paragraphs[j];
}
paragraphProgress -= toc[TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS];
paragraphProgress += paragraphIndex;
tocProgress.text("(" + Math.round(paragraphProgress / toc[TOCENTRY_TOTAL_PARAGRAPHS_POS] * 100).toFixed(0) + "%)");
tocProgress.closest("li").prevAll("li").each(function () {
jQuery(this).children("span").children("span").css("text-decoration-line", "line-through");
jQuery(this).find("li").children("span").children("span").css("text-decoration-line", "line-through");
});
tocProgress.closest("li").children("span").children("span").css("text-decoration-line", "");
tocProgress.closest("li").find("li").children("span").children("span").css("text-decoration-line", "");
tocProgress.closest("li").nextAll("li").each(function () {
jQuery(this).children("span").children("span").css("text-decoration-line", "");
jQuery(this).find("li").children("span").children("span").css("text-decoration-line", "");
});
} else {
tocProgress.text("");
}
}
}
});
}
var previousLocation = updateLocation(section, paragraph, tiddler.fields.epubposition);
if ((previousLocation != null) && (originSection != null) && (originParagraph != null)) {
controlButtons.find("span.back").show();
}
}
currentParagraphPosition = paragraphs[currentParagraph].position().top;
return positionChanged;
};
var loadSection;
var loadSections = function (currentSection) {
currentSection = (currentSection == null) ? tiddler.fields.epubsection : currentSection;
return new Promise(function (resolve, reject) {
var sectionLoaded = false;
// https://www.geeksforgeeks.org/how-to-detect-when-user-scrolls-to-the-bottom-of-a-div/
if (reader.scrollTop() + reader.innerHeight() + 2 >= reader[0].scrollHeight) {
var sectionIndex = ePub.spine.indexOf(currentSection) + 1;
while ((sectionIndex >= 0) && (sectionIndex < ePub.spine.length)) {
var sectionExists = false;
reader.children("span").each(function () {
if (jQuery(this).attr("section") === ePub.spine[sectionIndex]) {
sectionExists = true;
}
});
if (!sectionExists) {
sectionLoaded = true;
loadSection(ePub, ePub.spine[sectionIndex], null, false).then(resolve);
break;
}
sectionIndex++;
}
} else if (reader.scrollTop() === 0) {
var sectionIndex = ePub.spine.indexOf(currentSection) - 1;
while ((sectionIndex >= 0) && (sectionIndex < ePub.spine.length)) {
var sectionExists = false;
reader.children("span").each(function () {
if (jQuery(this).attr("section") === ePub.spine[sectionIndex]) {
sectionExists = true;
}
});
if (!sectionExists) {
sectionLoaded = true;
loadSection(ePub, ePub.spine[sectionIndex], null, true).then(resolve);
break;
}
sectionIndex--;
}
}
if (!sectionLoaded) {
resolve(false);
}
});
};
var scrollBlocked = true;
var scroll = function () {
if (!scrollBlocked) {
determineCurrentParagraph();
loadSections();
}
scrollBlocked = false;
};
var addScrollEventHandlers = function () {
reader.on("scroll", scroll);
reader.on("click", scroll);
reader.on("mouseover", scroll);
};
var removeScrollEventHandlers = function () {
reader.off("scroll", scroll);
reader.off("click", scroll);
reader.off("mouseover", scroll);
};
var resize = function () {
reader.css("max-height", determineReaderHeight());
determineCurrentParagraph();
};
jQuery(window).on("resize", resize);
var unloadReader = function () {
removeScrollEventHandlers();
jQuery(window).off("resize", resize);
};
config.macros.epub.readers[tiddler.title] = config.macros.epub.readers[tiddler.title] || {};
config.macros.epub.readers[tiddler.title].unloadReader = unloadReader;
var editTiddlerCommand = jQuery(place).closest(".tiddler").children(".toolbar").children(".command_editTiddler ");
editTiddlerCommand.on("click", unloadReader);
ePub.removeEventHandlers = function () {
unloadReader();
editTiddlerCommand.off("click", unloadReader);
};
var updateFontSize = function () {
scrollBlocked = true;
jQuery(place).find(".fontsize").each(function () {
var fontSizePlace = jQuery(this);
fontSizePlace.css("font-size", fontSize + "em");
fontSizePlace.css("line-height", (fontSize * 1.25) + "em");
});
if ((currentParagraph != null) && paragraphs[currentParagraph]) {
if (localStoragePositionKey) {
localStorage.removeItem(localStoragePositionKey);
}
tiddler.fields.epubposition = "";
reader.scrollTop(reader.scrollTop() + paragraphs[currentParagraph].position().top);
}
tiddler.fields.epubfontsize = fontSize.toFixed(2);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
};
controlButtons.find("button.increase").off("click").on("click", function () {
fontSize += 0.05;
updateFontSize();
});
controlButtons.find("button.decrease").off("click").on("click", function () {
fontSize -= 0.05;
fontSize = (fontSize < 0.05) ? 0.05 : fontSize;
updateFontSize();
});
var sectionLoading = false;
loadSection = function (ePub, path, paragraphId, prepend) {
return new Promise(function (resolve, reject) {
if (sectionLoading) {
resolve(false);
} else {
sectionLoading = true;
var containStyles = function (css, containerClass) {
if (css) {
var css = css.replace(/^\.(.*)$/mg, "." + containerClass + " .$1").replace(/\/\*[\s\S]*?\*\//mg, "");
var stylePlaceholder = "__EPUBPlugin_stylesheet_" + containerClass + "__";
var originalCss;
do {
originalCss = css;
css = css.replace(/^([\s\S]*?[\s,])(?:html|body)([\s,\{][\s\S]*?\{[\s\S]*?\}[\s\S]*?)/gi, "$1" + stylePlaceholder + "$2");
css = css.replace(/^(\s*|[\s\S]*?[\},]\s*)(:link|:visited|a|a\[href\]|a:link|a:visited|b|body|code|div|h1|h2|h3|h4|h5|h6|hr|img|input|ol|p|pre|span|strong|table|tr|td|textarea|th|ul|\.title)([\s,:\{][\s\S]*?\{[\s\S]*?\}[\s\S]*?)/gi, "$1" + stylePlaceholder + "$2$3");
} while (css != originalCss);
css = css.replace(new RegExp(stylePlaceholder, "gi"), "." + containerClass + " ");
}
return css;
};
loadContent(ePub, [{type: "string", prefix: "", path: path}], ePub.items).then(function (result) {
var ePubStyle = result.items[result.paths[0].path].style || ("epubstyle_" + (epubReaderKey ? epubReaderKey : (new Date()).getTime() + "_" + Math.floor(Math.random() * 10000)));
result.items[result.paths[0].path].style = ePubStyle;
var section = jQuery('<span class="epubsection ' + ePubStyle + '">' + result.items[result.paths[0].path].content + '</span>');
if (!section.find(paragraphElements).length) {
section = jQuery('<span class="epubsection ' + ePubStyle + '"><div>' + result.items[result.paths[0].path].content + '</div></span>');
}
section.attr("section", path);
jQuery("<style>\n"
+ "\t.viewer ." + ePubStyle + " * {\n"
+ "\t\tbackground: unset;\n"
+ "\t\tborder: unset;\n"
+ "\t}\n"
+ "\t.viewer ." + ePubStyle + " a:hover {\n"
+ "\t\tcolor: unset;\n"
+ "\t}\n"
+ "</style>").appendTo(section);
var resourcePaths = [];
section.find("link").each(function () {
var link = jQuery(this);
if (link.attr("rel") === "stylesheet") {
var item = resolveItem(result.ePub.items, link.attr("href"), path);
if (item) {
resourcePaths.push({type: "string", prefix: "", path: item.path});
}
}
});
section.find("img").each(function () {
var item = resolveItem(result.ePub.items, jQuery(this).attr("src"), path);
if (item) {
resourcePaths.push({type: "base64", prefix: "data:" + item.type + ";base64,", path: item.path});
}
});
section.find("image").each(function () {
var attributes = this.attributes;
if (attributes) {
var href = attributes.getNamedItemNS("http://www.w3.org/1999/xlink", "href");
if (href) {
var item = resolveItem(result.ePub.items, href.value, path);
if (item) {
resourcePaths.push({type: "base64", prefix: "data:" + item.type + ";base64,", path: item.path});
}
}
}
});
section.find("style").each(function () {
jQuery(this).text(containStyles(jQuery(this).text(), ePubStyle));
});
if (prepend) {
reader.prepend(section);
} else {
reader.append(section);
}
loadContent(result.ePub, resourcePaths, result.ePub.items, true).then(function () {
section.find("link").each(function () {
var link = jQuery(this);
if (link.attr("rel") === "stylesheet") {
var item = resolveItem(result.ePub.items, link.attr("href"), path);
if (item && item.content) {
link.replaceWith(jQuery("<style></style>").text(containStyles(item.content, ePubStyle)));
}
}
});
section.find("img").each(function () {
var item = resolveItem(result.ePub.items, jQuery(this).attr("src"), path);
if (item && item.content) {
jQuery(this).attr("src", item.content);
}
});
section.find("image").each(function () {
var attributes = this.attributes;
if (attributes) {
var href = attributes.getNamedItemNS("http://www.w3.org/1999/xlink", "href");
if (href) {
var item = resolveItem(result.ePub.items, href.value, path);
if (item && item.content) {
href.value = item.content;
}
}
}
});
var firstParagraph = paragraphs.length;
var indexedParagraphs = indexParagraphs(section);
sectionParagraphIds[path] = indexedParagraphs.paragraphIds;
sectionParagraphNos[path] = indexedParagraphs.paragraphNos;
for (var i = 0; i < indexedParagraphs.paragraphs.length; i++) {
if (prepend) {
paragraphs.splice(i, 0, indexedParagraphs.paragraphs[i]);
paragraphNos.splice(i, 0, path + " " + indexedParagraphs.paragraphNos[i]);
} else {
paragraphs.push(indexedParagraphs.paragraphs[i]);
paragraphNos.push(path + " " + indexedParagraphs.paragraphNos[i]);
}
}
if (prepend) {
firstParagraph = indexedParagraphs.paragraphs.length;
}
var topParagraph = null;
if (indexedParagraphs.paragraphs.length > 0) {
section.find("a").each(function () {
var a = jQuery(this);
var href = a.attr("href");
if (href) {
var target = href;
var anchor = null;
if (href.indexOf("#") > -1) {
target = href.substring(0, href.indexOf("#")).trim();
anchor = href.substring(href.indexOf("#") + 1);
}
var resolvedTarget = resolveItem(result.ePub.items, target, path);
target = resolvedTarget ? resolvedTarget.path : target;
if ((!target && anchor) || result.ePub.items[target]) {
a.removeAttr("href");
a.css("cursor", "pointer");
if (!target && anchor) {
(function () {
var section = path;
var paragraphId = anchor;
a.on("click", function () {
if (sectionParagraphIds[section][paragraphId] != null) {
removeScrollEventHandlers();
scrollBlocked = true;
originSection = tiddler.fields.epubsection;
originParagraph = tiddler.fields.epubparagraph;
originPosition = tiddler.fields.epubposition;
reader.scrollTop(reader.scrollTop() + paragraphs[paragraphNos.indexOf(section + " " + sectionParagraphIds[section][paragraphId])].position().top);
determineCurrentParagraph(paragraphNos.indexOf(section + " " + sectionParagraphIds[section][paragraphId]));
addScrollEventHandlers();
}
});
})();
} else {
(function () {
var section = target;
var paragraphId = anchor;
a.on("click", function () {
ePub.removeEventHandlers();
originSection = tiddler.fields.epubsection;
originParagraph = tiddler.fields.epubparagraph;
originPosition = tiddler.fields.epubposition;
localStoragePosition = 0;
loadReader(result.ePub, section, paragraphId);
});
})();
}
} else {
a.attr("target", "_blank")
}
}
});
var topParagraphIndex;
if ((tiddler.fields.epubparagraph != null) && (paragraphNos.indexOf(path + " " + tiddler.fields.epubparagraph) > -1)) {
topParagraphIndex = paragraphNos.indexOf(path + " " + tiddler.fields.epubparagraph);
} else {
topParagraphIndex = prepend ? firstParagraph : 0;
}
if (path !== tiddler.fields.epubsection) {
topParagraphIndex = prepend ? firstParagraph : 0;
}
if (paragraphId === TOP) {
topParagraphIndex = paragraphNos.indexOf(path + " " + sectionParagraphNos[path][0]);
} else if (paragraphId && sectionParagraphIds[path] && (sectionParagraphIds[path][paragraphId] != null) && (paragraphNos.indexOf(path + " " + sectionParagraphIds[path][paragraphId]) > -1)) {
topParagraphIndex = paragraphNos.indexOf(path + " " + sectionParagraphIds[path][paragraphId]);
}
topParagraphIndex = topParagraphIndex + (prepend ? 0 : firstParagraph);
topParagraphIndex = (topParagraphIndex < 0) ? 0 : topParagraphIndex;
topParagraphIndex = (topParagraphIndex >= paragraphs.length) ? paragraphs.length - 1 : topParagraphIndex;
topParagraph = paragraphs[topParagraphIndex];
if (readerLoaded && prepend) {
reader.scrollTop(reader.scrollTop() + topParagraph.position().top);
}
if (readerLoaded) {
determineCurrentParagraph();
}
}
sectionLoading = false;
resolve(topParagraph || true);
});
});
}
});
};
loadSection(ePub, path, paragraphId).then(function (result) {
var topParagraph = result;
(function next() {
loadSections(path).then(function (result) {
topParagraph = (typeof topParagraph === "boolean") ? result : topParagraph;
if (typeof topParagraph !== "boolean") {
reader.scrollTop(reader.scrollTop() + topParagraph.position().top - localStoragePosition);
}
if (result) {
next();
} else {
localStoragePosition = 0;
readerLoaded = true;
if (typeof topParagraph !== "boolean") {
if (paragraphId === TOP) {
determineCurrentParagraph(paragraphNos.indexOf(path + " " + sectionParagraphNos[path][0]));
} else if (paragraphId != null) {
var index = paragraphNos.indexOf(path + " " + sectionParagraphIds[path][paragraphId]);
if (index === -1) {
console.log("WARNING: The paragraph ID [" + paragraphId + "] does not exist in section [" + path + "]. Make sure the element with that ID is indexed by the indexParagraphs(section) function.");
determineCurrentParagraph(paragraphNos.indexOf(path + " " + sectionParagraphNos[path][0]));
} else {
determineCurrentParagraph(index);
}
} else {
determineCurrentParagraph();
}
}
addScrollEventHandlers();
}
});
})();
});
};
(new JSZip()).loadAsync(source, {base64: true}).then(function (zip) {
return loadContent({zip: zip}, [{type: "string", prefix: "", path: "META-INF/container.xml"}]);
}).then(function (result) {
var document;
try {
document = jQuery(jQuery.parseXML(result.items[result.paths[0].path].content));
} catch (e) {
console.log(e);
return Promise.reject("Error parsing " + result.paths[0].path);
}
var rootfile = document.find("rootfile");
if (!rootfile.length) {
return Promise.reject("Can't determine rootfile from " + result.paths[0].path);
}
if (rootfile.attr("full-path") == null) {
return Promise.reject("Can't determine rootfile path from " + result.paths[0].path);
}
return loadContent(result.ePub, [{type: "string", prefix: "", path: rootfile.attr("full-path")}]);
}).then(function (result) {
result.ePub.folder = result.items[result.paths[0].path].folder;
var document;
try {
document = jQuery(jQuery.parseXML(result.items[result.paths[0].path].content));
} catch (e) {
console.log(e);
return Promise.reject("Error parsing " + result.paths[0].path);
}
var title = document.children("package").children("metadata");
if (title.length) {
result.ePub.title = jQuery(title[0].getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", "title")).eq(0).text();
}
var items = {};
document.children("package").children("manifest").children("item").each(function () {
var itemElement = jQuery(this);
items[itemElement.attr("id")] = {id: itemElement.attr("id"), path: itemElement.attr("href"), type: itemElement.attr("media-type")};
});
result.ePub.items = {};
for (var k in items) {
result.ePub.items[items[k].path] = items[k];
}
var spineElement = document.children("package").children("spine");
if (spineElement.attr("toc")) {
result.ePub.toc = items[spineElement.attr("toc")];
}
var spine = [];
spineElement.children("itemref").each(function () {
var itemrefElement = jQuery(this);
if (itemrefElement.attr("idref") && items[itemrefElement.attr("idref")]) {
spine.push(items[itemrefElement.attr("idref")].path);
}
});
result.ePub.spine = spine;
var section = spine[0];
if (tiddler.fields.epubsection && (spine.indexOf(tiddler.fields.epubsection) > -1)) {
section = tiddler.fields.epubsection;
}
if (!section) {
console.log(JSON.stringify(result.ePub.spine), JSON.stringify(result.ePub.items));
return Promise.reject("Unable to determine ePub section");
}
result.ePub.section = section;
result.ePub.progress = {data: tiddler.fields.epubprogress ? JSON.parse(tiddler.fields.epubprogress) : null};
if ((result.ePub.progress.data == null) || (result.ePub.progress.data.version !== paragraphIndexVersion) || (result.ePub.progress.data.paragraphs.length !== spine.length)) {
result.ePub.progress.data = null;
var resourcePaths = [];
for (var i = 0; i < result.ePub.spine.length; i++) {
resourcePaths.push({type: "string", prefix: "", path: result.ePub.spine[i]});
}
return loadContent(result.ePub, resourcePaths, result.ePub.items);
} else {
return result;
}
}).then(function (result) {
var ePub = result.ePub;
if (!ePub.progress.data) {
ePub.progress.data = {version: paragraphIndexVersion, elements: paragraphElements};
var indexedSectionParagraphs = {};
var sectionParagraphs = [];
for (var i = 0; i < ePub.spine.length; i++) {
if (ePub.items[ePub.spine[i]].content) {
var section = jQuery("<span>" + ePub.items[ePub.spine[i]].content + "</span>");
var indexedParagraphs = indexParagraphs(section);
indexedSectionParagraphs[ePub.spine[i]] = indexedParagraphs;
sectionParagraphs[i] = indexedParagraphs.paragraphs.length || 1;
} else {
sectionParagraphs[i] = 0;
}
}
ePub.progress.paragraphs = indexedSectionParagraphs;
ePub.progress.data.paragraphs = sectionParagraphs;
ePub.progress.data.total = 0;
for (var i = 0; i < ePub.progress.data.paragraphs.length; i++) {
ePub.progress.data.total += ePub.progress.data.paragraphs[i];
}
if (!result.ePub.toc) {
tiddler.fields.epubprogress = JSON.stringify(ePub.progress.data);
}
}
if (result.ePub.toc) {
return loadContent(result.ePub, [{type: "string", prefix: "", path: result.ePub.toc.path}]);
} else {
return result;
}
}).then(function (result) {
var ePub = result.ePub;
if (ePub.toc) {
var document;
try {
document = jQuery(jQuery.parseXML(result.items[result.paths[0].path].content));
} catch (e) {
console.log(e);
return Promise.reject("Error parsing " + result.paths[0].path);
}
var navElements = document.children("ncx").children("docTitle").children("text");
if (navElements.length) {
ePub.title = navElements.text();
}
var progress = ePub.progress.data;
progress.toc = progress.toc || {};
ePub.nav = [];
var navList = [];
var navElements = document.children("ncx").children("navMap").children("navPoint");
var processNav = function (nav, parentNavPoint, navElements, id, order) {
var counter = 1;
navElements.each(function () {
var navId = (id ? "" : "t") + id + (id ? "_" : "") + counter++;
progress.toc[navId] = progress.toc[navId] || [];
var navElement = jQuery(this);
order[0] = jQuery.isNumeric(navElement.attr("playOrder")) ? parseInt(navElement.attr("playOrder")) : order[0] + 0.00001;
var navPoint = {id: navId, parent: parentNavPoint, order: order[0]};
nav.push(navPoint);
navList.push(navPoint);
navPoint.label = navElement.children("navLabel").children("text").text();
navPoint.section = null;
navPoint.paragraphId = null;
var contentElement = navElement.children("content");
if (contentElement.length) {
navPoint.section = contentElement.attr("src");
if (navPoint.section) {
if (navPoint.section.indexOf("#") > -1) {
navPoint.paragraphId = navPoint.section.substring(navPoint.section.indexOf("#") + 1);
navPoint.section = navPoint.section.substring(0, navPoint.section.indexOf("#")).trim();
}
if (ePub.progress.paragraphs && ePub.progress.paragraphs[navPoint.section] && (ePub.spine.indexOf(navPoint.section) > -1)) {
progress.toc[navId][TOCENTRY_START_SPINE_SECTION_INDEX_POS] = ePub.spine.indexOf(navPoint.section);
var paragraphNo = (navPoint.paragraphId == null) ? null : ePub.progress.paragraphs[navPoint.section].paragraphIds[navPoint.paragraphId];
if (paragraphNo != null) {
progress.toc[navId][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS] = ePub.progress.paragraphs[navPoint.section].paragraphNos.indexOf(paragraphNo);
} else {
progress.toc[navId][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS] = 0;
}
}
if (navPoint.paragraphId == null) {
navPoint.paragraphId = TOP;
}
}
}
navPoint.nav = [];
processNav(navPoint.nav, navPoint, navElement.children("navPoint"), navId, order);
});
};
processNav(ePub.nav, null, document.children("ncx").children("navMap").children("navPoint"), "", [0]);
if (ePub.progress.paragraphs) {
navList.sort(function (a, b) {
if (a.order > b.order) {
return 1;
} else if (a.order < b.order) {
return -1;
} else {
return 0;
}
});
for (var i = 0; i < navList.length; i++) {
for (var j = i + 1; j < navList.length; j++) {
if (progress.toc[navList[j].id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] != null) {
progress.toc[navList[i].id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] = progress.toc[navList[j].id][TOCENTRY_START_SPINE_SECTION_INDEX_POS];
progress.toc[navList[i].id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS] = progress.toc[navList[j].id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS];
break;
}
}
if (progress.toc[navList[i].id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] == null) {
progress.toc[navList[i].id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] = progress.paragraphs.length - 1;
progress.toc[navList[i].id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS] = progress.paragraphs[progress.paragraphs.length - 1] + 1;
}
var navItem = navList[i];
var parentNavItem = navItem.parent;
while (parentNavItem) {
if ((progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] == null)
|| ((progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[navItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] > progress.toc[navItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS]))) {
progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS];
progress.toc[parentNavItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS];
} else if ((progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[parentNavItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] === progress.toc[navItem.id][TOCENTRY_START_SPINE_SECTION_INDEX_POS])
&& (progress.toc[parentNavItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS] > progress.toc[navItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS])) {
progress.toc[parentNavItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS];
}
if ((progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] == null)
|| ((progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[navItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] < progress.toc[navItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS]))) {
progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS];
progress.toc[parentNavItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS];
} else if ((progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] != null)
&& (progress.toc[parentNavItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] === progress.toc[navItem.id][TOCENTRY_END_SPINE_SECTION_INDEX_POS])
&& (progress.toc[parentNavItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS] < progress.toc[navItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS])) {
progress.toc[parentNavItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS] = progress.toc[navItem.id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS];
}
navItem = parentNavItem;
parentNavItem = navItem.parent;
}
}
for (var i = 0; i < navList.length; i++) {
if ((progress.toc[navList[i].id][TOCENTRY_START_SPINE_SECTION_INDEX_POS] == null) || (progress.toc[navList[i].id][TOCENTRY_END_SPINE_SECTION_INDEX_POS] == null)) {
progress.toc[navList[i].id] = [];
} else {
var total = 0;
for (var j = progress.toc[navList[i].id][TOCENTRY_START_SPINE_SECTION_INDEX_POS]; j < progress.toc[navList[i].id][TOCENTRY_END_SPINE_SECTION_INDEX_POS]; j++) {
total += progress.paragraphs[j];
}
total -= progress.toc[navList[i].id][TOCENTRY_START_SECTION_PARAGRAPH_INDEX_POS];
total += progress.toc[navList[i].id][TOCENTRY_END_SECTION_PARAGRAPH_INDEX_POS];
if (total === 0) {
progress.toc[navList[i].id] = [];
} else {
progress.toc[navList[i].id][TOCENTRY_TOTAL_PARAGRAPHS_POS] = total;
}
}
}
tiddler.fields.epubprogress = JSON.stringify(ePub.progress.data);
}
}
loadReader(ePub, ePub.section, null);
}).catch(function (error) {
displayMessage(error);
});
}
}
};
//}}}
//{{{
(function () {
var code = eval("Story.prototype.closeTiddler").toString();
code = code.replace("var", "if (config.macros.epub.readers[title] && config.macros.epub.readers[title].unloadReader) try { config.macros.epub.readers[title].unloadReader(); } catch (e) {}; var");
eval("Story.prototype.closeTiddler = function closeTiddler" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name|EasterPlugin|
|Source|https://www.assa.org.au/edm#Computer|
|Documentation|https://www.assa.org.au/edm|
|License|[[TW Notes License]]|
|Requires|[[ReminderMacrosOverride]]|
!!!!!Code
***/
//{{{
config.macros.easter = {
reminders: '<' + '<reminder function:config.macros.easter.determineGoodFriday() leadtime:1 tag:"readOnlyReminder" title:"@@background-color:#ffaace;priority:5;Good Friday@@">>\n' +
'<' + '<reminder function:config.macros.easter.determineEasterSunday() leadtime:1 tag:"readOnlyReminder" title:"@@background-color:#ffaace;priority:5;Easter Sunday@@">>\n' +
'<' + '<reminder function:config.macros.easter.determineEasterMonday() leadtime:1 tag:"readOnlyReminder" title:"@@background-color:#ffaace;priority:5;Easter Monday@@">>',
determineGoodFriday: function (dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var easter = config.macros.easter.easterDate(baseDate.getFullYear());
easter.setDate(easter.getDate() - 2);
dateHash["year"] = easter.getFullYear();
dateHash["month"] = easter.getMonth() + 1;
dateHash["day"] = easter.getDate();
},
determineEasterSunday: function (dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var easter = config.macros.easter.easterDate(baseDate.getFullYear());
dateHash["year"] = easter.getFullYear();
dateHash["month"] = easter.getMonth() + 1;
dateHash["day"] = easter.getDate();
},
determineEasterMonday: function (dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var easter = config.macros.easter.easterDate(baseDate.getFullYear());
easter.setDate(easter.getDate() + 1);
dateHash["year"] = easter.getFullYear();
dateHash["month"] = easter.getMonth() + 1;
dateHash["day"] = easter.getDate();
},
/*
Sub EasterDate (d, m, y)
' EASTER DATE CALCULATION FOR YEARS 1583 TO 4099
' y is a 4 digit year 1583 to 4099
' d returns the day of the month of Easter
' m returns the month of Easter
' Easter Sunday is the Sunday following the Paschal Full Moon
' (PFM) date for the year
' This algorithm is an arithmetic interpretation of the 3 step
' Easter Dating Method developed by Ron Mallen 1985, as a vast
' improvement on the method described in the Common Prayer Book
' Because this algorithm is a direct translation of the
' official tables, it can be easily proved to be 100% correct
' This algorithm derives values by sequential inter-dependent
' calculations, so ... DO NOT MODIFY THE ORDER OF CALCULATIONS!
' The \ operator may be unfamiliar - it means integer division
' for example, 30 \ 7 = 4 (the remainder is ignored)
' All variables are integer data types
' It's free! Please do not modify code or comments!
' ==========================================================
Dim FirstDig, Remain19, temp 'intermediate results
Dim tA, tB, tC, tD, tE 'table A to E results
FirstDig = y \ 100 'first 2 digits of year
Remain19 = y Mod 19 'remainder of year / 19
' calculate PFM date
temp = (FirstDig - 15) \ 2 + 202 - 11 * Remain19
Select Case FirstDig
Case 21, 24, 25, 27 To 32, 34, 35, 38
temp = temp - 1
Case 33, 36, 37, 39, 40
temp = temp - 2
End Select
temp = temp Mod 30
tA = temp + 21
If temp = 29 Then tA = tA - 1
If (temp = 28 And Remain19 > 10) Then tA = tA - 1
'find the next Sunday
tB = (tA - 19) Mod 7
tC = (40 - FirstDig) Mod 4
If tC = 3 Then tC = tC + 1
If tC > 1 Then tC = tC + 1
temp = y Mod 100
tD = (temp + temp \ 4) Mod 7
tE = ((20 - tB - tC - tD) Mod 7) + 1
d = tA + tE
'return the date
If d > 31 Then
d = d - 31
m = 4
Else
m = 3
End If
End Sub
*/
easterDate: function (y) {
var FirstDig = Math.floor(y / 100);
var Remain19 = y % 19;
var temp = Math.floor((FirstDig - 15) / 2) + 202 - 11 * Remain19;
switch (FirstDig) {
case 21: case 24: case 25: case 27: case 28: case 29: case 30: case 31: case 32: case 34: case 35: case 38:
temp = temp - 1;
break;
case 33: case 36: case 37: case 39: case 40:
temp = temp - 2;
break;
}
temp = temp % 30;
var tA = temp + 21;
if (temp == 29) { tA = tA - 1; }
if ((temp == 28) && (Remain19 > 10)) { tA = tA - 1; }
var tB = (tA - 19) % 7;
var tC = (40 - FirstDig) % 4;
if (tC == 3) { tC = tC + 1; }
if (tC > 1) { tC = tC + 1; }
temp = y % 100;
var tD = (temp + Math.floor(temp / 4)) % 7;
var tE = ((20 - tB - tC - tD) % 7) + 1;
var d = tA + tE;
var m;
if (d > 31) {
d = d - 31
m = 4
} else {
m = 3
}
return new Date(y, m - 1, d);
}
}
//}}}
//{{{
config.shadowTiddlers["EasterPluginReminders"] = config.macros.easter.reminders;
//}}}
/***
|Name|[[EditSectionPlugin]]|
|Source|http://www.TiddlyTools.com/#EditSectionPlugin|
|Documentation|http://www.TiddlyTools.com/#EditSectionPlugin|
|Version|1.8.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|invoke popup 'section editor' for specified section of a tiddler|
!!!!!Usage
<<<
{{{
<<editSection TiddlerName##sectionname label tooltip>>
}}}
This macro adds a command link that invokes a popup editor for a specific section, where:
*''~TiddlerName##sectionname'' specifies the tiddler and section you want to edit.
**If you omit the "##sectionname" portion (i.e., only enter "~TiddlerName"), the entire content of the indicated tiddler is edited.
**If you omit the "~TiddlerName" portion (i.e., only enter "##sectionname"), the current containing tiddler (if any) is assumed.
**Changing the section name in the popup editor //renames// that section within the tiddler.
**Changing the tiddler title in the popup editor //copies// that section to another tiddler.
**If the indicated tiddler and/or section does not yet exist, it will be created when you press 'save'.
*''label'' and ''tooltip'' (both //optional//) specify the link label and mouseover help text for the 'edit section' command link.
You can also add the following macro, //at the end of a tiddler//, to automatically add an 'edit section' command link for each section shown in the tiddler.
{{{
<<editSections label tooltip>>
}}}
*''label'' and ''tooltip'' (both //optional//) specify the link label and mouseover help text for the 'edit section' command link.
>//Note: when a document is viewed in 'readOnly' mode, both of these macros are bypassed and no output is produced.//
<<<
!!!!!Sample
<<<
This is an example section for you to try
<<<
!!!!!Example
<<<
{{{
<<editSection ##Sample>>
}}}
<<editSection ##Sample>>
<<<
!!!!!Configuration
<<<
To customize and/or translate the HTML form layout used to render the section editor, edit the [[EditSectionTemplate]] shadow tiddler.
<<<
!!!!!Revisions
<<<
2012.01.29 1.8.1 invoke autoSaveChanges() when tiddlers are modified.
2011.12.22 1.8.0 added {{{<<editSections>>}}} macro for automatic adding of 'edit section...' links to headings
2011.12.20 1.7.0 added drag handling for editor panels
2011.10.28 1.6.8 fixed getMX()/getMY() for Chrome scroll offset handling
2011.09.02 1.6.7 more refactoring and cleanup of default form init/save handlers
2011.08.02 1.6.6 major code refactor to allow customization of form handling for type-specific [[PasteUpHelperPlugin]] extensions
2011.07.30 1.6.5 in removePanel(), call Popup.remove() so 'child' popups are closed when panel is closed
2011.07.24 1.6.4 refactored save() to provide updateTiddler() entry point for use with PasteUpHelperPlugin 'quickmenu' commands. Added getMX() and getMY() for cross-browser mouse coordinates
2011.06.05 1.6.3 added TiddlySpace cloneFields() to delete and save handlers so editing sections automatically copies/owns an included tiddler
2011.05.05 1.6.2 renamed delete() to deleteSection() to avoid javascript keyword errors
2011.05.04 1.6.1 in delete(), use removeTiddler() for proper notification handling
2011.05.01 1.6.0 added delete() functionality
2011.01.09 1.5.1 in handler(), don't render command link if document is readOnly
2010.12.24 1.5.0 replace use of core Popups with custom panel handling (bypass core interactions and errors)
2010.11.07 1.4.0 in popup(), render HTML form from EditSectionTemplate, and then applyHtmlMacros() to render wiki-syntax macro-generated form elements (e.g., {{{<<option>>, <<select>>}}}, etc.). Also, refactored form init/save handling to add customization 'hooks'
2010.10.25 1.3.0 added support for editing complete tiddlers using "~TiddlerName" syntax (omit "##sectionname")
2010.07.15 1.2.0 added confirmation when section rename will overwrite other section
2010.07.13 1.1.1 added 'dirty flag' confirmation to popup handling to avoid discarding unsaved changes
2010.07.11 1.1.0 fixed handling for creating new sections in existing tiddlers. Copied StickyPopupPlugin to eliminate dependency. Added Popup.showHere()
2010.05.24 1.0.2 in save(), escape regexp chars in section titles (e.g. "!SectionNameWith?InIt")
2009.09.07 1.0.1 documentation/code cleanup
2009.09.01 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.EditSectionPlugin= {major: 1, minor: 8, revision: 1, date: new Date(2012,1,29)};
config.macros.editSection = {
label: 'edit section...',
tip: 'edit %0',
sectionfmt: '{{hidden{\n!%0\n%1\n!end\n}}}',
sectionerr: 'Invalid tiddler/section name: %0',
newtiddlertxt: '',
discardmsg: 'Discard unsaved changes for %0?',
deletemsg: 'Are you sure you want to delete %0?',
overwritemsg: '%0##%2 already exists. Choose OK to overwrite it',
template: 'EditSectionTemplate', // DEFAULT FORM TEMPLATE
//}}}
// // PLUGIN INITIALIZATION
//{{{
init: function() {
// SHADOW PAYLOAD FOR DEFAULT EditSectionTemplate FORM DEFINITION
config.shadowTiddlers[this.template]
=store.getTiddlerText('EditSectionPlugin##HTML','');
// CLOSE PANELS IF CLICK on other than POPUP OR EDITOR PANEL
addEvent(document,'click',function(ev) {
var p=resolveTarget(ev||window.event);
while (p) {
if (hasClass(p,'editSectionPanel')) break;
if (hasClass(p,'popup')) break;
p=p.parentNode;
}
if (!p) config.macros.editSection.removeAllPanels();
return true;
});
// HIJACK QuickEditPlugin's getField() to support use with editSectionPanel
if (config.quickEdit) {
config.quickEdit.getTiddlerField=config.quickEdit.getField;
config.quickEdit.getField=function(where) {
var e=where; while(e) { if (hasClass(e,'editSectionPanel')) break; e=e.parentNode; }
return e?e.getElementsByTagName('textarea')[0]:this.getTiddlerField(where);
}
}
},
//}}}
// // GENERAL UTILITIES
//{{{
ok: function(ev) { var ev=ev||window.event;
ev.cancelBubble=true;
if(ev.stopPropagation)ev.stopPropagation();
return false;
},
getMX: function(ev) { var ev=ev||window.event; // GET MOUSE X
if (config.browser.isIE) return ev.clientX+findScrollX();// IE
if (config.userAgent.indexOf('chrome')!=-1) return ev.pageX; // Chrome
if (config.browser.isSafari) return ev.pageX+findScrollX(); // Webkit
else return ev.pageX; // Firefox/other
},
getMY: function(ev) { var ev=ev||window.event; // GET MOUSE Y
if (config.browser.isIE) return ev.clientY+findScrollY();// IE
if (config.userAgent.indexOf('chrome')!=-1) return ev.pageY; // Chrome
if (config.browser.isSafari) return ev.pageY+findScrollY(); // Webkit
else return ev.pageY; // Firefox/other
},
cloneFields: function(fields) { // for TIDDLYSPACE compatibility
var f=merge({},fields); // copy object
if (f["server.workspace"]!=config.defaultCustomFields["server.workspace"]) {
f=merge(f,config.defaultCustomFields); // overwrite with defaults
f["server.permissions"] = "read, write, create, delete";
delete f["server.page.revision"];
delete f["server.title"];
delete f["server.etag"];
}
return f;
},
//}}}
// // MACRO/CLICK HANDLER
//{{{
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if (readOnly) return;
var here=story.findContainingTiddler(place);
var tid=params[0];
var label=params[1]||this.label.format([tid]);
var tip=params[2]||this.tip.format([tid]);
var btn=createTiddlyButton(place,label,tip,this.click);
btn.setAttribute('tid',tid);
},
click: function(ev,type) { // note: optional 'type' is passed in from PasteUpPluginHelper
var parts=this.getAttribute('tid').split('##');
var title=parts[0]; var section=parts[1];
var here=story.findContainingTiddler(this);
if (!title&&here) title=here.getAttribute('tiddler');
if (!title) return false;
var tid=title; if (section&§ion.length) tid=[title,section].join('##');
return config.macros.editSection.createPanel(this,ev,tid,title,section,type);
},
//}}}
// // EDITOR PANEL HANDLER
//{{{
createPanel: function(here,ev,tid,title,section,type) {
if (!this.removeAllPanels()) return this.ok(ev);
var p=createTiddlyElement(document.body,"ol",
"editSectionPanel","popup smallform editSectionPanel");
p.root=here;
p.setAttribute('dirty',null);
p.setAttribute('message',this.discardmsg.format([tid]));
p.onmousedown=this.mousedown; p.style.cursor='move'; // for panel dragging
p.innerHTML=store.getRecursiveTiddlerText(this.getForm(tid,type),'',10);
applyHtmlMacros(p,store.getTiddler(title));
var f=p.getElementsByTagName('form')[0];
f.panel=p;
f.title.value=title;
f.section.value=section||'';
f.init=this.getInitForm(tid,type);
f.save=this.getSaveForm(tid,type);
f.init(here,f,title,section,type);
this.showPanel(p,here,ev);
return this.ok(ev);
},
showPanel: function(p,here,ev) {
var x=this.getMX(ev); var y=this.getMY(ev);
var winw=findWindowWidth();
var scrollw=winw-document.body.offsetWidth;
if(p.offsetWidth>winw*0.75) p.style.width=winw*0.75 + "px";
if(x+p.offsetWidth>winw-scrollw-1) x=winw-p.offsetWidth-scrollw-1;
var s=p.style; s.left=x+'px'; s.top=y+'px'; s.display='block';
if(config.options.chkAnimate && anim) anim.startAnimating(new Scroller(p));
else window.scrollTo(0,ensureVisible(p));
},
removePanel: function(p) {
Popup.remove(); // child popup (if any) is closed when panel is closed
if (!p || p.getAttribute('dirty')!='true' || confirm(p.getAttribute('message')))
{ if (p) removeNode(p); return true; }
return false;
},
removeAllPanels: function() {
var ok=true;
jQuery('.editSectionPanel').each(function(index){
var f=this.getElementsByTagName('form')[0];
if (f.content) f.content.blur(); // force onchange (sets 'dirty' flag as needed)
ok=config.macros.editSection.removePanel(this);
return true;
});
return ok; // FALSE if panels remain
},
//}}}
// // PANEL DRAG HANDLER
//{{{
mousedown: function(ev) { ev=ev||window.event; // MOVE PANEL
var cme=config.macros.editSection; // abbrev
// ignore clickthrough from form fields, links, and images
var target=resolveTarget(ev);
if (['TEXTAREA','SELECT','INPUT','A','IMG'].contains(target.nodeName.toUpperCase()))
return true;
// GET TRACKING ELEMENT
var track=this; // if 'capture' not supported, track in element only
if (document.body.setCapture) var track=document.body; // IE
if (window.captureEvents) var track=window; // moz
if (!track.save_onmousemove) track.save_onmousemove=track.onmousemove;
if (!track.save_onmouseup) track.save_onmouseup =track.onmouseup;
if (!track.save_onkeyup) track.save_onkeyup =track.onkeyup;
track.onmousemove=cme.dragmove;
track.onmouseup =cme.dragup;
track.onkeyup =cme.dragkeyup;
// SAVE INITIAL POSITION
track.elem=this; // panel element
track.start={
X: cme.getMX(ev), Y: cme.getMY(ev), // mouse position
T: this.offsetTop, L: this.offsetLeft, // panel position
}
return cme.ok(ev);
},
dragmove: function(ev) { ev=ev||window.event; // MOVE PANEL
var cme=config.macros.editSection; // abbrev
// CAPTURE MOUSE EVENTS DURING DRAG
if (document.body.setCapture) document.body.setCapture(); // IE
if (window.captureEvents) window.captureEvents(Event.MouseMove|Event.MouseUp,true); // moz
var e=this.elem; var s=e.style;
var dX=cme.getMX(ev)-this.start.X;
var dY=cme.getMY(ev)-this.start.Y;
e.changed=e.changed||(Math.abs(dX)>1)||(Math.abs(dY)>1); // MINIMUM 2px MOVEMENT
if (!e.changed) return cme.ok(ev);
s.top =this.start.T+dY+'px';
s.left=this.start.L+dX+'px';
return cme.ok(ev);
},
dragkeyup: function(ev) { ev=ev||window.event; // STOP DRAG (ESC key)
if (ev.keyCode==27) {
var s=this.elem.style;
s.top=this.start.T+'px';
s.left=this.start.L+'px';
return this.onmouseup(ev);
}
},
dragup: function(ev) { ev=ev||window.event; // RELEASE MOUSE
var cme=config.macros.editSection; // abbrev
if (document.body.releaseCapture) document.body.releaseCapture(); // IE
if (window.releaseEvents) window.releaseEvents(Event.MouseMove|Event.MouseUp); // moz
this.onmousemove=this.save_onmousemove;
this.onmouseup =this.save_onmouseup;
this.onkeyup =this.save_onkeyup;
return cme.ok(ev);
},
//}}}
// // EDITOR FORM HANDLER
//{{{
getForm: function(tid,type) { return this.template; }, // see PasteUpHelperPlugin
getInitForm: function(tid,type) { return this.initForm; }, // see PasteUpHelperPlugin
getSaveForm: function(tid,type) { return this.saveForm; }, // see PasteUpHelperPlugin
initForm: function(here,form,title,section,type) { // SET FORM CONTENT FROM TIDDLER SECTION
var tid=title; if (section) tid=[title,section].join('##');
form.newsection.value=tid; // target for output
form.content.value=store.getTiddlerText(tid,'');
if (version.extensions.TextAreaPlugin) new window.TextAreaResizer(form.content);
},
saveForm: function(here,ev) { // GET SECTION CONTENT FROM FORM (DEFAULT=TEXT CONTENT ONLY)
var f=here.form;
// GET TARGET TITLE/SECTION
var tid=f.newsection.value;
var parts=tid.split('##');
var title=parts[0];
var section=parts[1];
var oldsection=f.section.value;
var where=f.panel.root;
if (!title) title=story.findContainingTiddler(where).getAttribute('tiddler');
if (!title) {
displayMessage(this.sectionerr.format([f.newsection.value]));
f.newsection.focus(); f.newsection.select(); return false;
}
// CHECK FOR TIDDLER OVERWRITE
if (!section && title!=f.title.value && store.tiddlerExists(title)) {
if (!confirm(config.messages.overwriteWarning.format([title])))
{ f.newsection.focus(); f.newsection.select(); return this.ok(ev); }
}
// WRITE TIDDLER CONTENT and CLOSE PANEL
this.updateTiddler(f.content.value,title,section,oldsection);
f.panel.setAttribute('dirty',null); this.removePanel(f.panel);
return this.ok(ev);
},
changed: function(here,ev) {
here.form.panel.setAttribute('dirty','true');
return this.ok(ev);
},
cancel: function(here,ev) {
this.removePanel(here.form.panel);
return this.ok(ev);
},
remove: function(here,ev) {
var f=here.form;
var title=f.title.value;
var section=f.section.value;
var tid=title; if (section.length) tid=[title,section].join('##');
var msg=this.deletemsg.format([tid]);
if (!confirm(msg)) return this.ok(ev);
this.deleteSection(title,section);
f.panel.setAttribute('dirty',null);
this.removePanel(f.panel);
return this.ok(ev);
},
//}}}
// // TIDDLER I/O
//{{{
updateTiddler: function(txt,title,section,oldsection) {
// GET (or CREATE) TIDDLER
var t=store.getTiddler(title);
var who =t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
if (!t) {
t=new Tiddler(); t.text=store.getTiddlerText(title,'');
if (section&&!t.text.length) t.text=this.newtiddlertxt.format([title,section]);
}
// ADD/REVISE/RENAME SECTION CONTENT (if any)
if (section) {
// GET SECTION VALUES
if (!oldsection) var oldsection=section;
var oldval=store.getTiddlerText(title+'##'+oldsection); // previous section value
var newval=txt; // revised section value
var existingval=store.getTiddlerText(title+'##'+section); // existing section value
// REVISE TIDDLER TEXT
var txt=t.text; // default tiddler text = unchanged
var pattern=new RegExp('(.*!{1,6})'+oldsection.escapeRegExp()+'\\n'
+(oldval||'').escapeRegExp()+'((?:\\n!{1,6}|$).*)');
var altpattern=this.sectionfmt.format([oldsection,oldval||'']);
if (section!=oldsection && existingval) { // rename overwrites another section...
if (!confirm(this.overwritemsg.format([title,section])))
return this.ok(ev);
txt=txt.replace(altpattern,''); // REMOVE old section (auto-generated)
txt=txt.replace(pattern,'$2'); // REMOVE old section (generic format)
// TARGET new section name and value
pattern=new RegExp('(.*!{1,6})'+section.escapeRegExp()+'\\n'
+existingval.escapeRegExp()+'((?:\\n!{1,6}|$).*)');
oldval=existingval;
}
if (typeof oldval=="string") // section exists... update/rename it
txt=txt.replace(pattern,'$1'+section+'\n'+newval+'$2');
else // otherwise, append a new section to end of tiddler
txt=txt+this.sectionfmt.format([section,newval]);
}
// SAVE TIDDLER
var fields=this.cloneFields(t.fields);
store.saveTiddler(title,title,txt,who,when,t.tags,fields);
story.refreshTiddler(title,null,true);
autoSaveChanges();
},
deleteSection: function(title,section) {
// GET TIDDLER
var t=store.getTiddler(title); if (!t) return;
var who =t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
if (!section) { // REMOVE TIDDLER
store.removeTiddler(title);
} else { // REMOVE SECTION FROM TIDDLER
var val=store.getTiddlerText(title+'##'+section); // CURRENT SECTION VALUE
if (typeof val=="string") { // section exists
var txt=t.text; // default tiddler text = unchanged
var pattern=new RegExp('(.*!{1,6})'+section.escapeRegExp()+'\\n'
+(val||'').escapeRegExp()+'((?:\\n!{1,6}|$).*)');
var altpattern=this.sectionfmt.format([section,val||'']);
txt=txt.replace(altpattern,''); // REMOVE old section (auto-generated)
txt=txt.replace(pattern,'$2'); // REMOVE old section (generic format)
var fields=this.cloneFields(t.fields);
store.saveTiddler(title,title,txt,who,when,t.tags,fields);
story.refreshTiddler(title,null,true);
autoSaveChanges();
}
}
}
}
//}}}
// // EDIT SECTIONS MACRO
//{{{
config.macros.editSections = {
label: 'edit...',
tip: 'edit this section',
command: '~~<<editSection [[##%0]] "%1" "%2">>~~',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if (readOnly) return;
var elems=place.parentNode.getElementsByTagName("*");
for (var i=0; i<elems.length; i++) { var e=elems[i]; // for each heading element
if (!['H1','H2','H3','H4','H5'].contains(e.nodeName)) continue;
var section=e.textContent;
var label=(params[0]||this.label).format([section]);
var tip =(params[1]||this.tip ).format([section]);
wikify(this.command.format([section,label,tip]),e);
}
}
}
//}}}
/***
//{{{
!HTML
<!--{{{-->
<!--
|Name|EditSectionTemplate|
|Source||
|Version||
|Author||
|License|http://www.TiddlyTools.com/#LegalStatements|
|Type|template|
|Requires|EditSectionPlugin|
|Description|popup editor form template used by EditSectionPlugin|
-->
<form action='javascript:;' style="white-space:nowrap">
<input type="hidden" name="title" value="">
<input type="hidden" name="section" value="">
<input type="text" name="newsection" value="" autocomplete="off" style="width:61%"
onchange="return config.macros.editSection.changed(this,event);">
<input type=button value="save" style="width:12%"
onclick="return config.macros.editSection.saveForm(this,event)">
<input type=button value="cancel" style="width:12%"
onclick="return config.macros.editSection.cancel(this,event)">
<input type=button value="delete" style="width:12%"
onclick="return config.macros.editSection.remove(this,event)">
<div macro="tiddler QuickEditToolbar"></div>
<textarea name="content" rows="15" cols="80" autocomplete="off"
onchange="return config.macros.editSection.changed(this,event)"></textarea>
</form>
<!--}}}-->
!end
//}}}
***/
// //<<editSections "edit">>
/***
|Name|EditSectionPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[EditSectionPlugin]] [[SectionLinksPluginOverride]]|
!!!!!Code
***/
//{{{
config.macros.editSection.overwritemsg = '%0##%1 already exists. Choose OK to overwrite it';
//}}}
//{{{
config.macros.editSection.initOverride_base = config.macros.editSection.init;
config.macros.editSection.init = function () {
config.macros.editSection.initOverride_base();
config.shadowTiddlers[this.template] = store.getTiddlerText("TWNotesEditSectionTemplate", config.shadowTiddlers[this.template]);
};
//}}}
//{{{
(function () {
var code;
code = eval("config.macros.editSection.createPanel").toString();
code = code.replace("this.discardmsg.format([tid])", "this.discardmsg.format([tid.replace(/\\{\\d+\\}$$/, '')])");
eval("config.macros.editSection.createPanel = function createPanel" + code.substring(code.indexOf("(")));
code = eval("config.macros.editSection.initForm").toString();
code = code.replace("form.newsection.value=tid;", "form.newsection.value=tid.replace(/\\{\\d+\\}$$/, '')");
eval("config.macros.editSection.initForm = function initForm" + code.substring(code.indexOf("(")));
code = eval("config.macros.editSection.saveForm").toString();
code = code.replace("var where", "if (oldsection && section && /\\{\\d+\\}$$/.test(oldsection) && (oldsection.replace(/\\{\\d+\\}$$/, '') === section)) section += oldsection.substring(oldsection.lastIndexOf('{'));\nvar where");
eval("config.macros.editSection.saveForm = function saveForm" + code.substring(code.indexOf("(")));
code = eval("config.macros.editSection.remove").toString();
code = code.replace("this.deletemsg.format([tid])", "this.deletemsg.format([tid.replace(/\\{\\d+\\}$$/, '')])");
eval("config.macros.editSection.remove= function remove" + code.substring(code.indexOf("(")));
var replace = function () {
var indexInfo = config.macros.sectionTOC.determineIndexInfo(oldsection);
var sectionIndex = 0;
var sectionFound = false;
var sectionTitle = (oldsection && (oldsection === section)) ? oldsection.replace(/\{\d+\}$/, '') : section;
var start = 0;
var hasMatch;
do {
hasMatch = false;
var segment = txt.substring(start).replace(pattern,
function replacer(match, p1, p2, offset, string, groups) {
hasMatch = true;
if (!sectionFound && ((indexInfo == null) || (indexInfo.index === sectionIndex))) {
sectionFound = true;
return p1 + sectionTitle + '\n' + newval + p2;
}
sectionIndex++;
start += offset + match.length - p2.length + (p2.length ? 1 : 0);
return match;
}
);
if (sectionFound) {
txt = txt.substring(0, start) + segment;
}
} while (!sectionFound && hasMatch);
};
replace = replace.toString();
replace = replace.substring(replace.indexOf("{"), replace.lastIndexOf("}") + 1).replace(/\$/g, '$$$$');
code = eval("config.macros.editSection.updateTiddler").toString();
code = code.replace("oldsection.escapeRegExp()", "(oldsection||'').replace(/\\{\\d+\\}$$/, '').escapeRegExp()");
code = code.replace("(oldval||'').escapeRegExp()", "'[\\\\s\\\\S]*?'");
code = code.replace("existingval.escapeRegExp()", "'[\\\\s\\\\S]*?'");
code = code.replace("txt=txt.replace(pattern,'$1'+section+'\\n'+newval+'$2');", replace);
eval("config.macros.editSection.updateTiddler = function updateTiddler" + code.substring(code.indexOf("(")));
code = eval("config.macros.editSection.deleteSection").toString();
code = code.replace("section.escapeRegExp()", "(section||'').replace(/\\{\\d+\\}$$/, '').escapeRegExp()");
code = code.replace("(val||'').escapeRegExp()", "'[\\\\s\\\\S]*?'");
code = code.replace("txt=txt.replace(pattern,'$2');", replace.replace(/oldsection/g, "section").replace("p1 + sectionTitle + '\\n' + newval + p2", "p2"));
eval("config.macros.editSection.deleteSection = function deleteSection" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
config.macros.editSections.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (readOnly) return;
var elems=jQuery(place).children('H1, H2, H3, H4, H5, H6');
var sectionIndexes = {};
for (var i=0; i<elems.length; i++) { var e=elems[i]; // for each heading element
var section=jQuery(e).clone() // clone the element
.children() // select all the children
.remove() // remove all the children
.end() // again go back to selected element
.text(); // get the text of element (http://viralpatel.net/blogs/2011/02/jquery-get-text-element-without-child-element.html)
sectionIndexes[section] = (sectionIndexes[section] == null) ? 1 : sectionIndexes[section] + 1;
var label=(params[0]||this.label).format([section]);
var tip =(params[1]||this.tip ).format([section]);
wikify(this.command.format([section + '{' + sectionIndexes[section] + '}',label,tip]),e);
}
};
//}}}
/***
|Name|EmailPlugin|
|License|[[TW Notes License]]|
|Requires|[[AttachFilePluginOverride]] [[FileDropPluginOverride]]|
!!!!!Code
***/
//{{{
config.macros.email = {
displayedInfo: [["from", "From:"], ["to", "To:"], ["cc", "Cc:"], ["bcc", "Bcc:"], ["date", "Date:"], ["subject", "Subject:"], ["attachments", "Attachments:"], ["download", "download"]],
transformers: {
stylecontainer: function () {
if ((typeof message !== "undefined") && message) {
var style = "stylecontainer_" + (new Date()).getTime() + "_" + Math.floor(Math.random() * 10000);
var stylePlaceholder = "__EmailPlugin_transformers_" + style + "__";
var originalMessage;
do {
originalMessage = message;
message = message.replace(/(<style.*?>[\s\S]*?)(?:\/\*[\s\S]*?\*\/)([\s\S]*?<\/style>)/gi, "$1$2");
} while (message != originalMessage);
do {
originalMessage = message;
message = message.replace(/(<style.*?>[\s\S]*?[\s,])(?:html|body)([\s,\{][\s\S]*?\{[\s\S]*?\}[\s\S]*?<\/style>)/gi, "$1" + stylePlaceholder + "$2");
message = message.replace(/(<style.*?>(?:\s*|[\s\S]*?[\},]\s*))(:link|:visited|a|a\[href\]|a:link|a:visited|b|body|code|div|h1|h2|h3|h4|h5|h6|hr|img|input|ol|p|pre|span|strong|table|tr|td|textarea|th|ul|\.title)([\s,:\{][\s\S]*?\{[\s\S]*?\}[\s\S]*?<\/style>)/gi, "$1" + stylePlaceholder + "$2$3");
} while (message != originalMessage);
message = message.replace(new RegExp(stylePlaceholder, "gi"), "." + style + " ");
message = '<span class="' + style + '">' + message + '</span>';
}
}
},
unicode: (function () {
var charsetMaps = {
// http://www.lingua-systems.com/knowledge/unicode-mappings
"iso-8859-2": {"00": 0x0000, "01": 0x0001, "02": 0x0002, "03": 0x0003, "04": 0x0004, "05": 0x0005, "06": 0x0006, "07": 0x0007, "08": 0x0008, "09": 0x0009, "0A": 0x000A, "0B": 0x000B, "0C": 0x000C, "0D": 0x000D, "0E": 0x000E, "0F": 0x000F, "10": 0x0010, "11": 0x0011, "12": 0x0012, "13": 0x0013, "14": 0x0014, "15": 0x0015, "16": 0x0016, "17": 0x0017, "18": 0x0018, "19": 0x0019, "1A": 0x001A, "1B": 0x001B, "1C": 0x001C, "1D": 0x001D, "1E": 0x001E, "1F": 0x001F, "20": 0x0020, "21": 0x0021, "22": 0x0022, "23": 0x0023, "24": 0x0024, "25": 0x0025, "26": 0x0026, "27": 0x0027, "28": 0x0028, "29": 0x0029, "2A": 0x002A, "2B": 0x002B, "2C": 0x002C, "2D": 0x002D, "2E": 0x002E, "2F": 0x002F, "30": 0x0030, "31": 0x0031, "32": 0x0032, "33": 0x0033, "34": 0x0034, "35": 0x0035, "36": 0x0036, "37": 0x0037, "38": 0x0038, "39": 0x0039, "3A": 0x003A, "3B": 0x003B, "3C": 0x003C, "3D": 0x003D, "3E": 0x003E, "3F": 0x003F, "40": 0x0040, "41": 0x0041, "42": 0x0042, "43": 0x0043, "44": 0x0044, "45": 0x0045, "46": 0x0046, "47": 0x0047, "48": 0x0048, "49": 0x0049, "4A": 0x004A, "4B": 0x004B, "4C": 0x004C, "4D": 0x004D, "4E": 0x004E, "4F": 0x004F, "50": 0x0050, "51": 0x0051, "52": 0x0052, "53": 0x0053, "54": 0x0054, "55": 0x0055, "56": 0x0056, "57": 0x0057, "58": 0x0058, "59": 0x0059, "5A": 0x005A, "5B": 0x005B, "5C": 0x005C, "5D": 0x005D, "5E": 0x005E, "5F": 0x005F, "60": 0x0060, "61": 0x0061, "62": 0x0062, "63": 0x0063, "64": 0x0064, "65": 0x0065, "66": 0x0066, "67": 0x0067, "68": 0x0068, "69": 0x0069, "6A": 0x006A, "6B": 0x006B, "6C": 0x006C, "6D": 0x006D, "6E": 0x006E, "6F": 0x006F, "70": 0x0070, "71": 0x0071, "72": 0x0072, "73": 0x0073, "74": 0x0074, "75": 0x0075, "76": 0x0076, "77": 0x0077, "78": 0x0078, "79": 0x0079, "7A": 0x007A, "7B": 0x007B, "7C": 0x007C, "7D": 0x007D, "7E": 0x007E, "7F": 0x007F, "80": 0x0080, "81": 0x0081, "82": 0x0082, "83": 0x0083, "84": 0x0084, "85": 0x0085, "86": 0x0086, "87": 0x0087, "88": 0x0088, "89": 0x0089, "8A": 0x008A, "8B": 0x008B, "8C": 0x008C, "8D": 0x008D, "8E": 0x008E, "8F": 0x008F, "90": 0x0090, "91": 0x0091, "92": 0x0092, "93": 0x0093, "94": 0x0094, "95": 0x0095, "96": 0x0096, "97": 0x0097, "98": 0x0098, "99": 0x0099, "9A": 0x009A, "9B": 0x009B, "9C": 0x009C, "9D": 0x009D, "9E": 0x009E, "9F": 0x009F, "A0": 0x00A0, "A1": 0x0104, "A2": 0x02D8, "A3": 0x0141, "A4": 0x00A4, "A5": 0x013D, "A6": 0x015A, "A7": 0x00A7, "A8": 0x00A8, "A9": 0x0160, "AA": 0x015E, "AB": 0x0164, "AC": 0x0179, "AD": 0x00AD, "AE": 0x017D, "AF": 0x017B, "B0": 0x00B0, "B1": 0x0105, "B2": 0x02DB, "B3": 0x0142, "B4": 0x00B4, "B5": 0x013E, "B6": 0x015B, "B7": 0x02C7, "B8": 0x00B8, "B9": 0x0161, "BA": 0x015F, "BB": 0x0165, "BC": 0x017A, "BD": 0x02DD, "BE": 0x017E, "BF": 0x017C, "C0": 0x0154, "C1": 0x00C1, "C2": 0x00C2, "C3": 0x0102, "C4": 0x00C4, "C5": 0x0139, "C6": 0x0106, "C7": 0x00C7, "C8": 0x010C, "C9": 0x00C9, "CA": 0x0118, "CB": 0x00CB, "CC": 0x011A, "CD": 0x00CD, "CE": 0x00CE, "CF": 0x010E, "D0": 0x0110, "D1": 0x0143, "D2": 0x0147, "D3": 0x00D3, "D4": 0x00D4, "D5": 0x0150, "D6": 0x00D6, "D7": 0x00D7, "D8": 0x0158, "D9": 0x016E, "DA": 0x00DA, "DB": 0x0170, "DC": 0x00DC, "DD": 0x00DD, "DE": 0x0162, "DF": 0x00DF, "E0": 0x0155, "E1": 0x00E1, "E2": 0x00E2, "E3": 0x0103, "E4": 0x00E4, "E5": 0x013A, "E6": 0x0107, "E7": 0x00E7, "E8": 0x010D, "E9": 0x00E9, "EA": 0x0119, "EB": 0x00EB, "EC": 0x011B, "ED": 0x00ED, "EE": 0x00EE, "EF": 0x010F, "F0": 0x0111, "F1": 0x0144, "F2": 0x0148, "F3": 0x00F3, "F4": 0x00F4, "F5": 0x0151, "F6": 0x00F6, "F7": 0x00F7, "F8": 0x0159, "F9": 0x016F, "FA": 0x00FA, "FB": 0x0171, "FC": 0x00FC, "FD": 0x00FD, "FE": 0x0163, "FF": 0x02D9},
"windows-1252": {"92": 0x2019}
};
var toCharCode = function (code) {
return isNaN(parseInt(code, 16)) ? 0 : parseInt(code, 16);
}
var converters = {
"default": toCharCode,
"iso-8859-2": function (code) { return charsetMaps["iso-8859-2"][code] ? charsetMaps["iso-8859-2"][code] : toCharCode(code); },
"windows-1252": function (code) { return charsetMaps["windows-1252"][code] ? charsetMaps["windows-1252"][code] : toCharCode(code); }
};
return converters;
})(),
parseHeaders: function (source) {
var headers = null;
if (source) {
var lineEndIndex = source.indexOf("\n\n");
if (lineEndIndex > -1) {
var headersText = source.substring(0, lineEndIndex + 1);
lineEndIndex = headersText.indexOf("\n");
while (lineEndIndex > -1) {
var headerName = null;
var header = "";
do {
var text = headersText.substring(0, lineEndIndex);
if ((text.indexOf(" ") != 0) && (text.indexOf("\t") != 0)) {
if (text.indexOf(":") != -1) {
headerName = text.substring(0, text.indexOf(":")).trim().toLowerCase();
text = text.substring(text.indexOf(":") + 1).trim();
}
header = text;
} else {
header = header + "\n" + text;
}
headersText = headersText.substring(lineEndIndex + 1);
lineEndIndex = headersText.indexOf("\n");
} while ((headersText.indexOf(" ") == 0) || headersText.indexOf("\t") == 0);
if (headerName != null) {
headers = (headers == null) ? {} : headers;
headers[headerName] = (headers[headerName] == null) ? [] : headers[headerName];
headers[headerName][headers[headerName].length] = header;
}
}
}
}
return headers;
},
parseMessage: function (source) {
var cme = config.macros.email;
var message = null;
var headers = cme.parseHeaders(source);
if (headers) {
var body = source.substring(source.indexOf("\n\n") + 2);
var boundary = null;
if (headers["content-type"]) {
for (var i = 0; i < headers["content-type"].length; i++) {
var match = (/boundary\s*?=\s*?(["|']?)([^"'\s]*)(["|'])?/gi).exec(headers["content-type"][i]);
boundary = (match && match.length) ? match[2] : null;
if (boundary) {
if (!(match[1] && match[3] && (match[1] === match[3]))) {
boundary = (boundary.lastIndexOf(";") === boundary.length - 1) ? boundary.substring(0, boundary.length - 1) : boundary;
}
break;
}
}
}
if (boundary) {
var parts = [];
boundary = "--" + boundary;
var boundaryIndex = body.indexOf(boundary);
if (boundaryIndex > -1) {
body = body.substring(boundaryIndex + boundary.length + 1);
boundaryIndex = body.indexOf(boundary);
while (boundaryIndex > -1) {
var part = cme.parseMessage(body.substring(0, boundaryIndex));
if (part) {
parts[parts.length] = part;
}
body = body.substring(boundaryIndex + boundary.length + 1);
boundaryIndex = body.indexOf(boundary);
}
var part = cme.parseMessage(body);
if (part) {
parts[parts.length] = part;
}
}
if (parts.length) {
message = {headers: headers, parts: parts};
}
} else {
message = {headers: headers, body: body};
}
}
return message;
},
processMessage: function (source, preferredMimeType, partHeaders) {
var cme = config.macros.email;
var email = null;
if (source) {
source = source.replace(/(\r\n|\n|\r)/gm, "\n");
var message = cme.parseMessage(source);
if (message) {
var isPart = function (part, mimeType) {
var result = false;
if (mimeType) {
if (part.headers["content-type"]) {
var contentType = part.headers["content-type"][0].toLowerCase();
contentType = contentType.replace(new RegExp('type=".*?' + mimeType + '.*?"', 'g'), '');
result = contentType.indexOf(mimeType) > -1;
} else {
result = "text/plain".indexOf(mimeType) > -1;
}
}
return result;
};
preferredMimeType = preferredMimeType ? preferredMimeType : "text/html";
var content = {
preferred: null,
text: null,
html: null,
mixed: null,
parts: {},
attachments: []
};
content.preferred = isPart(message, preferredMimeType) ? message : null;
content.text = isPart(message, "text/plain") ? message : null;
content.html = isPart(message, "text/html") ? message : null;
var catalogContent = function (message) {
if (message.parts) {
var mixedContent = (isPart(message, "multipart/mixed") || isPart(message, "multipart/report")) ? [] : null;
var mixedContentValid = false;
for (var i = 0; i < message.parts.length; i++) {
var messagePart = message.parts[i];
if (isPart(messagePart, "message/rfc822")) {
messagePart = jQuery.extend(true, {}, messagePart);
messagePart.email = cme.processMessage(messagePart.body, preferredMimeType, messagePart.headers);
}
mixedContentValid = mixedContentValid || (isPart(messagePart, preferredMimeType)
|| isPart(messagePart, "text/")
|| isPart(messagePart, "image/")
|| isPart(messagePart, "message/rfc822"));
if (mixedContent && mixedContentValid) {
mixedContent[mixedContent.length] = messagePart;
} else {
content.preferred = content.preferred || (isPart(messagePart, preferredMimeType) ? messagePart : null);
content.text = content.text || (isPart(messagePart, "text/plain") ? messagePart : null);
content.html = content.html || (isPart(messagePart, "text/html") ? messagePart : null);
}
var contentDispositions = messagePart.headers["content-disposition"];
if (contentDispositions) {
for (var j = 0; contentDispositions && (j < contentDispositions.length); j++) {
var contentDisposition = contentDispositions[j].trim().toLowerCase();
if (contentDisposition.indexOf("attachment") === 0) {
content.attachments[content.attachments.length] = messagePart;
break;
}
}
}
var contentIds = messagePart.headers["content-id"];
if (contentIds && messagePart.body) {
var contentId = null;
for (var j = 0; j < contentIds.length; j++) {
var contentId = contentIds[j];
contentId = (contentId.indexOf("<") == 0) ? contentId.substring(1) : contentId;
contentId = (contentId.lastIndexOf(">") == contentId.length - 1) ? contentId.substring(0, contentId.length - 1) : contentId;
contentId = contentId.trim();
if (contentId) {
break;
}
}
if (contentId) {
content.parts[contentId] = messagePart;
}
}
if (messagePart.parts) {
catalogContent(messagePart);
}
}
if (mixedContent && !content.preferred) {
for (var i = 0; !content.preferred && (i < mixedContent.length); i++) {
content.preferred = content.preferred || (isPart(mixedContent[i], preferredMimeType) ? mixedContent[i] : null);
}
}
if (mixedContent && mixedContentValid) {
content.mixed = content.mixed || mixedContent;
}
}
};
catalogContent(message);
if (content.preferred && (isPart(content.preferred, "multipart/mixed") || isPart(content.preferred, "multipart/report"))) {
content.preferred = content.mixed;
}
var mainMessage = content.preferred || (content.html || content.text || content.mixed);
mainMessage = mainMessage ? (jQuery.isArray(mainMessage) ? mainMessage : [mainMessage]) : mainMessage;
if (mainMessage) {
var messageText = [];
for (var m = 0; m < mainMessage.length; m++) {
if (mainMessage[m].body && (!mainMessage[m].headers["content-type"] || isPart(mainMessage[m], "text/") || isPart(mainMessage[m], "message/delivery-status"))) {
messageText[m] = mainMessage[m].body;
var processContentTransferEncoding = function (headers) {
var charset = null;
if (headers && headers["content-transfer-encoding"]) {
if (headers["content-type"]) {
for (var i = 0; i < headers["content-type"].length; i++) {
var match = (/charset\s*?=\s*?["|']?([^"'\s;]*)["|']?/gi).exec(headers["content-type"][i]);
charset = (match && match.length) ? match[1] : null;
if (charset) {
break;
}
}
}
for (var i = 0; i < headers["content-transfer-encoding"].length; i++) {
if (headers["content-transfer-encoding"][i].toLowerCase().indexOf("base64") > -1) {
messageText[m] = (messageText[m].indexOf("%/") > -1) && (messageText[m].substring(messageText[m].lastIndexOf("%/") + 2).trim().length == 0) ? messageText[m] = messageText[m].substring(0, messageText[m].lastIndexOf("%/")) : messageText[m];
messageText[m] = config.macros.attach.decodeBase64(messageText[m].trim());
}
}
for (var i = 0; i < headers["content-transfer-encoding"].length; i++) {
if (headers["content-transfer-encoding"][i].toLowerCase().indexOf("quoted-printable") > -1) {
messageText[m] = cme.decodeQuotedPrintable(messageText[m], charset);
}
}
}
if (!charset || (charset.toLowerCase() == "utf-8")) {
var validUTF8 = config.macros.fileDrop.validUTF8(messageText[m]);
if (!validUTF8) {
var normalizedMessageText = messageText[m]
.replace(/‎/g, "")
.replace(/’/g, "'")
.replace(/Ã /g, String.fromCharCode(0xC3, 0xA0))
.replace(/Â /g, String.fromCharCode(0xC2, 0xA0))
.replace(/Ç/g, String.fromCharCode(0xC3, 0x87))
.replace(/€™/g, String.fromCharCode(0x80, 0x99))
.replace(/À/g, String.fromCharCode(0xC3, 0x80));
validUTF8 = config.macros.fileDrop.validUTF8(normalizedMessageText);
messageText[m] = validUTF8 ? normalizedMessageText : messageText[m];
}
if (validUTF8) {
messageText[m] = convertUTF8ToUnicode(messageText[m].replace(/\u2019/g, String.fromCharCode(0xE2, 0x80, 0x99)));
}
}
};
processContentTransferEncoding(partHeaders);
processContentTransferEncoding(mainMessage[m].headers);
messageText[m] = cme.encodeHTMLEntities(messageText[m]);
if (isPart(mainMessage[m], "text/html")) {
messageText[m] = messageText[m]
.replace(/\<html[\s\S]*?\>/g, "").replace(/\<\/html[\s\S]*?\>/g, "")
.replace(/\<body[\s\S]*?\>/g, "").replace(/\<\/body[\s\S]*?\>/g, "");
var index = 0;
while ((index = messageText[m].indexOf("cid:", index)) > -1) {
var endIndex = -1;
if (messageText[m].charAt(index - 1) == "=") {
endIndex = (messageText[m].indexOf(">", index) < messageText[m].indexOf(" ", index)) ? messageText[m].indexOf(">", index) : messageText[m].indexOf(" ", index);
} else {
endIndex = messageText[m].indexOf(messageText[m].charAt(index - 1), index);
}
var contentId = messageText[m].substring(index + "cid:".length, endIndex);
var contentPart = content.parts[contentId];
if (contentPart) {
var contentType = "application/octet-stream";
var contentTypeHeader = contentPart.headers["content-type"];
if (contentTypeHeader && contentTypeHeader.length) {
contentType = contentTypeHeader[0];
contentType = (contentType.indexOf(";") > -1) ? contentType.substring(0, contentType.indexOf(";")) : contentType;
}
var contentBody = contentPart.body.replace(/(\r\n|\n|\r)/gm, "");
var contentURL = "data:" + contentType + ";base64," + contentBody.trim();
var contentIdURL = "cid:" + contentId;
while (messageText[m].indexOf(contentIdURL) > -1) {
messageText[m] = messageText[m].replace(contentIdURL, contentURL);
}
}
index = index + 1;
}
}
if (!message.parts && (messageText[m].indexOf("%/") > -1) && (messageText[m].substring(messageText[m].lastIndexOf("%/") + 2).trim().length == 0)) {
messageText[m] = messageText[m].substring(0, messageText[m].lastIndexOf("%/"));
}
} else {
var isAttachment = false;
var contentDispositions = mainMessage[m].headers["content-disposition"];
if (contentDispositions) {
for (var j = 0; contentDispositions && (j < contentDispositions.length); j++) {
var contentDisposition = contentDispositions[j].trim().toLowerCase();
if (contentDisposition.indexOf("attachment") === 0) {
isAttachment = true;
break;
}
}
}
if (isAttachment || mainMessage[m].email) {
messageText[m] = null;
} else {
messageText[m] = "NON-TEXT CONTENT";
}
}
}
email = {message: message, main: mainMessage, text: messageText, attachments: content.attachments};
}
}
}
return email;
},
decodeQuotedPrintable: function (source, encoding) {
if (source) {
var toCharCode = encoding ? config.macros.email.unicode[encoding.toLowerCase()] : config.macros.email.unicode.default;
toCharCode = toCharCode || config.macros.email.unicode.default;
source = source
.replace(/=\n/g, "")
.replace(/=([A-Fa-f0-9]{2})/g, function (match, p1, offset, string) {
return String.fromCharCode(toCharCode(p1));
});
}
return source;
},
encodeHTMLEntities: function (source) {
if (source) {
source = source
.replace(/\x99/g, "™");
}
return source;
},
processHeaderValue: function (value) {
if (value) {
var cme = config.macros.email;
var cma = config.macros.attach;
value = value.replace(/\n /g, "");
value = value.replace(/\n\t/g, " ");
value = value.replace(/=\?ISO-8859-1\?B\?(.*?)\?=/gi, function (match, p1, offset, string) {
var text = cma.decodeBase64(p1.trim());
return text;
});
value = value.replace(/=\?UTF-8\?B\?(.*?)\?=/gi, function (match, p1, offset, string) {
var text = cma.decodeBase64(p1.trim());
return text;
});
var charset = null;
value = value.replace(/=\?(.*?)\?Q\?(.*?)\?=/gi, function (match, p1, p2, offset, string) {
charset = p1;
return cme.decodeQuotedPrintable(p2.replace(/_/g, " "), charset);
});
if ((!charset || (charset.toLowerCase() == "utf-8")) && config.macros.fileDrop.validUTF8(value)) {
value = convertUTF8ToUnicode(value);
}
value = cme.encodeHTMLEntities(value);
value = value.replace(/</g, "<").replace(/>/g, ">");
}
return value;
},
renderMessage: function (email, transformers, displayedInfo, sliderId) {
var message = null;
if (email) {
var cme = config.macros.email;
sliderId = sliderId ? sliderId : "";
displayedInfo = displayedInfo ? displayedInfo : cme.displayedInfo;
displayedInfo = displayedInfo ? displayedInfo : [];
var isBase64 = function (part) {
var isBase64 = false;
if (part) {
var encodingHeader = part.headers["content-transfer-encoding"];
if (encodingHeader) {
for (var k = 0; k < encodingHeader.length; k++) {
if (encodingHeader[k].toLowerCase().indexOf("base64") > -1) {
isBase64 = true;
break;
}
}
}
}
return isBase64;
};
var dataURItoObjectURL = function (dataURI, mimeType) {
// http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
// convert base64/URLEncoded data component to raw binary data held in a string
try {
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = unescape(dataURI.split(',')[1]);
}
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
// http://stackoverflow.com/questions/10412299/whats-the-difference-between-blobbuilder-and-the-new-blob-constructor
var dataView = new DataView(ab);
var blob = new Blob([dataView], { type: (mimeType ? mimeType : (mimeString ? mimeString : "application/octet-stream")) });
var DOMURL = self.URL || self.webkitURL || self;
return "" + DOMURL.createObjectURL(blob);
} catch (e) {
if (!config.macros.email.messages) {
config.macros.email.messages = {};
}
var messageId = "id_" + (new Date()).getTime() + "_" + Math.floor((Math.random() * 10000));
config.macros.email.messages[messageId] = dataURI.split(',')[1];
return "javascript:alert(config.macros.email.messages." + messageId + ")";
}
};
var processAttachment = function (part) {
var attachment = {};
if (part) {
var cma = config.macros.attach;
var fileName = null;
var contentType = null;
var contentTypeHeader = part.headers["content-type"];
if (contentTypeHeader && contentTypeHeader.length) {
contentType = contentTypeHeader[0];
contentType = (contentType.indexOf(";") > -1) ? contentType.substring(0, contentType.indexOf(";")) : contentType;
contentType = contentType.toLowerCase();
for (var k = 0; k < contentTypeHeader.length; k++) {
var match = (/name\s*=\s*["|']?([\s\S]*)/gi).exec(contentTypeHeader[k]);
fileName = (match && match.length) ? match[1] : null;
if (fileName) {
break;
}
}
}
if (!fileName) {
if (part.headers["content-disposition"]) {
for (var k = 0; k < part.headers["content-disposition"].length; k++) {
var match = (/filename\s*=\s*["|']?([\s\S]*)/gi).exec(part.headers["content-disposition"][k]);
fileName = (match && match.length) ? match[1] : null;
if (fileName) {
break;
}
}
}
}
if (fileName && fileName.length && ((fileName.charAt(fileName.length - 1) == '"') || (fileName.charAt(fileName.length - 1) == "'"))) {
fileName = fileName.substring(0, fileName.length - 1);
}
if (fileName) {
fileName = cme.processHeaderValue(fileName);
}
if (fileName && !contentType && (fileName.indexOf(".") > -1)) {
contentType = cma.getMIMEType(fileName);
} else if (!fileName && contentType) {
var list = store.getTiddlerText(cma.typeList);
if (list && list.trim()) {
var parts = list.split("\n----\n");
for (var p = 0; p < parts.length && !fileName; p++) {
var lines = parts[p].split("\n");
var mime = lines.shift().toLowerCase();
if (mime === contentType) {
var extensions = lines.shift().split(" ");
if (extensions.length) {
fileName = "document" + extensions[0];
}
}
}
}
}
fileName = fileName || "document.bin";
attachment.name = fileName;
contentType = contentType || "application/octet-stream";
attachment.type = contentType;
var content = isBase64(part) ? part.body.replace(/(\r\n|\n|\r)/gm, "") : cma.encodeBase64(part.body);
attachment.uri = "data:" + contentType + ";base64," + content.trim();
attachment.url = dataURItoObjectURL(attachment.uri);
config.urlFileNameMap = config.urlFileNameMap || {};
config.urlFileNameMap[attachment.url] = fileName;
attachment.link = jQuery('<a href="' + attachment.url + '" target="_blank"></a>');
attachment.link.attr("download", fileName);
attachment.link.text(fileName);
}
return attachment;
};
var messageInfo = jQuery("<table class='EmailPluginInfo'></table>");
for (var i = 0; i < displayedInfo.length; i++) {
if (displayedInfo[i][0] === "attachments") {
if (email.attachments.length > 0) {
var attachmentInfo = jQuery("<td></td>");
var downloadLabel = null;
for (var j = 0; j < displayedInfo.length; j++) {
if (displayedInfo[j][0] === "download") {
downloadLabel = displayedInfo[j][1];
}
}
for (var j = 0; j < email.attachments.length; j++) {
var attachment = email.attachments[j];
if (attachment.email) {
var emailPanelPlace = jQuery("<span></span>");
var emailPanel = config.macros.slider.createSlider(emailPanelPlace[0], sliderId + "attachment" + j + ":emailSliderPanel", "email");
jQuery(emailPanel).append(cme.renderMessage(attachment.email, transformers, displayedInfo, sliderId + "attachment" + j + ":"));
attachmentInfo = attachmentInfo.children().length ? attachmentInfo.append(", ").append(emailPanelPlace) : attachmentInfo.append(emailPanelPlace);
} else if (attachment.headers["content-type"] && (attachment.headers["content-type"][0].toLowerCase().indexOf("application/pdf") > -1) && config.macros.pdf) {
(function () {
var pdfViewerCreated = false;
var pdfAttachment = processAttachment(attachment);
var pdfPanelPlace = jQuery("<div></div>");
var cookie = sliderId + "attachment" + j + ":pdfSliderPanel";
var pdfPanel = jQuery("<div class='sliderPanel'><div class='pdfViewer'></div><div class='pdfDownload'></div></div>");
createTiddlyButton(pdfPanelPlace[0], pdfAttachment.name, null, function (e) {
config.macros.slider.onClickSlider.call(this, e);
if (config.options[cookie] && !pdfViewerCreated) {
config.macros.pdf.createViewer(pdfAttachment.url, pdfAttachment.name, pdfPanel.find(".pdfViewer"));
pdfViewerCreated = true;
}
});
pdfPanel.attr("cookie", cookie);
pdfPanel.hide();
if (config.options[cookie]) {
pdfPanel.show();
config.macros.pdf.createViewer(pdfAttachment.url, pdfAttachment.name, pdfPanel.find(".pdfViewer"));
pdfViewerCreated = true;
}
if (downloadLabel) {
config.macros.pdf.createLink(pdfAttachment.url, downloadLabel, pdfAttachment.name, pdfPanel.find(".pdfDownload"));
}
pdfPanelPlace[0].appendChild(pdfPanel[0]);
attachmentInfo = attachmentInfo.children().length ? attachmentInfo.append(", ").append(pdfPanelPlace) : attachmentInfo.append(pdfPanelPlace);
})();
} else {
attachmentInfo = attachmentInfo.children().length ? attachmentInfo.append(", ").append(processAttachment(attachment).link) : attachmentInfo.append(processAttachment(attachment).link);
}
}
messageInfo.append(jQuery("<tr></tr>").append("<td class='label'>" + displayedInfo[i][1] + "</td>").append(attachmentInfo));
}
} else if (displayedInfo[i][0] !== "download") {
var displayedHeaders = email.message.headers[displayedInfo[i][0]];
if (displayedHeaders) {
for (var j = 0; j < displayedHeaders.length; j++) {
var value = cme.processHeaderValue(displayedHeaders[j]);
if (displayedInfo[i][0] == "date") {
// http://stackoverflow.com/questions/9352003/rfc-2822-date-regex
var rfc2822DateRegexp = /^(?:(Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s+)?(0[1-9]|[1-2]?[0-9]|3[01])\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+(19[0-9]{2}|[2-9][0-9]{3})\s+(2[0-3]|[0-1][0-9]):([0-5][0-9])(?::(60|[0-5][0-9]))?\s+([-\+][0-9]{2}[0-5][0-9]|(?:UT|GMT|(?:E|C|M|P)(?:ST|ET|DT)|[A-IK-Z]))(\s+|\(([^\(\)]+|\\\(|\\\))*\))*$/;
var matches = rfc2822DateRegexp.exec(value);
if (matches.length == 11) {
var monthMap = {Jan: 1, Feb: 2, Mar: 3, Apr: 4, May: 5, Jun: 6, Jul: 7, Aug: 8, Sep: 9, Oct: 10, Nov: 11, Dec: 12};
var date = new Date(matches[4], monthMap[matches[3]] - 1, matches[2], matches[5], matches[6], matches[7], 0);
var timezone = matches[8];
if (!isNaN(parseFloat(timezone)) && isFinite(timezone) && (timezone.length > 3)) {
var timezoneOffset = ((timezone.charAt(0) == "-") ? -1 : 1) * ((parseInt(timezone.substring(timezone.length - 4, timezone.length - 2)) * 60) + parseInt(timezone.substring(timezone.length - 2)));
date.setMinutes(date.getMinutes() + ((-1 * date.getTimezoneOffset()) - timezoneOffset));
var timezoneHours = (date.getTimezoneOffset() / 60) | 0;
var timezoneMinutes = Math.abs(date.getTimezoneOffset() - (timezoneHours * 60));
var timezone = (Math.abs(timezoneHours) < 10 ? "0" : "") + Math.abs(timezoneHours) + (timezoneMinutes < 10 ? "0" : "") + timezoneMinutes;
timezone = (date.getTimezoneOffset() <= 0 ? "+" : "-") + timezone;
}
value = "" + date.getFullYear() + "-" + (date.getMonth() + 1 < 10 ? "0" : "") + (date.getMonth() + 1) + "-" + (date.getDate() < 10 ? "0" : "") + date.getDate() + " " + (date.getHours() < 10 ? "0" : "") + date.getHours() + ":" + (date.getMinutes() < 10 ? "0" : "") + date.getMinutes() + ":" + (date.getSeconds() < 10 ? "0" : "") + date.getSeconds() + " " + timezone;
}
}
messageInfo.append("<tr><td class='label'>" + displayedInfo[i][1] + "</td><td>" + value + "</td></tr>");
}
}
}
}
messageInfo = messageInfo.children().length ? messageInfo : null;
var styleClass = (new Date()).getTime() + "_" + Math.floor(Math.random() * 10000);
var contentStyleClass = "EmailPluginMessageContent_" + styleClass;
styleClass = "EmailPluginMessage_" + styleClass;
var style
= "<style>\n"
+ "\t." + styleClass + " table.EmailPluginInfo {\n"
+ "\t\width: 100%;\n"
+ "\t\tmargin: 0;\n"
+ "\t}\n"
+ "\t." + styleClass + " table.EmailPluginInfo, ." + styleClass + " .EmailPluginInfo tr, ." + styleClass + " .EmailPluginInfo td, ." + styleClass + " .htmlMessage table, ." + styleClass + " .htmlMessage tr, ." + styleClass + " .htmlMessage td {\n"
+ "\t\tborder: none;\n"
+ "\t}\n"
+ "\t." + styleClass + " .EmailPluginInfo td {\n"
+ "\t\tvertical-align: top;\n"
+ "\t}\n"
+ "\t." + styleClass + " .EmailPluginInfo td.label {\n"
+ "\t\twidth: 1%;\n"
+ "\t\twhite-space: nowrap;\n"
+ "\t\ttext-align: right;\n"
+ "\t\tfont-weight: bold;\n"
+ "\t}\n"
+ "\t\n"
+ "\t/*\n"
+ "\t\tJosh's Custom CSS Reset\n"
+ "\t\thttps://www.joshwcomeau.com/css/custom-css-reset/\n"
+ "\t*/\n"
+ "\t.viewer ." + contentStyleClass + " *, .viewer ." + contentStyleClass + " *::before, .viewer ." + contentStyleClass + " *::after {\n"
+ "\t\tbox-sizing: border-box;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " * {\n"
+ "\t\tmargin: 0;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " html, .viewer ." + contentStyleClass + " body {\n"
+ "\t\theight: 100%;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " body {\n"
+ "\t\tline-height: 1.5;\n"
+ "\t\t-webkit-font-smoothing: antialiased;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " img, .viewer ." + contentStyleClass + " picture, .viewer ." + contentStyleClass + " video, .viewer ." + contentStyleClass + " canvas, .viewer ." + contentStyleClass + " svg {\n"
// + "\t\tdisplay: block;\n"
// + "\t\tmax-width: 100%;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " input, .viewer ." + contentStyleClass + " button, .viewer ." + contentStyleClass + " textarea, .viewer ." + contentStyleClass + " select {\n"
+ "\t\tfont: inherit;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " p, .viewer ." + contentStyleClass + " h1, .viewer ." + contentStyleClass + " h2, .viewer ." + contentStyleClass + " h3, .viewer ." + contentStyleClass + " h4, .viewer ." + contentStyleClass + " h5, .viewer ." + contentStyleClass + " h6 {\n"
+ "\t\toverflow-wrap: break-word;\n"
+ "\t}\n"
+ "\t.viewer ." + contentStyleClass + " #root, .viewer ." + contentStyleClass + " #__next {\n"
+ "\t\tisolation: isolate;\n"
+ "\t}\n"
+ "\t\n"
+ "\t.viewer ." + contentStyleClass + " * {\n"
+ "\t\tbackground: unset;\n"
+ "\t\tborder: unset;\n"
+ "\t}\n"
+ "\t\n"
+ "\t.viewer ." + contentStyleClass + " pre.EmailPluginTextContent {\n"
+ "\t\tborder: none;\n"
+ "\t\tbackground: " + store.getTiddlerSlice("ColorPalette", "Background") + ";\n"
+ "\t\tbackground-color: transparent;\n"
+ "\t\tcolor: " + store.getTiddlerSlice("ColorPalette", "Foreground") + ";\n"
+ "\t\twhite-space: pre-wrap;\n"
+ "\t}\n"
+ "</style>";
message = email.text[0];
if (transformers) {
for (var i = 0; i < transformers.length; i++) {
var transformer = cme.transformers[transformers[i]] ? "(" + cme.transformers[transformers[i]].toString() + ")()" : transformers[i];
try {
eval("" + transformer);
} catch (e) {
throw "transformation error: " + e;
}
}
}
email.text[0] = message;
var renderedMessage = jQuery("<span></span>");
if (styleClass != null) {
renderedMessage.attr("class", styleClass);
}
if (messageInfo != null) {
renderedMessage.append(messageInfo);
}
var renderedMessageItems = jQuery("<span></span>");
renderedMessage.append(renderedMessageItems);
for (var i = 0; i < email.main.length; i++) {
var renderedMessageItemContainer = jQuery("<span></span>");
if (contentStyleClass != null) {
renderedMessageItemContainer.attr("class", contentStyleClass);
}
var renderedMessageItem = null;
if (email.main[i].email) {
renderedMessageItem = cme.renderMessage(email.main[i].email, transformers, displayedInfo, sliderId + "email" + i + ":")
} else if (email.text[i] == null) {
renderedMessageItem = null;
} else if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].toLowerCase().indexOf("text/html") > -1)) {
renderedMessageItem = jQuery("<div class='htmlMessage'>" + email.text[i] + "</div>");
} else if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].toLowerCase().indexOf("image/") > -1)
&& isBase64(email.main[i]) && email.main[i].body) {
var imageType = email.main[i].headers["content-type"][0].toLowerCase();
imageType = (imageType.indexOf(";") > -1) ? imageType.substring(0, imageType.indexOf(";")) : imageType;
renderedMessageItem = jQuery("<img src='data:" + imageType + ";base64," + email.main[i].body.trim() + "' />");
} else if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].toLowerCase().indexOf("text/calendar") > -1)) {
if (contentStyleClass != null) {
renderedMessageItemContainer.attr("class", contentStyleClass + "_calendar");
}
if (config.macros.ics && config.macros.ics.format) {
renderedMessageItem = jQuery("<span></span>");
renderedMessageItem.append(config.macros.ics.format(email.text[i]));
var icsPanelPlace = jQuery("<div></div>");
var icsPanel = config.macros.slider.createSlider(icsPanelPlace[0], sliderId + "calendar" + i + ":icsSliderPanel", "ics");
jQuery(icsPanel).append("<pre class='EmailPluginTextContent'>" + email.text[i].replace(/</g, "<").replace(/>/g, ">") + "</pre>");
renderedMessageItem.append(icsPanelPlace);
} else {
renderedMessageItem = jQuery("<pre class='EmailPluginTextContent'>" + email.text[i].replace(/</g, "<").replace(/>/g, ">") + "</pre>");
}
} else if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].toLowerCase().indexOf("text/") > -1)) {
renderedMessageItem = jQuery("<pre class='EmailPluginTextContent'>" + email.text[i].replace(/</g, "<").replace(/>/g, ">") + "</pre>");
} else if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].toLowerCase().indexOf("application/pdf") > -1) && config.macros.pdf) {
renderedMessageItem = jQuery("<div></div>");
var attachment = processAttachment(email.main[i]);
config.macros.pdf.createViewer(attachment.url, attachment.name, renderedMessageItem[0]);
} else if (email.main[i].headers["content-type"] && email.main[i].body) {
renderedMessageItem = processAttachment(email.main[i]).link;
} else {
renderedMessageItem = jQuery("<pre class='EmailPluginTextContent'>" + email.text[i].replace(/</g, "<").replace(/>/g, ">") + "</pre>");
}
if (renderedMessage.children().length) {
renderedMessage.append("<br />");
}
if (renderedMessageItem != null) {
renderedMessageItemContainer.append(renderedMessageItem);
renderedMessageItems.append(renderedMessageItemContainer);
}
}
if (style) {
renderedMessage.prepend(style);
}
}
return renderedMessage;
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var cme = config.macros.email;
var parsedParams = paramString.parseParams("anon", null, true, false, false);
var title = null;
var filter = (parsedParams.length && parsedParams[0].filter) ? parsedParams[0].filter[0] : null;
if (filter) {
var related = null;
if (filter.indexOf("guid:") == 0) {
filter = filter.substring("guid:".length);
try {
filter = eval(filter);
} catch (e) {}
var storeTiddlers = store.getTiddlers();
for (var i = 0; i < storeTiddlers.length; i++) {
if (storeTiddlers[i].fields && storeTiddlers[i].fields["guid"]) {
if (filter.test) {
if (filter.test(storeTiddlers[i].fields["guid"])) {
title = storeTiddlers[i].title;
break;
}
} else if (storeTiddlers[i].fields["guid"].indexOf(filter) > -1) {
title = storeTiddlers[i].title;
break;
}
}
}
} else {
if (filter.indexOf("tagged:") == 0) {
related = tiddler ? store.getTaggedTiddlers(tiddler.title) : related;
filter = filter.substring("tagged:".length);
} else if (filter.indexOf("tag:") == 0) {
related = tiddler ? tiddler.tags : related;
filter = filter.substring("tag:".length);
}
related = related ? related : store.getTiddlers();
try {
filter = eval(filter);
} catch (e) {}
for (var i = 0; i < (related ? related.length : 0); i++) {
var currentTitle = related[i].title ? related[i].title : related[i];
if (filter.test) {
if (filter.test(currentTitle)) {
title = currentTitle;
break;
}
} else if (currentTitle.indexOf(filter) > -1) {
title = currentTitle;
break;
}
}
}
}
var processors = parsedParams.length ? parsedParams[0].processor : null;
var transformers = parsedParams.length ? parsedParams[0].transformer : null;
var sourceReference = (parsedParams.length && parsedParams[0].source) ? parsedParams[0].source[0] : null;
var source = null;
if (title !== null) {
store.getTiddlerText(title);
source = (sourceReference !== null) ? store.getTiddlerText(title + sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(title + (processors ? "" : "##email"));
}
if (source === null) {
var tiddlerTitle = tiddler ? tiddler.title : "";
source = (sourceReference !== null) ? store.getTiddlerText(sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + ((sourceReference !== null) ? sourceReference : (processors ? "" : "##email")));
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + (processors ? "" : "##email"));
}
if (processors) {
for (var i = 0; i < processors.length; i++) {
try {
eval(processors[i]);
} catch (e) {
throw "processing error: " + e;
}
}
}
var displayedInfo = parsedParams.length ? parsedParams[0].info : null;
if (displayedInfo) {
try {
displayedInfo = eval(displayedInfo[0]);
} catch (e) {}
}
var message = cme.renderMessage(cme.processMessage(source, parsedParams.length ? parsedParams[0].format : false), transformers, displayedInfo, "config.macros.email:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : "") + ":");
if (message) {
var messageContainer = jQuery("<div></div>");
messageContainer.append(message);
jQuery(place).append(messageContainer);
setTimeout(function () {
jQuery(window).trigger("resize");
}, 0);
}
}
}
//}}}
/***
|Name|EventPlugin|
|License|[[TW Notes License]]|
|Requires|[[ical.js]]|
!!!!!Node.js Email Sender Server
Unzip [[email.zip|data:application/x-zip-compressed;base64,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]] and place the following {{{email.js}}} server script within the {{{email}}} folder.
Usage: {{{node email.js [<port> | (<port> <bind-address>)]}}}
{{{
const http = require("http");
const nodemailer = require("nodemailer");
var requestListener = function (request, response) {
var onRequestData;
var onRequestEnd;
var onRequestError;
var sendResponse = function (message) {
request.removeListener("data", onRequestData);
request.removeListener("end", onRequestEnd);
request.removeListener("error", onRequestError);
response.statusCode = 200;
response.setHeader("Server", "Email Server");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Content-Type", "application/json");
response.end(JSON.stringify(message));
};
response.on("error", function (error) {
console.log(error);
});
var chunks = [];
onRequestData = function (chunk) {
chunks.push(chunk);
};
onRequestEnd = function () {
try {
var data = Buffer.concat(chunks).toString();
if (!data) {
sendResponse({response: "no data"});
} else {
data = JSON.parse(data);
nodemailer.createTransport(data.settings).sendMail(data.email, function (error, info) {
if (error) {
onRequestError(error);
} else {
console.log("Email sent: " + info.response);
sendResponse({response: info.response});
}
});
}
} catch (error) {
onRequestError(error);
}
};
onRequestError = function (error) {
console.log(error);
sendResponse({error: error.message});
};
request.on("data", onRequestData);
request.on("end", onRequestEnd);
request.on("error", onRequestError);
};
var port = 6942;
var hostname = null;
if (process.argv.length > 3) {
port = process.argv[2];
hostname = process.argv[3];
} else if (process.argv.length > 2) {
port = process.argv[2];
}
http.createServer(requestListener).listen(port, hostname || "127.0.0.1", function () {
if (hostname != null) {
console.log("Listening on");
console.log(" address: " + hostname);
console.log(" port: " + port);
} else {
console.log("Listening on port: " + port);
}
console.log();
});
}}}
!!!!!Code
***/
//{{{
if (config.options.txtEventPluginDefaultAttendees === undefined) {
config.options.txtEventPluginDefaultAttendees = "";
}
if (config.options.txtEventPluginEmailSenderServerURL === undefined) {
config.options.txtEventPluginEmailSenderServerURL = "http://localhost:6942";
}
if (config.options.txtEventPluginEventReplyBCC === undefined) {
config.options.txtEventPluginEventReplyBCC = "";
}
if (config.options.txtEventPluginPreferredAttendees === undefined) {
config.options.txtEventPluginPreferredAttendees = "";
}
merge(config.optionsDesc, {
txtEventPluginDefaultAttendees: "The comma separated list of email addresses displayed if no attendees are associated to the event",
txtEventPluginEmailSenderServerURL: "The URL of the Email Sender Server",
txtEventPluginEventReplyBCC: "The comma separated list of email addresses to which event replies will also be sent",
txtEventPluginPreferredAttendees: "The list of email addresses used to preselect an attendee for which to send an event response: this list is updated by the EventPlugin"
});
//}}}
//{{{
config.macros.eventPlugin = {
emailServerSettings: {
host: "localhost",
port: 25,
secure: false,
auth: {
user: "user@domain.tld",
pass: "secretPassword"
}
},
formatDate: function (d, utc) {
var date;
if (utc) {
date = d.getUTCFullYear() + "-" + ("0" + (d.getUTCMonth() + 1)).slice(-2) + "-" + ("0" + d.getUTCDate()).slice(-2) + "T" + ("0" + d.getUTCHours()).slice(-2) + ":" + ("0" + d.getUTCMinutes()).slice(-2) + ":" + ("0" + d.getUTCSeconds()).slice(-2) + "Z";
} else {
date = d.getFullYear() + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" + ("0" + d.getDate()).slice(-2) + "T" + ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2) + ":" + ("0" + d.getSeconds()).slice(-2);
}
return date;
},
responseId: 0,
formatter: {
name: "vcalendar",
match: "<vcalendar>",
lookaheadRegExp: /<vcalendar>([\s\S]*?BEGIN:VCALENDAR[\s\S]*?\nEND:VCALENDAR[\s\S]*?)<\/vcalendar>/mg,
handler: function (w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if (lookaheadMatch && (lookaheadMatch.index === w.matchStart)) {
var matchText = lookaheadMatch[1];
var calendars = [];
var icsStart;
var icsEnd = 0;
while ((icsStart = matchText.indexOf("BEGIN:VCALENDAR", icsEnd)) > -1) {
icsEnd = matchText.indexOf("END:VCALENDAR", icsStart);
if (icsEnd > -1) {
var ics = matchText.substring(icsStart, icsEnd + "END:VCALENDAR".length);
calendars[calendars.length] = ics;
} else {
break;
}
}
for (var i = 0; i < calendars.length; i++) {
var vevents = (new ICAL.Component(ICAL.parse(calendars[i]))).getAllSubcomponents("vevent");
for (var j = 0; j < vevents.length; j++) {
var vevent = vevents[j];
var status = vevent.getFirstPropertyValue("status");
if (!status || (status.toUpperCase() !== "CANCELLED")) {
(function () {
var responseId = "responseid_" + config.macros.eventPlugin.responseId++;
var eventComponent = jQuery(
"<div>" +
"<span></span>" +
"<div style='margin-left: 10px'>" +
"<select></select> " +
" <label><input type='radio' name='" + responseId + "' value='ACCEPTED' checked='checked'> accept</label> " +
"<label><input type='radio' name='" + responseId + "' value='TENTATIVE'> tentatve</label> " +
"<label><input type='radio' name='" + responseId + "' value='DECLINED'> decline</label> " +
" <button type='button' style='padding: 2px;'>send</button>" +
"</div>" +
"</div>").appendTo(w.output);
var title = "";
var dtstart = vevent.getFirstProperty("dtstart");
var dtend = vevent.getFirstProperty("dtend");
if (dtstart || dtend) {
dtstart = dtstart || dtend;
dtend = dtend || dtstart;
tzid = dtstart.getParameter("tzid");
dtstart = "" + dtstart.getFirstValue();
dtend = "" + dtend.getFirstValue();
tzid = tzid || ((dtstart.indexOf("Z") > -1) ? "UTC" : "");
dtend = (dtstart.substring(0, 11) === dtend.substring(0, 11)) ? dtend.substring(11) : dtend;
dtstart = dtstart.replace("T", " ").replace("Z", "");
dtend = dtend.replace("T", " ").replace("Z", "");
title = dtstart + " - " + dtend + (tzid ? " (" + tzid + ")" : "");
}
var summary = vevent.getFirstPropertyValue("summary");
if (summary) {
title += (title.length ? " : " : "") + summary;
}
eventComponent.find("span").text(title);
var attendees = vevent.getAllProperties("attendee");
var attendeeList = [];
var emailList = [];
for (var k = 0; k < attendees.length; k++) {
var attendeeComponent = jQuery("<option></option>").appendTo(eventComponent.find("select"));
var attendeeEmail = "" + attendees[k].getFirstValue();
attendeeEmail = (attendeeEmail.toLowerCase().indexOf("mailto:") === 0) ? attendeeEmail.substring("mailto:".length) : attendeeEmail;
attendeeList.push([attendees[k], attendeeEmail]);
emailList.push(attendeeEmail);
attendeeComponent.attr("value", k);
attendeeComponent.text(attendeeEmail);
}
var defaultAttendeeList = false;
if (!attendeeList.length && config.options.txtEventPluginDefaultAttendees) {
var defaultAttendees = config.options.txtEventPluginDefaultAttendees.split(",");
defaultAttendeeList = true;
for (var k = 0; k < defaultAttendees.length; k++) {
var attendeeEmail = defaultAttendees[k].trim();
if (attendeeEmail) {
var attendeeComponent = jQuery("<option></option>").appendTo(eventComponent.find("select"));
attendeeList.push([new ICAL.Property(["attendee", {"rsvp": "TRUE", "partstat": "NEEDS-ACTION", "role": "REQ-PARTICIPANT"}, "cal-address", "mailto:" + attendeeEmail]), attendeeEmail]);
emailList.push(attendeeEmail);
attendeeComponent.attr("value", k);
attendeeComponent.text(attendeeEmail);
}
}
}
if (attendeeList.length) {
var preferredAttendees;
try {
preferredAttendees = JSON.parse(config.options.txtEventPluginPreferredAttendees);
} catch (e) {}
preferredAttendees = Array.isArray(preferredAttendees) ? preferredAttendees : [];
var preferredAttendee = 0;
for (var k = 0; k < preferredAttendees.length; k++) {
if (emailList.indexOf(preferredAttendees[k]) > -1) {
preferredAttendee = emailList.indexOf(preferredAttendees[k]);
break;
}
}
eventComponent.find("select").val(preferredAttendee);
}
var organizer = vevent.getFirstProperty("organizer");
if (!attendeeList.length || !organizer || !organizer.getFirstValue()) {
var button = eventComponent.find("button");
button.prop("disabled", true);
button.css("cursor", "not-allowed");
button.css("opacity", "0.5");
if (!attendeeList.length && (!organizer || !organizer.getFirstValue())) {
button.attr("title", "organizer and attendee missing");
} else if (!attendeeList.length) {
button.attr("title", "attendee missing");
} else {
button.attr("title", "organizer missing");
}
} else {
if (defaultAttendeeList) {
eventComponent.find("button").attr("title", "default attendee list");
}
var vcalendar = new ICAL.Component(ICAL.parse(calendars[i]));
vcalendar.removeAllSubcomponents("vevent");
vcalendar.addSubcomponent(vevent);
vcalendar.updatePropertyWithValue("prodid", "-//Galasoft Inc.//TW Notes v1.0//EN");
vcalendar.updatePropertyWithValue("method", "REPLY");
eventComponent.find("button").on("click", function () {
var organizerEmail = "" + organizer.getFirstValue();
organizerEmail = (organizerEmail.toLowerCase().indexOf("mailto:") === 0) ? organizerEmail.substring("mailto:".length) : organizerEmail;
var response = eventComponent.find("input[name='" + responseId + "']:checked").val();
if (confirm((defaultAttendeeList ? "WARNING: Default attendee list displayed, since none were associated to the event\n\n" : "") + "Send " + response + " to " + organizerEmail + " for: " + title)) {
var attendee = attendeeList[parseInt(eventComponent.find("select").val())];
var attendeeEmail = attendee[1];
var preferredAttendees;
try {
preferredAttendees = JSON.parse(config.options.txtEventPluginPreferredAttendees);
} catch (e) {}
preferredAttendees = Array.isArray(preferredAttendees) ? preferredAttendees : [];
if (preferredAttendees.indexOf(attendeeEmail) > -1) {
preferredAttendees.splice(preferredAttendees.indexOf(attendeeEmail), 1);
}
preferredAttendees.unshift(attendeeEmail);
config.options.txtEventPluginPreferredAttendees = JSON.stringify(preferredAttendees);
saveOption("txtEventPluginPreferredAttendees");
vevent.removeAllProperties("attendee");
vevent.addProperty(attendee[0]);
attendee[0].removeParameter("rsvp");
attendee[0].setParameter("partstat", response);
vevent.updatePropertyWithValue("dtstamp", config.macros.eventPlugin.formatDate(new Date(), true));
var settings;
try {
settings = JSON.parse(store.getTiddlerText("EmailServerSettings").replace(/\{\{\{/mg, "").replace(/\}\}\}/mg, ""));
} catch (e) {
displayMessage("Unable to load email server settings");
}
if (settings) {
var email = {
from: attendeeEmail,
to: organizerEmail,
subject: response.substring(0, 1) + response.substring(1).toLowerCase() + ": " + (summary ? summary : "meeting"),
alternatives: [{
contentType: "text/calendar; method=REPLY",
content: ICAL.stringify(vcalendar.jCal)
}]
};
if (config.options.txtEventPluginEventReplyBCC) {
email.bcc = config.options.txtEventPluginEventReplyBCC;
}
jQuery.ajax({
url: config.options.txtEventPluginEmailSenderServerURL,
method: "POST",
data: JSON.stringify({
email: email,
settings: settings
}),
contentType: "application/json"
}).done(function (data, textStatus, jqXHR) {
if (data.error) {
displayMessage("Failed to send reply: " + data.error);
} else if (data.response && (data.response.indexOf("2") !== 0)) {
displayMessage("Failed to send reply: " + data.response);
} else {
displayMessage("Reply sent");
}
}).fail(function (jqXHR, textStatus, errorThrown) {
displayMessage("Failed to send reply" + ((textStatus || errorThrown) ? ": " : "") + (textStatus ? textStatus : "") + ((textStatus && errorThrown) ? " : " : "") + (errorThrown ? errorThrown : ""));
});
}
}
});
}
})();
}
}
}
var wikifierSource = w.source;
var wikifierNextMatch = w.nextMatch;
w.source = lookaheadMatch[1];
w.nextMatch = 0;
w.subWikifyUnterm(createTiddlyElement(w.output, "span"));
w.source = wikifierSource;
w.nextMatch = wikifierNextMatch;
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
}
}
//}}}
//{{{
config.formatters[config.formatters.length] = config.macros.eventPlugin.formatter;
//}}}
//{{{
config.shadowTiddlers["EmailServerSettings"] = "{{{\n" + JSON.stringify(config.macros.eventPlugin.emailServerSettings, null, "\t") + "\n}}}";
//}}}
<<list filter "[tag[events AND NOT Trash]]">>
/***
|Name|FileDropPlugin|
|Source|http://www.TiddlyTools.com/#FileDropPlugin|
|Version|2.1.4|
|Author|BradleyMeck and Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|drag-and-drop files/directories to create tiddlers|
''requires FireFox or another Mozilla-compatible browser.''
!!!!!Usage
<<<
This plugin automatically creates tiddlers from files that are dropped onto an open TiddlyWiki document. You can drop multiple selected files and/or folders to create many tiddlers at once. New tiddler titles are created using the filename of each dropped file (i.e., omitting the path). If a title is already in use, you are prompted to enter a new title for that file. If you drop a folder, you will be asked if you want to create a simple 'directory list' of files in a single tiddler or create one tiddler for each file in that folder.
By default, it is assumed that all dropped files contain text. However, if [[AttachFilePlugin]], [[AttachFilePluginFormatters]] and [[AttachFileMIMETypes]] are installed, then you can drop ''//binary data files//'' as well as text files. If the MIME type of a dropped file is not "text/plain", then AttachFilePlugin is used to create an 'attachment' tiddler, rather than creating a simple text tiddler.
When creating text tiddlers, you can embed a //link// to the original external file at the top of the new tiddler, in addition to (or instead of) the text content itself. The format for this link (see Configuration, below) uses embedded ''//replacement markers//'' that allow you to generate a variety of wiki-formatted output, where:
*%0 = filename (without path)
*%1 = local """file://...""" URL
*%2 = local path and filename (OS-native format)
*%3 = relative path (if subdirectory of current document directory)
*%4 = file size
*%5 = file date
*%6 = current date
*%7 = current ~TiddlyWiki username
*\n = newline
By default, the link format uses the filename (%0) and local URL (%1), enclosed within a //hidden section// syntax, like this:
{{{
/%
!link
[[%0|%1]]
!end
%/
}}}
This permits the link to be embedded along with the text content, without changing the appearance of that content when the tiddler is viewed. To display the link in your tiddler content, use:
{{{
<<tiddler TiddlerName##link>>
}}}
<<<
!!!!!Configuration
<<<
__FileDropPlugin options:__
<<option chkFileDropContent>>Copy file content into tiddlers if smaller than: <<option txtFileDropDataLimit>> bytes
//(note: excess text content will be truncated, oversized binary files will skipped, 0=no limit)//
<<option chkFileDropLink>>Generate external links to files, using this format:{{editor{<html><nowiki><textarea rows="4" onchange="
config.macros.option.propagateOption('txtFileDropLinkFormat','value',this.value.escapeLineBreaks(),'input');
"></textarea></html><<tiddler {{
var ta=place.lastChild.getElementsByTagName('textarea')[0];
var v=config.options.txtFileDropLinkFormat.unescapeLineBreaks();
ta.value=v;
"";}}>>}}}<<option chkFileDropTrimFilename>>Omit file extensions from tiddler titles
<<option chkFileDropDisplay>>Automatically display newly created tiddlers
Tag newly created tiddlers with: <<option txtFileDropTags>>
__FileDropPlugin+AttachFilePlugin options:__ //(binary file data as encoded 'base64' text)//
<<option chkFileDropAttachLocalLink>> attachment includes reference to local path/filename
>Note: if the plugin does not seem to work, enter ''about:config'' in the Firefox address bar, and make sure that {{{signed.applets.codebase_principal_support}}} is set to ''true''
<<<
!!!!!Examples (custom handler functions)
<<<
Adds a single file with confirmation and prompting for title:
{{{
config.macros.fileDrop.addEventListener('application/x-moz-file',
function(nsiFile) {
var msg='You have dropped the file:\n'
+nsiFile.path+'\n'
+'onto the page, it will be imported as a tiddler. Is that ok?'
if(confirm(msg)) {
var newDate = new Date();
var title = prompt('what would you like to name the tiddler?');
store.saveTiddler(title,title,loadFile(nsiFile.path),config.options.txtUserName,newDate,[]);
}
return true;
});
}}}
Adds a single file without confirmation, using path/filename as tiddler title:
{{{
config.macros.fileDrop.addEventListener('application/x-moz-file',
function(nsiFile) {
var newDate = new Date();
store.saveTiddler(nsiFile.path,nsiFile.path,loadFile(nsiFile.path),config.options.txtUserName,newDate,[]);
story.displayTiddler(null,nsiFile.path)
return true;
});
}}}
<<<
!!!!!Revisions
<<<
2010.03.06 2.1.4 added event listener for 'dragover' (for FireFox 3.6+)
2009.10.10 2.1.3 fixed IE code error
2009.10.08 2.1.2 fixed chkFileDropContent bypass handling for binary attachments
2009.10.07 2.1.0 added chkFileDropContent and chkFileDropLink/txtFileDropLinkFormat
2009.08.19 2.0.0 fixed event listener registration for FireFox 3.5+. Also, merged with FileDropPluginConfig, with code cleanup/reduction
2008.08.11 1.5.1 added chkFileDropAttachLocalLink option to allow suppression of local path/file link
2007.xx.xx *.*.* add suspend/resume of notifications to improve performance when multiple files are handled
2007.01.01 0.9.9 extensions for AttachFilePlugin
2006.11.04 0.1.1 initial release by Bradley Meck
<<<
!!!!!Code
***/
//{{{
version.extensions.FileDropPlugin={major:2, minor:1, revision:4, date: new Date(2010,3,6)};
config.macros.fileDrop = {
customDropHandlers: [],
addEventListener: function(paramflavor,func,inFront) {
var obj={}; obj.flavor=paramflavor; obj.handler=func;
if (!inFront) this.customDropHandlers.push(obj);
else this.customDropHandlers.shift(obj);
},
dragDropHandler: function(evt) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var dragService = Components.classes['@mozilla.org/widget/dragservice;1'].getService(Components.interfaces.nsIDragService);
var dragSession = dragService.getCurrentSession();
var transferObject = Components.classes['@mozilla.org/widget/transferable;1'].createInstance();
transferObject = transferObject.QueryInterface(Components.interfaces.nsITransferable);
transferObject.addDataFlavor('application/x-moz-file');
var numItems = dragSession.numDropItems;
if (numItems>1) {
clearMessage();
displayMessage('Reading '+numItems+' files...');
store.suspendNotifications();
}
for (var i = 0; i < numItems; i++) {
dragSession.getData(transferObject, i);
var dataObj = {};
var dropSizeObj = {};
for(var ind=0; ind<config.macros.fileDrop.customDropHandlers.length; ind++) {
var item = config.macros.fileDrop.customDropHandlers[ind];
if(dragSession.isDataFlavorSupported(item.flavor)) {
transferObject.getTransferData(item.flavor, dataObj, dropSizeObj);
var droppedFile = dataObj.value.QueryInterface(Components.interfaces.nsIFile);
var result = item.handler.call(item,droppedFile);
evt.stopPropagation();
evt.preventDefault();
if (result) break;
}
}
}
if (numItems>1) {
store.resumeNotifications();
store.notifyAll();
displayMessage(numItems+' files have been processed');
}
}
}
//}}}
/***
!!!!!window event handlers
***/
//{{{
if(!window.event) {
window.addEventListener('dragdrop', // FireFox3.1-
config.macros.fileDrop.dragDropHandler, true);
window.addEventListener('drop', // FireFox3.5+
config.macros.fileDrop.dragDropHandler, true);
window.addEventListener('dragover', // FireFox3.6+
function(e){e.stopPropagation();e.preventDefault();}, true);
}
//}}}
/***
!!!!!handler for files, directories and binary attachments (see [[AttachFilePlugin]])
***/
//{{{
var defaults={
chkFileDropDisplay: true,
chkFileDropTrimFilename: false,
chkFileDropContent: true,
chkFileDropLink: true,
txtFileDropLinkFormat: '/%\\n!link\\n[[%0|%1]]\\n!end\\n%/',
txtFileDropDataLimit: '32768',
chkFileDropAttachLocalLink: true,
txtFileDropTags: ''
};
for (var id in defaults) if (config.options[id]===undefined)
config.options[id]=defaults[id];
config.macros.fileDrop.addEventListener('application/x-moz-file',function(nsiFile) {
var co=config.options; // abbrev
var header='Index of %0\n^^(as of %1)^^\n|!filename| !size | !modified |\n';
var item='|[[%0|%1]]| %2|%3|\n';
var footer='Total of %0 bytes in %1 files\n';
var now=new Date();
var files=[nsiFile];
if (nsiFile.isDirectory()) {
var folder=nsiFile.directoryEntries;
var files=[];
while (folder.hasMoreElements()) {
var f=folder.getNext().QueryInterface(Components.interfaces.nsILocalFile);
if (f instanceof Components.interfaces.nsILocalFile && !f.isDirectory()) files.push(f);
}
var msg=nsiFile.path.replace(/\\/g,'/')+'\n\n';
msg+='contains '+files.length+' files... ';
msg+='select OK to attach all files or CANCEL to create a list...';
if (!confirm(msg)) { // create a list in a tiddler
var title=nsiFile.leafName; // tiddler name is last directory name in path
while (title && title.length && store.tiddlerExists(title)) {
if (confirm(config.messages.overwriteWarning.format([title]))) break;
title=prompt('Enter a new tiddler title',nsiFile.path.replace(/\\/g,'/'));
}
if (!title || !title.length) return true; // cancelled
var text=header.format([nsiFile.path.replace(/\\/g,'/'),now.toLocaleString()]);
var total=0;
for (var i=0; i<files.length; i++) { var f=files[i];
var name=f.leafName;
if (co.chkFileDropTrimFilename)
{ var p=name.split('.'); if (p.length>1) p.pop(); name=p.join('.'); }
var path='file:///'+f.path.replace(/\\/g,'/');
var size=f.fileSize; total+=size;
var when=new Date(f.lastModifiedTime).formatString('YYYY.0MM.0DD 0hh:0mm:0ss');
text+=item.format([name,path,size,when]);
}
text+=footer.format([total,files.length]);
var newtags=co.txtFileDropTags?co.txtFileDropTags.readBracketedList():[];
store.saveTiddler(null,title,text,co.txtUserName,now,newtags);
if (co.chkFileDropDisplay) story.displayTiddler(null,title);
return true;
}
}
if (files.length>1) store.suspendNotifications();
for (i=0; i<files.length; i++) {
var file=files[i];
if (file.isDirectory()) continue; // skip over nested directories
var type='text/plain';
var title=file.leafName; // tiddler name is file name
if (co.chkFileDropTrimFilename)
{ var p=title.split('.'); if (p.length>1) p.pop(); title=p.join('.'); }
var name=file.leafName;
var path=file.path;
var url='file:///'+path.replace(/\\/g,'/');
var size=file.fileSize;
var when=new Date(file.lastModifiedTime);
var now=new Date();
var who=config.options.txtUserName;
var h=document.location.href;
var cwd=getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf('/')+1)));
var relpath=path.startsWith(cwd)?'./'+path.substr(cwd.length):path;
while (title && title.length && store.tiddlerExists(title)) {
if (confirm(config.messages.overwriteWarning.format([title]))) break;
title=prompt('Enter a new tiddler title',path.replace(/\\/g,'/'));
}
if (!title || !title.length) continue; // cancelled
if (config.macros.attach) {
type=config.macros.attach.getMIMEType(name,'');
if (!type.length)
type=prompt('Unknown file type. Enter a MIME type','text/plain');
if (!type||!type.length) continue; // cancelled
}
var newtags=co.txtFileDropTags?co.txtFileDropTags.readBracketedList():[];
if (type=='text/plain' || !co.chkFileDropContent) {
var txt=''; var fmt=co.txtFileDropLinkFormat.unescapeLineBreaks();
if (co.chkFileDropLink) txt+=fmt.format([name,url,path,relpath,size,when,now,who]);
if (co.chkFileDropContent) {
var out=loadFile(path); var lim=co.txtFileDropDataLimit;
txt+=co.txtFileDropDataLimit?out.substr(0,lim):out;
if (size>lim) txt+='\n----\nfilesize ('+size+')'
+' is larger than FileDrop limit ('+lim+')...\n'
+'additional content has been omitted';
}
store.saveTiddler(null,title,txt,co.txtUserName,now,newtags);
} else {
var embed=co.chkFileDropContent
&& (!co.txtFileDropDataLimit||size<co.txtFileDropDataLimit);
newtags.push('attachment'); newtags.push('excludeMissing');
config.macros.attach.createAttachmentTiddler(path,
now.formatString(config.macros.timeline.dateFormat),
'attached by FileDropPlugin', newtags, title,
embed, co.chkFileDropAttachLocalLink, false,
relpath, '', type,!co.chkFileDropDisplay);
}
if (co.chkFileDropDisplay) story.displayTiddler(null,title);
}
if (files.length>1) { store.resumeNotifications(); store.notifyAll(); }
return true;
})
//}}}
/***
|Name|FileDropPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[FileDropPlugin]]|
!!!!!Code
***/
//{{{
config.macros.fileDrop.fileDropSupported = function () {
return ((typeof FileReader !== "undefined") && (new FileReader).readAsBinaryString) || ((config.userAgent.indexOf("firefox") > -1) && (config.userAgent.indexOf("mobile") == -1));
}
config.macros.fileDrop.folderDropSupported = function () {
var isFirefox17 = ((typeof netscape == "undefined") || !netscape.security || !netscape.security.PrivilegeManager || !netscape.security.PrivilegeManager.enablePrivilege);
return config.macros.fileDrop.fileDropSupported() && isFirefox17;
}
//}}}
//{{{
config.macros.fileDrop.addEventListener = function(paramflavor,func,inFront) {
var obj={}; obj.flavor=paramflavor; obj.handler=func;
if (!inFront) this.customDropHandlers.push(obj);
else this.customDropHandlers.unshift(obj);
};
//}}}
//{{{
config.macros.fileDrop.dragDropHandler_original = config.macros.fileDrop.dragDropHandler;
config.macros.fileDrop.dragDropHandler = function(evt) {
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
config.macros.fileDrop.dragDropHandler_original(evt);
} catch (e) {
config.macros.fileDrop.dragDropHandler_safe(evt);
}
}
config.macros.fileDrop.dragDropHandler_safe = function (event) {
event.preventDefault();
event.stopPropagation();
var items = (event.originalEvent && event.originalEvent.dataTransfer) ? event.originalEvent.dataTransfer.items : event.dataTransfer.items;
var entries = [];
if (items && items.length) {
for (var i = 0; i < items.length; i++) {
var entry = items[i].webkitGetAsEntry();
if (entry) {
entries.push(entry);
}
}
}
var files = [];
var dragSession = {items: items, entries: entries, files: files, listeners: [], numDropItems: files.length, getData: function (transferObject, i) {}, isDataFlavorSupported: function (flavor) { return true; }};
var numItems = entries.length;
if (numItems>1) {
clearMessage();
displayMessage('Reading '+numItems+' files...');
store.suspendNotifications();
}
var readFile = function (entry, callback) {
var file = {};
file.leafName = entry.name;
file.isDirectory = function () { return false; };
file.path = entry.fullPath ? entry.fullPath.replace(/\\/g, "/").replace(/^\//, "") : "";
file.toString = function () {
return this.path;
};
file.fileSize = 0;
file.dragSession = dragSession;
entry.file(function (fileEntry) {
var reader = new FileReader();
reader.onload = function (e) {
file.fileData = reader.result;
file.fileSize = file.fileData ? file.fileData.length : 0;
callback(file);
};
reader.onerror = function (e) {
displayMessage("Error reading file: " + entry.name);
callback();
};
reader.onabort = function (e) {
displayMessage("Aborted file reading: " + entry.name);
callback();
};
reader.readAsBinaryString(fileEntry);
}, function (error) {
displayMessage("Error reading file: " + entry.name);
callback();
});
};
var createDirectory = function (entry) {
var directory = {};
directory.leafName = entry.name;
directory.isDirectory = function () { return true; };
directory.path = entry.fullPath ? entry.fullPath.replace(/\\/g, "/").replace(/^\//, "") : "";
directory.toString = function () {
return this.path;
};
directory.directoryEntries = {
index: 0,
entries: [],
hasMoreElements: function () {
return this.index < this.entries.length;
},
getNext: function () {
if (this.hasMoreElements()) {
return this.entries[this.index++];
}
return null;
}
};
directory.dragSession = dragSession;
return directory;
};
var handleDrop = function () {
for (var ind = 0; ind < config.macros.fileDrop.customDropHandlers.length; ind++) {
var item = config.macros.fileDrop.customDropHandlers[ind];
var result = null;
try {
result = item.handler.call(item, files[files.length - 1]);
} catch (e) {
console.log(item.handler.toString(), e);
}
if (result) {
break;
}
}
};
(function next(loop) {
var i = loop.i;
if (i >= loop.max) {
loop.end();
} else if (entries[i].isFile) {
readFile(entries[i], function (file) {
if (file) {
files.push(file);
dragSession.numDropItems = files.length;
handleDrop();
}
loop.i++;
next(loop);
});
} else if (entries[i].isDirectory) {
var directory = createDirectory(entries[i]);
files.push(directory);
dragSession.numDropItems = files.length;
entry.createReader().readEntries(function (dirEntries) {
(function nextDirEntry(loop) {
var i = loop.i;
if (i >= loop.max) {
loop.end();
} else if (dirEntries[i].isFile) {
readFile(dirEntries[i], function (file) {
if (file) {
directory.directoryEntries.entries.push(file);
}
loop.i++;
nextDirEntry(loop);
});
} else if (dirEntries[i].isDirectory) {
directory.directoryEntries.entries.push(createDirectory(dirEntries[i]));
loop.i++;
nextDirEntry(loop);
} else {
loop.i++;
nextDirEntry(loop);
}
})({i: 0, max: dirEntries.length, end: function () {
handleDrop();
loop.i++;
next(loop);
}});
}, function (error) {
displayMessage("Error reading directory: " + entry.name);
loop.i++;
next(loop);
});
} else {
loop.i++;
next(loop);
}
})({i: 0, max: numItems, end: function () {
for (var i = 0; i < dragSession.listeners.length; i++) {
if (jQuery.isFunction(dragSession.listeners[i].ondragsessionend)) {
try {
dragSession.listeners[i].ondragsessionend(dragSession);
} catch (e) {
console.log(dragSession.listeners[i].ondragsessionend.toString(), e);
}
}
}
store.resumeNotifications();
store.notifyAll();
displayMessage(numItems + ' files have been processed');
}});
}
//}}}
//{{{
if (typeof FileReader !== "undefined") {
jQuery(document).on({
"dragover dragenter": function (e) {
e.preventDefault();
e.stopPropagation();
},
"drop": config.macros.fileDrop.dragDropHandler
});
} else if (!window.event) {
window.removeEventListener('dragdrop', // FireFox3.1-
config.macros.fileDrop.dragDropHandler_original, true);
window.addEventListener('dragdrop', // FireFox3.1-
config.macros.fileDrop.dragDropHandler, true);
window.removeEventListener('drop', // FireFox3.5+
config.macros.fileDrop.dragDropHandler_original, true);
window.addEventListener('drop', // FireFox3.5+
config.macros.fileDrop.dragDropHandler, true);
}
//}}}
//{{{
(function () {
if (config.macros.fileDrop.customDropHandlers && config.macros.fileDrop.customDropHandlers[0] && config.macros.fileDrop.customDropHandlers[0].handler) {
var code = config.macros.fileDrop.customDropHandlers[0].handler.toString();
code = code.replace(".QueryInterface(Components.interfaces.nsILocalFile)", "");
code = code.replace("f instanceof Components.interfaces.nsILocalFile &&", "");
code = code.replace("var out=loadFile(path);", "var out = file.fileData ? file.fileData : loadFile(path);");
code = code.replace("config.macros.attach.createAttachmentTiddler(path,", "config.macros.attach.createAttachmentTiddler(file.fileData ? file : path,");
code = code.replace("store.saveTiddler(null,title,txt", "store.getTiddlerText(title); var tiddler = store.fetchTiddler(title); if (tiddler) { tiddler.set(undefined, txt, co.txtUserName, now); }; store.saveTiddler(tiddler ? tiddler : null,title,txt");
eval("config.macros.fileDrop.customDropHandlers[0].handler = function handler" + code.substring(code.indexOf("(")));
}
})();
//}}}
//{{{
// https://github.com/locutusjs/locutus/blob/master/src/php/xml/utf8_encode.js (commit 0dbbcfc434f74389a7bf075b205f71cb4c2965fb 2020-11-19)
config.macros.fileDrop.utf8_encode = function utf8_encode (argString) { // eslint-disable-line camelcase
// discuss at: https://locutus.io/php/utf8_encode/
// original by: Webtoolkit.info (https://www.webtoolkit.info/)
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: sowberry
// improved by: Jack
// improved by: Yves Sucaet
// improved by: kirilloid
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Ulrich
// bugfixed by: Rafał Kukawski (https://blog.kukawski.pl)
// bugfixed by: kirilloid
// example 1: utf8_encode('Kevin van Zonneveld')
// returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return ''
}
// .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
const string = (argString + '')
let utftext = ''
let start
let end
let stringl = 0
start = end = 0
stringl = string.length
for (let n = 0; n < stringl; n++) {
let c1 = string.charCodeAt(n)
let enc = null
if (c1 < 128) {
end++
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
)
} else if ((c1 & 0xF800) !== 0xD800) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
)
} else {
// surrogate pairs
if ((c1 & 0xFC00) !== 0xD800) {
throw new RangeError('Unmatched trail surrogate at ' + n)
}
const c2 = string.charCodeAt(++n)
if ((c2 & 0xFC00) !== 0xDC00) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1))
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
)
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end)
}
utftext += enc
start = end = n + 1
}
}
if (end > start) {
utftext += string.slice(start, stringl)
}
return utftext
};
// http://stackoverflow.com/questions/887148/how-to-determine-if-a-string-contains-invalid-encoded-characters
config.macros.fileDrop.validUTF8 = function(input) {
var Ox80 = parseInt("80", 16);
var OxBB = parseInt("BB", 16);
var OxBF = parseInt("BF", 16);
var OxC0 = parseInt("C0", 16);
var OxE0 = parseInt("E0", 16);
var OxEF = parseInt("EF", 16);
var OxF0 = parseInt("F0", 16);
var OxF8 = parseInt("F8", 16);
var OxFF = parseInt("FF", 16);
var i = 0;
// Check for BOM
if (input.length >= 3 && (input.charCodeAt(0) & OxFF) == OxEF
&& (input.charCodeAt(1) & OxFF) == OxBB & (input.charCodeAt(2) & OxFF) == OxBF) {
i = 3;
}
var end;
for (var j = input.length; i < j; ++i) {
var octet = input.charCodeAt(i);
if ((octet & Ox80) == 0) {
continue; // ASCII
}
// Check for UTF-8 leading byte
if ((octet & OxE0) == OxC0) {
end = i + 1;
} else if ((octet & OxF0) == OxE0) {
end = i + 2;
} else if ((octet & OxF8) == OxF0) {
end = i + 3;
} else {
// Java only supports BMP so 3 is max
return false;
}
while (i < end) {
i++;
octet = input.charCodeAt(i);
if ((octet & OxC0) != Ox80) {
// Not a valid trailing byte
return false;
}
}
}
return true;
};
//}}}
//{{{
// https://github.com/locutusjs/locutus/blob/master/src/php/xml/utf8_decode.js (commit 0dbbcfc434f74389a7bf075b205f71cb4c2965fb 2020-11-19)
config.macros.fileDrop.utf8_decode = function utf8_decode (strData) { // eslint-disable-line camelcase
// discuss at: https://locutus.io/php/utf8_decode/
// original by: Webtoolkit.info (https://www.webtoolkit.info/)
// input by: Aman Gupta
// input by: Brett Zamir (https://brett-zamir.me)
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: Norman "zEh" Fuchs
// bugfixed by: hitwork
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// bugfixed by: Kevin van Zonneveld (https://kvz.io)
// bugfixed by: kirilloid
// bugfixed by: w35l3y (https://www.wesley.eti.br)
// example 1: utf8_decode('Kevin van Zonneveld')
// returns 1: 'Kevin van Zonneveld'
const tmpArr = []
let i = 0
let c1 = 0
let seqlen = 0
strData += ''
while (i < strData.length) {
c1 = strData.charCodeAt(i) & 0xFF
seqlen = 0
// https://en.wikipedia.org/wiki/UTF-8#Codepage_layout
if (c1 <= 0xBF) {
c1 = (c1 & 0x7F)
seqlen = 1
} else if (c1 <= 0xDF) {
c1 = (c1 & 0x1F)
seqlen = 2
} else if (c1 <= 0xEF) {
c1 = (c1 & 0x0F)
seqlen = 3
} else {
c1 = (c1 & 0x07)
seqlen = 4
}
for (let ai = 1; ai < seqlen; ++ai) {
c1 = ((c1 << 0x06) | (strData.charCodeAt(ai + i) & 0x3F))
}
if (seqlen === 4) {
c1 -= 0x10000
tmpArr.push(String.fromCharCode(0xD800 | ((c1 >> 10) & 0x3FF)))
tmpArr.push(String.fromCharCode(0xDC00 | (c1 & 0x3FF)))
} else {
tmpArr.push(String.fromCharCode(c1))
}
i += seqlen
}
return tmpArr.join('')
};
manualConvertUTF8ToUnicode = config.macros.fileDrop.utf8_decode;
//}}}
/***
|Name|FileDropPluginOverrideICS|
|License|[[TW Notes License]]|
|Requires|[[FileDropPluginOverride]]|
!!!!!Code
***/
//{{{
if (config.macros.fileDrop) {
config.macros.fileDrop.icalendartag = "agenda";
config.macros.fileDrop.addEventListener(
'application/x-moz-file',
function (nsiFile) {
var emlText = null;
var text = null;
var error = null;
var isICS = function() {
return nsiFile && !nsiFile.isDirectory() && nsiFile.leafName && (nsiFile.leafName.toLowerCase().lastIndexOf(".ics") == nsiFile.leafName.length - ".ics".length);
};
var isEML = function() {
return nsiFile && !nsiFile.isDirectory() && nsiFile.leafName && (nsiFile.leafName.toLowerCase().lastIndexOf(".eml") == nsiFile.leafName.length - ".eml".length);
};
var loadFileText = function () {
try {
if (nsiFile.fileData) {
text = nsiFile.fileData;
}
if ((text == null) || (text == false) || (text == "")) {
text = mozillaLoadFile(path);
}
if ((text == null) || (text == false) || (text == "")) {
text = ieLoadFile(path);
}
if ((text == null) || (text == false) || (text == "")) {
var code = eval("javaLoadFile").toString().replace(/UTF-8/gi, 'ISO-8859-1');
eval("var localJavaLoadFile = " + code);
text = localJavaLoadFile(path);
}
text = text ? text : "";
} catch (err) {
error = err;
alert("There was a problem while retrieving the file.");
}
};
if (isEML() && config.macros.email) {
if (text === null) {
loadFileText();
if (error) {
return true;
}
}
emlText = text;
var email = config.macros.email.processMessage(text, "calendar");
text = null;
for (var i = 0; i < email.main.length; i++) {
if (email.main[i].headers["content-type"] && (email.main[i].headers["content-type"][0].indexOf("calendar") > -1)) {
text = email.text[i];
}
}
if (text == null) {
return false;
}
}
var title = nsiFile.leafName;
if (isEML() || isICS()) {
var path = nsiFile.path;
while (title && title.length && store.tiddlerExists(title)) {
if (confirm(config.messages.overwriteWarning.format([title]))) {
break;
}
var currentTime = new Date();
var timestamp = "" + currentTime.getFullYear() + (currentTime.getMonth() + 1 < 10 ? "0" : "") + (currentTime.getMonth() + 1) + (currentTime.getDate() < 10 ? "0" : "") + currentTime.getDate();
timestamp = timestamp + (currentTime.getHours() < 10 ? "0" : "") + currentTime.getHours() + (currentTime.getMinutes() < 10 ? "0" : "") + currentTime.getMinutes() + (currentTime.getSeconds() < 10 ? "0" : "") + currentTime.getSeconds();
timestamp = timestamp + (currentTime.getMilliseconds() < 10 ? "0" : "") + (currentTime.getMilliseconds() < 100 ? "0" : "") + currentTime.getMilliseconds()
title = title.lastIndexOf(".") > -1 ? title.substring(0, title.lastIndexOf(".")) + timestamp + title.substring(title.lastIndexOf(".")) : title + timestamp;
title = prompt('Enter a new tiddler title', title);
}
if (!title || !title.length) {
return true;
}
var newDate = new Date();
if (text === null) {
loadFileText();
if (error) {
return true;
}
}
var limit75 = function (text) {
var out = '';
while (text.length > 75) {
out += text.substr(0, 75) + '\r\n';
text = ' ' + text.substr(75);
}
out += text;
return out;
};
var FOLDED = /^\s(.*)$/;
var lines = text.replace(/\r\n/g, '\n').split('\n');
for (var i = lines.length - 1; i > 0; i--) {
var matches = FOLDED.exec(lines[i]);
if (matches) {
lines[i - 1] += matches[1];
lines[i] = '';
}
}
text = '';
for (var i = 0; i < lines.length; i++) {
if (lines[i] != '') {
lines[i] = limit75(config.macros.fileDrop.validUTF8(lines[i]) ? config.macros.fileDrop.utf8_decode(lines[i]) : lines[i]);
text = text + lines[i] + "\r\n";
}
}
if (text.length) {
text = "<vcalendar>\n{{{\n" + text + "\n}}}\n</vcalendar>";
}
if (emlText !== null) {
if (confirm("Include the original email?")) {
text = "<" + "<email>" + ">\n\n/" + "%\n!email\n" + emlText.replace(/\r\n/g, '\n') + "\n%" + "/\n\n" + text;
}
}
store.getTiddlerText(title);
var tiddler = store.fetchTiddler(title);
var tiddlerTags = config.macros.fileDrop.icalendartag ? ["" + config.macros.fileDrop.icalendartag] : [];
if (tiddler) {
tiddlerTags = tiddler.getTags();
tiddler.set(undefined, text, config.options.txtUserName, newDate);
}
store.saveTiddler(tiddler ? tiddler : title, title, text, config.options.txtUserName, newDate, tiddlerTags);
story.displayTiddler(null, title);
story.refreshTiddler(title, null, true);
return true;
}
return false;
},
true);
}
//}}}
/***
|Name|FileDropPluginOverrideTiddler|
|License|[[TW Notes License]]|
|Requires|[[FileDropPluginOverride]] [[LoadTiddlersPluginOverride]]|
!!!!!Code
***/
//{{{
if (config.options.txtFileDropExcludeFunction === undefined) {
config.options.txtFileDropExcludeFunction = "";
}
if (config.options.txtFileDropExcludeTags === undefined) {
config.options.txtFileDropExcludeTags = "doNotImport";
}
merge(config.optionsDesc, {
txtFileDropExcludeFunction: "The function used to determine the tiddlers that are not to be imported during a file drop operation; the tiddler object, which migh be null, is passed as a parameter; the function must return true if the tiddler is to be excluded and false if it is to be imported; if specified, the txtFileDropExcludeTags option is ignored",
txtFileDropExcludeTags: "The bracketed list of tags of tiddlers that are not to be imported during a file drop operation; this option is ignored if the txtFileDropExcludeFunction option is specified"
});
//}}}
//{{{
if (config.macros.fileDrop) {
config.macros.fileDrop.fileDropPluginOverrideTiddler = {
customTiddlerHandlers: [],
loadTiddlersParams: {
quiet: true,
ask: true,
filter: 'updates', // 'all' 'new' 'changes' 'updates' 'tiddler:TiddlerName' 'tag:value'
force: false,
init: false,
nodirty: false,
norefresh: false,
noreport: true,
autosave: false,
newtags: [],
tiddlerValidator: {
exclude: function (tiddler) {
if (config.options.txtFileDropExcludeFunction) {
try {
eval("var excludeFunction = " + config.options.txtFileDropExcludeFunction);
return excludeFunction(tiddler);
} catch (e) {}
}
if (tiddler && tiddler.tags) {
var excludeTags = config.options.txtFileDropExcludeTags ? config.options.txtFileDropExcludeTags.readBracketedList() : null;
if (excludeTags) {
for (var i = 0; i < excludeTags.length; i++) {
if (tiddler.tags.indexOf(excludeTags[i]) != -1) {
return true;
}
}
}
}
return false;
}
}
},
loadSmallBackupTiddlersParams: {
quiet: true,
ask: false,
filter: 'all', // 'all' 'new' 'changes' 'updates' 'tiddler:TiddlerName' 'tag:value'
force: true,
init: false,
nodirty: false,
norefresh: false,
noreport: true,
autosave: false,
newtags: [],
tiddlerValidator: {
exclude: function (tiddler) {
return false;
}
}
}
};
}
//}}}
//{{{
if (config.macros.fileDrop) {
config.macros.fileDrop.addEventListener(
'application/x-moz-file',
function(nsiFile) {
var title = nsiFile.leafName;
if (config.macros.loadTiddlers && !nsiFile.isDirectory() && ((title.toLowerCase().lastIndexOf(".html") == title.length - ".html".length) || (title.toLowerCase().lastIndexOf(".htm") == title.length - ".htm".length))) {
var cml = config.macros.loadTiddlers;
var path = nsiFile.path;
var text = "";
try {
if (nsiFile.fileData) {
text = nsiFile.fileData;
}
if ((text == null) || (text == false) || (text == "")) {
text = loadFile(path);
}
} catch (err) {
alert("There was a problem while retrieving the file.");
return true;
}
text = text ? text : "";
if (text.length) {
if (version.major + (version.minor * 0.1) + (version.revision * 0.01) != 2.52) {
try {
var textTemp = "";
while (text.length > 0) {
var index = text.indexOf(" ", 10000);
index = (index > -1) ? index : text.length;
textTemp = textTemp + convertUTF8ToUnicode(text.substring(0, index));
text = text.substring(index);
}
text = textTemp;
} catch (err) {
alert(err);
return false;
}
}
if (text.indexOf("TiddlyWiki") > -1) {
var pluginConfig = config.macros.fileDrop.fileDropPluginOverrideTiddler;
var result = false;
for (var i = 0; (i < pluginConfig.customTiddlerHandlers.length) && !result; i++) {
var customTiddlerHandler = pluginConfig.customTiddlerHandlers[i];
if (customTiddlerHandler && jQuery.isFunction(customTiddlerHandler.load)) {
result = customTiddlerHandler.load(title, path, text);
}
}
if (!result) {
var restoreSmallBackup = function (path, text) {
var addedTiddlers = text.substring(addedTiddlersIndex + "<!-- Added Tiddlers".length, text.indexOf("-->", addedTiddlersIndex));
addedTiddlers = addedTiddlers.split(/[\r\n]+/);
for (var i = 0; i < addedTiddlers.length; i++) {
if (addedTiddlers[i]) {
addedTiddlers[i] = addedTiddlers[i].htmlDecode();
store.removeTiddler(addedTiddlers[i]);
story.closeTiddler(addedTiddlers[i], true);
}
}
var tiddlers = cml.readTiddlersFromHTML(text);
if (tiddlers && tiddlers.length) {
var currentNewTags = cml.newTags;
var currentLockedTag = cml.lockedTag;
var currentReportTitle = cml.reportTitle;
cml.newTags = pluginConfig.loadSmallBackupTiddlersParams["newtags"];
cml.lockedTag = null;
cml.reportTitle = null;
cml.doImport(true, pluginConfig.loadSmallBackupTiddlersParams, text, path, null);
cml.newTags = currentNewTags;
cml.lockedTag = currentLockedTag;
cml.reportTitle = currentReportTitle;
}
};
var listener = null;
var addedTiddlersIndex = text.indexOf("<!-- Added Tiddlers");
if ((addedTiddlersIndex > -1) && nsiFile.dragSession && nsiFile.dragSession.listeners) {
var listeners = nsiFile.dragSession.listeners;
for (var i = 0; i < listeners.length; i++) {
if (listeners[i].type === "small-backup-restore") {
listener = listeners[i];
break
}
}
if (!listener) {
listener = {type: "small-backup-restore", backupPaths: [], backups: {}, confirmation: confirm("Restore small backup(s)?"),
ondragsessionend: function (dragSession) {
if (listener.confirmation) {
listener.backupPaths.sort().reverse();
for (var i = 0; i < listener.backupPaths.length; i++) {
restoreSmallBackup(listener.backupPaths[i], listener.backups[listener.backupPaths[i]]);
}
}
}
};
listeners.push(listener);
}
if (listener.confirmation) {
listener.backupPaths.push(path);
listener.backups[listener.backupPaths[listener.backupPaths.length - 1]] = text;
}
}
if (!listener && (addedTiddlersIndex > -1) && confirm("Restore small backup?")) {
restoreSmallBackup(path, text);
displayMessage("The TiddlyWiki was successfully processed.");
} else if (!listener || !listener.confirmation) {
var currentNewTags = cml.newTags;
cml.newTags = pluginConfig.loadTiddlersParams["newtags"];
cml.doImport(true, pluginConfig.loadTiddlersParams, text, path, null);
cml.newTags = currentNewTags;
displayMessage("The TiddlyWiki was successfully processed.");
}
}
return true;
}
}
}
return false;
},
true);
}
//}}}
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
/***
<<checkForDataTiddlerPlugin>>
|''Name:''|FormTiddlerPlugin|
|''Version:''|1.0.7 (2012-04-19)|
|''Summary:''|Use form-based tiddlers to enter your tiddler data using text fields, listboxes, checkboxes etc. (All standard HTML Form input elements supported).|
|''Documentation:''|[[Introduction|FormTiddler Introduction]], [[Examples|FormTiddler Examples]]|
|''Source:''|http://tiddlywiki.abego-software.de/#FormTiddlerPlugin|
|''Twitter:''|[[@abego|https://twitter.com/#!/abego]]|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''License:''|[[BSD open source license|http://www.abego-software.de/legal/apl-v10.html]]|
|''Requires:''|DataTiddlerPlugin|
!Description
Use form-based tiddlers to enter your tiddler data using text fields, listboxes, checkboxes etc. (All standard HTML Form input elements supported).
''Syntax:''
|>|{{{<<}}}''formTiddler'' //tiddlerName//{{{>>}}}|
|//tiddlerName//|The name of the FormTemplate tiddler to be used to edit the data of the tiddler containing the macro.|
|>|{{{<<}}}''newTiddlerWithForm'' //formTemplateName// //buttonLabel// [//titleExpression// [''askUser'']] {{{>>}}}|
|//formTemplateName//|The name of the tiddler that defines the form the new tiddler should use.|
|//buttonLabel//|The label of the button|
|//titleExpression//|A (quoted) JavaScript String expression that defines the title (/name) of the new tiddler.|
|''askUser''|Typically the user is not asked for the title when a title is specified (and not yet used). When ''askUser'' is given the user will be asked in any case. This may be used when the calculated title is just a suggestion that must be confirmed by the user|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
For details and how to use the macros see the [[introduction|FormTiddler Introduction]] and the [[examples|FormTiddler Examples]].
!Revision history
* v1.0.7 (2012-04-19)
** Bugfix: FormTiddlerPlugin fails to handle tiddlers with certain names in TW 2.6.5. Thanks to Terence Jacyno, Thomas Manley, Sebastjan Hribar, and Eric Shulman for reporting or suggesting a solution.
** remove warnings
* v1.0.6 (2007-06-24)
** Fixed problem when using SELECT component in Internet Explorer (thanks to MaikBoenig for reporting)
* v1.0.5 (2006-02-24)
** Removed "debugger;" instruction
* v1.0.4 (2006-02-07)
** Bug: On IE no data is written to data section when field values changed (thanks to KenGirard for reporting)
* v1.0.3 (2006-02-05)
** Bug: {{{"No form template specified in <<formTiddler>>"}}} when using formTiddler macro on InternetExplorer (thanks to KenGirard for reporting)
* v1.0.2 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.1 (2005-12-22)
** Features:
*** Support InternetExplorer
*** Added newTiddlerWithForm Macro
* v1.0.0 (2005-12-14)
** initial version
!Source Code
***/
//{{{
//============================================================================
//============================================================================
// FormTiddlerPlugin
//============================================================================
//============================================================================
if (!window.abego) window.abego = {};
abego.getOptionsValue = function(element,i) {
var v = element.options[i].value;
if (!v && element.options[i].text)
v = element.options[i].text;
return v;
};
version.extensions.FormTiddlerPlugin = {
major: 1, minor: 0, revision: 7,
date: new Date(2012, 3, 19),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#FormTiddlerPlugin"
};
// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window;
if (!TiddlyWiki.prototype.getTiddler) TiddlyWiki.prototype.getTiddler = function(title) { return t = this.tiddlers[title]; return (t != undefined && t instanceof Tiddler) ? t : null; };
//============================================================================
// formTiddler Macro
//============================================================================
// -------------------------------------------------------------------------------
// Configurations and constants
// -------------------------------------------------------------------------------
config.macros.formTiddler = {
// Standard Properties
label: "formTiddler",
prompt: "Edit tiddler data using forms",
// Define the "setters" that set the values of INPUT elements of a given type
// (must match the corresponding "getter")
setter: {
button: function(e, value) {/*contains no data */ },
checkbox: function(e, value) {e.checked = value;},
file: function(e, value) {try {e.value = value;} catch(e) {/* ignore, possibly security error*/}},
hidden: function(e, value) {e.value = value;},
password: function(e, value) {e.value = value;},
radio: function(e, value) {e.checked = (e.value == value);},
reset: function(e, value) {/*contains no data */ },
"select-one": function(e, value) {config.macros.formTiddler.setSelectOneValue(e,value);},
"select-multiple": function(e, value) {config.macros.formTiddler.setSelectMultipleValue(e,value);},
submit: function(e, value) {/*contains no data */},
text: function(e, value) {e.value = value;},
textarea: function(e, value) {e.value = value;}
},
// Define the "getters" that return the value of INPUT elements of a given type
// Return undefined to not store any data.
getter: {
button: function(e, value) {return undefined;},
checkbox: function(e, value) {return e.checked;},
file: function(e, value) {return e.value;},
hidden: function(e, value) {return e.value;},
password: function(e, value) {return e.value;},
radio: function(e, value) {return e.checked ? e.value : undefined;},
reset: function(e, value) {return undefined;},
"select-one": function(e, value) {return config.macros.formTiddler.getSelectOneValue(e);},
"select-multiple": function(e, value) {return config.macros.formTiddler.getSelectMultipleValue(e);},
submit: function(e, value) {return undefined;},
text: function(e, value) {return e.value;},
textarea: function(e, value) {return e.value;}
}
};
// -------------------------------------------------------------------------------
// The formTiddler Macro Handler
// -------------------------------------------------------------------------------
config.macros.formTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!config.macros.formTiddler.checkForExtensions(place, macroName)) {
return;
}
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// get the name of the form template tiddler
var formTemplateName = undefined;
if (i < params.length) {
formTemplateName = params[i];
i++;
}
if (!formTemplateName) {
config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
return;
}
// --- Processing ------------------------------------------
// Get the form template text.
// (This contains the INPUT elements for the form.)
var formTemplateTiddler = store.getTiddler(formTemplateName);
if (!formTemplateTiddler) {
config.macros.formTiddler.createErrorElement(place, "Form template '" + formTemplateName + "' not found.");
return;
}
var templateText = formTemplateTiddler.text;
if(!templateText) {
// Shortcut: when template text is empty we do nothing.
return;
}
// Get the name of the tiddler containing this "formTiddler" macro
// (i.e. the tiddler, that will be edited and that contains the data)
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
// Append a "form" element.
var formName = "form"+formTemplateName+"__"+tiddlerName;
var e = document.createElement("form");
e.setAttribute("name", formName);
place.appendChild(e);
// "Embed" the elements defined by the templateText (i.e. the INPUT elements)
// into the "form" element we just created
wikify(templateText, e);
// Initialize the INPUT elements.
config.macros.formTiddler.initValuesAndHandlersInFormElements(formName, DataTiddler.getDataObject(tiddlerName));
};
// -------------------------------------------------------------------------------
// Form Data Access
// -------------------------------------------------------------------------------
// Internal.
//
// Initialize the INPUT elements of the form with the values of their "matching"
// data fields in the tiddler. Also setup the onChange handler to ensure that
// changes in the INPUT elements are stored in the tiddler's data.
//
config.macros.formTiddler.initValuesAndHandlersInFormElements = function(formName, data) {
// config.macros.formTiddler.trace("initValuesAndHandlersInFormElements(formName="+formName+", data="+data+")");
// find the form
var form = config.macros.formTiddler.findForm(formName);
if (!form) {
return;
}
try {
var elems = form.elements;
for (var i = 0; i < elems.length; i++) {
var c = elems[i];
var setter = config.macros.formTiddler.setter[c.type];
if (setter) {
var value = data[c.name];
if (value != null) {
setter(c, value);
}
c.onchange = onFormTiddlerChange;
} else {
config.macros.formTiddler.displayFormTiddlerError("No setter defined for INPUT element of type '"+c.type+"'. (Element '"+c.name+"' in form '"+formName+"')");
}
}
} catch(e) {
config.macros.formTiddler.displayFormTiddlerError("Error when updating elements with new formData. "+e);
}
};
// Internal.
//
// @return [may be null]
//
config.macros.formTiddler.findForm = function(formName) {
// We must manually iterate through the document's forms, since
// IE does not support the "document[formName]" approach
var forms = window.document.forms;
for (var i = 0; i < forms.length; i++) {
var form = forms[i];
if (form.name == formName) {
return form;
}
}
return null;
};
// Internal.
//
config.macros.formTiddler.setSelectOneValue = function(element,value) {
var n = element.options.length;
for (var i = 0; i < n; i++) {
element.options[i].selected = abego.getOptionsValue(element,i) == value;
}
};
// Internal.
//
config.macros.formTiddler.setSelectMultipleValue = function(element,value) {
var values = {};
for (var i = 0; i < value.length; i++) {
values[value[i]] = true;
}
var n = element.length;
for (var i = 0; i < n; i++) {
element.options[i].selected = !(!values[abego.getOptionsValue(element,i)]);
}
};
// Internal.
//
config.macros.formTiddler.getSelectOneValue = function(element) {
var i = element.selectedIndex;
return (i >= 0) ? abego.getOptionsValue(element,i) : null;
};
// Internal.
//
config.macros.formTiddler.getSelectMultipleValue = function(element) {
var values = [];
var n = element.length;
for (var i = 0; i < n; i++) {
if (element.options[i].selected) {
values.push(abego.getOptionsValue(element,i));
}
}
return values;
};
// -------------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------------
// Internal.
//
config.macros.formTiddler.checkForExtensions = function(place,macroName) {
if (!version.extensions.DataTiddlerPlugin) {
config.macros.formTiddler.createErrorElement(place, "<<" + macroName + ">> requires the DataTiddlerPlugin. (You can get it from http://tiddlywiki.abego-software.de/#DataTiddlerPlugin)");
return false;
}
return true;
};
// Internal.
//
// Displays a trace message in the "TiddlyWiki" message pane.
// (used for debugging)
//
config.macros.formTiddler.trace = function(s) {
displayMessage("Trace: "+s);
};
// Internal.
//
// Display some error message in the "TiddlyWiki" message pane.
//
config.macros.formTiddler.displayFormTiddlerError = function(s) {
alert("FormTiddlerPlugin Error: "+s);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.formTiddler.createErrorElement = function(place, message) {
return createTiddlyElement(place,"span",null,"formTiddlerError",message);
};
// Internal.
//
// Returns the name of the tiddler containing the given element.
//
config.macros.formTiddler.getContainingTiddlerName = function(element) {
return story.findContainingTiddler(element).getAttribute("tiddler");
};
// -------------------------------------------------------------------------------
// Event Handlers
// -------------------------------------------------------------------------------
// This function must be called by the INPUT elements whenever their
// data changes. Typically this is done through an "onChange" handler.
//
function onFormTiddlerChange (e) {
// config.macros.formTiddler.trace("onFormTiddlerChange "+e);
if (!e) e = window.event;
var target = resolveTarget(e);
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(target);
var getter = config.macros.formTiddler.getter[target.type];
if (getter) {
var value = getter(target);
DataTiddler.setData(tiddlerName, target.name, value);
} else {
config.macros.formTiddler.displayFormTiddlerError("No getter defined for INPUT element of type '"+target.type+"'. (Element '"+target.name+"' used in tiddler '"+tiddlerName+"')");
}
}
// ensure that the function can be used in HTML event handler
window.onFormTiddlerChange = onFormTiddlerChange;
// -------------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// -------------------------------------------------------------------------------
setStylesheet(
".formTiddlerError{color: #ffffff;background-color: #880000;}",
"formTiddler");
//============================================================================
// checkForDataTiddlerPlugin Macro
//============================================================================
config.macros.checkForDataTiddlerPlugin = {
// Standard Properties
label: "checkForDataTiddlerPlugin",
version: {major: 1, minor: 0, revision: 0, date: new Date(2005, 12, 14)},
prompt: "Check if the DataTiddlerPlugin exists"
};
config.macros.checkForDataTiddlerPlugin.handler = function(place,macroName,params) {
config.macros.formTiddler.checkForExtensions(place, config.macros.formTiddler.label);
};
//============================================================================
// newTiddlerWithForm Macro
//============================================================================
config.macros.newTiddlerWithForm = {
// Standard Properties
label: "newTiddlerWithForm",
version: {major: 1, minor: 0, revision: 1, date: new Date(2006, 1, 6)},
prompt: "Creates a new Tiddler with a <<formTiddler ...>> macro"
};
config.macros.newTiddlerWithForm.handler = function(place,macroName,params) {
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// get the name of the form template tiddler
var formTemplateName = undefined;
if (i < params.length) {
formTemplateName = params[i];
i++;
}
if (!formTemplateName) {
config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
return;
}
// get the button label
var buttonLabel = undefined;
if (i < params.length) {
buttonLabel = params[i];
i++;
}
if (!buttonLabel) {
config.macros.formTiddler.createErrorElement(place, "No button label specified in <<" + macroName + ">>.");
return;
}
// get the (optional) tiddlerName script and "askUser"
var tiddlerNameScript = undefined;
var askUser = false;
if (i < params.length) {
tiddlerNameScript = params[i];
i++;
if (i < params.length && params[i] == "askUser") {
askUser = true;
i++;
}
}
// --- Processing ------------------------------------------
if(!readOnly) {
var onClick = function() {
var tiddlerName = undefined;
if (tiddlerNameScript) {
try {
tiddlerName = eval(tiddlerNameScript);
} catch (ex) {
}
}
if (!tiddlerName || askUser) {
tiddlerName = prompt("Please specify a tiddler name.", askUser ? tiddlerName : "");
}
while (tiddlerName && store.getTiddler(tiddlerName)) {
tiddlerName = prompt("A tiddler named '"+tiddlerName+"' already exists.\n\n"+"Please specify a tiddler name.", tiddlerName);
}
// tiddlerName is either null (user canceled) or a name that is not yet in the store.
if (tiddlerName) {
var body = "<<formTiddler [["+formTemplateName+"]]>>";
var tags = [];
store.saveTiddler(tiddlerName,tiddlerName,body,config.options.txtUserName,new Date(),tags);
story.displayTiddler(null,tiddlerName,1);
}
};
createTiddlyButton(place,buttonLabel,buttonLabel,onClick);
}
};
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
//{{{
config.macros.formTiddler.getContainingTiddlerNameOverride_base = config.macros.formTiddler.getContainingTiddlerName;
config.macros.formTiddler.getContainingTiddlerName = function (element) {
var e = element;
while (e) {
var baseTiddler = e.getAttribute ? e.getAttribute("basetiddler") : null;
if ((baseTiddler !== undefined) && (baseTiddler !== null)) {
return baseTiddler;
}
e = jQuery(e).hasClass("popup") && Popup.stack[0] ? Popup.stack[0].root : e.parentNode;
}
return config.macros.formTiddler.getContainingTiddlerNameOverride_base(element);
};
//}}}
//{{{
var code = eval("config.macros.tiddler.handler").toString();
var newCode = null;
var startPosition = code.indexOf("refresh");
if (startPosition > -1) {
newCode = code.substring(0, startPosition - 1) + "basetiddler: tiddler ? tiddler.title : null, " + code.substring(startPosition - 1);
code = newCode;
}
if (newCode != null) {
eval("config.macros.tiddler.handler = " + newCode);
}
//}}}
/***
|Name|FormatLinkPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.txtWebmailURLTemplate === undefined) {
config.options.txtWebmailURLTemplate = "";
}
merge(config.optionsDesc, {
txtWebmailURLTemplate: "The URL template of a webmail server for sending emails: the {name} and {email} placeholders will be replaced accordingly",
});
//}}}
//{{{
window.createExternalLink_flp = window.createExternalLink;
window.createExternalLink = function (place, url, label) {
var link = window.createExternalLink_flp(place, url, label);
var webmailURLTemplate = config.options.txtWebmailURLTemplate;
if (webmailURLTemplate && link && link.href && (link.href.trim().toLowerCase().indexOf("mailto:") === 0)) {
link.href.replace(/^.*?:(.*?)<(.*?)>|^.*?:(.*)/, function (m, p1, p2, p3) {
var mailTo = ((p3 == null) ? p2 : p3) || "";
var displayName = ((p3 == null) ? p1 : p3) || "";
link.href = webmailURLTemplate.replace(/\{name\}/gi, displayName.trim()).replace(/\{email\}/gi, mailTo.trim());
});
}
return link;
};
//}}}
//{{{
(function () {
var insertionIndex = config.textPrimitives.urlPattern.indexOf("|mailto|");
config.textPrimitives.urlPattern = config.textPrimitives.urlPattern.substring(0, insertionIndex) + "|sms|tel" + config.textPrimitives.urlPattern.substring(insertionIndex);
for (var i = 0; i < config.formatters.length; i++) {
if (config.formatters[i]["name"] == "urlLink") {
config.formatters[i]["match"] = config.textPrimitives.urlPattern;
break;
}
}
})();
//}}}
<html><input type="checkbox" id="freeBusyPublishOnServer" value="1" /></html> <<slider 233bf59e-ed85-49a2-9928-c1f3cdde170e [[Free/Busy##instructions]] 'publish on server' 'Click to display instructions'>>
;<html><label>number of months: <input type="text" id="freeBusyMonths" size="4" value="2" /></label></html>
;<html><label>tag filter: <input type="text" id="freeBusyTagFilter" size="75" /></label></html>
;organizer
:<html><label>name: <input type="text" id="freeBusyOrganizerName" size="25" value="Some User" /></label></html>
:<html><label>email: <input type="text" id="freeBusyOrganizerEmail" size="25" value="someuser@example.com" /></label></html>
<script>
window.determineDaysBetweenDates = function determineDaysBetweenDates(date1, date2) {
return ((date2.getTime() - date1.getTime() + ((date1.getTimezoneOffset() - date2.getTimezoneOffset()) * 60 * 1000)) / 1000 / 60 / 60 / 24) | 0; // "| 0" casts the division result to an integer
}
var monthsField = jQuery("#freeBusyMonths").on("blur", function () {
var value = jQuery(this).val();
value = jQuery.isNumeric(value) ? parseInt(value) : -1;
if (value > 0) {
config.options["txtFreeBusyMonths"] = value;
saveOption("txtFreeBusyMonths");
} else {
removeCookie("txtFreeBusyMonths");
delete config.options["txtFreeBusyMonths"];
jQuery(this).val(2);
}
});
var noMonths = config.options["txtFreeBusyMonths"] || monthsField.val();
noMonths = jQuery.isNumeric(noMonths) ? parseInt(noMonths) : 2;
monthsField.val(noMonths);
var tagFilterField = jQuery("#freeBusyTagFilter").on("blur", function () {
var value = jQuery(this).val();
if (value) {
config.options["txtFreeBusyTagFilter"] = value;
saveOption("txtFreeBusyTagFilter");
} else {
removeCookie("txtFreeBusyTagFilter");
delete config.options["txtFreeBusyTagFilter"];
}
});
var tagFilter = config.options["txtFreeBusyTagFilter"];
if (!tagFilter) {
var excludeTags = config.options.txtReminderPublishExcludeTags ? config.options.txtReminderPublishExcludeTags.readBracketedList() : null;
var excludeTagsCondition = "";
for (var i = 0; excludeTags && (i < excludeTags.length); i++) {
excludeTagsCondition = excludeTagsCondition + (excludeTagsCondition.length ? " AND " : "") + "NOT " + excludeTags[i];
}
tagFilter = excludeTagsCondition;
}
tagFilterField.val(tagFilter);
var organizerNameField = jQuery("#freeBusyOrganizerName").on("blur", function () {
var value = jQuery(this).val();
if (value) {
config.options["txtFreeBusyOrganizerName"] = value;
saveOption("txtFreeBusyOrganizerName");
} else {
removeCookie("txtFreeBusyOrganizerName");
delete config.options["txtFreeBusyOrganizerName"];
}
});
var organizerName = config.options["txtFreeBusyOrganizerName"] || organizerNameField.val();
var organizerEmailField = jQuery("#freeBusyOrganizerEmail").on("blur", function () {
var value = jQuery(this).val();
if (value) {
config.options["txtFreeBusyOrganizerEmail"] = value;
saveOption("txtFreeBusyOrganizerEmail");
} else {
removeCookie("txtFreeBusyOrganizerEmail");
delete config.options["txtFreeBusyOrganizerEmail"];
}
});
var organizerEmail = config.options["txtFreeBusyOrganizerEmail"] || organizerEmailField.val();
if (store.getTiddler('FreeBusy Calendar Info')) {
var tiddlerText = store.getTiddler('FreeBusy Calendar Info').text;
try {
var tiddlerMap = eval("function _convertFilenameMap() {return " + tiddlerText + "}; _convertFilenameMap();");
if (tiddlerMap && tiddlerMap["organizer"]) {
organizerName = tiddlerMap["organizer"]["name"] ? tiddlerMap["organizer"]["name"] : organizerName;
organizerEmail = tiddlerMap["organizer"]["email"] ? tiddlerMap["organizer"]["email"] : organizerEmail;
}
} catch (e) {}
}
organizerNameField.val(organizerName);
organizerEmailField.val(organizerEmail);
</script>
;<script label="Generate Free/Busy">
var label = "Generate Free/Busy";
var resultPlace = jQuery(place);
if (resultPlace.data("timeoutID")) {
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
} else {
var noMonths = jQuery("#freeBusyMonths").val();
noMonths = jQuery.isNumeric(noMonths) ? parseInt(noMonths) : 2;
var formatUTCDateString = function (date) {
return date.getUTCFullYear() + ("0" + (date.getUTCMonth() + 1)).slice(-2) + ("0" + date.getUTCDate()).slice(-2) + "T"
+ ("0" + date.getUTCHours()).slice(-2) + ("0" + date.getUTCMinutes()).slice(-2) + ("0" + date.getUTCSeconds()).slice(-2) + "Z";
};
var currentDate = new Date();
var startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1, 0, 0, 0, 0);
var endDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + noMonths, 1, 0, 0, 0, 0);
var numberOfDays = window.determineDaysBetweenDates(startDate, endDate);
var organizerName = jQuery("#freeBusyOrganizerName").val() || "Some User";
var organizerEmail = jQuery("#freeBusyOrganizerEmail").val() || "someuser@example.com";
var freebusy
= "BEGIN:VCALENDAR\r\n"
+ "PRODID:-//Galasoft Inc.//TW Notes v1.0//EN\r\n"
+ "VERSION:2.0\r\n"
+ "METHOD:PUBLISH\r\n"
+ "BEGIN:VFREEBUSY\r\n"
+ "ORGANIZER;CN=" + organizerName + ":MAILTO:" + organizerEmail + "\r\n"
+ "DTSTAMP:" + formatUTCDateString(currentDate) + "\r\n"
+ "DTSTART:" + formatUTCDateString(startDate) + "\r\n"
+ "DTEND:" + formatUTCDateString(endDate) + "\r\n";
var freeBusyEvents = new ScheduleReminderEvents();
var excludeTagsCondition = jQuery("#freeBusyTagFilter").val().length ? " AND " + jQuery("#freeBusyTagFilter").val() : "";
var determineFreeBusyForDay = function () {
var eventDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + dayCounter, 0, 0, 0, 0);
var date = eventDate;
var freeBusyEventList = [];
freeBusyEvents.determineEvents(date, "(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT Trash" + excludeTagsCondition);
for (var i = 0; i < freeBusyEvents.list.length; i++) {
var freeBusyEvent = freeBusyEvents.list[i];
if (freeBusyEvent.start && freeBusyEvent.end && (freeBusyEvent.start != freeBusyEvent.end) && !freeBusyEvent.reminder.isTagged(excludeTagsCondition)) {
var eventStartDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventStartDateTime.setHours(Number(freeBusyEvent.start.substring(0, freeBusyEvent.start.indexOf(":"))));
eventStartDateTime.setMinutes(Number(freeBusyEvent.start.substring(freeBusyEvent.start.indexOf(":") + 1)));
var eventEndDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventEndDateTime.setHours(Number(freeBusyEvent.end.substring(0, freeBusyEvent.end.indexOf(":"))));
eventEndDateTime.setMinutes(Number(freeBusyEvent.end.substring(freeBusyEvent.end.indexOf(":") + 1)));
var event = (eventEndDateTime.getTime() > eventStartDateTime.getTime()) ? {start: eventStartDateTime, end: eventEndDateTime} : {start: eventEndDateTime, end: eventStartDateTime};
for (var j = 0; j <= freeBusyEventList.length; j++) {
if (j == freeBusyEventList.length) {
freeBusyEventList[freeBusyEventList.length] = event;
break;
} else if ((event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) && (event["end"].getTime() < freeBusyEventList[j]["start"].getTime())) {
freeBusyEventList.splice(j, 0, event);
break;
} else if (!((event["start"].getTime() > freeBusyEventList[j]["end"].getTime()) && (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()))) {
if (event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) {
freeBusyEventList[j]["start"] = event["start"];
}
if (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()) {
freeBusyEventList[j]["end"] = event["end"];
}
break;
}
}
}
}
for (var i = 0; i < freeBusyEventList.length; i++) {
freebusy = freebusy + "FREEBUSY:" + formatUTCDateString(freeBusyEventList[i]["start"]);
freebusy = freebusy + "/" + formatUTCDateString(freeBusyEventList[i]["end"]) + "\r\n";
}
};
var dayCounter = 0;
var determineFreeBusy = function (resultPlaceId) {
var resultPlace = jQuery("#" + resultPlaceId);
determineFreeBusyForDay();
dayCounter++;
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
if (dayCounter < numberOfDays) {
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlaceId) }, 50));
} else {
freebusy = freebusy
+ "END:VFREEBUSY\r\n"
+ "END:VCALENDAR\r\n";
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
resultPlace.parent().parent().children(".result").remove();
if (store.tiddlerExists("CopyToClipboardPlugin")) {
wikify('<clip title="">{{{\n' + freebusy.replace(/\r\n/g, "\n") + '}}}</clip>', jQuery("<span class='result'></span>").appendTo(resultPlace.parent().parent())[0]);
} else {
wikify('{{{\n' + freebusy.replace(/\r\n/g, "\n") + '}}}', jQuery("<span class='result'></span>").appendTo(resultPlace.parent().parent())[0]);
}
if (jQuery('#freeBusyPublishOnServer').is(':checked') && store.getTiddler('FreeBusy Calendar Info') && store.getTiddler('FreeBusy Server') && store.getTiddler('FreeBusy Server Password') && (store.getTiddler('FreeBusy Server Password').text.length > 0)) {
var filename = store.getTiddler('FreeBusy Calendar Info').text;
try {
var filenameMap = eval("function _convertFilenameMap() {return " + filename + "}; _convertFilenameMap();");
if (filenameMap && filenameMap["freebusy"]) {
filename = filenameMap["freebusy"];
}
} catch (e) {}
config.macros.serverupload.save([{name: filename, data: freebusy}], store.getTiddler('FreeBusy Server').text, store.getTiddler('FreeBusy Server Password').text, function (result) {
if (typeof result === "boolean") {
displayMessage(result ? "The calendar was published successfully." : "The calendar could not be published. Check server details and directory permissions.");
}
});
}
}
};
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlace.attr("id")) }, 50));
}
</script>
;<script label="Generate iCalendar">
var label = "Generate iCalendar";
var resultPlace = jQuery(place);
if (resultPlace.data("timeoutID")) {
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
} else {
var noMonths = jQuery("#freeBusyMonths").val();
noMonths = jQuery.isNumeric(noMonths) ? parseInt(noMonths) : 2;
var formatUTCDateString = function (date) {
return date.getUTCFullYear() + ("0" + (date.getUTCMonth() + 1)).slice(-2) + ("0" + date.getUTCDate()).slice(-2) + "T"
+ ("0" + date.getUTCHours()).slice(-2) + ("0" + date.getUTCMinutes()).slice(-2) + ("0" + date.getUTCSeconds()).slice(-2) + "Z";
};
var currentDate = new Date();
var startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1, 0, 0, 0, 0);
var endDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + noMonths, 1, 0, 0, 0, 0);
var numberOfDays = window.determineDaysBetweenDates(startDate, endDate);
var freebusy
= "BEGIN:VCALENDAR\r\n"
+ "PRODID:-//Galasoft Inc.//TW Notes v1.0//EN\r\n"
+ "VERSION:2.0\r\n";
var freeBusyEvents = new ScheduleReminderEvents();
var counter = 0;
var organizerName = jQuery("#freeBusyOrganizerName").val() || "Some User";
var organizerEmail = jQuery("#freeBusyOrganizerEmail").val() || "someuser@example.com";
var summary = jQuery("#freeBusyGenerateICalendarSummary").val() || "Busy";
var excludeTagsCondition = jQuery("#freeBusyTagFilter").val().length ? " AND " + jQuery("#freeBusyTagFilter").val() : "";
var uidPrefix = jQuery("#freeBusyGenerateICalendarUIDPrefix").val() || "TWNICALUID";
var generateUID = function (event) {
return uidPrefix + "S" + formatUTCDateString(event.start) + "E" + formatUTCDateString(event.end);
};
var determineFreeBusyForDay = function () {
var eventDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + dayCounter, 0, 0, 0, 0);
var date = eventDate;
var freeBusyEventList = [];
freeBusyEvents.determineEvents(date, "(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT Trash" + excludeTagsCondition);
for (var i = 0; i < freeBusyEvents.list.length; i++) {
var freeBusyEvent = freeBusyEvents.list[i];
if (freeBusyEvent.start && freeBusyEvent.end && (freeBusyEvent.start != freeBusyEvent.end) && !freeBusyEvent.reminder.isTagged(excludeTagsCondition)) {
var eventStartDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventStartDateTime.setHours(Number(freeBusyEvent.start.substring(0, freeBusyEvent.start.indexOf(":"))));
eventStartDateTime.setMinutes(Number(freeBusyEvent.start.substring(freeBusyEvent.start.indexOf(":") + 1)));
var eventEndDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventEndDateTime.setHours(Number(freeBusyEvent.end.substring(0, freeBusyEvent.end.indexOf(":"))));
eventEndDateTime.setMinutes(Number(freeBusyEvent.end.substring(freeBusyEvent.end.indexOf(":") + 1)));
var event = (eventEndDateTime.getTime() > eventStartDateTime.getTime()) ? {start: eventStartDateTime, end: eventEndDateTime} : {start: eventEndDateTime, end: eventStartDateTime};
event["uid"] = generateUID(event);
for (var j = 0; j <= freeBusyEventList.length; j++) {
if (j == freeBusyEventList.length) {
freeBusyEventList[freeBusyEventList.length] = event;
break;
} else if ((event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) && (event["end"].getTime() < freeBusyEventList[j]["start"].getTime())) {
freeBusyEventList.splice(j, 0, event);
break;
} else if (!((event["start"].getTime() > freeBusyEventList[j]["end"].getTime()) && (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()))) {
if (event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) {
freeBusyEventList[j]["start"] = event["start"];
freeBusyEventList[j]["uid"] = generateUID(freeBusyEventList[j]);
}
if (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()) {
freeBusyEventList[j]["end"] = event["end"];
freeBusyEventList[j]["uid"] = generateUID(freeBusyEventList[j]);
}
break;
}
}
}
};
var groupWiseBusy = jQuery('#freeBusyGenerateICalendarGroupWiseBusy').is(':checked');
for (var i = 0; i < freeBusyEventList.length; i++) {
freebusy = freebusy
+ "BEGIN:VEVENT\r\n"
+ "UID:" + freeBusyEventList[i]["uid"] + "\r\n"
+ "DTSTAMP:" + formatUTCDateString(currentDate) + "\r\n"
+ "ORGANIZER;CN=" + organizerName + ":MAILTO:" + organizerEmail + "\r\n"
+ "DTSTART:" + formatUTCDateString(freeBusyEventList[i]["start"]) + "\r\n"
+ "DTEND:" + formatUTCDateString(freeBusyEventList[i]["end"]) + "\r\n"
+ "SUMMARY:" + summary + "\r\n"
+ (groupWiseBusy ? "X-GWSHOW-AS:BUSY\r\n" : "")
+ "END:VEVENT\r\n";
}
};
var dayCounter = 0;
var determineFreeBusy = function (resultPlaceId) {
var resultPlace = jQuery("#" + resultPlaceId);
determineFreeBusyForDay();
dayCounter++;
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
if (dayCounter < numberOfDays) {
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlaceId) }, 50));
} else {
freebusy = freebusy
+ "END:VCALENDAR\r\n";
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
resultPlace.parent().parent().children(".result").remove();
if (store.tiddlerExists("CopyToClipboardPlugin")) {
wikify('<clip title="">{{{\n' + freebusy.replace(/\r\n/g, "\n") + '}}}</clip>', jQuery("<span class='result'></span>").appendTo(resultPlace.parent().parent())[0]);
} else {
wikify('{{{\n' + freebusy.replace(/\r\n/g, "\n") + '}}}', jQuery("<span class='result'></span>").appendTo(resultPlace.parent().parent())[0]);
}
if (jQuery('#freeBusyPublishOnServer').is(':checked') && store.getTiddler('FreeBusy Calendar Info') && store.getTiddler('FreeBusy Server') && store.getTiddler('FreeBusy Server Password') && (store.getTiddler('FreeBusy Server Password').text.length > 0)) {
var filename = store.getTiddler('FreeBusy Calendar Info').text;
try {
var filenameMap = eval("function _convertFilenameMap() {return " + filename + "}; _convertFilenameMap();");
if (filenameMap && filenameMap["icalendar"]) {
filename = filenameMap["icalendar"];
}
} catch (e) {}
config.macros.serverupload.save([{name: filename, data: freebusy}], store.getTiddler('FreeBusy Server').text, store.getTiddler('FreeBusy Server Password').text, function (result) {
displayMessage(result ? "The calendar was published successfully." : "The calendar could not be published. Check server details and directory permissions.");
});
}
}
};
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlace.attr("id")) }, 50));
}
</script>
:<html><label>summary: <input type="text" id="freeBusyGenerateICalendarSummary" size="25" value="Busy" /></label></html>
:<html><label>UID prefix: <input type="text" id="freeBusyGenerateICalendarUIDPrefix" size="25" value="TWNICALUID" /></label></html>
:Include indicators: <html><label><input type="checkbox" id="freeBusyGenerateICalendarGroupWiseBusy" value="1" /> Novell GroupWise <em>BUSY</em></label></html> <script>
var summaryField = jQuery("#freeBusyGenerateICalendarSummary").on("blur", function () {
var value = jQuery(this).val();
if (value) {
config.options["txtFreeBusyGenerateICalendarSummary"] = value;
saveOption("txtFreeBusyGenerateICalendarSummary");
} else {
removeCookie("txtFreeBusyGenerateICalendarSummary");
delete config.options["txtFreeBusyGenerateICalendarSummary"];
}
});
if (config.options["txtFreeBusyGenerateICalendarSummary"]) {
summaryField.val(config.options["txtFreeBusyGenerateICalendarSummary"]);
}
var summaryField = jQuery("#freeBusyGenerateICalendarUIDPrefix").on("blur", function () {
var value = jQuery(this).val();
if (value) {
config.options["txtFreeBusyGenerateICalendarUIDPrefix"] = value;
saveOption("txtFreeBusyGenerateICalendarUIDPrefix");
} else {
removeCookie("txtFreeBusyGenerateICalendarUIDPrefix");
delete config.options["txtFreeBusyGenerateICalendarUIDPrefix"];
}
});
if (config.options["txtFreeBusyGenerateICalendarUIDPrefix"]) {
summaryField.val(config.options["txtFreeBusyGenerateICalendarUIDPrefix"]);
}
</script>
;<script label="Generate Calendar">
var label = "Generate Calendar";
var resultPlace = jQuery(place);
if (resultPlace.data("timeoutID")) {
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
} else {
var noMonths = jQuery("#freeBusyMonths").val();
noMonths = jQuery.isNumeric(noMonths) ? parseInt(noMonths) : 2;
var calendarTemplateHTML
= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
+ '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\n'
+ '<head>\n'
+ '<meta http-equiv="Cache-Control" content="no-cache" />\n'
+ '<meta http-equiv="Expires" content="Sat, 1 Jan 2000 12:00:00 GMT" />\n'
+ '<meta http-equiv="Pragma" content="no-cache" />\n'
+ '<meta http-equiv="content-type" content="text/html; charset=utf-8" />\n'
+ '@@calendar-style@@\n'
+ '<title>Calendar</title>\n'
+ '</head>\n'
+ '<body>\n'
+ '@@calendar@@\n'
+ '</body>\n'
+ '</html>\n';
var calendarTemplate = {"en": [], "en-files":[], "fr": [], "fr-files": []};
for (var i = 0; i < noMonths; i++) {
calendarTemplate["en"][i] = calendarTemplateHTML;
calendarTemplate["en-files"][i] = (i + 1) + ".html";
calendarTemplate["fr"][i] = calendarTemplateHTML;
calendarTemplate["fr-files"][i] = (i + 1) + "_fr.html";
}
if (store.getTiddler('FreeBusy Calendar Template')) {
var tiddlerTemplate = store.getTiddler('FreeBusy Calendar Template').text;
while (tiddlerTemplate != null) {
var templateStart = tiddlerTemplate.indexOf("<!-- template begin");
var templateEnd = tiddlerTemplate.indexOf("<!-- template end");
if ((templateStart > -1) && (templateEnd > -1)) {
var infoStart = tiddlerTemplate.indexOf("{", templateStart);
var infoEnd = tiddlerTemplate.indexOf("}", templateStart);
if ((infoStart > -1) && (infoEnd > -1)) {
var infoMap = null;
try {
infoMap = eval("function _convertInfoMap() {return " + tiddlerTemplate.substring(infoStart, infoEnd + 1) + "}; _convertInfoMap();");
} catch (e) {}
if (infoMap != null) {
var lang = infoMap["lang"];
var file = infoMap["file"];
var offset = infoMap["offset"];
if (lang && file && offset && !isNaN(offset)) {
offset = Number(offset);
calendarTemplate[lang][offset] = tiddlerTemplate.substring(tiddlerTemplate.indexOf("-->", templateStart) + "-->".length, templateEnd);
calendarTemplate[lang + "-files"][offset] = file;
}
}
}
tiddlerTemplate = tiddlerTemplate.substring(templateEnd + 1);
} else {
tiddlerTemplate = null;
}
}
}
var calendarStyle
= '<style type="text/css">\n'
+ '@media screen, projection {\n'
+ ' a {\n'
+ ' cursor: pointer;\n'
+ ' text-decoration: none;\n'
+ ' }\n'
+ ' \n'
+ ' body {\n'
+ ' color: #000000;\n'
+ ' cursor: default;\n'
+ ' margin: 0;\n'
+ ' padding: 0;\n'
+ ' }\n'
+ ' \n'
+ ' caption {\n'
+ ' margin: 0 auto;\n'
+ ' text-align: right;\n'
+ ' font: 20px/30px Georgia, serif;\n'
+ ' }\n'
+ ' \n'
+ ' p {\n'
+ ' margin: 0;\n'
+ ' }\n'
+ ' \n'
+ ' strong {\n'
+ ' text-transform: uppercase;\n'
+ ' }\n'
+ ' \n'
+ ' table {\n'
+ ' background: #fff;\n'
+ ' border-bottom: 3px solid #ccc;\n'
+ ' border-right: 1px solid #999;\n'
+ ' margin: 0 auto;\n'
+ ' line-height: 1em;\n'
+ ' }\n'
+ ' \n'
+ ' th, td {\n'
+ ' border-left: 1px solid #999;\n'
+ ' border-top: 1px solid #999;\n'
+ ' font-family: Verdana, sans-serif;\n'
+ ' padding: 2px;\n'
+ ' vertical-align: top;\n'
+ ' width: 100px;\n'
+ ' height: inherit;\n'
+ ' }\n'
+ ' \n'
+ ' td a {\n'
+ ' color: #000;\n'
+ ' padding: 0;\n'
+ ' }\n'
+ ' \n'
+ ' td a:hover {\n'
+ ' text-decoration: underline;\n'
+ ' }\n'
+ ' \n'
+ ' col.sat, col.sun {\n'
+ ' background: #eef;\n'
+ ' }\n'
+ ' \n'
+ ' td.today {\n'
+ ' background: #fcf8e3;\n'
+ ' }\n'
+ ' \n'
+ ' div.buttons {\n'
+ ' font: 10px Verdana, sans-serif;\n'
+ ' padding: 7px;\n'
+ ' text-align: center;\n'
+ ' }\n'
+ ' \n'
+ ' div.buttons a {\n'
+ ' background: #ddd;\n'
+ ' border: 1px solid #bbb;\n'
+ ' color: #000;\n'
+ ' padding: 3px 5px;\n'
+ ' }\n'
+ ' \n'
+ ' div.buttons a:hover {\n'
+ ' background-color: #eef;\n'
+ ' color: #369;\n'
+ ' }\n'
+ ' \n'
+ ' tr.day {\n'
+ ' color: #666;\n'
+ ' font-size: 9px;\n'
+ ' height: 90px;\n'
+ ' }\n'
+ ' \n'
+ ' tr.number {\n'
+ ' background: #ddd;\n'
+ ' color: #888;\n'
+ ' font-size: 10px;\n'
+ ' height: 15px;\n'
+ ' text-align: right;\n'
+ ' vertical-align: middle;\n'
+ ' }\n'
+ ' \n'
+ ' th {\n'
+ ' background: #fff;\n'
+ ' color: #999;\n'
+ ' font-size: 11px;\n'
+ ' font-variant: small-caps;\n'
+ ' font-weight: normal;\n'
+ ' height: 15px;\n'
+ ' text-transform: capitalize;\n'
+ ' text-align: center;\n'
+ ' vertical-align: middle;\n'
+ ' }\n'
+ '}\n'
+ '\n'
+ '@media print {\n'
+ ' * {\n'
+ ' background: #fff;\n'
+ ' color: #000;\n'
+ ' }\n'
+ ' \n'
+ ' a {\n'
+ ' text-decoration: none;\n'
+ ' }\n'
+ ' \n'
+ ' body {\n'
+ ' margin: 0;\n'
+ ' padding: 0;\n'
+ ' }\n'
+ ' \n'
+ ' caption {\n'
+ ' text-align: center;\n'
+ ' text-transform: capitalize;\n'
+ ' font: small-caps 20px/30px Georgia, serif;\n'
+ ' }\n'
+ ' \n'
+ ' p {\n'
+ ' margin: 0;\n'
+ ' }\n'
+ ' \n'
+ ' strong {\n'
+ ' text-transform: uppercase;\n'
+ ' }\n'
+ ' \n'
+ ' table {\n'
+ ' border: solid #000 !important;\n'
+ ' border-width: 1px 0 0 1px !important;\n'
+ ' border-collapse:collapse;\n'
+ ' }\n'
+ ' \n'
+ ' th, td {\n'
+ ' border: solid #000 !important;\n'
+ ' border-width: 0 1px 1px 0 !important;\n'
+ ' font-family: Verdana, sans-serif;\n'
+ ' padding: 2px;\n'
+ ' vertical-align: top;\n'
+ ' width: 100px;\n'
+ ' height: inherit;\n'
+ ' }\n'
+ ' \n'
+ ' div.buttons {\n'
+ ' display: none;\n'
+ ' }\n'
+ ' \n'
+ ' tr.day {\n'
+ ' font-size: 9px;\n'
+ ' height: 90px;\n'
+ ' }\n'
+ ' \n'
+ ' tr.number {\n'
+ ' font-size: 10px;\n'
+ ' height: 15px;\n'
+ ' text-align: right;\n'
+ ' vertical-align: middle;\n'
+ ' }\n'
+ ' \n'
+ ' th {\n'
+ ' font-size: 11px;\n'
+ ' font-variant: small-caps;\n'
+ ' font-weight: normal;\n'
+ ' height: 15px;\n'
+ ' text-transform: capitalize;\n'
+ ' text-align: center;\n'
+ ' vertical-align: middle;\n'
+ ' }\n'
+ '}\n'
+ '</style>\n';
var calendarPrefix1
= '<table cellspacing="0">\n'
+ ' <caption>';
var calendarPrefix2
= '</caption>\n'
+ ' <colgroup>\n'
+ ' <col class="sun" />\n'
+ ' <col class="mon" />\n'
+ ' <col class="tue" />\n'
+ ' <col class="wed" />\n'
+ ' <col class="thu" />\n'
+ ' <col class="fri" />\n'
+ ' <col class="sat" />\n'
+ ' </colgroup>\n'
+ ' <thead>\n'
var calendarPrefix3 = {
en: ' <tr>\n'
+ ' <th scope="col">Sunday</th>\n'
+ ' <th scope="col">Monday</th>\n'
+ ' <th scope="col">Tuesday</th>\n'
+ ' <th scope="col">Wednesday</th>\n'
+ ' <th scope="col">Thursday</th>\n'
+ ' <th scope="col">Friday</th>\n'
+ ' <th scope="col">Saturday</th>\n'
+ ' </tr>\n'
+ ' </thead>\n'
+ ' <tbody>\n',
fr: ' <tr>\n'
+ ' <th scope="col">dimanche</th>\n'
+ ' <th scope="col">lundi</th>\n'
+ ' <th scope="col">mardi</th>\n'
+ ' <th scope="col">mercredi</th>\n'
+ ' <th scope="col">jeudi</th>\n'
+ ' <th scope="col">vendredi</th>\n'
+ ' <th scope="col">samedi</th>\n'
+ ' </tr>\n'
+ ' </thead>\n'
+ ' <tbody>\n'};
calendarNavigation = {en: ["Next Month >>", "<< Previous Month"], fr: ["Mois suivant >>", "<< Mois précédent"]};
calendarBusy = {en: "Busy", fr: "Occupé"};
var excludeTagsCondition = jQuery("#freeBusyTagFilter").val().length ? " AND " + jQuery("#freeBusyTagFilter").val() : "";
var includeDetails = jQuery('#freeBusyGenerateCalendarDetails').is(':checked');
var generateDayStructure = function (year, month, day) {
var date = new Date(year, month - 1, day, 0, 0, 0, 0);
if (date.getMonth() + 1 != month) {
return null;
}
var daysInMonth = window.determineDaysBetweenDates(new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0), new Date(date.getFullYear(), date.getMonth() + 1, 1, 0, 0, 0, 0));
if (day > daysInMonth) {
return null;
}
var dayStructure = "";
if ((date.getDay() == 0) || (day == 1)) {
var blankDayStructure = "";
dayStructure = ' <tr class="number">\n';
var weekdayCounter = 0;
if (day == 1) {
for (var i = 0; i < date.getDay(); i++) {
blankDayStructure = blankDayStructure + ' <td> </td>\n';
weekdayCounter++;
}
dayStructure = dayStructure + blankDayStructure;
} else {
dayStructure = ' </tr>\n' + dayStructure;
}
for (var i = 0; i + weekdayCounter < 7; i++) {
if (date.getDate() + i > daysInMonth) {
dayStructure = dayStructure + ' <td> </td>\n';
} else {
dayStructure = dayStructure + ' <td>' + (date.getDate() + i) + '</td>\n';
}
}
dayStructure = dayStructure + ' </tr>\n';
dayStructure = dayStructure + ' <tr class="day">\n';
dayStructure = dayStructure + blankDayStructure;
}
var freeBusyEvents = new ScheduleReminderEvents();
dayStructure = dayStructure + ' <td id="y' + date.getFullYear() + 'm' + (date.getMonth() + 1)+ 'd' + date.getDate() + '">';
var freeBusyEventList = [];
freeBusyEvents.determineEvents(date, "(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT Trash" + excludeTagsCondition);
for (var i = 0; i < freeBusyEvents.list.length; i++) {
var freeBusyEvent = freeBusyEvents.list[i];
if (freeBusyEvent.start && freeBusyEvent.end && (freeBusyEvent.start != freeBusyEvent.end) && !freeBusyEvent.reminder.isTagged(excludeTagsCondition)) {
var eventStartDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventStartDateTime.setHours(Number(freeBusyEvent.start.substring(0, freeBusyEvent.start.indexOf(":"))));
eventStartDateTime.setMinutes(Number(freeBusyEvent.start.substring(freeBusyEvent.start.indexOf(":") + 1)));
var eventEndDateTime = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventEndDateTime.setHours(Number(freeBusyEvent.end.substring(0, freeBusyEvent.end.indexOf(":"))));
eventEndDateTime.setMinutes(Number(freeBusyEvent.end.substring(freeBusyEvent.end.indexOf(":") + 1)));
var event = (eventEndDateTime.getTime() > eventStartDateTime.getTime()) ? {start: eventStartDateTime, end: eventEndDateTime} : {start: eventEndDateTime, end: eventStartDateTime};
var eventTitle = "";
if (includeDetails) {
var reminder = freeBusyEvent.reminder;
reminder["params"]["format"] = "TITLE";
var eventTitle = wikifyPlainText(getReminderMessageForDisplay(reminder["diff"], reminder["params"], reminder["matchedDate"], reminder["tiddler"]), 0, tiddler);
eventTitle = (eventTitle.lastIndexOf(">") === (eventTitle.length - 1)) ? eventTitle.substring(0, eventTitle.length - 1).trim() : eventTitle.trim();
}
event.detail = [eventTitle];
for (var j = 0; j <= freeBusyEventList.length; j++) {
if (j == freeBusyEventList.length) {
freeBusyEventList[freeBusyEventList.length] = event;
break;
} else if ((event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) && (event["end"].getTime() < freeBusyEventList[j]["start"].getTime())) {
freeBusyEventList.splice(j, 0, event);
break;
} else if (!((event["start"].getTime() > freeBusyEventList[j]["end"].getTime()) && (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()))) {
if (event["start"].getTime() < freeBusyEventList[j]["start"].getTime()) {
freeBusyEventList[j]["start"] = event["start"];
}
if (event["end"].getTime() > freeBusyEventList[j]["end"].getTime()) {
freeBusyEventList[j]["end"] = event["end"];
}
if (includeDetails) {
freeBusyEventList[j].detail = freeBusyEventList[j].detail.concat(event.detail);
}
break;
}
}
}
}
for (var i = 0; i < freeBusyEventList.length; i++) {
var startTime = (freeBusyEventList[i]["start"].getHours() < 10 ? "0" : "") + freeBusyEventList[i]["start"].getHours() + ":" + (freeBusyEventList[i]["start"].getMinutes() < 10 ? "0" : "") + freeBusyEventList[i]["start"].getMinutes();
var endTime = (freeBusyEventList[i]["end"].getHours() < 10 ? "0" : "") + freeBusyEventList[i]["end"].getHours() + ":" + (freeBusyEventList[i]["end"].getMinutes() < 10 ? "0" : "") + freeBusyEventList[i]["end"].getMinutes();
if (i > 0) {
dayStructure = dayStructure + '<br />';
}
var startEndTime = startTime + " - " + endTime;
var timezoneOffset = -date.getTimezoneOffset();
var hoursOffset = (Math.abs(timezoneOffset) / 60) | 0;
var minutesOffset = Math.abs(timezoneOffset) - hoursOffset * 60;
startEndTime = startEndTime + " UTC" + ((timezoneOffset > 0 ) ? "+" : "-") + (hoursOffset < 10 ? "0" : "") + hoursOffset + "" + (minutesOffset < 10 ? "0" : "") + minutesOffset;
var detail = "Busy " + date.getFullYear() + "-" + (date.getMonth() + 1 < 10 ? "0" : "") + (date.getMonth() + 1) + "-" + (date.getDate() < 10 ? "0" : "") + date.getDate() + " " + startEndTime;
if (includeDetails) {
detail = "";
freeBusyEventList[i]["detail"].sort();
for (var j = 0; j < freeBusyEventList[i]["detail"].length; j++) {
detail += (detail.length ? "\\n" : "") + freeBusyEventList[i]["detail"][j];
}
}
dayStructure = dayStructure + ' <a href="#" title="Busy ' + startEndTime + '" onclick="alert(\'' + detail.replace(/'/g, "\\'") + '\'); return false;">' + startTime + ' - ' + endTime + '</a>';
}
if (freeBusyEventList.length == 0) {
dayStructure = dayStructure + ' ';
}
dayStructure = dayStructure + '</td>\n';
if (day == daysInMonth) {
for (var i = 0; i + date.getDay() + 1 < 7; i++) {
dayStructure = dayStructure + ' <td> </td>\n';
}
dayStructure = dayStructure + ' </tr>\n';
}
return dayStructure;
};
var currentDate = new Date();
var calentarMonth = [];
var daysStructureMonth = [];
for (var i = 0; i < noMonths; i++) {
calentarMonth[i] = new Date(currentDate.getFullYear(), currentDate.getMonth() + i, 1, 0, 0, 0, 0);
daysStructureMonth[i] = (i == 0) ? "" : null;
}
var numberOfDays = calentarMonth.length ? window.determineDaysBetweenDates(calentarMonth[0], new Date(currentDate.getFullYear(), currentDate.getMonth() + noMonths, 1, 0, 0, 0, 0)) : 0;
var dayCounter = 1;
var previousCounter = 0;
var determineFreeBusy = function (resultPlaceId) {
var resultPlace = jQuery("#" + resultPlaceId);
var monthIndex = -1;
for (var i = 0; i < daysStructureMonth.length; i++) {
if (daysStructureMonth[i] == null) {
break;
}
monthIndex++;
}
var dayStructure = null;
if ((monthIndex > -1) && (monthIndex < daysStructureMonth.length)) {
dayStructure = generateDayStructure(calentarMonth[monthIndex].getFullYear(), calentarMonth[monthIndex].getMonth() + 1, dayCounter - previousCounter);
if ((dayStructure == null) && (monthIndex + 1 < daysStructureMonth.length)) {
daysStructureMonth[monthIndex + 1] = "";
previousCounter = dayCounter - 1;
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlaceId) }, 50));
return;
}
}
if (dayStructure != null) {
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
dayCounter++;
daysStructureMonth[monthIndex] = daysStructureMonth[monthIndex] + dayStructure;
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlaceId) }, 50));
} else {
clearTimeout(resultPlace.data("timeoutID"));
resultPlace.removeData("timeoutID");
resultPlace.text(label);
var makeCalendar = function (lang, offset, maxOffset) {
var calendar = calendarTemplate[lang][offset];
if (calendar.indexOf("@@calendar-style@@") > -1) {
calendar = calendar.substring(0, calendar.indexOf("@@calendar-style@@")) + calendarStyle + calendar.substring(calendar.indexOf("@@calendar-style@@") +"@@calendar-style@@".length);
}
if (calendar.indexOf("@@calendar@@") > -1) {
var calendarContent = calendarPrefix1 + calentarMonth[offset].getFullYear() + "-" + (calentarMonth[offset].getMonth() + 1 < 10 ? "0" : "") + (calentarMonth[offset].getMonth() + 1) + calendarPrefix2 + calendarPrefix3[lang] + daysStructureMonth[offset].replace(/Busy/g, calendarBusy[lang]);
calendarContent = calendarContent
+ ' </tbody>\n'
+ '</table>\n'
+ '<div id="' + calendarTemplate[lang + "-files"][offset] + '" class="buttons">\n';
if (offset > 0) {
calendarContent = calendarContent
+ '<a onclick="this.href = this.href + \'?t=\' + (new Date()).getTime(); return true;" href="';
var relatedOffset = offset - 1;
calendarContent = calendarContent + ((calendarTemplate[lang + "-files"][relatedOffset].indexOf("/") > -1) ? calendarTemplate[lang + "-files"][relatedOffset].substring(calendarTemplate[lang + "-files"][relatedOffset].lastIndexOf("/") + 1) : calendarTemplate[lang + "-files"][relatedOffset]);
calendarContent = calendarContent
+ '">' + calendarNavigation[lang][1] + '</a>\n';
}
if ((offset > 0) && (offset < maxOffset)) {
calendarContent = calendarContent + ' \n';
}
if (offset < maxOffset) {
calendarContent = calendarContent
+ '<a onclick="this.href = this.href + \'?t=\' + (new Date()).getTime(); return true;" href="';
var relatedOffset = offset + 1;
calendarContent = calendarContent + ((calendarTemplate[lang + "-files"][relatedOffset].indexOf("/") > -1) ? calendarTemplate[lang + "-files"][relatedOffset].substring(calendarTemplate[lang + "-files"][relatedOffset].lastIndexOf("/") + 1) : calendarTemplate[lang + "-files"][relatedOffset]);
calendarContent = calendarContent
+ '">' + calendarNavigation[lang][0] + '</a>\n';
}
calendarContent = calendarContent
+ '</div>\n'
+ '<script type="text/javascript">\n'
+ ' <!--\n'
+ ' var date = new Date();\n'
+ ' var dayCell= document.getElementById("y" + date.getFullYear() + "m" + (date.getMonth() + 1)+ "d" + date.getDate());\n'
+ ' if (dayCell) {\n'
+ ' dayCell.className = dayCell.className + " today";\n'
+ ' }\n'
+ ' //-->\n'
+ '<' + '/script>\n';
calendar = calendar.substring(0, calendar.indexOf("@@calendar@@")) + calendarContent + calendar.substring(calendar.indexOf("@@calendar@@") + "@@calendar@@".length);
}
return calendar;
};
var calendars = []
for (var i = 0; i < calendarTemplate["en"].length; i++) {
calendars[calendars.length] = makeCalendar("en", i, calendarTemplate["en"].length - 1);
}
for (var i = 0; i < calendarTemplate["fr"].length; i++) {
calendars[calendars.length] = makeCalendar("fr", i, calendarTemplate["fr"].length - 1);
}
var result = "";
for (var i = 0; i < calendars.length; i++) {
result += (i ? "\n" : "") + "{{{\n" + calendars[i] + "\n}}}";
}
result = store.tiddlerExists("CopyToClipboardPlugin") ? '<clip title="">' + result + '</clip>' : result;
resultPlace.parent().parent().children(".result").remove();
wikify(result, jQuery("<span class='result'></span>").appendTo(resultPlace.parent().parent())[0]);
if (jQuery('#freeBusyPublishOnServer').is(':checked') && store.getTiddler('FreeBusy Server') && store.getTiddler('FreeBusy Server Password') && (store.getTiddler('FreeBusy Server Password').text.length > 0)) {
var calendarFiles = [];
var calendarFileNames = [];
for (var i = 0; i < calendarTemplate["en-files"].length; i++) {
calendarFileNames[calendarFileNames.length] = calendarTemplate["en-files"][i];
}
for (var i = 0; i < calendarTemplate["fr-files"].length; i++) {
calendarFileNames[calendarFileNames.length] = calendarTemplate["fr-files"][i];
}
for (var i = 0; i < calendars.length; i++) {
calendarFiles[i] = {name: calendarFileNames[i], data: calendars[i]};
}
config.macros.serverupload.save(calendarFiles, store.getTiddler('FreeBusy Server').text, store.getTiddler('FreeBusy Server Password').text, function (result) {
displayMessage(result ? "The calendar was published successfully." : "The calendar could not be published. Check server details and directory permissions.");
});
}
}
};
resultPlace.text("Generating: " + Math.round(dayCounter / numberOfDays * 100) + "%");
resultPlace.data("timeoutID", setTimeout(function () { determineFreeBusy(resultPlace.attr("id")) }, 50));
}
</script>
:<html><label><input type="checkbox" id="freeBusyGenerateCalendarDetails" value="1" /> Include details</label></html>
/%
!instructions
# Place the PHP scripts found in the [[ServerUploadPlugin]] and [[index.php]] tiddlers on the server where the calendars are to be published. [[index.php]] is a convenience script allowing to redirect to the published HTML calendars.
# Place the [[.htaccess]] file in the folder where the Free/Busy and iCalendar calendars are to be uploaded.
# Configure the [[calendar|FreeBusy Calendar Info]] tiddler for Free/Busy and iCalendar publication, for example <html><pre>
{
freebusy: "../freebusy/YourName.vfb",
icalendar: "../freebusy/YourName.ics",
organizer: {name: "Your Name", email: "yourname@domain.tld"}
}
</pre></html>
# Configure the [[template|FreeBusy Calendar Template]] tiddler for HTML publication, for example <html><pre>
<!-- template begin: {lang: "en", file: "../cal/YourName.html", offset: "0"} -->
<html>
<head>
@@calendar-style@@
</head>
<body>
English &middot; <a onclick="this.href = this.href + '?t=' + (new Date()).getTime(); return true;" href="YourName_fr.html">Fran&ccedil;ais</a>
@@calendar@@
</body>
</html>
<!-- template end -->
<!-- template begin: {lang: "en", file: "../cal/YourName2.html", offset: "1"} -->
<html>
<head>
@@calendar-style@@
</head>
<body>
English &middot; <a onclick="this.href = this.href + '?t=' + (new Date()).getTime(); return true;" href="YourName2_fr.html">Fran&ccedil;ais</a>
@@calendar@@
</body>
</html>
<!-- template end -->
<!-- template begin: {lang: "fr", file: "../cal/YourName_fr.html", offset: "0"} -->
<html>
<head>
@@calendar-style@@
</head>
<body>
<a onclick="this.href = this.href + '?t=' + (new Date()).getTime(); return true;" href="YourName.html">English</a> &middot; Fran&ccedil;ais
@@calendar@@
</body>
</html>
<!-- template end -->
<!-- template begin: {lang: "fr", file: "../cal/YourName2_fr.html", offset: "1"} -->
<html>
<head>
@@calendar-style@@
</head>
<body>
<a onclick="this.href = this.href + '?t=' + (new Date()).getTime(); return true;" href="YourName2.html">English</a> &middot; Fran&ccedil;ais
@@calendar@@
</body>
</html>
<!-- template end -->
</pre></html>
# Place the URL of the [[ServerUploadPlugin]] PHP script into the [[server|FreeBusy Server]] tiddler.
# Place the password specified in the [[ServerUploadPlugin]] PHP script into the [[password|FreeBusy Server Password]] tiddler.
!instructions-end
%/ <script>
jQuery(place).closest("div.tiddler").find("a").each(function () {
var link = jQuery(this);
switch (link.text()) {
case "Generate Free/Busy":
link.attr("id", "freeBusyGenerateFreeBusy");
break;
case "Generate iCalendar":
link.attr("id", "freeBusyGenerateICalendar");
break;
case "Generate Calendar":
link.attr("id", "freeBusyGenerateCalendar");
break;
}
});
</script>
/***
|Name|FullCalendar|
|Source|https://github.com/fullcalendar/fullcalendar/releases/tag/v1.6.4|
|Documentation|https://github.com/fullcalendar/fullcalendar/tree/v1.6.4|
|Version|1.6.4|
|License|[[MIT|https://opensource.org/licenses/MIT]]|
|Description|Full-sized drag & drop event calendar|
***/
* [[init|##init]]
* [[reset.css|##reset.css]]
* [[fullcalendar.css|##fullcalendar.css]]
* [[fullcalendar.print.css|##fullcalendar.print.css]]
* [[fullcalendar.js|##fullcalendar.js]]
* [[jquery.ui.js|##jquery.ui.js]]
* [[jquery.ui.touch-punch.js|##jquery.ui.touch-punch.js]]
!Date formatting
* s - seconds
* ss - seconds, 2 digits
* m - minutes
* mm - minutes, 2 digits
* h - hours, 12-hour format
* hh - hours, 12-hour format, 2 digits
* H - hours, 24-hour format
* HH - hours, 24-hour format, 2 digits
* d - date number
* dd - date number, 2 digits
* ddd - date name, short
* dddd - date name, full
* M - month number
* MM - month number, 2 digits
* MMM - month name, short
* MMMM - month name, full
* yy - year, 2 digits
* yyyy - year, 4 digits
* t - 'a' or 'p'
* tt - 'am' or 'pm'
* T - 'A' or 'P'
* TT - 'AM' or 'PM'
* u - ISO8601 format
* S - 'st', 'nd', 'rd', 'th' for the date
* W - the ISO8601 week number
* '...' - literal text
* """''""" - single quote (represented by two single quotes)
* (...) - only displays format if one of the enclosed variables is non-zero
<html><div id='calendar' class='fullcalendar'></div></html>
http://arshaw.com/fullcalendar/docs/usage
<script>
eval(store.getTiddlerText('FullCalendar##init'));
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var calendar = jQuery('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
selectable: true,
selectHelper: true,
select: function(start, end, allDay) {
var title = prompt('Event Title:');
if (title) {
calendar.fullCalendar('renderEvent',
{
title: title,
start: start,
end: end,
allDay: allDay
},
true // make the event "stick"
);
}
calendar.fullCalendar('unselect');
},
editable: true,
events: [
{
title: 'All Day Event',
start: new Date(y, m, 1)
},
{
title: 'Long Event',
start: new Date(y, m, d-5),
end: new Date(y, m, d-2)
},
{
id: 999,
title: 'Repeating Event',
start: new Date(y, m, d-3, 16, 0),
allDay: false
},
{
id: 999,
title: 'Repeating Event',
start: new Date(y, m, d+4, 16, 0),
allDay: false
},
{
title: 'Meeting',
start: new Date(y, m, d, 10, 30),
allDay: false
},
{
title: 'Lunch',
start: new Date(y, m, d, 12, 0),
end: new Date(y, m, d, 14, 0),
allDay: false
},
{
title: 'Birthday Party',
start: new Date(y, m, d+1, 19, 0),
end: new Date(y, m, d+1, 22, 30),
allDay: false
},
{
title: 'Click for Google',
start: new Date(y, m, 28),
end: new Date(y, m, 29),
url: 'http://google.com/'
}
]
});
</script>
!init
//{{{
if (!jQuery.fullCalendar) {
setStylesheet(store.getTiddlerText('FullCalendar##reset.css.start') + '\n' + store.getTiddlerText('FullCalendar##fullcalendar.css.start') + '\n@media print {\n' + store.getTiddlerText('FullCalendar##fullcalendar.print.css.start') + '\n}', 'fullcalendar.css');
eval(store.getTiddlerText('FullCalendar##jquery.ui.core.js'));
eval(store.getTiddlerText('FullCalendar##jquery.ui.widget.js'));
eval(store.getTiddlerText('FullCalendar##jquery.ui.mouse.js').replace('.bind("click."+this.widgetName', '.bind("contextmenu."+this.widgetName, function(event) { that._mouseDown(event); return false; }).bind("click."+this.widgetName').replace('btnIsLeft = (event.which === 1)', 'btnIsLeft = true'));
eval(store.getTiddlerText('FullCalendar##jquery.ui.draggable.js'));
eval(store.getTiddlerText('FullCalendar##jquery.ui.resizable.js'));
eval(store.getTiddlerText('FullCalendar##jquery.ui.touch-punch.js'));
eval(store.getTiddlerText('FullCalendar##fullcalendar.js')
.replace("slotHeight = slotTable.find('tr:first').height() + 1;", "slotHeight = slotTable.find('tr:first').outerHeight() + (window.devicePixelRatio ? window.devicePixelRatio / 2.75 : 1);")
.replace("e.textColor = event.textColor;", "e.textColor = event.textColor;\ne.fontWeight = event.fontWeight;")
.replace("var statements = [];", "var fontWeight = event.fontWeight || source.fontWeight || opt('eventFontWeight');\nvar statements = [];\nif (fontWeight) { statements.push('font-weight:' + fontWeight); }"));
}
//}}}
!reset.css
//{{{
!reset.css.start
.fullcalendar {
width: 100%;
margin: 0 auto;
text-align: center;
font-size: 14px;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
}
.fullcalendar table {
margin: 0;
}
.fullcalendar th, .fullcalendar td {
border-color: #cccccc;
}
.fullcalendar .fc-agenda-allday td {
border: none;
}
.fullcalendar table, .fullcalendar tr {
border: none;
}
.fullcalendar .fc-header td {
border: none;
}
.fullcalendar th, .fullcalendar thead td {
background-color: #ffffff;
color: #000000;
}
.fullcalendar h2 {
border: none;
color: #000000;
}
.fullcalendar .fc-header-title h2 sub {
display: none;
}
!reset.css.end
//}}}
!fullcalendar.css
//{{{
!fullcalendar.css.start
/*!
* FullCalendar v1.6.4 Stylesheet
* Docs & License: http://arshaw.com/fullcalendar/
* (c) 2013 Adam Shaw
*/
.fc {
direction: ltr;
text-align: left;
}
.fc table {
border-collapse: collapse;
border-spacing: 0;
}
html .fc,
.fc table {
font-size: 1em;
}
.fc td,
.fc th {
padding: 0;
vertical-align: top;
}
/* Header
------------------------------------------------------------------------*/
.fc-header td {
white-space: nowrap;
}
.fc-header-left {
width: 25%;
text-align: left;
}
.fc-header-center {
text-align: center;
}
.fc-header-right {
width: 25%;
text-align: right;
}
.fc-header-title {
display: inline-block;
vertical-align: top;
}
.fc-header-title h2 {
margin-top: 0;
white-space: nowrap;
}
.fc .fc-header-space {
padding-left: 10px;
}
.fc-header .fc-button {
margin-bottom: 1em;
vertical-align: top;
}
/* buttons edges butting together */
.fc-header .fc-button {
margin-right: -1px;
}
.fc-header .fc-corner-right, /* non-theme */
.fc-header .ui-corner-right { /* theme */
margin-right: 0; /* back to normal */
}
/* button layering (for border precedence) */
.fc-header .fc-state-hover,
.fc-header .ui-state-hover {
z-index: 2;
}
.fc-header .fc-state-down {
z-index: 3;
}
.fc-header .fc-state-active,
.fc-header .ui-state-active {
z-index: 4;
}
/* Content
------------------------------------------------------------------------*/
.fc-content {
clear: both;
zoom: 1; /* for IE7, gives accurate coordinates for [un]freezeContentHeight */
}
.fc-view {
width: 100%;
overflow: hidden;
}
/* Cell Styles
------------------------------------------------------------------------*/
.fc-widget-header, /* <th>, usually */
.fc-widget-content { /* <td>, usually */
border: 1px solid #ddd;
}
.fc-state-highlight { /* <td> today cell */ /* TODO: add .fc-today to <th> */
background: #fcf8e3;
}
.fc-cell-overlay { /* semi-transparent rectangle while dragging */
background: #bce8f1;
opacity: .3;
filter: alpha(opacity=30); /* for IE */
}
/* Buttons
------------------------------------------------------------------------*/
.fc-button {
position: relative;
display: inline-block;
padding: 0 .6em;
overflow: hidden;
height: 1.9em;
line-height: 1.9em;
white-space: nowrap;
cursor: pointer;
}
.fc-state-default { /* non-theme */
border: 1px solid;
}
.fc-state-default.fc-corner-left { /* non-theme */
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.fc-state-default.fc-corner-right { /* non-theme */
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
/*
Our default prev/next buttons use HTML entities like ‹ › « »
and we'll try to make them look good cross-browser.
*/
.fc-text-arrow {
margin: 0 .1em;
font-size: 2em;
font-family: "Courier New", Courier, monospace;
vertical-align: baseline; /* for IE7 */
}
.fc-button-prev .fc-text-arrow,
.fc-button-next .fc-text-arrow { /* for ‹ › */
font-weight: bold;
}
/* icon (for jquery ui) */
.fc-button .fc-icon-wrap {
position: relative;
float: left;
top: 50%;
}
.fc-button .ui-icon {
position: relative;
float: left;
margin-top: -50%;
*margin-top: 0;
*top: -50%;
}
/*
button states
borrowed from twitter bootstrap (http://twitter.github.com/bootstrap/)
*/
.fc-state-default {
background-color: #f5f5f5;
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
background-repeat: repeat-x;
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
color: #333;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.fc-state-hover,
.fc-state-down,
.fc-state-active,
.fc-state-disabled {
color: #333333;
background-color: #e6e6e6;
}
.fc-state-hover {
color: #333333;
text-decoration: none;
background-position: 0 -15px;
-webkit-transition: background-position 0.1s linear;
-moz-transition: background-position 0.1s linear;
-o-transition: background-position 0.1s linear;
transition: background-position 0.1s linear;
}
.fc-state-down,
.fc-state-active {
background-color: #cccccc;
background-image: none;
outline: 0;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.fc-state-disabled {
cursor: default;
background-image: none;
opacity: 0.65;
filter: alpha(opacity=65);
box-shadow: none;
}
/* Global Event Styles
------------------------------------------------------------------------*/
.fc-event-container > * {
z-index: 8;
}
.fc-event-container > .ui-draggable-dragging,
.fc-event-container > .ui-resizable-resizing {
z-index: 9;
}
.fc-event {
border: 1px solid #3a87ad; /* default BORDER color */
background-color: #3a87ad; /* default BACKGROUND color */
color: #fff; /* default TEXT color */
font-size: .85em;
cursor: default;
}
a.fc-event {
text-decoration: none;
}
a.fc-event,
.fc-event-draggable {
cursor: pointer;
}
.fc-rtl .fc-event {
text-align: right;
}
.fc-event-inner {
width: 100%;
height: 100%;
overflow: hidden;
}
.fc-event-time,
.fc-event-title {
padding: 0 1px;
}
.fc .ui-resizable-handle {
display: block;
position: absolute;
z-index: 99999;
overflow: hidden; /* hacky spaces (IE6/7) */
font-size: 300%; /* */
line-height: 50%; /* */
}
/* Horizontal Events
------------------------------------------------------------------------*/
.fc-event-hori {
border-width: 1px 0;
margin-bottom: 1px;
}
.fc-ltr .fc-event-hori.fc-event-start,
.fc-rtl .fc-event-hori.fc-event-end {
border-left-width: 1px;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
.fc-ltr .fc-event-hori.fc-event-end,
.fc-rtl .fc-event-hori.fc-event-start {
border-right-width: 1px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
/* resizable */
.fc-event-hori .ui-resizable-e {
top: 0 !important; /* importants override pre jquery ui 1.7 styles */
right: -3px !important;
width: 7px !important;
height: 100% !important;
cursor: e-resize;
}
.fc-event-hori .ui-resizable-w {
top: 0 !important;
left: -3px !important;
width: 7px !important;
height: 100% !important;
cursor: w-resize;
}
.fc-event-hori .ui-resizable-handle {
_padding-bottom: 14px; /* IE6 had 0 height */
}
/* Reusable Separate-border Table
------------------------------------------------------------*/
table.fc-border-separate {
border-collapse: separate;
}
.fc-border-separate th,
.fc-border-separate td {
border-width: 1px 0 0 1px;
}
.fc-border-separate th.fc-last,
.fc-border-separate td.fc-last {
border-right-width: 1px;
}
.fc-border-separate tr.fc-last th,
.fc-border-separate tr.fc-last td {
border-bottom-width: 1px;
}
.fc-border-separate tbody tr.fc-first td,
.fc-border-separate tbody tr.fc-first th {
border-top-width: 0;
}
/* Month View, Basic Week View, Basic Day View
------------------------------------------------------------------------*/
.fc-grid th {
text-align: center;
}
.fc .fc-week-number {
width: 22px;
text-align: center;
}
.fc .fc-week-number div {
padding: 0 2px;
}
.fc-grid .fc-day-number {
float: right;
padding: 0 2px;
}
.fc-grid .fc-other-month .fc-day-number {
opacity: 0.3;
filter: alpha(opacity=30); /* for IE */
/* opacity with small font can sometimes look too faded
might want to set the 'color' property instead
making day-numbers bold also fixes the problem */
}
.fc-grid .fc-day-content {
clear: both;
padding: 2px 2px 1px; /* distance between events and day edges */
}
/* event styles */
.fc-grid .fc-event-time {
font-weight: bold;
}
/* right-to-left */
.fc-rtl .fc-grid .fc-day-number {
float: left;
}
.fc-rtl .fc-grid .fc-event-time {
float: right;
}
/* Agenda Week View, Agenda Day View
------------------------------------------------------------------------*/
.fc-agenda table {
border-collapse: separate;
}
.fc-agenda-days th {
text-align: center;
}
.fc-agenda .fc-agenda-axis {
width: 50px;
padding: 0 4px;
vertical-align: middle;
text-align: right;
white-space: nowrap;
font-weight: normal;
}
.fc-agenda .fc-week-number {
font-weight: bold;
}
.fc-agenda .fc-day-content {
padding: 2px 2px 1px;
}
/* make axis border take precedence */
.fc-agenda-days .fc-agenda-axis {
border-right-width: 1px;
}
.fc-agenda-days .fc-col0 {
border-left-width: 0;
}
/* all-day area */
.fc-agenda-allday th {
border-width: 0 1px;
}
.fc-agenda-allday .fc-day-content {
min-height: 34px; /* TODO: doesnt work well in quirksmode */
_height: 34px;
}
/* divider (between all-day and slots) */
.fc-agenda-divider-inner {
height: 2px;
overflow: hidden;
}
.fc-widget-header .fc-agenda-divider-inner {
background: #eee;
}
/* slot rows */
.fc-agenda-slots th {
border-width: 1px 1px 0;
}
.fc-agenda-slots td {
border-width: 1px 0 0;
background: none;
}
.fc-agenda-slots td div {
height: 20px;
}
.fc-agenda-slots tr.fc-slot0 th,
.fc-agenda-slots tr.fc-slot0 td {
border-top-width: 0;
}
.fc-agenda-slots tr.fc-minor th,
.fc-agenda-slots tr.fc-minor td {
border-top-style: dotted;
}
.fc-agenda-slots tr.fc-minor th.ui-widget-header {
*border-top-style: solid; /* doesn't work with background in IE6/7 */
}
/* Vertical Events
------------------------------------------------------------------------*/
.fc-event-vert {
border-width: 0 1px;
}
.fc-event-vert.fc-event-start {
border-top-width: 1px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
.fc-event-vert.fc-event-end {
border-bottom-width: 1px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
}
.fc-event-vert .fc-event-time {
white-space: nowrap;
font-size: 10px;
}
.fc-event-vert .fc-event-inner {
position: relative;
z-index: 2;
}
.fc-event-vert .fc-event-bg { /* makes the event lighter w/ a semi-transparent overlay */
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
opacity: .25;
filter: alpha(opacity=25);
}
.fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */
.fc-select-helper .fc-event-bg {
display: none\9; /* for IE6/7/8. nested opacity filters while dragging don't work */
}
/* resizable */
.fc-event-vert .ui-resizable-s {
bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */
width: 100% !important;
height: 8px !important;
overflow: hidden !important;
line-height: 8px !important;
font-size: 11px !important;
font-family: monospace;
text-align: center;
cursor: s-resize;
}
.fc-agenda .ui-resizable-resizing { /* TODO: better selector */
_overflow: hidden;
}
!fullcalendar.css.end
//}}}
!fullcalendar.print.css
//{{{
!fullcalendar.print.css.start
/*!
* FullCalendar v1.6.4 Print Stylesheet
* Docs & License: http://arshaw.com/fullcalendar/
* (c) 2013 Adam Shaw
*/
/*
* Include this stylesheet on your page to get a more printer-friendly calendar.
* When including this stylesheet, use the media='print' attribute of the <link> tag.
* Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
*/
/* Events
-----------------------------------------------------*/
.fc-event {
background: #fff !important;
color: #000 !important;
}
/* for vertical events */
.fc-event-bg {
display: none !important;
}
.fc-event .ui-resizable-handle {
display: none !important;
}
!fullcalendar.print.css.end
//}}}
!fullcalendar.js
//{{{
/*!
* FullCalendar v1.6.4
* Docs & License: http://arshaw.com/fullcalendar/
* (c) 2013 Adam Shaw
*/
/*
* Use fullcalendar.css for basic styling.
* For event drag & drop, requires jQuery UI draggable.
* For event resizing, requires jQuery UI resizable.
*/
(function($, undefined) {
;;
var defaults = {
// display
defaultView: 'month',
aspectRatio: 1.35,
header: {
left: 'title',
center: '',
right: 'today prev,next'
},
weekends: true,
weekNumbers: false,
weekNumberCalculation: 'iso',
weekNumberTitle: 'W',
// editing
//editable: false,
//disableDragging: false,
//disableResizing: false,
allDayDefault: true,
ignoreTimezone: true,
// event ajax
lazyFetching: true,
startParam: 'start',
endParam: 'end',
// time formats
titleFormat: {
month: 'MMMM yyyy',
week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}",
day: 'dddd, MMM d, yyyy'
},
columnFormat: {
month: 'ddd',
week: 'ddd M/d',
day: 'dddd M/d'
},
timeFormat: { // for event elements
'': 'h(:mm)t' // default
},
// locale
isRTL: false,
firstDay: 0,
monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
buttonText: {
prev: "<span class='fc-text-arrow'>‹</span>",
next: "<span class='fc-text-arrow'>›</span>",
prevYear: "<span class='fc-text-arrow'>«</span>",
nextYear: "<span class='fc-text-arrow'>»</span>",
today: 'today',
month: 'month',
week: 'week',
day: 'day'
},
// jquery-ui theming
theme: false,
buttonIcons: {
prev: 'circle-triangle-w',
next: 'circle-triangle-e'
},
//selectable: false,
unselectAuto: true,
dropAccept: '*',
handleWindowResize: true
};
// right-to-left defaults
var rtlDefaults = {
header: {
left: 'next,prev today',
center: '',
right: 'title'
},
buttonText: {
prev: "<span class='fc-text-arrow'>›</span>",
next: "<span class='fc-text-arrow'>‹</span>",
prevYear: "<span class='fc-text-arrow'>»</span>",
nextYear: "<span class='fc-text-arrow'>«</span>"
},
buttonIcons: {
prev: 'circle-triangle-e',
next: 'circle-triangle-w'
}
};
;;
var fc = $.fullCalendar = { version: "1.6.4" };
var fcViews = fc.views = {};
$.fn.fullCalendar = function(options) {
// method calling
if (typeof options == 'string') {
var args = Array.prototype.slice.call(arguments, 1);
var res;
this.each(function() {
var calendar = $.data(this, 'fullCalendar');
if (calendar && $.isFunction(calendar[options])) {
var r = calendar[options].apply(calendar, args);
if (res === undefined) {
res = r;
}
if (options == 'destroy') {
$.removeData(this, 'fullCalendar');
}
}
});
if (res !== undefined) {
return res;
}
return this;
}
options = options || {};
// would like to have this logic in EventManager, but needs to happen before options are recursively extended
var eventSources = options.eventSources || [];
delete options.eventSources;
if (options.events) {
eventSources.push(options.events);
delete options.events;
}
options = $.extend(true, {},
defaults,
(options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
options
);
this.each(function(i, _element) {
var element = $(_element);
var calendar = new Calendar(element, options, eventSources);
element.data('fullCalendar', calendar); // TODO: look into memory leak implications
calendar.render();
});
return this;
};
// function for adding/overriding defaults
function setDefaults(d) {
$.extend(true, defaults, d);
}
;;
function Calendar(element, options, eventSources) {
var t = this;
// exports
t.options = options;
t.render = render;
t.destroy = destroy;
t.refetchEvents = refetchEvents;
t.reportEvents = reportEvents;
t.reportEventChange = reportEventChange;
t.rerenderEvents = rerenderEvents;
t.changeView = changeView;
t.select = select;
t.unselect = unselect;
t.prev = prev;
t.next = next;
t.prevYear = prevYear;
t.nextYear = nextYear;
t.today = today;
t.gotoDate = gotoDate;
t.incrementDate = incrementDate;
t.formatDate = function(format, date) { return formatDate(format, date, options) };
t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) };
t.getDate = getDate;
t.getView = getView;
t.option = option;
t.trigger = trigger;
// imports
EventManager.call(t, options, eventSources);
var isFetchNeeded = t.isFetchNeeded;
var fetchEvents = t.fetchEvents;
// locals
var _element = element[0];
var header;
var headerElement;
var content;
var tm; // for making theme classes
var currentView;
var elementOuterWidth;
var suggestedViewHeight;
var resizeUID = 0;
var ignoreWindowResize = 0;
var date = new Date();
var events = [];
var _dragElement;
/* Main Rendering
-----------------------------------------------------------------------------*/
setYMD(date, options.year, options.month, options.date);
function render(inc) {
if (!content) {
initialRender();
}
else if (elementVisible()) {
// mainly for the public API
calcSize();
_renderView(inc);
}
}
function initialRender() {
tm = options.theme ? 'ui' : 'fc';
element.addClass('fc');
if (options.isRTL) {
element.addClass('fc-rtl');
}
else {
element.addClass('fc-ltr');
}
if (options.theme) {
element.addClass('ui-widget');
}
content = $("<div class='fc-content' style='position:relative'/>")
.prependTo(element);
header = new Header(t, options);
headerElement = header.render();
if (headerElement) {
element.prepend(headerElement);
}
changeView(options.defaultView);
if (options.handleWindowResize) {
$(window).resize(windowResize);
}
// needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
if (!bodyVisible()) {
lateRender();
}
}
// called when we know the calendar couldn't be rendered when it was initialized,
// but we think it's ready now
function lateRender() {
setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
renderView();
}
},0);
}
function destroy() {
if (currentView) {
trigger('viewDestroy', currentView, currentView, currentView.element);
currentView.triggerEventDestroy();
}
$(window).unbind('resize', windowResize);
header.destroy();
content.remove();
element.removeClass('fc fc-rtl ui-widget');
}
function elementVisible() {
return element.is(':visible');
}
function bodyVisible() {
return $('body').is(':visible');
}
/* View Rendering
-----------------------------------------------------------------------------*/
function changeView(newViewName) {
if (!currentView || newViewName != currentView.name) {
_changeView(newViewName);
}
}
function _changeView(newViewName) {
ignoreWindowResize++;
if (currentView) {
trigger('viewDestroy', currentView, currentView, currentView.element);
unselect();
currentView.triggerEventDestroy(); // trigger 'eventDestroy' for each event
freezeContentHeight();
currentView.element.remove();
header.deactivateButton(currentView.name);
}
header.activateButton(newViewName);
currentView = new fcViews[newViewName](
$("<div class='fc-view fc-view-" + newViewName + "' style='position:relative'/>")
.appendTo(content),
t // the calendar object
);
renderView();
unfreezeContentHeight();
ignoreWindowResize--;
}
function renderView(inc) {
if (
!currentView.start || // never rendered before
inc || date < currentView.start || date >= currentView.end // or new date range
) {
if (elementVisible()) {
_renderView(inc);
}
}
}
function _renderView(inc) { // assumes elementVisible
ignoreWindowResize++;
if (currentView.start) { // already been rendered?
trigger('viewDestroy', currentView, currentView, currentView.element);
unselect();
clearEvents();
}
freezeContentHeight();
currentView.render(date, inc || 0); // the view's render method ONLY renders the skeleton, nothing else
setSize();
unfreezeContentHeight();
(currentView.afterRender || noop)();
updateTitle();
updateTodayButton();
trigger('viewRender', currentView, currentView, currentView.element);
currentView.trigger('viewDisplay', _element); // deprecated
ignoreWindowResize--;
getAndRenderEvents();
}
/* Resizing
-----------------------------------------------------------------------------*/
function updateSize() {
if (elementVisible()) {
unselect();
clearEvents();
calcSize();
setSize();
renderEvents();
}
}
function calcSize() { // assumes elementVisible
if (options.contentHeight) {
suggestedViewHeight = options.contentHeight;
}
else if (options.height) {
suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
}
else {
suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
}
}
function setSize() { // assumes elementVisible
if (suggestedViewHeight === undefined) {
calcSize(); // for first time
// NOTE: we don't want to recalculate on every renderView because
// it could result in oscillating heights due to scrollbars.
}
ignoreWindowResize++;
currentView.setHeight(suggestedViewHeight);
currentView.setWidth(content.width());
ignoreWindowResize--;
elementOuterWidth = element.outerWidth();
}
function windowResize() {
if (!ignoreWindowResize) {
if (currentView.start) { // view has already been rendered
var uid = ++resizeUID;
setTimeout(function() { // add a delay
if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
ignoreWindowResize++; // in case the windowResize callback changes the height
updateSize();
currentView.trigger('windowResize', _element);
ignoreWindowResize--;
}
}
}, 200);
}else{
// calendar must have been initialized in a 0x0 iframe that has just been resized
lateRender();
}
}
}
/* Event Fetching/Rendering
-----------------------------------------------------------------------------*/
// TODO: going forward, most of this stuff should be directly handled by the view
function refetchEvents() { // can be called as an API method
clearEvents();
fetchAndRenderEvents();
}
function rerenderEvents(modifiedEventID) { // can be called as an API method
clearEvents();
renderEvents(modifiedEventID);
}
function renderEvents(modifiedEventID) { // TODO: remove modifiedEventID hack
if (elementVisible()) {
currentView.setEventData(events); // for View.js, TODO: unify with renderEvents
currentView.renderEvents(events, modifiedEventID); // actually render the DOM elements
currentView.trigger('eventAfterAllRender');
}
}
function clearEvents() {
currentView.triggerEventDestroy(); // trigger 'eventDestroy' for each event
currentView.clearEvents(); // actually remove the DOM elements
currentView.clearEventData(); // for View.js, TODO: unify with clearEvents
}
function getAndRenderEvents() {
if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
fetchAndRenderEvents();
}
else {
renderEvents();
}
}
function fetchAndRenderEvents() {
fetchEvents(currentView.visStart, currentView.visEnd);
// ... will call reportEvents
// ... which will call renderEvents
}
// called when event data arrives
function reportEvents(_events) {
events = _events;
renderEvents();
}
// called when a single event's data has been changed
function reportEventChange(eventID) {
rerenderEvents(eventID);
}
/* Header Updating
-----------------------------------------------------------------------------*/
function updateTitle() {
header.updateTitle(currentView.title);
}
function updateTodayButton() {
var today = new Date();
if (today >= currentView.start && today < currentView.end) {
header.disableButton('today');
}
else {
header.enableButton('today');
}
}
/* Selection
-----------------------------------------------------------------------------*/
function select(start, end, allDay) {
currentView.select(start, end, allDay===undefined ? true : allDay);
}
function unselect() { // safe to be called before renderView
if (currentView) {
currentView.unselect();
}
}
/* Date
-----------------------------------------------------------------------------*/
function prev() {
renderView(-1);
}
function next() {
renderView(1);
}
function prevYear() {
addYears(date, -1);
renderView();
}
function nextYear() {
addYears(date, 1);
renderView();
}
function today() {
date = new Date();
renderView();
}
function gotoDate(year, month, dateOfMonth) {
if (year instanceof Date) {
date = cloneDate(year); // provided 1 argument, a Date
}else{
setYMD(date, year, month, dateOfMonth);
}
renderView();
}
function incrementDate(years, months, days) {
if (years !== undefined) {
addYears(date, years);
}
if (months !== undefined) {
addMonths(date, months);
}
if (days !== undefined) {
addDays(date, days);
}
renderView();
}
function getDate() {
return cloneDate(date);
}
/* Height "Freezing"
-----------------------------------------------------------------------------*/
function freezeContentHeight() {
content.css({
width: '100%',
height: content.height(),
overflow: 'hidden'
});
}
function unfreezeContentHeight() {
content.css({
width: '',
height: '',
overflow: ''
});
}
/* Misc
-----------------------------------------------------------------------------*/
function getView() {
return currentView;
}
function option(name, value) {
if (value === undefined) {
return options[name];
}
if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
options[name] = value;
updateSize();
}
}
function trigger(name, thisObj) {
if (options[name]) {
return options[name].apply(
thisObj || _element,
Array.prototype.slice.call(arguments, 2)
);
}
}
/* External Dragging
------------------------------------------------------------------------*/
if (options.droppable) {
$(document)
.bind('dragstart', function(ev, ui) {
var _e = ev.target;
var e = $(_e);
if (!e.parents('.fc').length) { // not already inside a calendar
var accept = options.dropAccept;
if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
_dragElement = _e;
currentView.dragStart(_dragElement, ev, ui);
}
}
})
.bind('dragstop', function(ev, ui) {
if (_dragElement) {
currentView.dragStop(_dragElement, ev, ui);
_dragElement = null;
}
});
}
}
;;
function Header(calendar, options) {
var t = this;
// exports
t.render = render;
t.destroy = destroy;
t.updateTitle = updateTitle;
t.activateButton = activateButton;
t.deactivateButton = deactivateButton;
t.disableButton = disableButton;
t.enableButton = enableButton;
// locals
var element = $([]);
var tm;
function render() {
tm = options.theme ? 'ui' : 'fc';
var sections = options.header;
if (sections) {
element = $("<table class='fc-header' style='width:100%'/>")
.append(
$("<tr/>")
.append(renderSection('left'))
.append(renderSection('center'))
.append(renderSection('right'))
);
return element;
}
}
function destroy() {
element.remove();
}
function renderSection(position) {
var e = $("<td class='fc-header-" + position + "'/>");
var buttonStr = options.header[position];
if (buttonStr) {
$.each(buttonStr.split(' '), function(i) {
if (i > 0) {
e.append("<span class='fc-header-space'/>");
}
var prevButton;
$.each(this.split(','), function(j, buttonName) {
if (buttonName == 'title') {
e.append("<span class='fc-header-title'><h2> </h2></span>");
if (prevButton) {
prevButton.addClass(tm + '-corner-right');
}
prevButton = null;
}else{
var buttonClick;
if (calendar[buttonName]) {
buttonClick = calendar[buttonName]; // calendar method
}
else if (fcViews[buttonName]) {
buttonClick = function() {
button.removeClass(tm + '-state-hover'); // forget why
calendar.changeView(buttonName);
};
}
if (buttonClick) {
var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
var button = $(
"<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +
(icon ?
"<span class='fc-icon-wrap'>" +
"<span class='ui-icon ui-icon-" + icon + "'/>" +
"</span>" :
text
) +
"</span>"
)
.click(function() {
if (!button.hasClass(tm + '-state-disabled')) {
buttonClick();
}
})
.mousedown(function() {
button
.not('.' + tm + '-state-active')
.not('.' + tm + '-state-disabled')
.addClass(tm + '-state-down');
})
.mouseup(function() {
button.removeClass(tm + '-state-down');
})
.hover(
function() {
button
.not('.' + tm + '-state-active')
.not('.' + tm + '-state-disabled')
.addClass(tm + '-state-hover');
},
function() {
button
.removeClass(tm + '-state-hover')
.removeClass(tm + '-state-down');
}
)
.appendTo(e);
disableTextSelection(button);
if (!prevButton) {
button.addClass(tm + '-corner-left');
}
prevButton = button;
}
}
});
if (prevButton) {
prevButton.addClass(tm + '-corner-right');
}
});
}
return e;
}
function updateTitle(html) {
element.find('h2')
.html(html);
}
function activateButton(buttonName) {
element.find('span.fc-button-' + buttonName)
.addClass(tm + '-state-active');
}
function deactivateButton(buttonName) {
element.find('span.fc-button-' + buttonName)
.removeClass(tm + '-state-active');
}
function disableButton(buttonName) {
element.find('span.fc-button-' + buttonName)
.addClass(tm + '-state-disabled');
}
function enableButton(buttonName) {
element.find('span.fc-button-' + buttonName)
.removeClass(tm + '-state-disabled');
}
}
;;
fc.sourceNormalizers = [];
fc.sourceFetchers = [];
var ajaxDefaults = {
dataType: 'json',
cache: false
};
var eventGUID = 1;
function EventManager(options, _sources) {
var t = this;
// exports
t.isFetchNeeded = isFetchNeeded;
t.fetchEvents = fetchEvents;
t.addEventSource = addEventSource;
t.removeEventSource = removeEventSource;
t.updateEvent = updateEvent;
t.renderEvent = renderEvent;
t.removeEvents = removeEvents;
t.clientEvents = clientEvents;
t.normalizeEvent = normalizeEvent;
// imports
var trigger = t.trigger;
var getView = t.getView;
var reportEvents = t.reportEvents;
// locals
var stickySource = { events: [] };
var sources = [ stickySource ];
var rangeStart, rangeEnd;
var currentFetchID = 0;
var pendingSourceCnt = 0;
var loadingLevel = 0;
var cache = [];
for (var i=0; i<_sources.length; i++) {
_addEventSource(_sources[i]);
}
/* Fetching
-----------------------------------------------------------------------------*/
function isFetchNeeded(start, end) {
return !rangeStart || start < rangeStart || end > rangeEnd;
}
function fetchEvents(start, end) {
rangeStart = start;
rangeEnd = end;
cache = [];
var fetchID = ++currentFetchID;
var len = sources.length;
pendingSourceCnt = len;
for (var i=0; i<len; i++) {
fetchEventSource(sources[i], fetchID);
}
}
function fetchEventSource(source, fetchID) {
_fetchEventSource(source, function(events) {
if (fetchID == currentFetchID) {
if (events) {
if (options.eventDataTransform) {
events = $.map(events, options.eventDataTransform);
}
if (source.eventDataTransform) {
events = $.map(events, source.eventDataTransform);
}
// TODO: this technique is not ideal for static array event sources.
// For arrays, we'll want to process all events right in the beginning, then never again.
for (var i=0; i<events.length; i++) {
events[i].source = source;
normalizeEvent(events[i]);
}
cache = cache.concat(events);
}
pendingSourceCnt--;
if (!pendingSourceCnt) {
reportEvents(cache);
}
}
});
}
function _fetchEventSource(source, callback) {
var i;
var fetchers = fc.sourceFetchers;
var res;
for (i=0; i<fetchers.length; i++) {
res = fetchers[i](source, rangeStart, rangeEnd, callback);
if (res === true) {
// the fetcher is in charge. made its own async request
return;
}
else if (typeof res == 'object') {
// the fetcher returned a new source. process it
_fetchEventSource(res, callback);
return;
}
}
var events = source.events;
if (events) {
if ($.isFunction(events)) {
pushLoading();
events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
callback(events);
popLoading();
});
}
else if ($.isArray(events)) {
callback(events);
}
else {
callback();
}
}else{
var url = source.url;
if (url) {
var success = source.success;
var error = source.error;
var complete = source.complete;
// retrieve any outbound GET/POST $.ajax data from the options
var customData;
if ($.isFunction(source.data)) {
// supplied as a function that returns a key/value object
customData = source.data();
}
else {
// supplied as a straight key/value object
customData = source.data;
}
// use a copy of the custom data so we can modify the parameters
// and not affect the passed-in object.
var data = $.extend({}, customData || {});
var startParam = firstDefined(source.startParam, options.startParam);
var endParam = firstDefined(source.endParam, options.endParam);
if (startParam) {
data[startParam] = Math.round(+rangeStart / 1000);
}
if (endParam) {
data[endParam] = Math.round(+rangeEnd / 1000);
}
pushLoading();
$.ajax($.extend({}, ajaxDefaults, source, {
data: data,
success: function(events) {
events = events || [];
var res = applyAll(success, this, arguments);
if ($.isArray(res)) {
events = res;
}
callback(events);
},
error: function() {
applyAll(error, this, arguments);
callback();
},
complete: function() {
applyAll(complete, this, arguments);
popLoading();
}
}));
}else{
callback();
}
}
}
/* Sources
-----------------------------------------------------------------------------*/
function addEventSource(source) {
source = _addEventSource(source);
if (source) {
pendingSourceCnt++;
fetchEventSource(source, currentFetchID); // will eventually call reportEvents
}
}
function _addEventSource(source) {
if ($.isFunction(source) || $.isArray(source)) {
source = { events: source };
}
else if (typeof source == 'string') {
source = { url: source };
}
if (typeof source == 'object') {
normalizeSource(source);
sources.push(source);
return source;
}
}
function removeEventSource(source) {
sources = $.grep(sources, function(src) {
return !isSourcesEqual(src, source);
});
// remove all client events from that source
cache = $.grep(cache, function(e) {
return !isSourcesEqual(e.source, source);
});
reportEvents(cache);
}
/* Manipulation
-----------------------------------------------------------------------------*/
function updateEvent(event) { // update an existing event
var i, len = cache.length, e,
defaultEventEnd = getView().defaultEventEnd, // getView???
startDelta = event.start - event._start,
endDelta = event.end ?
(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
: 0; // was null and event was just resized
for (i=0; i<len; i++) {
e = cache[i];
if (e._id == event._id && e != event) {
e.start = new Date(+e.start + startDelta);
if (event.end) {
if (e.end) {
e.end = new Date(+e.end + endDelta);
}else{
e.end = new Date(+defaultEventEnd(e) + endDelta);
}
}else{
e.end = null;
}
e.title = event.title;
e.url = event.url;
e.allDay = event.allDay;
e.className = event.className;
e.editable = event.editable;
e.color = event.color;
e.backgroundColor = event.backgroundColor;
e.borderColor = event.borderColor;
e.textColor = event.textColor;
normalizeEvent(e);
}
}
normalizeEvent(event);
reportEvents(cache);
}
function renderEvent(event, stick) {
normalizeEvent(event);
if (!event.source) {
if (stick) {
stickySource.events.push(event);
event.source = stickySource;
}
cache.push(event);
}
reportEvents(cache);
}
function removeEvents(filter) {
if (!filter) { // remove all
cache = [];
// clear all array sources
for (var i=0; i<sources.length; i++) {
if ($.isArray(sources[i].events)) {
sources[i].events = [];
}
}
}else{
if (!$.isFunction(filter)) { // an event ID
var id = filter + '';
filter = function(e) {
return e._id == id;
};
}
cache = $.grep(cache, filter, true);
// remove events from array sources
for (var i=0; i<sources.length; i++) {
if ($.isArray(sources[i].events)) {
sources[i].events = $.grep(sources[i].events, filter, true);
}
}
}
reportEvents(cache);
}
function clientEvents(filter) {
if ($.isFunction(filter)) {
return $.grep(cache, filter);
}
else if (filter) { // an event ID
filter += '';
return $.grep(cache, function(e) {
return e._id == filter;
});
}
return cache; // else, return all
}
/* Loading State
-----------------------------------------------------------------------------*/
function pushLoading() {
if (!loadingLevel++) {
trigger('loading', null, true, getView());
}
}
function popLoading() {
if (!--loadingLevel) {
trigger('loading', null, false, getView());
}
}
/* Event Normalization
-----------------------------------------------------------------------------*/
function normalizeEvent(event) {
var source = event.source || {};
var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
if (event.date) {
if (!event.start) {
event.start = event.date;
}
delete event.date;
}
event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
event.end = parseDate(event.end, ignoreTimezone);
if (event.end && event.end <= event.start) {
event.end = null;
}
event._end = event.end ? cloneDate(event.end) : null;
if (event.allDay === undefined) {
event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
}
if (event.className) {
if (typeof event.className == 'string') {
event.className = event.className.split(/\s+/);
}
}else{
event.className = [];
}
// TODO: if there is no start date, return false to indicate an invalid event
}
/* Utils
------------------------------------------------------------------------------*/
function normalizeSource(source) {
if (source.className) {
// TODO: repeat code, same code for event classNames
if (typeof source.className == 'string') {
source.className = source.className.split(/\s+/);
}
}else{
source.className = [];
}
var normalizers = fc.sourceNormalizers;
for (var i=0; i<normalizers.length; i++) {
normalizers[i](source);
}
}
function isSourcesEqual(source1, source2) {
return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
}
function getSourcePrimitive(source) {
return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
}
}
;;
fc.addDays = addDays;
fc.cloneDate = cloneDate;
fc.parseDate = parseDate;
fc.parseISO8601 = parseISO8601;
fc.parseTime = parseTime;
fc.formatDate = formatDate;
fc.formatDates = formatDates;
/* Date Math
-----------------------------------------------------------------------------*/
var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
DAY_MS = 86400000,
HOUR_MS = 3600000,
MINUTE_MS = 60000;
function addYears(d, n, keepTime) {
d.setFullYear(d.getFullYear() + n);
if (!keepTime) {
clearTime(d);
}
return d;
}
function addMonths(d, n, keepTime) { // prevents day overflow/underflow
if (+d) { // prevent infinite looping on invalid dates
var m = d.getMonth() + n,
check = cloneDate(d);
check.setDate(1);
check.setMonth(m);
d.setMonth(m);
if (!keepTime) {
clearTime(d);
}
while (d.getMonth() != check.getMonth()) {
d.setDate(d.getDate() + (d < check ? 1 : -1));
}
}
return d;
}
function addDays(d, n, keepTime) { // deals with daylight savings
if (+d) {
var dd = d.getDate() + n,
check = cloneDate(d);
check.setHours(9); // set to middle of day
check.setDate(dd);
d.setDate(dd);
if (!keepTime) {
clearTime(d);
}
fixDate(d, check);
}
return d;
}
function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
if (+d) { // prevent infinite looping on invalid dates
while (d.getDate() != check.getDate()) {
d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
}
}
}
function addMinutes(d, n) {
d.setMinutes(d.getMinutes() + n);
return d;
}
function clearTime(d) {
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
return d;
}
function cloneDate(d, dontKeepTime) {
if (dontKeepTime) {
return clearTime(new Date(+d));
}
return new Date(+d);
}
function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
var i=0, d;
do {
d = new Date(1970, i++, 1);
} while (d.getHours()); // != 0
return d;
}
function dayDiff(d1, d2) { // d1 - d2
return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
}
function setYMD(date, y, m, d) {
if (y !== undefined && y != date.getFullYear()) {
date.setDate(1);
date.setMonth(0);
date.setFullYear(y);
}
if (m !== undefined && m != date.getMonth()) {
date.setDate(1);
date.setMonth(m);
}
if (d !== undefined) {
date.setDate(d);
}
}
/* Date Parsing
-----------------------------------------------------------------------------*/
function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
if (typeof s == 'object') { // already a Date object
return s;
}
if (typeof s == 'number') { // a UNIX timestamp
return new Date(s * 1000);
}
if (typeof s == 'string') {
if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
return new Date(parseFloat(s) * 1000);
}
if (ignoreTimezone === undefined) {
ignoreTimezone = true;
}
return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
}
// TODO: never return invalid dates (like from new Date(<string>)), return null instead
return null;
}
function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
// derived from http://delete.me.uk/2005/03/iso8601.html
// TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
if (!m) {
return null;
}
var date = new Date(m[1], 0, 1);
if (ignoreTimezone || !m[13]) {
var check = new Date(m[1], 0, 1, 9, 0);
if (m[3]) {
date.setMonth(m[3] - 1);
check.setMonth(m[3] - 1);
}
if (m[5]) {
date.setDate(m[5]);
check.setDate(m[5]);
}
fixDate(date, check);
if (m[7]) {
date.setHours(m[7]);
}
if (m[8]) {
date.setMinutes(m[8]);
}
if (m[10]) {
date.setSeconds(m[10]);
}
if (m[12]) {
date.setMilliseconds(Number("0." + m[12]) * 1000);
}
fixDate(date, check);
}else{
date.setUTCFullYear(
m[1],
m[3] ? m[3] - 1 : 0,
m[5] || 1
);
date.setUTCHours(
m[7] || 0,
m[8] || 0,
m[10] || 0,
m[12] ? Number("0." + m[12]) * 1000 : 0
);
if (m[14]) {
var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
offset *= m[15] == '-' ? 1 : -1;
date = new Date(+date + (offset * 60 * 1000));
}
}
return date;
}
function parseTime(s) { // returns minutes since start of day
if (typeof s == 'number') { // an hour
return s * 60;
}
if (typeof s == 'object') { // a Date object
return s.getHours() * 60 + s.getMinutes();
}
var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
if (m) {
var h = parseInt(m[1], 10);
if (m[3]) {
h %= 12;
if (m[3].toLowerCase().charAt(0) == 'p') {
h += 12;
}
}
return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
}
}
/* Date Formatting
-----------------------------------------------------------------------------*/
// TODO: use same function formatDate(date, [date2], format, [options])
function formatDate(date, format, options) {
return formatDates(date, null, format, options);
}
function formatDates(date1, date2, format, options) {
options = options || defaults;
var date = date1,
otherDate = date2,
i, len = format.length, c,
i2, formatter,
res = '';
for (i=0; i<len; i++) {
c = format.charAt(i);
if (c == "'") {
for (i2=i+1; i2<len; i2++) {
if (format.charAt(i2) == "'") {
if (date) {
if (i2 == i+1) {
res += "'";
}else{
res += format.substring(i+1, i2);
}
i = i2;
}
break;
}
}
}
else if (c == '(') {
for (i2=i+1; i2<len; i2++) {
if (format.charAt(i2) == ')') {
var subres = formatDate(date, format.substring(i+1, i2), options);
if (parseInt(subres.replace(/\D/, ''), 10)) {
res += subres;
}
i = i2;
break;
}
}
}
else if (c == '[') {
for (i2=i+1; i2<len; i2++) {
if (format.charAt(i2) == ']') {
var subformat = format.substring(i+1, i2);
var subres = formatDate(date, subformat, options);
if (subres != formatDate(otherDate, subformat, options)) {
res += subres;
}
i = i2;
break;
}
}
}
else if (c == '{') {
date = date2;
otherDate = date1;
}
else if (c == '}') {
date = date1;
otherDate = date2;
}
else {
for (i2=len; i2>i; i2--) {
if (formatter = dateFormatters[format.substring(i, i2)]) {
if (date) {
res += formatter(date, options);
}
i = i2 - 1;
break;
}
}
if (i2 == i) {
if (date) {
res += c;
}
}
}
}
return res;
};
var dateFormatters = {
s : function(d) { return d.getSeconds() },
ss : function(d) { return zeroPad(d.getSeconds()) },
m : function(d) { return d.getMinutes() },
mm : function(d) { return zeroPad(d.getMinutes()) },
h : function(d) { return d.getHours() % 12 || 12 },
hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
H : function(d) { return d.getHours() },
HH : function(d) { return zeroPad(d.getHours()) },
d : function(d) { return d.getDate() },
dd : function(d) { return zeroPad(d.getDate()) },
ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
dddd: function(d,o) { return o.dayNames[d.getDay()] },
M : function(d) { return d.getMonth() + 1 },
MM : function(d) { return zeroPad(d.getMonth() + 1) },
MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
yy : function(d) { return (d.getFullYear()+'').substring(2) },
yyyy: function(d) { return d.getFullYear() },
t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
S : function(d) {
var date = d.getDate();
if (date > 10 && date < 20) {
return 'th';
}
return ['st', 'nd', 'rd'][date%10-1] || 'th';
},
w : function(d, o) { // local
return o.weekNumberCalculation(d);
},
W : function(d) { // ISO
return iso8601Week(d);
}
};
fc.dateFormatters = dateFormatters;
/* thanks jQuery UI (https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js)
*
* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
* `date` - the date to get the week for
* `number` - the number of the week within the year that contains this date
*/
function iso8601Week(date) {
var time;
var checkDate = new Date(date.getTime());
// Find Thursday of this week starting on Monday
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
time = checkDate.getTime();
checkDate.setMonth(0); // Compare with Jan 1
checkDate.setDate(1);
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}
;;
fc.applyAll = applyAll;
/* Event Date Math
-----------------------------------------------------------------------------*/
function exclEndDay(event) {
if (event.end) {
return _exclEndDay(event.end, event.allDay);
}else{
return addDays(cloneDate(event.start), 1);
}
}
function _exclEndDay(end, allDay) {
end = cloneDate(end);
return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
// why don't we check for seconds/ms too?
}
/* Event Element Binding
-----------------------------------------------------------------------------*/
function lazySegBind(container, segs, bindHandlers) {
container.unbind('mouseover').mouseover(function(ev) {
var parent=ev.target, e,
i, seg;
while (parent != this) {
e = parent;
parent = parent.parentNode;
}
if ((i = e._fci) !== undefined) {
e._fci = undefined;
seg = segs[i];
bindHandlers(seg.event, seg.element, seg);
$(ev.target).trigger(ev);
}
ev.stopPropagation();
});
}
/* Element Dimensions
-----------------------------------------------------------------------------*/
function setOuterWidth(element, width, includeMargins) {
for (var i=0, e; i<element.length; i++) {
e = $(element[i]);
e.width(Math.max(0, width - hsides(e, includeMargins)));
}
}
function setOuterHeight(element, height, includeMargins) {
for (var i=0, e; i<element.length; i++) {
e = $(element[i]);
e.height(Math.max(0, height - vsides(e, includeMargins)));
}
}
function hsides(element, includeMargins) {
return hpadding(element) + hborders(element) + (includeMargins ? hmargins(element) : 0);
}
function hpadding(element) {
return (parseFloat($.css(element[0], 'paddingLeft', true)) || 0) +
(parseFloat($.css(element[0], 'paddingRight', true)) || 0);
}
function hmargins(element) {
return (parseFloat($.css(element[0], 'marginLeft', true)) || 0) +
(parseFloat($.css(element[0], 'marginRight', true)) || 0);
}
function hborders(element) {
return (parseFloat($.css(element[0], 'borderLeftWidth', true)) || 0) +
(parseFloat($.css(element[0], 'borderRightWidth', true)) || 0);
}
function vsides(element, includeMargins) {
return vpadding(element) + vborders(element) + (includeMargins ? vmargins(element) : 0);
}
function vpadding(element) {
return (parseFloat($.css(element[0], 'paddingTop', true)) || 0) +
(parseFloat($.css(element[0], 'paddingBottom', true)) || 0);
}
function vmargins(element) {
return (parseFloat($.css(element[0], 'marginTop', true)) || 0) +
(parseFloat($.css(element[0], 'marginBottom', true)) || 0);
}
function vborders(element) {
return (parseFloat($.css(element[0], 'borderTopWidth', true)) || 0) +
(parseFloat($.css(element[0], 'borderBottomWidth', true)) || 0);
}
/* Misc Utils
-----------------------------------------------------------------------------*/
//TODO: arraySlice
//TODO: isFunction, grep ?
function noop() { }
function dateCompare(a, b) {
return a - b;
}
function arrayMax(a) {
return Math.max.apply(Math, a);
}
function zeroPad(n) {
return (n < 10 ? '0' : '') + n;
}
function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
if (obj[name] !== undefined) {
return obj[name];
}
var parts = name.split(/(?=[A-Z])/),
i=parts.length-1, res;
for (; i>=0; i--) {
res = obj[parts[i].toLowerCase()];
if (res !== undefined) {
return res;
}
}
return obj[''];
}
function htmlEscape(s) {
return s.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/\n/g, '<br />');
}
function disableTextSelection(element) {
element
.attr('unselectable', 'on')
.css('MozUserSelect', 'none')
.bind('selectstart.ui', function() { return false; });
}
/*
function enableTextSelection(element) {
element
.attr('unselectable', 'off')
.css('MozUserSelect', '')
.unbind('selectstart.ui');
}
*/
function markFirstLast(e) {
e.children()
.removeClass('fc-first fc-last')
.filter(':first-child')
.addClass('fc-first')
.end()
.filter(':last-child')
.addClass('fc-last');
}
function setDayID(cell, date) {
cell.each(function(i, _cell) {
_cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
// TODO: make a way that doesn't rely on order of classes
});
}
function getSkinCss(event, opt) {
var source = event.source || {};
var eventColor = event.color;
var sourceColor = source.color;
var optionColor = opt('eventColor');
var backgroundColor =
event.backgroundColor ||
eventColor ||
source.backgroundColor ||
sourceColor ||
opt('eventBackgroundColor') ||
optionColor;
var borderColor =
event.borderColor ||
eventColor ||
source.borderColor ||
sourceColor ||
opt('eventBorderColor') ||
optionColor;
var textColor =
event.textColor ||
source.textColor ||
opt('eventTextColor');
var statements = [];
if (backgroundColor) {
statements.push('background-color:' + backgroundColor);
}
if (borderColor) {
statements.push('border-color:' + borderColor);
}
if (textColor) {
statements.push('color:' + textColor);
}
return statements.join(';');
}
function applyAll(functions, thisObj, args) {
if ($.isFunction(functions)) {
functions = [ functions ];
}
if (functions) {
var i;
var ret;
for (i=0; i<functions.length; i++) {
ret = functions[i].apply(thisObj, args) || ret;
}
return ret;
}
}
function firstDefined() {
for (var i=0; i<arguments.length; i++) {
if (arguments[i] !== undefined) {
return arguments[i];
}
}
}
;;
fcViews.month = MonthView;
function MonthView(element, calendar) {
var t = this;
// exports
t.render = render;
// imports
BasicView.call(t, element, calendar, 'month');
var opt = t.opt;
var renderBasic = t.renderBasic;
var skipHiddenDays = t.skipHiddenDays;
var getCellsPerWeek = t.getCellsPerWeek;
var formatDate = calendar.formatDate;
function render(date, delta) {
if (delta) {
addMonths(date, delta);
date.setDate(1);
}
var firstDay = opt('firstDay');
var start = cloneDate(date, true);
start.setDate(1);
var end = addMonths(cloneDate(start), 1);
var visStart = cloneDate(start);
addDays(visStart, -((visStart.getDay() - firstDay + 7) % 7));
skipHiddenDays(visStart);
var visEnd = cloneDate(end);
addDays(visEnd, (7 - visEnd.getDay() + firstDay) % 7);
skipHiddenDays(visEnd, -1, true);
var colCnt = getCellsPerWeek();
var rowCnt = Math.round(dayDiff(visEnd, visStart) / 7); // should be no need for Math.round
if (opt('weekMode') == 'fixed') {
addDays(visEnd, (6 - rowCnt) * 7); // add weeks to make up for it
rowCnt = 6;
}
t.title = formatDate(start, opt('titleFormat'));
t.start = start;
t.end = end;
t.visStart = visStart;
t.visEnd = visEnd;
renderBasic(rowCnt, colCnt, true);
}
}
;;
fcViews.basicWeek = BasicWeekView;
function BasicWeekView(element, calendar) {
var t = this;
// exports
t.render = render;
// imports
BasicView.call(t, element, calendar, 'basicWeek');
var opt = t.opt;
var renderBasic = t.renderBasic;
var skipHiddenDays = t.skipHiddenDays;
var getCellsPerWeek = t.getCellsPerWeek;
var formatDates = calendar.formatDates;
function render(date, delta) {
if (delta) {
addDays(date, delta * 7);
}
var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
var end = addDays(cloneDate(start), 7);
var visStart = cloneDate(start);
skipHiddenDays(visStart);
var visEnd = cloneDate(end);
skipHiddenDays(visEnd, -1, true);
var colCnt = getCellsPerWeek();
t.start = start;
t.end = end;
t.visStart = visStart;
t.visEnd = visEnd;
t.title = formatDates(
visStart,
addDays(cloneDate(visEnd), -1),
opt('titleFormat')
);
renderBasic(1, colCnt, false);
}
}
;;
fcViews.basicDay = BasicDayView;
function BasicDayView(element, calendar) {
var t = this;
// exports
t.render = render;
// imports
BasicView.call(t, element, calendar, 'basicDay');
var opt = t.opt;
var renderBasic = t.renderBasic;
var skipHiddenDays = t.skipHiddenDays;
var formatDate = calendar.formatDate;
function render(date, delta) {
if (delta) {
addDays(date, delta);
}
skipHiddenDays(date, delta < 0 ? -1 : 1);
var start = cloneDate(date, true);
var end = addDays(cloneDate(start), 1);
t.title = formatDate(date, opt('titleFormat'));
t.start = t.visStart = start;
t.end = t.visEnd = end;
renderBasic(1, 1, false);
}
}
;;
setDefaults({
weekMode: 'fixed'
});
function BasicView(element, calendar, viewName) {
var t = this;
// exports
t.renderBasic = renderBasic;
t.setHeight = setHeight;
t.setWidth = setWidth;
t.renderDayOverlay = renderDayOverlay;
t.defaultSelectionEnd = defaultSelectionEnd;
t.renderSelection = renderSelection;
t.clearSelection = clearSelection;
t.reportDayClick = reportDayClick; // for selection (kinda hacky)
t.dragStart = dragStart;
t.dragStop = dragStop;
t.defaultEventEnd = defaultEventEnd;
t.getHoverListener = function() { return hoverListener };
t.colLeft = colLeft;
t.colRight = colRight;
t.colContentLeft = colContentLeft;
t.colContentRight = colContentRight;
t.getIsCellAllDay = function() { return true };
t.allDayRow = allDayRow;
t.getRowCnt = function() { return rowCnt };
t.getColCnt = function() { return colCnt };
t.getColWidth = function() { return colWidth };
t.getDaySegmentContainer = function() { return daySegmentContainer };
// imports
View.call(t, element, calendar, viewName);
OverlayManager.call(t);
SelectionManager.call(t);
BasicEventRenderer.call(t);
var opt = t.opt;
var trigger = t.trigger;
var renderOverlay = t.renderOverlay;
var clearOverlays = t.clearOverlays;
var daySelectionMousedown = t.daySelectionMousedown;
var cellToDate = t.cellToDate;
var dateToCell = t.dateToCell;
var rangeToSegments = t.rangeToSegments;
var formatDate = calendar.formatDate;
// locals
var table;
var head;
var headCells;
var body;
var bodyRows;
var bodyCells;
var bodyFirstCells;
var firstRowCellInners;
var firstRowCellContentInners;
var daySegmentContainer;
var viewWidth;
var viewHeight;
var colWidth;
var weekNumberWidth;
var rowCnt, colCnt;
var showNumbers;
var coordinateGrid;
var hoverListener;
var colPositions;
var colContentPositions;
var tm;
var colFormat;
var showWeekNumbers;
var weekNumberTitle;
var weekNumberFormat;
/* Rendering
------------------------------------------------------------*/
disableTextSelection(element.addClass('fc-grid'));
function renderBasic(_rowCnt, _colCnt, _showNumbers) {
rowCnt = _rowCnt;
colCnt = _colCnt;
showNumbers = _showNumbers;
updateOptions();
if (!body) {
buildEventContainer();
}
buildTable();
}
function updateOptions() {
tm = opt('theme') ? 'ui' : 'fc';
colFormat = opt('columnFormat');
// week # options. (TODO: bad, logic also in other views)
showWeekNumbers = opt('weekNumbers');
weekNumberTitle = opt('weekNumberTitle');
if (opt('weekNumberCalculation') != 'iso') {
weekNumberFormat = "w";
}
else {
weekNumberFormat = "W";
}
}
function buildEventContainer() {
daySegmentContainer =
$("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
.appendTo(element);
}
function buildTable() {
var html = buildTableHTML();
if (table) {
table.remove();
}
table = $(html).appendTo(element);
head = table.find('thead');
headCells = head.find('.fc-day-header');
body = table.find('tbody');
bodyRows = body.find('tr');
bodyCells = body.find('.fc-day');
bodyFirstCells = bodyRows.find('td:first-child');
firstRowCellInners = bodyRows.eq(0).find('.fc-day > div');
firstRowCellContentInners = bodyRows.eq(0).find('.fc-day-content > div');
markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
markFirstLast(bodyRows); // marks first+last td's
bodyRows.eq(0).addClass('fc-first');
bodyRows.filter(':last').addClass('fc-last');
bodyCells.each(function(i, _cell) {
var date = cellToDate(
Math.floor(i / colCnt),
i % colCnt
);
trigger('dayRender', t, date, $(_cell));
});
dayBind(bodyCells);
}
/* HTML Building
-----------------------------------------------------------*/
function buildTableHTML() {
var html =
"<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
buildHeadHTML() +
buildBodyHTML() +
"</table>";
return html;
}
function buildHeadHTML() {
var headerClass = tm + "-widget-header";
var html = '';
var col;
var date;
html += "<thead><tr>";
if (showWeekNumbers) {
html +=
"<th class='fc-week-number " + headerClass + "'>" +
htmlEscape(weekNumberTitle) +
"</th>";
}
for (col=0; col<colCnt; col++) {
date = cellToDate(0, col);
html +=
"<th class='fc-day-header fc-" + dayIDs[date.getDay()] + " " + headerClass + "'>" +
htmlEscape(formatDate(date, colFormat)) +
"</th>";
}
html += "</tr></thead>";
return html;
}
function buildBodyHTML() {
var contentClass = tm + "-widget-content";
var html = '';
var row;
var col;
var date;
html += "<tbody>";
for (row=0; row<rowCnt; row++) {
html += "<tr class='fc-week'>";
if (showWeekNumbers) {
date = cellToDate(row, 0);
html +=
"<td class='fc-week-number " + contentClass + "'>" +
"<div>" +
htmlEscape(formatDate(date, weekNumberFormat)) +
"</div>" +
"</td>";
}
for (col=0; col<colCnt; col++) {
date = cellToDate(row, col);
html += buildCellHTML(date);
}
html += "</tr>";
}
html += "</tbody>";
return html;
}
function buildCellHTML(date) {
var contentClass = tm + "-widget-content";
var month = t.start.getMonth();
var today = clearTime(new Date());
var html = '';
var classNames = [
'fc-day',
'fc-' + dayIDs[date.getDay()],
contentClass
];
if (date.getMonth() != month) {
classNames.push('fc-other-month');
}
if (+date == +today) {
classNames.push(
'fc-today',
tm + '-state-highlight'
);
}
else if (date < today) {
classNames.push('fc-past');
}
else {
classNames.push('fc-future');
}
html +=
"<td" +
" class='" + classNames.join(' ') + "'" +
" data-date='" + formatDate(date, 'yyyy-MM-dd') + "'" +
">" +
"<div>";
if (showNumbers) {
html += "<div class='fc-day-number'>" + date.getDate() + "</div>";
}
html +=
"<div class='fc-day-content'>" +
"<div style='position:relative'> </div>" +
"</div>" +
"</div>" +
"</td>";
return html;
}
/* Dimensions
-----------------------------------------------------------*/
function setHeight(height) {
viewHeight = height;
var bodyHeight = viewHeight - head.height();
var rowHeight;
var rowHeightLast;
var cell;
if (opt('weekMode') == 'variable') {
rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
}else{
rowHeight = Math.floor(bodyHeight / rowCnt);
rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
}
bodyFirstCells.each(function(i, _cell) {
if (i < rowCnt) {
cell = $(_cell);
cell.find('> div').css(
'min-height',
(i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
);
}
});
}
function setWidth(width) {
viewWidth = width;
colPositions.clear();
colContentPositions.clear();
weekNumberWidth = 0;
if (showWeekNumbers) {
weekNumberWidth = head.find('th.fc-week-number').outerWidth();
}
colWidth = Math.floor((viewWidth - weekNumberWidth) / colCnt);
setOuterWidth(headCells.slice(0, -1), colWidth);
}
/* Day clicking and binding
-----------------------------------------------------------*/
function dayBind(days) {
days.click(dayClick)
.mousedown(daySelectionMousedown);
}
function dayClick(ev) {
if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
var date = parseISO8601($(this).data('date'));
trigger('dayClick', this, date, true, ev);
}
}
/* Semi-transparent Overlay Helpers
------------------------------------------------------*/
// TODO: should be consolidated with AgendaView's methods
function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
if (refreshCoordinateGrid) {
coordinateGrid.build();
}
var segments = rangeToSegments(overlayStart, overlayEnd);
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
dayBind(
renderCellOverlay(
segment.row,
segment.leftCol,
segment.row,
segment.rightCol
)
);
}
}
function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
return renderOverlay(rect, element);
}
/* Selection
-----------------------------------------------------------------------*/
function defaultSelectionEnd(startDate, allDay) {
return cloneDate(startDate);
}
function renderSelection(startDate, endDate, allDay) {
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
}
function clearSelection() {
clearOverlays();
}
function reportDayClick(date, allDay, ev) {
var cell = dateToCell(date);
var _element = bodyCells[cell.row*colCnt + cell.col];
trigger('dayClick', _element, date, allDay, ev);
}
/* External Dragging
-----------------------------------------------------------------------*/
function dragStart(_dragElement, ev, ui) {
hoverListener.start(function(cell) {
clearOverlays();
if (cell) {
renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
}
}, ev);
}
function dragStop(_dragElement, ev, ui) {
var cell = hoverListener.stop();
clearOverlays();
if (cell) {
var d = cellToDate(cell);
trigger('drop', _dragElement, d, true, ev, ui);
}
}
/* Utilities
--------------------------------------------------------*/
function defaultEventEnd(event) {
return cloneDate(event.start);
}
coordinateGrid = new CoordinateGrid(function(rows, cols) {
var e, n, p;
headCells.each(function(i, _e) {
e = $(_e);
n = e.offset().left;
if (i) {
p[1] = n;
}
p = [n];
cols[i] = p;
});
p[1] = n + e.outerWidth();
bodyRows.each(function(i, _e) {
if (i < rowCnt) {
e = $(_e);
n = e.offset().top;
if (i) {
p[1] = n;
}
p = [n];
rows[i] = p;
}
});
p[1] = n + e.outerHeight();
});
hoverListener = new HoverListener(coordinateGrid);
colPositions = new HorizontalPositionCache(function(col) {
return firstRowCellInners.eq(col);
});
colContentPositions = new HorizontalPositionCache(function(col) {
return firstRowCellContentInners.eq(col);
});
function colLeft(col) {
return colPositions.left(col);
}
function colRight(col) {
return colPositions.right(col);
}
function colContentLeft(col) {
return colContentPositions.left(col);
}
function colContentRight(col) {
return colContentPositions.right(col);
}
function allDayRow(i) {
return bodyRows.eq(i);
}
}
;;
function BasicEventRenderer() {
var t = this;
// exports
t.renderEvents = renderEvents;
t.clearEvents = clearEvents;
// imports
DayEventRenderer.call(t);
function renderEvents(events, modifiedEventId) {
t.renderDayEvents(events, modifiedEventId);
}
function clearEvents() {
t.getDaySegmentContainer().empty();
}
// TODO: have this class (and AgendaEventRenderer) be responsible for creating the event container div
}
;;
fcViews.agendaWeek = AgendaWeekView;
function AgendaWeekView(element, calendar) {
var t = this;
// exports
t.render = render;
// imports
AgendaView.call(t, element, calendar, 'agendaWeek');
var opt = t.opt;
var renderAgenda = t.renderAgenda;
var skipHiddenDays = t.skipHiddenDays;
var getCellsPerWeek = t.getCellsPerWeek;
var formatDates = calendar.formatDates;
function render(date, delta) {
if (delta) {
addDays(date, delta * 7);
}
var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
var end = addDays(cloneDate(start), 7);
var visStart = cloneDate(start);
skipHiddenDays(visStart);
var visEnd = cloneDate(end);
skipHiddenDays(visEnd, -1, true);
var colCnt = getCellsPerWeek();
t.title = formatDates(
visStart,
addDays(cloneDate(visEnd), -1),
opt('titleFormat')
);
t.start = start;
t.end = end;
t.visStart = visStart;
t.visEnd = visEnd;
renderAgenda(colCnt);
}
}
;;
fcViews.agendaDay = AgendaDayView;
function AgendaDayView(element, calendar) {
var t = this;
// exports
t.render = render;
// imports
AgendaView.call(t, element, calendar, 'agendaDay');
var opt = t.opt;
var renderAgenda = t.renderAgenda;
var skipHiddenDays = t.skipHiddenDays;
var formatDate = calendar.formatDate;
function render(date, delta) {
if (delta) {
addDays(date, delta);
}
skipHiddenDays(date, delta < 0 ? -1 : 1);
var start = cloneDate(date, true);
var end = addDays(cloneDate(start), 1);
t.title = formatDate(date, opt('titleFormat'));
t.start = t.visStart = start;
t.end = t.visEnd = end;
renderAgenda(1);
}
}
;;
setDefaults({
allDaySlot: true,
allDayText: 'all-day',
firstHour: 6,
slotMinutes: 30,
defaultEventMinutes: 120,
axisFormat: 'h(:mm)tt',
timeFormat: {
agenda: 'h:mm{ - h:mm}'
},
dragOpacity: {
agenda: .5
},
minTime: 0,
maxTime: 24,
slotEventOverlap: true
});
// TODO: make it work in quirks mode (event corners, all-day height)
// TODO: test liquid width, especially in IE6
function AgendaView(element, calendar, viewName) {
var t = this;
// exports
t.renderAgenda = renderAgenda;
t.setWidth = setWidth;
t.setHeight = setHeight;
t.afterRender = afterRender;
t.defaultEventEnd = defaultEventEnd;
t.timePosition = timePosition;
t.getIsCellAllDay = getIsCellAllDay;
t.allDayRow = getAllDayRow;
t.getCoordinateGrid = function() { return coordinateGrid }; // specifically for AgendaEventRenderer
t.getHoverListener = function() { return hoverListener };
t.colLeft = colLeft;
t.colRight = colRight;
t.colContentLeft = colContentLeft;
t.colContentRight = colContentRight;
t.getDaySegmentContainer = function() { return daySegmentContainer };
t.getSlotSegmentContainer = function() { return slotSegmentContainer };
t.getMinMinute = function() { return minMinute };
t.getMaxMinute = function() { return maxMinute };
t.getSlotContainer = function() { return slotContainer };
t.getRowCnt = function() { return 1 };
t.getColCnt = function() { return colCnt };
t.getColWidth = function() { return colWidth };
t.getSnapHeight = function() { return snapHeight };
t.getSnapMinutes = function() { return snapMinutes };
t.defaultSelectionEnd = defaultSelectionEnd;
t.renderDayOverlay = renderDayOverlay;
t.renderSelection = renderSelection;
t.clearSelection = clearSelection;
t.reportDayClick = reportDayClick; // selection mousedown hack
t.dragStart = dragStart;
t.dragStop = dragStop;
// imports
View.call(t, element, calendar, viewName);
OverlayManager.call(t);
SelectionManager.call(t);
AgendaEventRenderer.call(t);
var opt = t.opt;
var trigger = t.trigger;
var renderOverlay = t.renderOverlay;
var clearOverlays = t.clearOverlays;
var reportSelection = t.reportSelection;
var unselect = t.unselect;
var daySelectionMousedown = t.daySelectionMousedown;
var slotSegHtml = t.slotSegHtml;
var cellToDate = t.cellToDate;
var dateToCell = t.dateToCell;
var rangeToSegments = t.rangeToSegments;
var formatDate = calendar.formatDate;
// locals
var dayTable;
var dayHead;
var dayHeadCells;
var dayBody;
var dayBodyCells;
var dayBodyCellInners;
var dayBodyCellContentInners;
var dayBodyFirstCell;
var dayBodyFirstCellStretcher;
var slotLayer;
var daySegmentContainer;
var allDayTable;
var allDayRow;
var slotScroller;
var slotContainer;
var slotSegmentContainer;
var slotTable;
var selectionHelper;
var viewWidth;
var viewHeight;
var axisWidth;
var colWidth;
var gutterWidth;
var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
var snapMinutes;
var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4)
var snapHeight; // holds the pixel hight of a "selection" slot
var colCnt;
var slotCnt;
var coordinateGrid;
var hoverListener;
var colPositions;
var colContentPositions;
var slotTopCache = {};
var tm;
var rtl;
var minMinute, maxMinute;
var colFormat;
var showWeekNumbers;
var weekNumberTitle;
var weekNumberFormat;
/* Rendering
-----------------------------------------------------------------------------*/
disableTextSelection(element.addClass('fc-agenda'));
function renderAgenda(c) {
colCnt = c;
updateOptions();
if (!dayTable) { // first time rendering?
buildSkeleton(); // builds day table, slot area, events containers
}
else {
buildDayTable(); // rebuilds day table
}
}
function updateOptions() {
tm = opt('theme') ? 'ui' : 'fc';
rtl = opt('isRTL')
minMinute = parseTime(opt('minTime'));
maxMinute = parseTime(opt('maxTime'));
colFormat = opt('columnFormat');
// week # options. (TODO: bad, logic also in other views)
showWeekNumbers = opt('weekNumbers');
weekNumberTitle = opt('weekNumberTitle');
if (opt('weekNumberCalculation') != 'iso') {
weekNumberFormat = "w";
}
else {
weekNumberFormat = "W";
}
snapMinutes = opt('snapMinutes') || opt('slotMinutes');
}
/* Build DOM
-----------------------------------------------------------------------*/
function buildSkeleton() {
var headerClass = tm + "-widget-header";
var contentClass = tm + "-widget-content";
var s;
var d;
var i;
var maxd;
var minutes;
var slotNormal = opt('slotMinutes') % 15 == 0;
buildDayTable();
slotLayer =
$("<div style='position:absolute;z-index:2;left:0;width:100%'/>")
.appendTo(element);
if (opt('allDaySlot')) {
daySegmentContainer =
$("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
.appendTo(slotLayer);
s =
"<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
"<tr>" +
"<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
"<td>" +
"<div class='fc-day-content'><div style='position:relative'/></div>" +
"</td>" +
"<th class='" + headerClass + " fc-agenda-gutter'> </th>" +
"</tr>" +
"</table>";
allDayTable = $(s).appendTo(slotLayer);
allDayRow = allDayTable.find('tr');
dayBind(allDayRow.find('td'));
slotLayer.append(
"<div class='fc-agenda-divider " + headerClass + "'>" +
"<div class='fc-agenda-divider-inner'/>" +
"</div>"
);
}else{
daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
}
slotScroller =
$("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>")
.appendTo(slotLayer);
slotContainer =
$("<div style='position:relative;width:100%;overflow:hidden'/>")
.appendTo(slotScroller);
slotSegmentContainer =
$("<div class='fc-event-container' style='position:absolute;z-index:8;top:0;left:0'/>")
.appendTo(slotContainer);
s =
"<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
"<tbody>";
d = zeroDate();
maxd = addMinutes(cloneDate(d), maxMinute);
addMinutes(d, minMinute);
slotCnt = 0;
for (i=0; d < maxd; i++) {
minutes = d.getMinutes();
s +=
"<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
"<th class='fc-agenda-axis " + headerClass + "'>" +
((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') +
"</th>" +
"<td class='" + contentClass + "'>" +
"<div style='position:relative'> </div>" +
"</td>" +
"</tr>";
addMinutes(d, opt('slotMinutes'));
slotCnt++;
}
s +=
"</tbody>" +
"</table>";
slotTable = $(s).appendTo(slotContainer);
slotBind(slotTable.find('td'));
}
/* Build Day Table
-----------------------------------------------------------------------*/
function buildDayTable() {
var html = buildDayTableHTML();
if (dayTable) {
dayTable.remove();
}
dayTable = $(html).appendTo(element);
dayHead = dayTable.find('thead');
dayHeadCells = dayHead.find('th').slice(1, -1); // exclude gutter
dayBody = dayTable.find('tbody');
dayBodyCells = dayBody.find('td').slice(0, -1); // exclude gutter
dayBodyCellInners = dayBodyCells.find('> div');
dayBodyCellContentInners = dayBodyCells.find('.fc-day-content > div');
dayBodyFirstCell = dayBodyCells.eq(0);
dayBodyFirstCellStretcher = dayBodyCellInners.eq(0);
markFirstLast(dayHead.add(dayHead.find('tr')));
markFirstLast(dayBody.add(dayBody.find('tr')));
// TODO: now that we rebuild the cells every time, we should call dayRender
}
function buildDayTableHTML() {
var html =
"<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'>" +
buildDayTableHeadHTML() +
buildDayTableBodyHTML() +
"</table>";
return html;
}
function buildDayTableHeadHTML() {
var headerClass = tm + "-widget-header";
var date;
var html = '';
var weekText;
var col;
html +=
"<thead>" +
"<tr>";
if (showWeekNumbers) {
date = cellToDate(0, 0);
weekText = formatDate(date, weekNumberFormat);
if (rtl) {
weekText += weekNumberTitle;
}
else {
weekText = weekNumberTitle + weekText;
}
html +=
"<th class='fc-agenda-axis fc-week-number " + headerClass + "'>" +
htmlEscape(weekText) +
"</th>";
}
else {
html += "<th class='fc-agenda-axis " + headerClass + "'> </th>";
}
for (col=0; col<colCnt; col++) {
date = cellToDate(0, col);
html +=
"<th class='fc-" + dayIDs[date.getDay()] + " fc-col" + col + ' ' + headerClass + "'>" +
htmlEscape(formatDate(date, colFormat)) +
"</th>";
}
html +=
"<th class='fc-agenda-gutter " + headerClass + "'> </th>" +
"</tr>" +
"</thead>";
return html;
}
function buildDayTableBodyHTML() {
var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called
var contentClass = tm + "-widget-content";
var date;
var today = clearTime(new Date());
var col;
var cellsHTML;
var cellHTML;
var classNames;
var html = '';
html +=
"<tbody>" +
"<tr>" +
"<th class='fc-agenda-axis " + headerClass + "'> </th>";
cellsHTML = '';
for (col=0; col<colCnt; col++) {
date = cellToDate(0, col);
classNames = [
'fc-col' + col,
'fc-' + dayIDs[date.getDay()],
contentClass
];
if (+date == +today) {
classNames.push(
tm + '-state-highlight',
'fc-today'
);
}
else if (date < today) {
classNames.push('fc-past');
}
else {
classNames.push('fc-future');
}
cellHTML =
"<td class='" + classNames.join(' ') + "'>" +
"<div>" +
"<div class='fc-day-content'>" +
"<div style='position:relative'> </div>" +
"</div>" +
"</div>" +
"</td>";
cellsHTML += cellHTML;
}
html += cellsHTML;
html +=
"<td class='fc-agenda-gutter " + contentClass + "'> </td>" +
"</tr>" +
"</tbody>";
return html;
}
// TODO: data-date on the cells
/* Dimensions
-----------------------------------------------------------------------*/
function setHeight(height) {
if (height === undefined) {
height = viewHeight;
}
viewHeight = height;
slotTopCache = {};
var headHeight = dayBody.position().top;
var allDayHeight = slotScroller.position().top; // including divider
var bodyHeight = Math.min( // total body height, including borders
height - headHeight, // when scrollbars
slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border
);
dayBodyFirstCellStretcher
.height(bodyHeight - vsides(dayBodyFirstCell));
slotLayer.css('top', headHeight);
slotScroller.height(bodyHeight - allDayHeight - 1);
// the stylesheet guarantees that the first row has no border.
// this allows .height() to work well cross-browser.
slotHeight = slotTable.find('tr:first').height() + 1; // +1 for bottom border
snapRatio = opt('slotMinutes') / snapMinutes;
snapHeight = slotHeight / snapRatio;
}
function setWidth(width) {
viewWidth = width;
colPositions.clear();
colContentPositions.clear();
var axisFirstCells = dayHead.find('th:first');
if (allDayTable) {
axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
}
axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
axisWidth = 0;
setOuterWidth(
axisFirstCells
.width('')
.each(function(i, _cell) {
axisWidth = Math.max(axisWidth, $(_cell).outerWidth());
}),
axisWidth
);
var gutterCells = dayTable.find('.fc-agenda-gutter');
if (allDayTable) {
gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
}
var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7)
gutterWidth = slotScroller.width() - slotTableWidth;
if (gutterWidth) {
setOuterWidth(gutterCells, gutterWidth);
gutterCells
.show()
.prev()
.removeClass('fc-last');
}else{
gutterCells
.hide()
.prev()
.addClass('fc-last');
}
colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt);
setOuterWidth(dayHeadCells.slice(0, -1), colWidth);
}
/* Scrolling
-----------------------------------------------------------------------*/
function resetScroll() {
var d0 = zeroDate();
var scrollDate = cloneDate(d0);
scrollDate.setHours(opt('firstHour'));
var top = timePosition(d0, scrollDate) + 1; // +1 for the border
function scroll() {
slotScroller.scrollTop(top);
}
scroll();
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
}
function afterRender() { // after the view has been freshly rendered and sized
resetScroll();
}
/* Slot/Day clicking and binding
-----------------------------------------------------------------------*/
function dayBind(cells) {
cells.click(slotClick)
.mousedown(daySelectionMousedown);
}
function slotBind(cells) {
cells.click(slotClick)
.mousedown(slotSelectionMousedown);
}
function slotClick(ev) {
if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
var col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
var date = cellToDate(0, col);
var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
if (rowMatch) {
var mins = parseInt(rowMatch[1]) * opt('slotMinutes');
var hours = Math.floor(mins/60);
date.setHours(hours);
date.setMinutes(mins%60 + minMinute);
trigger('dayClick', dayBodyCells[col], date, false, ev);
}else{
trigger('dayClick', dayBodyCells[col], date, true, ev);
}
}
}
/* Semi-transparent Overlay Helpers
-----------------------------------------------------*/
// TODO: should be consolidated with BasicView's methods
function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
if (refreshCoordinateGrid) {
coordinateGrid.build();
}
var segments = rangeToSegments(overlayStart, overlayEnd);
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
dayBind(
renderCellOverlay(
segment.row,
segment.leftCol,
segment.row,
segment.rightCol
)
);
}
}
function renderCellOverlay(row0, col0, row1, col1) { // only for all-day?
var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer);
return renderOverlay(rect, slotLayer);
}
function renderSlotOverlay(overlayStart, overlayEnd) {
for (var i=0; i<colCnt; i++) {
var dayStart = cellToDate(0, i);
var dayEnd = addDays(cloneDate(dayStart), 1);
var stretchStart = new Date(Math.max(dayStart, overlayStart));
var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
if (stretchStart < stretchEnd) {
var rect = coordinateGrid.rect(0, i, 0, i, slotContainer); // only use it for horizontal coords
var top = timePosition(dayStart, stretchStart);
var bottom = timePosition(dayStart, stretchEnd);
rect.top = top;
rect.height = bottom - top;
slotBind(
renderOverlay(rect, slotContainer)
);
}
}
}
/* Coordinate Utilities
-----------------------------------------------------------------------------*/
coordinateGrid = new CoordinateGrid(function(rows, cols) {
var e, n, p;
dayHeadCells.each(function(i, _e) {
e = $(_e);
n = e.offset().left;
if (i) {
p[1] = n;
}
p = [n];
cols[i] = p;
});
p[1] = n + e.outerWidth();
if (opt('allDaySlot')) {
e = allDayRow;
n = e.offset().top;
rows[0] = [n, n+e.outerHeight()];
}
var slotTableTop = slotContainer.offset().top;
var slotScrollerTop = slotScroller.offset().top;
var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight();
function constrain(n) {
return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n));
}
for (var i=0; i<slotCnt*snapRatio; i++) { // adapt slot count to increased/decreased selection slot count
rows.push([
constrain(slotTableTop + snapHeight*i),
constrain(slotTableTop + snapHeight*(i+1))
]);
}
});
hoverListener = new HoverListener(coordinateGrid);
colPositions = new HorizontalPositionCache(function(col) {
return dayBodyCellInners.eq(col);
});
colContentPositions = new HorizontalPositionCache(function(col) {
return dayBodyCellContentInners.eq(col);
});
function colLeft(col) {
return colPositions.left(col);
}
function colContentLeft(col) {
return colContentPositions.left(col);
}
function colRight(col) {
return colPositions.right(col);
}
function colContentRight(col) {
return colContentPositions.right(col);
}
function getIsCellAllDay(cell) {
return opt('allDaySlot') && !cell.row;
}
function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system
var d = cellToDate(0, cell.col);
var slotIndex = cell.row;
if (opt('allDaySlot')) {
slotIndex--;
}
if (slotIndex >= 0) {
addMinutes(d, minMinute + slotIndex * snapMinutes);
}
return d;
}
// get the Y coordinate of the given time on the given day (both Date objects)
function timePosition(day, time) { // both date objects. day holds 00:00 of current day
day = cloneDate(day, true);
if (time < addMinutes(cloneDate(day), minMinute)) {
return 0;
}
if (time >= addMinutes(cloneDate(day), maxMinute)) {
return slotTable.height();
}
var slotMinutes = opt('slotMinutes'),
minutes = time.getHours()*60 + time.getMinutes() - minMinute,
slotI = Math.floor(minutes / slotMinutes),
slotTop = slotTopCache[slotI];
if (slotTop === undefined) {
slotTop = slotTopCache[slotI] =
slotTable.find('tr').eq(slotI).find('td div')[0].offsetTop;
// .eq() is faster than ":eq()" selector
// [0].offsetTop is faster than .position().top (do we really need this optimization?)
// a better optimization would be to cache all these divs
}
return Math.max(0, Math.round(
slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
));
}
function getAllDayRow(index) {
return allDayRow;
}
function defaultEventEnd(event) {
var start = cloneDate(event.start);
if (event.allDay) {
return start;
}
return addMinutes(start, opt('defaultEventMinutes'));
}
/* Selection
---------------------------------------------------------------------------------*/
function defaultSelectionEnd(startDate, allDay) {
if (allDay) {
return cloneDate(startDate);
}
return addMinutes(cloneDate(startDate), opt('slotMinutes'));
}
function renderSelection(startDate, endDate, allDay) { // only for all-day
if (allDay) {
if (opt('allDaySlot')) {
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
}
}else{
renderSlotSelection(startDate, endDate);
}
}
function renderSlotSelection(startDate, endDate) {
var helperOption = opt('selectHelper');
coordinateGrid.build();
if (helperOption) {
var col = dateToCell(startDate).col;
if (col >= 0 && col < colCnt) { // only works when times are on same day
var rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords
var top = timePosition(startDate, startDate);
var bottom = timePosition(startDate, endDate);
if (bottom > top) { // protect against selections that are entirely before or after visible range
rect.top = top;
rect.height = bottom - top;
rect.left += 2;
rect.width -= 5;
if ($.isFunction(helperOption)) {
var helperRes = helperOption(startDate, endDate);
if (helperRes) {
rect.position = 'absolute';
selectionHelper = $(helperRes)
.css(rect)
.appendTo(slotContainer);
}
}else{
rect.isStart = true; // conside rect a "seg" now
rect.isEnd = true; //
selectionHelper = $(slotSegHtml(
{
title: '',
start: startDate,
end: endDate,
className: ['fc-select-helper'],
editable: false
},
rect
));
selectionHelper.css('opacity', opt('dragOpacity'));
}
if (selectionHelper) {
slotBind(selectionHelper);
slotContainer.append(selectionHelper);
setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
setOuterHeight(selectionHelper, rect.height, true);
}
}
}
}else{
renderSlotOverlay(startDate, endDate);
}
}
function clearSelection() {
clearOverlays();
if (selectionHelper) {
selectionHelper.remove();
selectionHelper = null;
}
}
function slotSelectionMousedown(ev) {
if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
unselect(ev);
var dates;
hoverListener.start(function(cell, origCell) {
clearSelection();
if (cell && cell.col == origCell.col && !getIsCellAllDay(cell)) {
var d1 = realCellToDate(origCell);
var d2 = realCellToDate(cell);
dates = [
d1,
addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes
d2,
addMinutes(cloneDate(d2), snapMinutes)
].sort(dateCompare);
renderSlotSelection(dates[0], dates[3]);
}else{
dates = null;
}
}, ev);
$(document).one('mouseup', function(ev) {
hoverListener.stop();
if (dates) {
if (+dates[0] == +dates[1]) {
reportDayClick(dates[0], false, ev);
}
reportSelection(dates[0], dates[3], false, ev);
}
});
}
}
function reportDayClick(date, allDay, ev) {
trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev);
}
/* External Dragging
--------------------------------------------------------------------------------*/
function dragStart(_dragElement, ev, ui) {
hoverListener.start(function(cell) {
clearOverlays();
if (cell) {
if (getIsCellAllDay(cell)) {
renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
}else{
var d1 = realCellToDate(cell);
var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
renderSlotOverlay(d1, d2);
}
}
}, ev);
}
function dragStop(_dragElement, ev, ui) {
var cell = hoverListener.stop();
clearOverlays();
if (cell) {
trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui);
}
}
}
;;
function AgendaEventRenderer() {
var t = this;
// exports
t.renderEvents = renderEvents;
t.clearEvents = clearEvents;
t.slotSegHtml = slotSegHtml;
// imports
DayEventRenderer.call(t);
var opt = t.opt;
var trigger = t.trigger;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var eventEnd = t.eventEnd;
var eventElementHandlers = t.eventElementHandlers;
var setHeight = t.setHeight;
var getDaySegmentContainer = t.getDaySegmentContainer;
var getSlotSegmentContainer = t.getSlotSegmentContainer;
var getHoverListener = t.getHoverListener;
var getMaxMinute = t.getMaxMinute;
var getMinMinute = t.getMinMinute;
var timePosition = t.timePosition;
var getIsCellAllDay = t.getIsCellAllDay;
var colContentLeft = t.colContentLeft;
var colContentRight = t.colContentRight;
var cellToDate = t.cellToDate;
var getColCnt = t.getColCnt;
var getColWidth = t.getColWidth;
var getSnapHeight = t.getSnapHeight;
var getSnapMinutes = t.getSnapMinutes;
var getSlotContainer = t.getSlotContainer;
var reportEventElement = t.reportEventElement;
var showEvents = t.showEvents;
var hideEvents = t.hideEvents;
var eventDrop = t.eventDrop;
var eventResize = t.eventResize;
var renderDayOverlay = t.renderDayOverlay;
var clearOverlays = t.clearOverlays;
var renderDayEvents = t.renderDayEvents;
var calendar = t.calendar;
var formatDate = calendar.formatDate;
var formatDates = calendar.formatDates;
// overrides
t.draggableDayEvent = draggableDayEvent;
/* Rendering
----------------------------------------------------------------------------*/
function renderEvents(events, modifiedEventId) {
var i, len=events.length,
dayEvents=[],
slotEvents=[];
for (i=0; i<len; i++) {
if (events[i].allDay) {
dayEvents.push(events[i]);
}else{
slotEvents.push(events[i]);
}
}
if (opt('allDaySlot')) {
renderDayEvents(dayEvents, modifiedEventId);
setHeight(); // no params means set to viewHeight
}
renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId);
}
function clearEvents() {
getDaySegmentContainer().empty();
getSlotSegmentContainer().empty();
}
function compileSlotSegs(events) {
var colCnt = getColCnt(),
minMinute = getMinMinute(),
maxMinute = getMaxMinute(),
d,
visEventEnds = $.map(events, slotEventEnd),
i,
j, seg,
colSegs,
segs = [];
for (i=0; i<colCnt; i++) {
d = cellToDate(0, i);
addMinutes(d, minMinute);
colSegs = sliceSegs(
events,
visEventEnds,
d,
addMinutes(cloneDate(d), maxMinute-minMinute)
);
colSegs = placeSlotSegs(colSegs); // returns a new order
for (j=0; j<colSegs.length; j++) {
seg = colSegs[j];
seg.col = i;
segs.push(seg);
}
}
return segs;
}
function sliceSegs(events, visEventEnds, start, end) {
var segs = [],
i, len=events.length, event,
eventStart, eventEnd,
segStart, segEnd,
isStart, isEnd;
for (i=0; i<len; i++) {
event = events[i];
eventStart = event.start;
eventEnd = visEventEnds[i];
if (eventEnd > start && eventStart < end) {
if (eventStart < start) {
segStart = cloneDate(start);
isStart = false;
}else{
segStart = eventStart;
isStart = true;
}
if (eventEnd > end) {
segEnd = cloneDate(end);
isEnd = false;
}else{
segEnd = eventEnd;
isEnd = true;
}
segs.push({
event: event,
start: segStart,
end: segEnd,
isStart: isStart,
isEnd: isEnd
});
}
}
return segs.sort(compareSlotSegs);
}
function slotEventEnd(event) {
if (event.end) {
return cloneDate(event.end);
}else{
return addMinutes(cloneDate(event.start), opt('defaultEventMinutes'));
}
}
// renders events in the 'time slots' at the bottom
// TODO: when we refactor this, when user returns `false` eventRender, don't have empty space
// TODO: refactor will include using pixels to detect collisions instead of dates (handy for seg cmp)
function renderSlotSegs(segs, modifiedEventId) {
var i, segCnt=segs.length, seg,
event,
top,
bottom,
columnLeft,
columnRight,
columnWidth,
width,
left,
right,
html = '',
eventElements,
eventElement,
triggerRes,
titleElement,
height,
slotSegmentContainer = getSlotSegmentContainer(),
isRTL = opt('isRTL');
// calculate position/dimensions, create html
for (i=0; i<segCnt; i++) {
seg = segs[i];
event = seg.event;
top = timePosition(seg.start, seg.start);
bottom = timePosition(seg.start, seg.end);
columnLeft = colContentLeft(seg.col);
columnRight = colContentRight(seg.col);
columnWidth = columnRight - columnLeft;
// shave off space on right near scrollbars (2.5%)
// TODO: move this to CSS somehow
columnRight -= columnWidth * .025;
columnWidth = columnRight - columnLeft;
width = columnWidth * (seg.forwardCoord - seg.backwardCoord);
if (opt('slotEventOverlap')) {
// double the width while making sure resize handle is visible
// (assumed to be 20px wide)
width = Math.max(
(width - (20/2)) * 2,
width // narrow columns will want to make the segment smaller than
// the natural width. don't allow it
);
}
if (isRTL) {
right = columnRight - seg.backwardCoord * columnWidth;
left = right - width;
}
else {
left = columnLeft + seg.backwardCoord * columnWidth;
right = left + width;
}
// make sure horizontal coordinates are in bounds
left = Math.max(left, columnLeft);
right = Math.min(right, columnRight);
width = right - left;
seg.top = top;
seg.left = left;
seg.outerWidth = width;
seg.outerHeight = bottom - top;
html += slotSegHtml(event, seg);
}
slotSegmentContainer[0].innerHTML = html; // faster than html()
eventElements = slotSegmentContainer.children();
// retrieve elements, run through eventRender callback, bind event handlers
for (i=0; i<segCnt; i++) {
seg = segs[i];
event = seg.event;
eventElement = $(eventElements[i]); // faster than eq()
triggerRes = trigger('eventRender', event, event, eventElement);
if (triggerRes === false) {
eventElement.remove();
}else{
if (triggerRes && triggerRes !== true) {
eventElement.remove();
eventElement = $(triggerRes)
.css({
position: 'absolute',
top: seg.top,
left: seg.left
})
.appendTo(slotSegmentContainer);
}
seg.element = eventElement;
if (event._id === modifiedEventId) {
bindSlotSeg(event, eventElement, seg);
}else{
eventElement[0]._fci = i; // for lazySegBind
}
reportEventElement(event, eventElement);
}
}
lazySegBind(slotSegmentContainer, segs, bindSlotSeg);
// record event sides and title positions
for (i=0; i<segCnt; i++) {
seg = segs[i];
if (eventElement = seg.element) {
seg.vsides = vsides(eventElement, true);
seg.hsides = hsides(eventElement, true);
titleElement = eventElement.find('.fc-event-title');
if (titleElement.length) {
seg.contentTop = titleElement[0].offsetTop;
}
}
}
// set all positions/dimensions at once
for (i=0; i<segCnt; i++) {
seg = segs[i];
if (eventElement = seg.element) {
eventElement[0].style.width = Math.max(0, seg.outerWidth - seg.hsides) + 'px';
height = Math.max(0, seg.outerHeight - seg.vsides);
eventElement[0].style.height = height + 'px';
event = seg.event;
if (seg.contentTop !== undefined && height - seg.contentTop < 10) {
// not enough room for title, put it in the time (TODO: maybe make both display:inline instead)
eventElement.find('div.fc-event-time')
.text(formatDate(event.start, opt('timeFormat')) + ' - ' + event.title);
eventElement.find('div.fc-event-title')
.remove();
}
trigger('eventAfterRender', event, event, eventElement);
}
}
}
function slotSegHtml(event, seg) {
var html = "<";
var url = event.url;
var skinCss = getSkinCss(event, opt);
var classes = ['fc-event', 'fc-event-vert'];
if (isEventDraggable(event)) {
classes.push('fc-event-draggable');
}
if (seg.isStart) {
classes.push('fc-event-start');
}
if (seg.isEnd) {
classes.push('fc-event-end');
}
classes = classes.concat(event.className);
if (event.source) {
classes = classes.concat(event.source.className || []);
}
if (url) {
html += "a href='" + htmlEscape(event.url) + "'";
}else{
html += "div";
}
html +=
" class='" + classes.join(' ') + "'" +
" style=" +
"'" +
"position:absolute;" +
"top:" + seg.top + "px;" +
"left:" + seg.left + "px;" +
skinCss +
"'" +
">" +
"<div class='fc-event-inner'>" +
"<div class='fc-event-time'>" +
htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
"</div>" +
"<div class='fc-event-title'>" +
htmlEscape(event.title || '') +
"</div>" +
"</div>" +
"<div class='fc-event-bg'></div>";
if (seg.isEnd && isEventResizable(event)) {
html +=
"<div class='ui-resizable-handle ui-resizable-s'>=</div>";
}
html +=
"</" + (url ? "a" : "div") + ">";
return html;
}
function bindSlotSeg(event, eventElement, seg) {
var timeElement = eventElement.find('div.fc-event-time');
if (isEventDraggable(event)) {
draggableSlotEvent(event, eventElement, timeElement);
}
if (seg.isEnd && isEventResizable(event)) {
resizableSlotEvent(event, eventElement, timeElement);
}
eventElementHandlers(event, eventElement);
}
/* Dragging
-----------------------------------------------------------------------------------*/
// when event starts out FULL-DAY
// overrides DayEventRenderer's version because it needs to account for dragging elements
// to and from the slot area.
function draggableDayEvent(event, eventElement, seg) {
var isStart = seg.isStart;
var origWidth;
var revert;
var allDay = true;
var dayDelta;
var hoverListener = getHoverListener();
var colWidth = getColWidth();
var snapHeight = getSnapHeight();
var snapMinutes = getSnapMinutes();
var minMinute = getMinMinute();
eventElement.draggable({
opacity: opt('dragOpacity', 'month'), // use whatever the month view was using
revertDuration: opt('dragRevertDuration'),
start: function(ev, ui) {
trigger('eventDragStart', eventElement, event, ev, ui);
hideEvents(event, eventElement);
origWidth = eventElement.width();
hoverListener.start(function(cell, origCell) {
clearOverlays();
if (cell) {
revert = false;
var origDate = cellToDate(0, origCell.col);
var date = cellToDate(0, cell.col);
dayDelta = dayDiff(date, origDate);
if (!cell.row) {
// on full-days
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
resetElement();
}else{
// mouse is over bottom slots
if (isStart) {
if (allDay) {
// convert event to temporary slot-event
eventElement.width(colWidth - 10); // don't use entire width
setOuterHeight(
eventElement,
snapHeight * Math.round(
(event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) /
snapMinutes
)
);
eventElement.draggable('option', 'grid', [colWidth, 1]);
allDay = false;
}
}else{
revert = true;
}
}
revert = revert || (allDay && !dayDelta);
}else{
resetElement();
revert = true;
}
eventElement.draggable('option', 'revert', revert);
}, ev, 'drag');
},
stop: function(ev, ui) {
hoverListener.stop();
clearOverlays();
trigger('eventDragStop', eventElement, event, ev, ui);
if (revert) {
// hasn't moved or is out of bounds (draggable has already reverted)
resetElement();
eventElement.css('filter', ''); // clear IE opacity side-effects
showEvents(event, eventElement);
}else{
// changed!
var minuteDelta = 0;
if (!allDay) {
minuteDelta = Math.round((eventElement.offset().top - getSlotContainer().offset().top) / snapHeight)
* snapMinutes
+ minMinute
- (event.start.getHours() * 60 + event.start.getMinutes());
}
eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
}
}
});
function resetElement() {
if (!allDay) {
eventElement
.width(origWidth)
.height('')
.draggable('option', 'grid', null);
allDay = true;
}
}
}
// when event starts out IN TIMESLOTS
function draggableSlotEvent(event, eventElement, timeElement) {
var coordinateGrid = t.getCoordinateGrid();
var colCnt = getColCnt();
var colWidth = getColWidth();
var snapHeight = getSnapHeight();
var snapMinutes = getSnapMinutes();
// states
var origPosition; // original position of the element, not the mouse
var origCell;
var isInBounds, prevIsInBounds;
var isAllDay, prevIsAllDay;
var colDelta, prevColDelta;
var dayDelta; // derived from colDelta
var minuteDelta, prevMinuteDelta;
eventElement.draggable({
scroll: false,
grid: [ colWidth, snapHeight ],
axis: colCnt==1 ? 'y' : false,
opacity: opt('dragOpacity'),
revertDuration: opt('dragRevertDuration'),
start: function(ev, ui) {
trigger('eventDragStart', eventElement, event, ev, ui);
hideEvents(event, eventElement);
coordinateGrid.build();
// initialize states
origPosition = eventElement.position();
origCell = coordinateGrid.cell(ev.pageX, ev.pageY);
isInBounds = prevIsInBounds = true;
isAllDay = prevIsAllDay = getIsCellAllDay(origCell);
colDelta = prevColDelta = 0;
dayDelta = 0;
minuteDelta = prevMinuteDelta = 0;
},
drag: function(ev, ui) {
// NOTE: this `cell` value is only useful for determining in-bounds and all-day.
// Bad for anything else due to the discrepancy between the mouse position and the
// element position while snapping. (problem revealed in PR #55)
//
// PS- the problem exists for draggableDayEvent() when dragging an all-day event to a slot event.
// We should overhaul the dragging system and stop relying on jQuery UI.
var cell = coordinateGrid.cell(ev.pageX, ev.pageY);
// update states
isInBounds = !!cell;
if (isInBounds) {
isAllDay = getIsCellAllDay(cell);
// calculate column delta
colDelta = Math.round((ui.position.left - origPosition.left) / colWidth);
if (colDelta != prevColDelta) {
// calculate the day delta based off of the original clicked column and the column delta
var origDate = cellToDate(0, origCell.col);
var col = origCell.col + colDelta;
col = Math.max(0, col);
col = Math.min(colCnt-1, col);
var date = cellToDate(0, col);
dayDelta = dayDiff(date, origDate);
}
// calculate minute delta (only if over slots)
if (!isAllDay) {
minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes;
}
}
// any state changes?
if (
isInBounds != prevIsInBounds ||
isAllDay != prevIsAllDay ||
colDelta != prevColDelta ||
minuteDelta != prevMinuteDelta
) {
updateUI();
// update previous states for next time
prevIsInBounds = isInBounds;
prevIsAllDay = isAllDay;
prevColDelta = colDelta;
prevMinuteDelta = minuteDelta;
}
// if out-of-bounds, revert when done, and vice versa.
eventElement.draggable('option', 'revert', !isInBounds);
},
stop: function(ev, ui) {
clearOverlays();
trigger('eventDragStop', eventElement, event, ev, ui);
if (isInBounds && (isAllDay || dayDelta || minuteDelta)) { // changed!
eventDrop(this, event, dayDelta, isAllDay ? 0 : minuteDelta, isAllDay, ev, ui);
}
else { // either no change or out-of-bounds (draggable has already reverted)
// reset states for next time, and for updateUI()
isInBounds = true;
isAllDay = false;
colDelta = 0;
dayDelta = 0;
minuteDelta = 0;
updateUI();
eventElement.css('filter', ''); // clear IE opacity side-effects
// sometimes fast drags make event revert to wrong position, so reset.
// also, if we dragged the element out of the area because of snapping,
// but the *mouse* is still in bounds, we need to reset the position.
eventElement.css(origPosition);
showEvents(event, eventElement);
}
}
});
function updateUI() {
clearOverlays();
if (isInBounds) {
if (isAllDay) {
timeElement.hide();
eventElement.draggable('option', 'grid', null); // disable grid snapping
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
}
else {
updateTimeText(minuteDelta);
timeElement.css('display', ''); // show() was causing display=inline
eventElement.draggable('option', 'grid', [colWidth, snapHeight]); // re-enable grid snapping
}
}
}
function updateTimeText(minuteDelta) {
var newStart = addMinutes(cloneDate(event.start), minuteDelta);
var newEnd;
if (event.end) {
newEnd = addMinutes(cloneDate(event.end), minuteDelta);
}
timeElement.text(formatDates(newStart, newEnd, opt('timeFormat')));
}
}
/* Resizing
--------------------------------------------------------------------------------------*/
function resizableSlotEvent(event, eventElement, timeElement) {
var snapDelta, prevSnapDelta;
var snapHeight = getSnapHeight();
var snapMinutes = getSnapMinutes();
eventElement.resizable({
handles: {
s: '.ui-resizable-handle'
},
grid: snapHeight,
start: function(ev, ui) {
snapDelta = prevSnapDelta = 0;
hideEvents(event, eventElement);
trigger('eventResizeStart', this, event, ev, ui);
},
resize: function(ev, ui) {
// don't rely on ui.size.height, doesn't take grid into account
snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight);
if (snapDelta != prevSnapDelta) {
timeElement.text(
formatDates(
event.start,
(!snapDelta && !event.end) ? null : // no change, so don't display time range
addMinutes(eventEnd(event), snapMinutes*snapDelta),
opt('timeFormat')
)
);
prevSnapDelta = snapDelta;
}
},
stop: function(ev, ui) {
trigger('eventResizeStop', this, event, ev, ui);
if (snapDelta) {
eventResize(this, event, 0, snapMinutes*snapDelta, ev, ui);
}else{
showEvents(event, eventElement);
// BUG: if event was really short, need to put title back in span
}
}
});
}
}
/* Agenda Event Segment Utilities
-----------------------------------------------------------------------------*/
// Sets the seg.backwardCoord and seg.forwardCoord on each segment and returns a new
// list in the order they should be placed into the DOM (an implicit z-index).
function placeSlotSegs(segs) {
var levels = buildSlotSegLevels(segs);
var level0 = levels[0];
var i;
computeForwardSlotSegs(levels);
if (level0) {
for (i=0; i<level0.length; i++) {
computeSlotSegPressures(level0[i]);
}
for (i=0; i<level0.length; i++) {
computeSlotSegCoords(level0[i], 0, 0);
}
}
return flattenSlotSegLevels(levels);
}
// Builds an array of segments "levels". The first level will be the leftmost tier of segments
// if the calendar is left-to-right, or the rightmost if the calendar is right-to-left.
function buildSlotSegLevels(segs) {
var levels = [];
var i, seg;
var j;
for (i=0; i<segs.length; i++) {
seg = segs[i];
// go through all the levels and stop on the first level where there are no collisions
for (j=0; j<levels.length; j++) {
if (!computeSlotSegCollisions(seg, levels[j]).length) {
break;
}
}
(levels[j] || (levels[j] = [])).push(seg);
}
return levels;
}
// For every segment, figure out the other segments that are in subsequent
// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
function computeForwardSlotSegs(levels) {
var i, level;
var j, seg;
var k;
for (i=0; i<levels.length; i++) {
level = levels[i];
for (j=0; j<level.length; j++) {
seg = level[j];
seg.forwardSegs = [];
for (k=i+1; k<levels.length; k++) {
computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
}
}
}
}
// Figure out which path forward (via seg.forwardSegs) results in the longest path until
// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
function computeSlotSegPressures(seg) {
var forwardSegs = seg.forwardSegs;
var forwardPressure = 0;
var i, forwardSeg;
if (seg.forwardPressure === undefined) { // not already computed
for (i=0; i<forwardSegs.length; i++) {
forwardSeg = forwardSegs[i];
// figure out the child's maximum forward path
computeSlotSegPressures(forwardSeg);
// either use the existing maximum, or use the child's forward pressure
// plus one (for the forwardSeg itself)
forwardPressure = Math.max(
forwardPressure,
1 + forwardSeg.forwardPressure
);
}
seg.forwardPressure = forwardPressure;
}
}
// Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
// from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
// seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
//
// The segment might be part of a "series", which means consecutive segments with the same pressure
// who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
// segments behind this one in the current series, and `seriesBackwardCoord` is the starting
// coordinate of the first segment in the series.
function computeSlotSegCoords(seg, seriesBackwardPressure, seriesBackwardCoord) {
var forwardSegs = seg.forwardSegs;
var i;
if (seg.forwardCoord === undefined) { // not already computed
if (!forwardSegs.length) {
// if there are no forward segments, this segment should butt up against the edge
seg.forwardCoord = 1;
}
else {
// sort highest pressure first
forwardSegs.sort(compareForwardSlotSegs);
// this segment's forwardCoord will be calculated from the backwardCoord of the
// highest-pressure forward segment.
computeSlotSegCoords(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
seg.forwardCoord = forwardSegs[0].backwardCoord;
}
// calculate the backwardCoord from the forwardCoord. consider the series
seg.backwardCoord = seg.forwardCoord -
(seg.forwardCoord - seriesBackwardCoord) / // available width for series
(seriesBackwardPressure + 1); // # of segments in the series
// use this segment's coordinates to computed the coordinates of the less-pressurized
// forward segments
for (i=0; i<forwardSegs.length; i++) {
computeSlotSegCoords(forwardSegs[i], 0, seg.forwardCoord);
}
}
}
// Outputs a flat array of segments, from lowest to highest level
function flattenSlotSegLevels(levels) {
var segs = [];
var i, level;
var j;
for (i=0; i<levels.length; i++) {
level = levels[i];
for (j=0; j<level.length; j++) {
segs.push(level[j]);
}
}
return segs;
}
// Find all the segments in `otherSegs` that vertically collide with `seg`.
// Append into an optionally-supplied `results` array and return.
function computeSlotSegCollisions(seg, otherSegs, results) {
results = results || [];
for (var i=0; i<otherSegs.length; i++) {
if (isSlotSegCollision(seg, otherSegs[i])) {
results.push(otherSegs[i]);
}
}
return results;
}
// Do these segments occupy the same vertical space?
function isSlotSegCollision(seg1, seg2) {
return seg1.end > seg2.start && seg1.start < seg2.end;
}
// A cmp function for determining which forward segment to rely on more when computing coordinates.
function compareForwardSlotSegs(seg1, seg2) {
// put higher-pressure first
return seg2.forwardPressure - seg1.forwardPressure ||
// put segments that are closer to initial edge first (and favor ones with no coords yet)
(seg1.backwardCoord || 0) - (seg2.backwardCoord || 0) ||
// do normal sorting...
compareSlotSegs(seg1, seg2);
}
// A cmp function for determining which segment should be closer to the initial edge
// (the left edge on a left-to-right calendar).
function compareSlotSegs(seg1, seg2) {
return seg1.start - seg2.start || // earlier start time goes first
(seg2.end - seg2.start) - (seg1.end - seg1.start) || // tie? longer-duration goes first
(seg1.event.title || '').localeCompare(seg2.event.title); // tie? alphabetically by title
}
;;
function View(element, calendar, viewName) {
var t = this;
// exports
t.element = element;
t.calendar = calendar;
t.name = viewName;
t.opt = opt;
t.trigger = trigger;
t.isEventDraggable = isEventDraggable;
t.isEventResizable = isEventResizable;
t.setEventData = setEventData;
t.clearEventData = clearEventData;
t.eventEnd = eventEnd;
t.reportEventElement = reportEventElement;
t.triggerEventDestroy = triggerEventDestroy;
t.eventElementHandlers = eventElementHandlers;
t.showEvents = showEvents;
t.hideEvents = hideEvents;
t.eventDrop = eventDrop;
t.eventResize = eventResize;
// t.title
// t.start, t.end
// t.visStart, t.visEnd
// imports
var defaultEventEnd = t.defaultEventEnd;
var normalizeEvent = calendar.normalizeEvent; // in EventManager
var reportEventChange = calendar.reportEventChange;
// locals
var eventsByID = {}; // eventID mapped to array of events (there can be multiple b/c of repeating events)
var eventElementsByID = {}; // eventID mapped to array of jQuery elements
var eventElementCouples = []; // array of objects, { event, element } // TODO: unify with segment system
var options = calendar.options;
function opt(name, viewNameOverride) {
var v = options[name];
if ($.isPlainObject(v)) {
return smartProperty(v, viewNameOverride || viewName);
}
return v;
}
function trigger(name, thisObj) {
return calendar.trigger.apply(
calendar,
[name, thisObj || t].concat(Array.prototype.slice.call(arguments, 2), [t])
);
}
/* Event Editable Boolean Calculations
------------------------------------------------------------------------------*/
function isEventDraggable(event) {
var source = event.source || {};
return firstDefined(
event.startEditable,
source.startEditable,
opt('eventStartEditable'),
event.editable,
source.editable,
opt('editable')
)
&& !opt('disableDragging'); // deprecated
}
function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
var source = event.source || {};
return firstDefined(
event.durationEditable,
source.durationEditable,
opt('eventDurationEditable'),
event.editable,
source.editable,
opt('editable')
)
&& !opt('disableResizing'); // deprecated
}
/* Event Data
------------------------------------------------------------------------------*/
function setEventData(events) { // events are already normalized at this point
eventsByID = {};
var i, len=events.length, event;
for (i=0; i<len; i++) {
event = events[i];
if (eventsByID[event._id]) {
eventsByID[event._id].push(event);
}else{
eventsByID[event._id] = [event];
}
}
}
function clearEventData() {
eventsByID = {};
eventElementsByID = {};
eventElementCouples = [];
}
// returns a Date object for an event's end
function eventEnd(event) {
return event.end ? cloneDate(event.end) : defaultEventEnd(event);
}
/* Event Elements
------------------------------------------------------------------------------*/
// report when view creates an element for an event
function reportEventElement(event, element) {
eventElementCouples.push({ event: event, element: element });
if (eventElementsByID[event._id]) {
eventElementsByID[event._id].push(element);
}else{
eventElementsByID[event._id] = [element];
}
}
function triggerEventDestroy() {
$.each(eventElementCouples, function(i, couple) {
t.trigger('eventDestroy', couple.event, couple.event, couple.element);
});
}
// attaches eventClick, eventMouseover, eventMouseout
function eventElementHandlers(event, eventElement) {
eventElement
.click(function(ev) {
if (!eventElement.hasClass('ui-draggable-dragging') &&
!eventElement.hasClass('ui-resizable-resizing')) {
return trigger('eventClick', this, event, ev);
}
})
.hover(
function(ev) {
trigger('eventMouseover', this, event, ev);
},
function(ev) {
trigger('eventMouseout', this, event, ev);
}
);
// TODO: don't fire eventMouseover/eventMouseout *while* dragging is occuring (on subject element)
// TODO: same for resizing
}
function showEvents(event, exceptElement) {
eachEventElement(event, exceptElement, 'show');
}
function hideEvents(event, exceptElement) {
eachEventElement(event, exceptElement, 'hide');
}
function eachEventElement(event, exceptElement, funcName) {
// NOTE: there may be multiple events per ID (repeating events)
// and multiple segments per event
var elements = eventElementsByID[event._id],
i, len = elements.length;
for (i=0; i<len; i++) {
if (!exceptElement || elements[i][0] != exceptElement[0]) {
elements[i][funcName]();
}
}
}
/* Event Modification Reporting
---------------------------------------------------------------------------------*/
function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
var oldAllDay = event.allDay;
var eventId = event._id;
moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay);
trigger(
'eventDrop',
e,
event,
dayDelta,
minuteDelta,
allDay,
function() {
// TODO: investigate cases where this inverse technique might not work
moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
reportEventChange(eventId);
},
ev,
ui
);
reportEventChange(eventId);
}
function eventResize(e, event, dayDelta, minuteDelta, ev, ui) {
var eventId = event._id;
elongateEvents(eventsByID[eventId], dayDelta, minuteDelta);
trigger(
'eventResize',
e,
event,
dayDelta,
minuteDelta,
function() {
// TODO: investigate cases where this inverse technique might not work
elongateEvents(eventsByID[eventId], -dayDelta, -minuteDelta);
reportEventChange(eventId);
},
ev,
ui
);
reportEventChange(eventId);
}
/* Event Modification Math
---------------------------------------------------------------------------------*/
function moveEvents(events, dayDelta, minuteDelta, allDay) {
minuteDelta = minuteDelta || 0;
for (var e, len=events.length, i=0; i<len; i++) {
e = events[i];
if (allDay !== undefined) {
e.allDay = allDay;
}
addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
if (e.end) {
e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
}
normalizeEvent(e, options);
}
}
function elongateEvents(events, dayDelta, minuteDelta) {
minuteDelta = minuteDelta || 0;
for (var e, len=events.length, i=0; i<len; i++) {
e = events[i];
e.end = addMinutes(addDays(eventEnd(e), dayDelta, true), minuteDelta);
normalizeEvent(e, options);
}
}
// ====================================================================================================
// Utilities for day "cells"
// ====================================================================================================
// The "basic" views are completely made up of day cells.
// The "agenda" views have day cells at the top "all day" slot.
// This was the obvious common place to put these utilities, but they should be abstracted out into
// a more meaningful class (like DayEventRenderer).
// ====================================================================================================
// For determining how a given "cell" translates into a "date":
//
// 1. Convert the "cell" (row and column) into a "cell offset" (the # of the cell, cronologically from the first).
// Keep in mind that column indices are inverted with isRTL. This is taken into account.
//
// 2. Convert the "cell offset" to a "day offset" (the # of days since the first visible day in the view).
//
// 3. Convert the "day offset" into a "date" (a JavaScript Date object).
//
// The reverse transformation happens when transforming a date into a cell.
// exports
t.isHiddenDay = isHiddenDay;
t.skipHiddenDays = skipHiddenDays;
t.getCellsPerWeek = getCellsPerWeek;
t.dateToCell = dateToCell;
t.dateToDayOffset = dateToDayOffset;
t.dayOffsetToCellOffset = dayOffsetToCellOffset;
t.cellOffsetToCell = cellOffsetToCell;
t.cellToDate = cellToDate;
t.cellToCellOffset = cellToCellOffset;
t.cellOffsetToDayOffset = cellOffsetToDayOffset;
t.dayOffsetToDate = dayOffsetToDate;
t.rangeToSegments = rangeToSegments;
// internals
var hiddenDays = opt('hiddenDays') || []; // array of day-of-week indices that are hidden
var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
var cellsPerWeek;
var dayToCellMap = []; // hash from dayIndex -> cellIndex, for one week
var cellToDayMap = []; // hash from cellIndex -> dayIndex, for one week
var isRTL = opt('isRTL');
// initialize important internal variables
(function() {
if (opt('weekends') === false) {
hiddenDays.push(0, 6); // 0=sunday, 6=saturday
}
// Loop through a hypothetical week and determine which
// days-of-week are hidden. Record in both hashes (one is the reverse of the other).
for (var dayIndex=0, cellIndex=0; dayIndex<7; dayIndex++) {
dayToCellMap[dayIndex] = cellIndex;
isHiddenDayHash[dayIndex] = $.inArray(dayIndex, hiddenDays) != -1;
if (!isHiddenDayHash[dayIndex]) {
cellToDayMap[cellIndex] = dayIndex;
cellIndex++;
}
}
cellsPerWeek = cellIndex;
if (!cellsPerWeek) {
throw 'invalid hiddenDays'; // all days were hidden? bad.
}
})();
// Is the current day hidden?
// `day` is a day-of-week index (0-6), or a Date object
function isHiddenDay(day) {
if (typeof day == 'object') {
day = day.getDay();
}
return isHiddenDayHash[day];
}
function getCellsPerWeek() {
return cellsPerWeek;
}
// Keep incrementing the current day until it is no longer a hidden day.
// If the initial value of `date` is not a hidden day, don't do anything.
// Pass `isExclusive` as `true` if you are dealing with an end date.
// `inc` defaults to `1` (increment one day forward each time)
function skipHiddenDays(date, inc, isExclusive) {
inc = inc || 1;
while (
isHiddenDayHash[ ( date.getDay() + (isExclusive ? inc : 0) + 7 ) % 7 ]
) {
addDays(date, inc);
}
}
//
// TRANSFORMATIONS: cell -> cell offset -> day offset -> date
//
// cell -> date (combines all transformations)
// Possible arguments:
// - row, col
// - { row:#, col: # }
function cellToDate() {
var cellOffset = cellToCellOffset.apply(null, arguments);
var dayOffset = cellOffsetToDayOffset(cellOffset);
var date = dayOffsetToDate(dayOffset);
return date;
}
// cell -> cell offset
// Possible arguments:
// - row, col
// - { row:#, col:# }
function cellToCellOffset(row, col) {
var colCnt = t.getColCnt();
// rtl variables. wish we could pre-populate these. but where?
var dis = isRTL ? -1 : 1;
var dit = isRTL ? colCnt - 1 : 0;
if (typeof row == 'object') {
col = row.col;
row = row.row;
}
var cellOffset = row * colCnt + (col * dis + dit); // column, adjusted for RTL (dis & dit)
return cellOffset;
}
// cell offset -> day offset
function cellOffsetToDayOffset(cellOffset) {
var day0 = t.visStart.getDay(); // first date's day of week
cellOffset += dayToCellMap[day0]; // normlize cellOffset to beginning-of-week
return Math.floor(cellOffset / cellsPerWeek) * 7 // # of days from full weeks
+ cellToDayMap[ // # of days from partial last week
(cellOffset % cellsPerWeek + cellsPerWeek) % cellsPerWeek // crazy math to handle negative cellOffsets
]
- day0; // adjustment for beginning-of-week normalization
}
// day offset -> date (JavaScript Date object)
function dayOffsetToDate(dayOffset) {
var date = cloneDate(t.visStart);
addDays(date, dayOffset);
return date;
}
//
// TRANSFORMATIONS: date -> day offset -> cell offset -> cell
//
// date -> cell (combines all transformations)
function dateToCell(date) {
var dayOffset = dateToDayOffset(date);
var cellOffset = dayOffsetToCellOffset(dayOffset);
var cell = cellOffsetToCell(cellOffset);
return cell;
}
// date -> day offset
function dateToDayOffset(date) {
return dayDiff(date, t.visStart);
}
// day offset -> cell offset
function dayOffsetToCellOffset(dayOffset) {
var day0 = t.visStart.getDay(); // first date's day of week
dayOffset += day0; // normalize dayOffset to beginning-of-week
return Math.floor(dayOffset / 7) * cellsPerWeek // # of cells from full weeks
+ dayToCellMap[ // # of cells from partial last week
(dayOffset % 7 + 7) % 7 // crazy math to handle negative dayOffsets
]
- dayToCellMap[day0]; // adjustment for beginning-of-week normalization
}
// cell offset -> cell (object with row & col keys)
function cellOffsetToCell(cellOffset) {
var colCnt = t.getColCnt();
// rtl variables. wish we could pre-populate these. but where?
var dis = isRTL ? -1 : 1;
var dit = isRTL ? colCnt - 1 : 0;
var row = Math.floor(cellOffset / colCnt);
var col = ((cellOffset % colCnt + colCnt) % colCnt) * dis + dit; // column, adjusted for RTL (dis & dit)
return {
row: row,
col: col
};
}
//
// Converts a date range into an array of segment objects.
// "Segments" are horizontal stretches of time, sliced up by row.
// A segment object has the following properties:
// - row
// - cols
// - isStart
// - isEnd
//
function rangeToSegments(startDate, endDate) {
var rowCnt = t.getRowCnt();
var colCnt = t.getColCnt();
var segments = []; // array of segments to return
// day offset for given date range
var rangeDayOffsetStart = dateToDayOffset(startDate);
var rangeDayOffsetEnd = dateToDayOffset(endDate); // exclusive
// first and last cell offset for the given date range
// "last" implies inclusivity
var rangeCellOffsetFirst = dayOffsetToCellOffset(rangeDayOffsetStart);
var rangeCellOffsetLast = dayOffsetToCellOffset(rangeDayOffsetEnd) - 1;
// loop through all the rows in the view
for (var row=0; row<rowCnt; row++) {
// first and last cell offset for the row
var rowCellOffsetFirst = row * colCnt;
var rowCellOffsetLast = rowCellOffsetFirst + colCnt - 1;
// get the segment's cell offsets by constraining the range's cell offsets to the bounds of the row
var segmentCellOffsetFirst = Math.max(rangeCellOffsetFirst, rowCellOffsetFirst);
var segmentCellOffsetLast = Math.min(rangeCellOffsetLast, rowCellOffsetLast);
// make sure segment's offsets are valid and in view
if (segmentCellOffsetFirst <= segmentCellOffsetLast) {
// translate to cells
var segmentCellFirst = cellOffsetToCell(segmentCellOffsetFirst);
var segmentCellLast = cellOffsetToCell(segmentCellOffsetLast);
// view might be RTL, so order by leftmost column
var cols = [ segmentCellFirst.col, segmentCellLast.col ].sort();
// Determine if segment's first/last cell is the beginning/end of the date range.
// We need to compare "day offset" because "cell offsets" are often ambiguous and
// can translate to multiple days, and an edge case reveals itself when we the
// range's first cell is hidden (we don't want isStart to be true).
var isStart = cellOffsetToDayOffset(segmentCellOffsetFirst) == rangeDayOffsetStart;
var isEnd = cellOffsetToDayOffset(segmentCellOffsetLast) + 1 == rangeDayOffsetEnd; // +1 for comparing exclusively
segments.push({
row: row,
leftCol: cols[0],
rightCol: cols[1],
isStart: isStart,
isEnd: isEnd
});
}
}
return segments;
}
}
;;
function DayEventRenderer() {
var t = this;
// exports
t.renderDayEvents = renderDayEvents;
t.draggableDayEvent = draggableDayEvent; // made public so that subclasses can override
t.resizableDayEvent = resizableDayEvent; // "
// imports
var opt = t.opt;
var trigger = t.trigger;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var eventEnd = t.eventEnd;
var reportEventElement = t.reportEventElement;
var eventElementHandlers = t.eventElementHandlers;
var showEvents = t.showEvents;
var hideEvents = t.hideEvents;
var eventDrop = t.eventDrop;
var eventResize = t.eventResize;
var getRowCnt = t.getRowCnt;
var getColCnt = t.getColCnt;
var getColWidth = t.getColWidth;
var allDayRow = t.allDayRow; // TODO: rename
var colLeft = t.colLeft;
var colRight = t.colRight;
var colContentLeft = t.colContentLeft;
var colContentRight = t.colContentRight;
var dateToCell = t.dateToCell;
var getDaySegmentContainer = t.getDaySegmentContainer;
var formatDates = t.calendar.formatDates;
var renderDayOverlay = t.renderDayOverlay;
var clearOverlays = t.clearOverlays;
var clearSelection = t.clearSelection;
var getHoverListener = t.getHoverListener;
var rangeToSegments = t.rangeToSegments;
var cellToDate = t.cellToDate;
var cellToCellOffset = t.cellToCellOffset;
var cellOffsetToDayOffset = t.cellOffsetToDayOffset;
var dateToDayOffset = t.dateToDayOffset;
var dayOffsetToCellOffset = t.dayOffsetToCellOffset;
// Render `events` onto the calendar, attach mouse event handlers, and call the `eventAfterRender` callback for each.
// Mouse event will be lazily applied, except if the event has an ID of `modifiedEventId`.
// Can only be called when the event container is empty (because it wipes out all innerHTML).
function renderDayEvents(events, modifiedEventId) {
// do the actual rendering. Receive the intermediate "segment" data structures.
var segments = _renderDayEvents(
events,
false, // don't append event elements
true // set the heights of the rows
);
// report the elements to the View, for general drag/resize utilities
segmentElementEach(segments, function(segment, element) {
reportEventElement(segment.event, element);
});
// attach mouse handlers
attachHandlers(segments, modifiedEventId);
// call `eventAfterRender` callback for each event
segmentElementEach(segments, function(segment, element) {
trigger('eventAfterRender', segment.event, segment.event, element);
});
}
// Render an event on the calendar, but don't report them anywhere, and don't attach mouse handlers.
// Append this event element to the event container, which might already be populated with events.
// If an event's segment will have row equal to `adjustRow`, then explicitly set its top coordinate to `adjustTop`.
// This hack is used to maintain continuity when user is manually resizing an event.
// Returns an array of DOM elements for the event.
function renderTempDayEvent(event, adjustRow, adjustTop) {
// actually render the event. `true` for appending element to container.
// Recieve the intermediate "segment" data structures.
var segments = _renderDayEvents(
[ event ],
true, // append event elements
false // don't set the heights of the rows
);
var elements = [];
// Adjust certain elements' top coordinates
segmentElementEach(segments, function(segment, element) {
if (segment.row === adjustRow) {
element.css('top', adjustTop);
}
elements.push(element[0]); // accumulate DOM nodes
});
return elements;
}
// Render events onto the calendar. Only responsible for the VISUAL aspect.
// Not responsible for attaching handlers or calling callbacks.
// Set `doAppend` to `true` for rendering elements without clearing the existing container.
// Set `doRowHeights` to allow setting the height of each row, to compensate for vertical event overflow.
function _renderDayEvents(events, doAppend, doRowHeights) {
// where the DOM nodes will eventually end up
var finalContainer = getDaySegmentContainer();
// the container where the initial HTML will be rendered.
// If `doAppend`==true, uses a temporary container.
var renderContainer = doAppend ? $("<div/>") : finalContainer;
var segments = buildSegments(events);
var html;
var elements;
// calculate the desired `left` and `width` properties on each segment object
calculateHorizontals(segments);
// build the HTML string. relies on `left` property
html = buildHTML(segments);
// render the HTML. innerHTML is considerably faster than jQuery's .html()
renderContainer[0].innerHTML = html;
// retrieve the individual elements
elements = renderContainer.children();
// if we were appending, and thus using a temporary container,
// re-attach elements to the real container.
if (doAppend) {
finalContainer.append(elements);
}
// assigns each element to `segment.event`, after filtering them through user callbacks
resolveElements(segments, elements);
// Calculate the left and right padding+margin for each element.
// We need this for setting each element's desired outer width, because of the W3C box model.
// It's important we do this in a separate pass from acually setting the width on the DOM elements
// because alternating reading/writing dimensions causes reflow for every iteration.
segmentElementEach(segments, function(segment, element) {
segment.hsides = hsides(element, true); // include margins = `true`
});
// Set the width of each element
segmentElementEach(segments, function(segment, element) {
element.width(
Math.max(0, segment.outerWidth - segment.hsides)
);
});
// Grab each element's outerHeight (setVerticals uses this).
// To get an accurate reading, it's important to have each element's width explicitly set already.
segmentElementEach(segments, function(segment, element) {
segment.outerHeight = element.outerHeight(true); // include margins = `true`
});
// Set the top coordinate on each element (requires segment.outerHeight)
setVerticals(segments, doRowHeights);
return segments;
}
// Generate an array of "segments" for all events.
function buildSegments(events) {
var segments = [];
for (var i=0; i<events.length; i++) {
var eventSegments = buildSegmentsForEvent(events[i]);
segments.push.apply(segments, eventSegments); // append an array to an array
}
return segments;
}
// Generate an array of segments for a single event.
// A "segment" is the same data structure that View.rangeToSegments produces,
// with the addition of the `event` property being set to reference the original event.
function buildSegmentsForEvent(event) {
var startDate = event.start;
var endDate = exclEndDay(event);
var segments = rangeToSegments(startDate, endDate);
for (var i=0; i<segments.length; i++) {
segments[i].event = event;
}
return segments;
}
// Sets the `left` and `outerWidth` property of each segment.
// These values are the desired dimensions for the eventual DOM elements.
function calculateHorizontals(segments) {
var isRTL = opt('isRTL');
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
// Determine functions used for calulating the elements left/right coordinates,
// depending on whether the view is RTL or not.
// NOTE:
// colLeft/colRight returns the coordinate butting up the edge of the cell.
// colContentLeft/colContentRight is indented a little bit from the edge.
var leftFunc = (isRTL ? segment.isEnd : segment.isStart) ? colContentLeft : colLeft;
var rightFunc = (isRTL ? segment.isStart : segment.isEnd) ? colContentRight : colRight;
var left = leftFunc(segment.leftCol);
var right = rightFunc(segment.rightCol);
segment.left = left;
segment.outerWidth = right - left;
}
}
// Build a concatenated HTML string for an array of segments
function buildHTML(segments) {
var html = '';
for (var i=0; i<segments.length; i++) {
html += buildHTMLForSegment(segments[i]);
}
return html;
}
// Build an HTML string for a single segment.
// Relies on the following properties:
// - `segment.event` (from `buildSegmentsForEvent`)
// - `segment.left` (from `calculateHorizontals`)
function buildHTMLForSegment(segment) {
var html = '';
var isRTL = opt('isRTL');
var event = segment.event;
var url = event.url;
// generate the list of CSS classNames
var classNames = [ 'fc-event', 'fc-event-hori' ];
if (isEventDraggable(event)) {
classNames.push('fc-event-draggable');
}
if (segment.isStart) {
classNames.push('fc-event-start');
}
if (segment.isEnd) {
classNames.push('fc-event-end');
}
// use the event's configured classNames
// guaranteed to be an array via `normalizeEvent`
classNames = classNames.concat(event.className);
if (event.source) {
// use the event's source's classNames, if specified
classNames = classNames.concat(event.source.className || []);
}
// generate a semicolon delimited CSS string for any of the "skin" properties
// of the event object (`backgroundColor`, `borderColor` and such)
var skinCss = getSkinCss(event, opt);
if (url) {
html += "<a href='" + htmlEscape(url) + "'";
}else{
html += "<div";
}
html +=
" class='" + classNames.join(' ') + "'" +
" style=" +
"'" +
"position:absolute;" +
"left:" + segment.left + "px;" +
skinCss +
"'" +
">" +
"<div class='fc-event-inner'>";
if (!event.allDay && segment.isStart) {
html +=
"<span class='fc-event-time'>" +
htmlEscape(
formatDates(event.start, event.end, opt('timeFormat'))
) +
"</span>";
}
html +=
"<span class='fc-event-title'>" +
htmlEscape(event.title || '') +
"</span>" +
"</div>";
if (segment.isEnd && isEventResizable(event)) {
html +=
"<div class='ui-resizable-handle ui-resizable-" + (isRTL ? 'w' : 'e') + "'>" +
" " + // makes hit area a lot better for IE6/7
"</div>";
}
html += "</" + (url ? "a" : "div") + ">";
// TODO:
// When these elements are initially rendered, they will be briefly visibile on the screen,
// even though their widths/heights are not set.
// SOLUTION: initially set them as visibility:hidden ?
return html;
}
// Associate each segment (an object) with an element (a jQuery object),
// by setting each `segment.element`.
// Run each element through the `eventRender` filter, which allows developers to
// modify an existing element, supply a new one, or cancel rendering.
function resolveElements(segments, elements) {
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
var event = segment.event;
var element = elements.eq(i);
// call the trigger with the original element
var triggerRes = trigger('eventRender', event, event, element);
if (triggerRes === false) {
// if `false`, remove the event from the DOM and don't assign it to `segment.event`
element.remove();
}
else {
if (triggerRes && triggerRes !== true) {
// the trigger returned a new element, but not `true` (which means keep the existing element)
// re-assign the important CSS dimension properties that were already assigned in `buildHTMLForSegment`
triggerRes = $(triggerRes)
.css({
position: 'absolute',
left: segment.left
});
element.replaceWith(triggerRes);
element = triggerRes;
}
segment.element = element;
}
}
}
/* Top-coordinate Methods
-------------------------------------------------------------------------------------------------*/
// Sets the "top" CSS property for each element.
// If `doRowHeights` is `true`, also sets each row's first cell to an explicit height,
// so that if elements vertically overflow, the cell expands vertically to compensate.
function setVerticals(segments, doRowHeights) {
var rowContentHeights = calculateVerticals(segments); // also sets segment.top
var rowContentElements = getRowContentElements(); // returns 1 inner div per row
var rowContentTops = [];
// Set each row's height by setting height of first inner div
if (doRowHeights) {
for (var i=0; i<rowContentElements.length; i++) {
rowContentElements[i].height(rowContentHeights[i]);
}
}
// Get each row's top, relative to the views's origin.
// Important to do this after setting each row's height.
for (var i=0; i<rowContentElements.length; i++) {
rowContentTops.push(
rowContentElements[i].position().top
);
}
// Set each segment element's CSS "top" property.
// Each segment object has a "top" property, which is relative to the row's top, but...
segmentElementEach(segments, function(segment, element) {
element.css(
'top',
rowContentTops[segment.row] + segment.top // ...now, relative to views's origin
);
});
}
// Calculate the "top" coordinate for each segment, relative to the "top" of the row.
// Also, return an array that contains the "content" height for each row
// (the height displaced by the vertically stacked events in the row).
// Requires segments to have their `outerHeight` property already set.
function calculateVerticals(segments) {
var rowCnt = getRowCnt();
var colCnt = getColCnt();
var rowContentHeights = []; // content height for each row
var segmentRows = buildSegmentRows(segments); // an array of segment arrays, one for each row
for (var rowI=0; rowI<rowCnt; rowI++) {
var segmentRow = segmentRows[rowI];
// an array of running total heights for each column.
// initialize with all zeros.
var colHeights = [];
for (var colI=0; colI<colCnt; colI++) {
colHeights.push(0);
}
// loop through every segment
for (var segmentI=0; segmentI<segmentRow.length; segmentI++) {
var segment = segmentRow[segmentI];
// find the segment's top coordinate by looking at the max height
// of all the columns the segment will be in.
segment.top = arrayMax(
colHeights.slice(
segment.leftCol,
segment.rightCol + 1 // make exclusive for slice
)
);
// adjust the columns to account for the segment's height
for (var colI=segment.leftCol; colI<=segment.rightCol; colI++) {
colHeights[colI] = segment.top + segment.outerHeight;
}
}
// the tallest column in the row should be the "content height"
rowContentHeights.push(arrayMax(colHeights));
}
return rowContentHeights;
}
// Build an array of segment arrays, each representing the segments that will
// be in a row of the grid, sorted by which event should be closest to the top.
function buildSegmentRows(segments) {
var rowCnt = getRowCnt();
var segmentRows = [];
var segmentI;
var segment;
var rowI;
// group segments by row
for (segmentI=0; segmentI<segments.length; segmentI++) {
segment = segments[segmentI];
rowI = segment.row;
if (segment.element) { // was rendered?
if (segmentRows[rowI]) {
// already other segments. append to array
segmentRows[rowI].push(segment);
}
else {
// first segment in row. create new array
segmentRows[rowI] = [ segment ];
}
}
}
// sort each row
for (rowI=0; rowI<rowCnt; rowI++) {
segmentRows[rowI] = sortSegmentRow(
segmentRows[rowI] || [] // guarantee an array, even if no segments
);
}
return segmentRows;
}
// Sort an array of segments according to which segment should appear closest to the top
function sortSegmentRow(segments) {
var sortedSegments = [];
// build the subrow array
var subrows = buildSegmentSubrows(segments);
// flatten it
for (var i=0; i<subrows.length; i++) {
sortedSegments.push.apply(sortedSegments, subrows[i]); // append an array to an array
}
return sortedSegments;
}
// Take an array of segments, which are all assumed to be in the same row,
// and sort into subrows.
function buildSegmentSubrows(segments) {
// Give preference to elements with certain criteria, so they have
// a chance to be closer to the top.
segments.sort(compareDaySegments);
var subrows = [];
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
// loop through subrows, starting with the topmost, until the segment
// doesn't collide with other segments.
for (var j=0; j<subrows.length; j++) {
if (!isDaySegmentCollision(segment, subrows[j])) {
break;
}
}
// `j` now holds the desired subrow index
if (subrows[j]) {
subrows[j].push(segment);
}
else {
subrows[j] = [ segment ];
}
}
return subrows;
}
// Return an array of jQuery objects for the placeholder content containers of each row.
// The content containers don't actually contain anything, but their dimensions should match
// the events that are overlaid on top.
function getRowContentElements() {
var i;
var rowCnt = getRowCnt();
var rowDivs = [];
for (i=0; i<rowCnt; i++) {
rowDivs[i] = allDayRow(i)
.find('div.fc-day-content > div');
}
return rowDivs;
}
/* Mouse Handlers
---------------------------------------------------------------------------------------------------*/
// TODO: better documentation!
function attachHandlers(segments, modifiedEventId) {
var segmentContainer = getDaySegmentContainer();
segmentElementEach(segments, function(segment, element, i) {
var event = segment.event;
if (event._id === modifiedEventId) {
bindDaySeg(event, element, segment);
}else{
element[0]._fci = i; // for lazySegBind
}
});
lazySegBind(segmentContainer, segments, bindDaySeg);
}
function bindDaySeg(event, eventElement, segment) {
if (isEventDraggable(event)) {
t.draggableDayEvent(event, eventElement, segment); // use `t` so subclasses can override
}
if (
segment.isEnd && // only allow resizing on the final segment for an event
isEventResizable(event)
) {
t.resizableDayEvent(event, eventElement, segment); // use `t` so subclasses can override
}
// attach all other handlers.
// needs to be after, because resizableDayEvent might stopImmediatePropagation on click
eventElementHandlers(event, eventElement);
}
function draggableDayEvent(event, eventElement) {
var hoverListener = getHoverListener();
var dayDelta;
eventElement.draggable({
delay: 50,
opacity: opt('dragOpacity'),
revertDuration: opt('dragRevertDuration'),
start: function(ev, ui) {
trigger('eventDragStart', eventElement, event, ev, ui);
hideEvents(event, eventElement);
hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
clearOverlays();
if (cell) {
var origDate = cellToDate(origCell);
var date = cellToDate(cell);
dayDelta = dayDiff(date, origDate);
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
}else{
dayDelta = 0;
}
}, ev, 'drag');
},
stop: function(ev, ui) {
hoverListener.stop();
clearOverlays();
trigger('eventDragStop', eventElement, event, ev, ui);
if (dayDelta) {
eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
}else{
eventElement.css('filter', ''); // clear IE opacity side-effects
showEvents(event, eventElement);
}
}
});
}
function resizableDayEvent(event, element, segment) {
var isRTL = opt('isRTL');
var direction = isRTL ? 'w' : 'e';
var handle = element.find('.ui-resizable-' + direction); // TODO: stop using this class because we aren't using jqui for this
var isResizing = false;
// TODO: look into using jquery-ui mouse widget for this stuff
disableTextSelection(element); // prevent native <a> selection for IE
element
.mousedown(function(ev) { // prevent native <a> selection for others
ev.preventDefault();
})
.click(function(ev) {
if (isResizing) {
ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
// (eventElementHandlers needs to be bound after resizableDayEvent)
}
});
handle.mousedown(function(ev) {
if (ev.which != 1) {
return; // needs to be left mouse button
}
isResizing = true;
var hoverListener = getHoverListener();
var rowCnt = getRowCnt();
var colCnt = getColCnt();
var elementTop = element.css('top');
var dayDelta;
var helpers;
var eventCopy = $.extend({}, event);
var minCellOffset = dayOffsetToCellOffset( dateToDayOffset(event.start) );
clearSelection();
$('body')
.css('cursor', direction + '-resize')
.one('mouseup', mouseup);
trigger('eventResizeStart', this, event, ev);
hoverListener.start(function(cell, origCell) {
if (cell) {
var origCellOffset = cellToCellOffset(origCell);
var cellOffset = cellToCellOffset(cell);
// don't let resizing move earlier than start date cell
cellOffset = Math.max(cellOffset, minCellOffset);
dayDelta =
cellOffsetToDayOffset(cellOffset) -
cellOffsetToDayOffset(origCellOffset);
if (dayDelta) {
eventCopy.end = addDays(eventEnd(event), dayDelta, true);
var oldHelpers = helpers;
helpers = renderTempDayEvent(eventCopy, segment.row, elementTop);
helpers = $(helpers); // turn array into a jQuery object
helpers.find('*').css('cursor', direction + '-resize');
if (oldHelpers) {
oldHelpers.remove();
}
hideEvents(event);
}
else {
if (helpers) {
showEvents(event);
helpers.remove();
helpers = null;
}
}
clearOverlays();
renderDayOverlay( // coordinate grid already rebuilt with hoverListener.start()
event.start,
addDays( exclEndDay(event), dayDelta )
// TODO: instead of calling renderDayOverlay() with dates,
// call _renderDayOverlay (or whatever) with cell offsets.
);
}
}, ev);
function mouseup(ev) {
trigger('eventResizeStop', this, event, ev);
$('body').css('cursor', '');
hoverListener.stop();
clearOverlays();
if (dayDelta) {
eventResize(this, event, dayDelta, 0, ev);
// event redraw will clear helpers
}
// otherwise, the drag handler already restored the old events
setTimeout(function() { // make this happen after the element's click event
isResizing = false;
},0);
}
});
}
}
/* Generalized Segment Utilities
-------------------------------------------------------------------------------------------------*/
function isDaySegmentCollision(segment, otherSegments) {
for (var i=0; i<otherSegments.length; i++) {
var otherSegment = otherSegments[i];
if (
otherSegment.leftCol <= segment.rightCol &&
otherSegment.rightCol >= segment.leftCol
) {
return true;
}
}
return false;
}
function segmentElementEach(segments, callback) { // TODO: use in AgendaView?
for (var i=0; i<segments.length; i++) {
var segment = segments[i];
var element = segment.element;
if (element) {
callback(segment, element, i);
}
}
}
// A cmp function for determining which segments should appear higher up
function compareDaySegments(a, b) {
return (b.rightCol - b.leftCol) - (a.rightCol - a.leftCol) || // put wider events first
b.event.allDay - a.event.allDay || // if tie, put all-day events first (booleans cast to 0/1)
a.event.start - b.event.start || // if a tie, sort by event start date
(a.event.title || '').localeCompare(b.event.title) // if a tie, sort by event title
}
;;
//BUG: unselect needs to be triggered when events are dragged+dropped
function SelectionManager() {
var t = this;
// exports
t.select = select;
t.unselect = unselect;
t.reportSelection = reportSelection;
t.daySelectionMousedown = daySelectionMousedown;
// imports
var opt = t.opt;
var trigger = t.trigger;
var defaultSelectionEnd = t.defaultSelectionEnd;
var renderSelection = t.renderSelection;
var clearSelection = t.clearSelection;
// locals
var selected = false;
// unselectAuto
if (opt('selectable') && opt('unselectAuto')) {
$(document).mousedown(function(ev) {
var ignore = opt('unselectCancel');
if (ignore) {
if ($(ev.target).parents(ignore).length) { // could be optimized to stop after first match
return;
}
}
unselect(ev);
});
}
function select(startDate, endDate, allDay) {
unselect();
if (!endDate) {
endDate = defaultSelectionEnd(startDate, allDay);
}
renderSelection(startDate, endDate, allDay);
reportSelection(startDate, endDate, allDay);
}
function unselect(ev) {
if (selected) {
selected = false;
clearSelection();
trigger('unselect', null, ev);
}
}
function reportSelection(startDate, endDate, allDay, ev) {
selected = true;
trigger('select', null, startDate, endDate, allDay, ev);
}
function daySelectionMousedown(ev) { // not really a generic manager method, oh well
var cellToDate = t.cellToDate;
var getIsCellAllDay = t.getIsCellAllDay;
var hoverListener = t.getHoverListener();
var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
unselect(ev);
var _mousedownElement = this;
var dates;
hoverListener.start(function(cell, origCell) { // TODO: maybe put cellToDate/getIsCellAllDay info in cell
clearSelection();
if (cell && getIsCellAllDay(cell)) {
dates = [ cellToDate(origCell), cellToDate(cell) ].sort(dateCompare);
renderSelection(dates[0], dates[1], true);
}else{
dates = null;
}
}, ev);
$(document).one('mouseup', function(ev) {
hoverListener.stop();
if (dates) {
if (+dates[0] == +dates[1]) {
reportDayClick(dates[0], true, ev);
}
reportSelection(dates[0], dates[1], true, ev);
}
});
}
}
}
;;
function OverlayManager() {
var t = this;
// exports
t.renderOverlay = renderOverlay;
t.clearOverlays = clearOverlays;
// locals
var usedOverlays = [];
var unusedOverlays = [];
function renderOverlay(rect, parent) {
var e = unusedOverlays.shift();
if (!e) {
e = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3'/>");
}
if (e[0].parentNode != parent[0]) {
e.appendTo(parent);
}
usedOverlays.push(e.css(rect).show());
return e;
}
function clearOverlays() {
var e;
while (e = usedOverlays.shift()) {
unusedOverlays.push(e.hide().unbind());
}
}
}
;;
function CoordinateGrid(buildFunc) {
var t = this;
var rows;
var cols;
t.build = function() {
rows = [];
cols = [];
buildFunc(rows, cols);
};
t.cell = function(x, y) {
var rowCnt = rows.length;
var colCnt = cols.length;
var i, r=-1, c=-1;
for (i=0; i<rowCnt; i++) {
if (y >= rows[i][0] && y < rows[i][1]) {
r = i;
break;
}
}
for (i=0; i<colCnt; i++) {
if (x >= cols[i][0] && x < cols[i][1]) {
c = i;
break;
}
}
return (r>=0 && c>=0) ? { row:r, col:c } : null;
};
t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
var origin = originElement.offset();
return {
top: rows[row0][0] - origin.top,
left: cols[col0][0] - origin.left,
width: cols[col1][1] - cols[col0][0],
height: rows[row1][1] - rows[row0][0]
};
};
}
;;
function HoverListener(coordinateGrid) {
var t = this;
var bindType;
var change;
var firstCell;
var cell;
t.start = function(_change, ev, _bindType) {
change = _change;
firstCell = cell = null;
coordinateGrid.build();
mouse(ev);
bindType = _bindType || 'mousemove';
$(document).bind(bindType, mouse);
};
function mouse(ev) {
_fixUIEvent(ev); // see below
var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
if (newCell) {
if (!firstCell) {
firstCell = newCell;
}
change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col);
}else{
change(newCell, firstCell);
}
cell = newCell;
}
}
t.stop = function() {
$(document).unbind(bindType, mouse);
return cell;
};
}
// this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1)
// upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem
// but keep this in here for 1.8.16 users
// and maybe remove it down the line
function _fixUIEvent(event) { // for issue 1168
if (event.pageX === undefined) {
event.pageX = event.originalEvent.pageX;
event.pageY = event.originalEvent.pageY;
}
}
;;
function HorizontalPositionCache(getElement) {
var t = this,
elements = {},
lefts = {},
rights = {};
function e(i) {
return elements[i] = elements[i] || getElement(i);
}
t.left = function(i) {
return lefts[i] = lefts[i] === undefined ? e(i).position().left : lefts[i];
};
t.right = function(i) {
return rights[i] = rights[i] === undefined ? t.left(i) + e(i).width() : rights[i];
};
t.clear = function() {
elements = {};
lefts = {};
rights = {};
};
}
;;
})(jQuery);
//}}}
!jquery.ui.js
//{{{
!jquery.ui.core.js
/*!
* jQuery UI Core 1.10.3
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/ui-core/
*/
(function( $, undefined ) {
var uuid = 0,
runiqueId = /^ui-id-\d+$/;
// $.ui might exist from components with no dependencies, e.g., $.ui.position
$.ui = $.ui || {};
$.extend( $.ui, {
version: "1.10.3",
keyCode: {
BACKSPACE: 8,
COMMA: 188,
DELETE: 46,
DOWN: 40,
END: 35,
ENTER: 13,
ESCAPE: 27,
HOME: 36,
LEFT: 37,
NUMPAD_ADD: 107,
NUMPAD_DECIMAL: 110,
NUMPAD_DIVIDE: 111,
NUMPAD_ENTER: 108,
NUMPAD_MULTIPLY: 106,
NUMPAD_SUBTRACT: 109,
PAGE_DOWN: 34,
PAGE_UP: 33,
PERIOD: 190,
RIGHT: 39,
SPACE: 32,
TAB: 9,
UP: 38
}
});
// plugins
$.fn.extend({
focus: (function( orig ) {
return function( delay, fn ) {
return typeof delay === "number" ?
this.each(function() {
var elem = this;
setTimeout(function() {
$( elem ).focus();
if ( fn ) {
fn.call( elem );
}
}, delay );
}) :
orig.apply( this, arguments );
};
})( $.fn.focus ),
scrollParent: function() {
var scrollParent;
if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
scrollParent = this.parents().filter(function() {
return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
}).eq(0);
} else {
scrollParent = this.parents().filter(function() {
return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
}).eq(0);
}
return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
},
zIndex: function( zIndex ) {
if ( zIndex !== undefined ) {
return this.css( "zIndex", zIndex );
}
if ( this.length ) {
var elem = $( this[ 0 ] ), position, value;
while ( elem.length && elem[ 0 ] !== document ) {
// Ignore z-index if position is set to a value where z-index is ignored by the browser
// This makes behavior of this function consistent across browsers
// WebKit always returns auto if the element is positioned
position = elem.css( "position" );
if ( position === "absolute" || position === "relative" || position === "fixed" ) {
// IE returns 0 when zIndex is not specified
// other browsers return a string
// we ignore the case of nested elements with an explicit value of 0
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
value = parseInt( elem.css( "zIndex" ), 10 );
if ( !isNaN( value ) && value !== 0 ) {
return value;
}
}
elem = elem.parent();
}
}
return 0;
},
uniqueId: function() {
return this.each(function() {
if ( !this.id ) {
this.id = "ui-id-" + (++uuid);
}
});
},
removeUniqueId: function() {
return this.each(function() {
if ( runiqueId.test( this.id ) ) {
$( this ).removeAttr( "id" );
}
});
}
});
// selectors
function focusable( element, isTabIndexNotNaN ) {
var map, mapName, img,
nodeName = element.nodeName.toLowerCase();
if ( "area" === nodeName ) {
map = element.parentNode;
mapName = map.name;
if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
return false;
}
img = $( "img[usemap=#" + mapName + "]" )[0];
return !!img && visible( img );
}
return ( /input|select|textarea|button|object/.test( nodeName ) ?
!element.disabled :
"a" === nodeName ?
element.href || isTabIndexNotNaN :
isTabIndexNotNaN) &&
// the element and all of its ancestors must be visible
visible( element );
}
function visible( element ) {
return $.expr.filters.visible( element ) &&
!$( element ).parents().addBack().filter(function() {
return $.css( this, "visibility" ) === "hidden";
}).length;
}
$.extend( $.expr[ ":" ], {
data: $.expr.createPseudo ?
$.expr.createPseudo(function( dataName ) {
return function( elem ) {
return !!$.data( elem, dataName );
};
}) :
// support: jQuery <1.8
function( elem, i, match ) {
return !!$.data( elem, match[ 3 ] );
},
focusable: function( element ) {
return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
},
tabbable: function( element ) {
var tabIndex = $.attr( element, "tabindex" ),
isTabIndexNaN = isNaN( tabIndex );
return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
}
});
// support: jQuery <1.8
if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
$.each( [ "Width", "Height" ], function( i, name ) {
var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
type = name.toLowerCase(),
orig = {
innerWidth: $.fn.innerWidth,
innerHeight: $.fn.innerHeight,
outerWidth: $.fn.outerWidth,
outerHeight: $.fn.outerHeight
};
function reduce( elem, size, border, margin ) {
$.each( side, function() {
size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
if ( border ) {
size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
}
if ( margin ) {
size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
}
});
return size;
}
$.fn[ "inner" + name ] = function( size ) {
if ( size === undefined ) {
return orig[ "inner" + name ].call( this );
}
return this.each(function() {
$( this ).css( type, reduce( this, size ) + "px" );
});
};
$.fn[ "outer" + name] = function( size, margin ) {
if ( typeof size !== "number" ) {
return orig[ "outer" + name ].call( this, size );
}
return this.each(function() {
$( this).css( type, reduce( this, size, true, margin ) + "px" );
});
};
});
}
// support: jQuery <1.8
if ( !$.fn.addBack ) {
$.fn.addBack = function( selector ) {
return this.add( selector == null ?
this.prevObject : this.prevObject.filter( selector )
);
};
}
// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
$.fn.removeData = (function( removeData ) {
return function( key ) {
if ( arguments.length ) {
return removeData.call( this, $.camelCase( key ) );
} else {
return removeData.call( this );
}
};
})( $.fn.removeData );
}
// deprecated
$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
$.support.selectstart = "onselectstart" in document.createElement( "div" );
$.fn.extend({
disableSelection: function() {
return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
".ui-disableSelection", function( event ) {
event.preventDefault();
});
},
enableSelection: function() {
return this.unbind( ".ui-disableSelection" );
}
});
$.extend( $.ui, {
// $.ui.plugin is deprecated. Use $.widget() extensions instead.
plugin: {
add: function( module, option, set ) {
var i,
proto = $.ui[ module ].prototype;
for ( i in set ) {
proto.plugins[ i ] = proto.plugins[ i ] || [];
proto.plugins[ i ].push( [ option, set[ i ] ] );
}
},
call: function( instance, name, args ) {
var i,
set = instance.plugins[ name ];
if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
return;
}
for ( i = 0; i < set.length; i++ ) {
if ( instance.options[ set[ i ][ 0 ] ] ) {
set[ i ][ 1 ].apply( instance.element, args );
}
}
}
},
// only used by resizable
hasScroll: function( el, a ) {
//If overflow is hidden, the element might have extra content, but the user wants to hide it
if ( $( el ).css( "overflow" ) === "hidden") {
return false;
}
var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
has = false;
if ( el[ scroll ] > 0 ) {
return true;
}
// TODO: determine which cases actually cause this to happen
// if the element doesn't have the scroll set, see if it's possible to
// set the scroll
el[ scroll ] = 1;
has = ( el[ scroll ] > 0 );
el[ scroll ] = 0;
return has;
}
});
})( jQuery );
!jquery.ui.widget.js
/*!
* jQuery UI Widget 1.10.3
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/jQuery.widget/
*/
(function( $, undefined ) {
var uuid = 0,
slice = Array.prototype.slice,
_cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
try {
$( elem ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
}
_cleanData( elems );
};
$.widget = function( name, base, prototype ) {
var fullName, existingConstructor, constructor, basePrototype,
// proxiedPrototype allows the provided prototype to remain unmodified
// so that it can be used as a mixin for multiple widgets (#8876)
proxiedPrototype = {},
namespace = name.split( "." )[ 0 ];
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
// create selector for plugin
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
return !!$.data( elem, fullName );
};
$[ namespace ] = $[ namespace ] || {};
existingConstructor = $[ namespace ][ name ];
constructor = $[ namespace ][ name ] = function( options, element ) {
// allow instantiation without "new" keyword
if ( !this._createWidget ) {
return new constructor( options, element );
}
// allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
if ( arguments.length ) {
this._createWidget( options, element );
}
};
// extend with the existing constructor to carry over any static properties
$.extend( constructor, existingConstructor, {
version: prototype.version,
// copy the object used to create the prototype in case we need to
// redefine the widget later
_proto: $.extend( {}, prototype ),
// track widgets that inherit from this widget in case this widget is
// redefined after a widget inherits from it
_childConstructors: []
});
basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options );
$.each( prototype, function( prop, value ) {
if ( !$.isFunction( value ) ) {
proxiedPrototype[ prop ] = value;
return;
}
proxiedPrototype[ prop ] = (function() {
var _super = function() {
return base.prototype[ prop ].apply( this, arguments );
},
_superApply = function( args ) {
return base.prototype[ prop ].apply( this, args );
};
return function() {
var __super = this._super,
__superApply = this._superApply,
returnValue;
this._super = _super;
this._superApply = _superApply;
returnValue = value.apply( this, arguments );
this._super = __super;
this._superApply = __superApply;
return returnValue;
};
})();
});
constructor.prototype = $.widget.extend( basePrototype, {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
}, proxiedPrototype, {
constructor: constructor,
namespace: namespace,
widgetName: name,
widgetFullName: fullName
});
// If this widget is being redefined then we need to find all widgets that
// are inheriting from it and redefine all of them so that they inherit from
// the new version of this widget. We're essentially trying to replace one
// level in the prototype chain.
if ( existingConstructor ) {
$.each( existingConstructor._childConstructors, function( i, child ) {
var childPrototype = child.prototype;
// redefine the child widget using the same prototype that was
// originally used, but inherit from the new version of the base
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
});
// remove the list of existing child constructors from the old constructor
// so the old child constructors can be garbage collected
delete existingConstructor._childConstructors;
} else {
base._childConstructors.push( constructor );
}
$.widget.bridge( name, constructor );
};
$.widget.extend = function( target ) {
var input = slice.call( arguments, 1 ),
inputIndex = 0,
inputLength = input.length,
key,
value;
for ( ; inputIndex < inputLength; inputIndex++ ) {
for ( key in input[ inputIndex ] ) {
value = input[ inputIndex ][ key ];
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
// Clone objects
if ( $.isPlainObject( value ) ) {
target[ key ] = $.isPlainObject( target[ key ] ) ?
$.widget.extend( {}, target[ key ], value ) :
// Don't extend strings, arrays, etc. with objects
$.widget.extend( {}, value );
// Copy everything else by reference
} else {
target[ key ] = value;
}
}
}
}
return target;
};
$.widget.bridge = function( name, object ) {
var fullName = object.prototype.widgetFullName || name;
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = slice.call( arguments, 1 ),
returnValue = this;
// allow multiple hashes to be passed on init
options = !isMethodCall && args.length ?
$.widget.extend.apply( null, [ options ].concat(args) ) :
options;
if ( isMethodCall ) {
this.each(function() {
var methodValue,
instance = $.data( this, fullName );
if ( !instance ) {
return $.error( "cannot call methods on " + name + " prior to initialization; " +
"attempted to call method '" + options + "'" );
}
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
}
methodValue = instance[ options ].apply( instance, args );
if ( methodValue !== instance && methodValue !== undefined ) {
returnValue = methodValue && methodValue.jquery ?
returnValue.pushStack( methodValue.get() ) :
methodValue;
return false;
}
});
} else {
this.each(function() {
var instance = $.data( this, fullName );
if ( instance ) {
instance.option( options || {} )._init();
} else {
$.data( this, fullName, new object( options, this ) );
}
});
}
return returnValue;
};
};
$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];
$.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
defaultElement: "<div>",
options: {
disabled: false,
// callbacks
create: null
},
_createWidget: function( options, element ) {
element = $( element || this.defaultElement || this )[ 0 ];
this.element = $( element );
this.uuid = uuid++;
this.eventNamespace = "." + this.widgetName + this.uuid;
this.options = $.widget.extend( {},
this.options,
this._getCreateOptions(),
options );
this.bindings = $();
this.hoverable = $();
this.focusable = $();
if ( element !== this ) {
$.data( element, this.widgetFullName, this );
this._on( true, this.element, {
remove: function( event ) {
if ( event.target === element ) {
this.destroy();
}
}
});
this.document = $( element.style ?
// element within the document
element.ownerDocument :
// element is window or document
element.document || element );
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
}
this._create();
this._trigger( "create", null, this._getCreateEventData() );
this._init();
},
_getCreateOptions: $.noop,
_getCreateEventData: $.noop,
_create: $.noop,
_init: $.noop,
destroy: function() {
this._destroy();
// we can probably remove the unbind calls in 2.0
// all event bindings should go through this._on()
this.element
.unbind( this.eventNamespace )
// 1.9 BC for #7810
// TODO remove dual storage
.removeData( this.widgetName )
.removeData( this.widgetFullName )
// support: jquery <1.6.3
// http://bugs.jquery.com/ticket/9413
.removeData( $.camelCase( this.widgetFullName ) );
this.widget()
.unbind( this.eventNamespace )
.removeAttr( "aria-disabled" )
.removeClass(
this.widgetFullName + "-disabled " +
"ui-state-disabled" );
// clean up events and states
this.bindings.unbind( this.eventNamespace );
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
},
_destroy: $.noop,
widget: function() {
return this.element;
},
option: function( key, value ) {
var options = key,
parts,
curOption,
i;
if ( arguments.length === 0 ) {
// don't return a reference to the internal hash
return $.widget.extend( {}, this.options );
}
if ( typeof key === "string" ) {
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
options = {};
parts = key.split( "." );
key = parts.shift();
if ( parts.length ) {
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
for ( i = 0; i < parts.length - 1; i++ ) {
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
curOption = curOption[ parts[ i ] ];
}
key = parts.pop();
if ( value === undefined ) {
return curOption[ key ] === undefined ? null : curOption[ key ];
}
curOption[ key ] = value;
} else {
if ( value === undefined ) {
return this.options[ key ] === undefined ? null : this.options[ key ];
}
options[ key ] = value;
}
}
this._setOptions( options );
return this;
},
_setOptions: function( options ) {
var key;
for ( key in options ) {
this._setOption( key, options[ key ] );
}
return this;
},
_setOption: function( key, value ) {
this.options[ key ] = value;
if ( key === "disabled" ) {
this.widget()
.toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
.attr( "aria-disabled", value );
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
}
return this;
},
enable: function() {
return this._setOption( "disabled", false );
},
disable: function() {
return this._setOption( "disabled", true );
},
_on: function( suppressDisabledCheck, element, handlers ) {
var delegateElement,
instance = this;
// no suppressDisabledCheck flag, shuffle arguments
if ( typeof suppressDisabledCheck !== "boolean" ) {
handlers = element;
element = suppressDisabledCheck;
suppressDisabledCheck = false;
}
// no element argument, shuffle and use this.element
if ( !handlers ) {
handlers = element;
element = this.element;
delegateElement = this.widget();
} else {
// accept selectors, DOM elements
element = delegateElement = $( element );
this.bindings = this.bindings.add( element );
}
$.each( handlers, function( event, handler ) {
function handlerProxy() {
// allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
if ( !suppressDisabledCheck &&
( instance.options.disabled === true ||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
return;
}
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
// copy the guid so direct unbinding works
if ( typeof handler !== "string" ) {
handlerProxy.guid = handler.guid =
handler.guid || handlerProxy.guid || $.guid++;
}
var match = event.match( /^(\w+)\s*(.*)$/ ),
eventName = match[1] + instance.eventNamespace,
selector = match[2];
if ( selector ) {
delegateElement.delegate( selector, eventName, handlerProxy );
} else {
element.bind( eventName, handlerProxy );
}
});
},
_off: function( element, eventName ) {
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
element.unbind( eventName ).undelegate( eventName );
},
_delay: function( handler, delay ) {
function handlerProxy() {
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
var instance = this;
return setTimeout( handlerProxy, delay || 0 );
},
_hoverable: function( element ) {
this.hoverable = this.hoverable.add( element );
this._on( element, {
mouseenter: function( event ) {
$( event.currentTarget ).addClass( "ui-state-hover" );
},
mouseleave: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-hover" );
}
});
},
_focusable: function( element ) {
this.focusable = this.focusable.add( element );
this._on( element, {
focusin: function( event ) {
$( event.currentTarget ).addClass( "ui-state-focus" );
},
focusout: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-focus" );
}
});
},
_trigger: function( type, event, data ) {
var prop, orig,
callback = this.options[ type ];
data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[ 0 ];
// copy original event properties over to the new event
orig = event.originalEvent;
if ( orig ) {
for ( prop in orig ) {
if ( !( prop in event ) ) {
event[ prop ] = orig[ prop ];
}
}
}
this.element.trigger( event, data );
return !( $.isFunction( callback ) &&
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
event.isDefaultPrevented() );
}
};
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
if ( typeof options === "string" ) {
options = { effect: options };
}
var hasOptions,
effectName = !options ?
method :
options === true || typeof options === "number" ?
defaultEffect :
options.effect || defaultEffect;
options = options || {};
if ( typeof options === "number" ) {
options = { duration: options };
}
hasOptions = !$.isEmptyObject( options );
options.complete = callback;
if ( options.delay ) {
element.delay( options.delay );
}
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
element[ method ]( options );
} else if ( effectName !== method && element[ effectName ] ) {
element[ effectName ]( options.duration, options.easing, callback );
} else {
element.queue(function( next ) {
$( this )[ method ]();
if ( callback ) {
callback.call( element[ 0 ] );
}
next();
});
}
};
});
})( jQuery );
!jquery.ui.mouse.js
/*!
* jQuery UI Mouse 1.10.3
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/mouse/
*
* Depends:
* jquery.ui.widget.js
*/
(function( $, undefined ) {
var mouseHandled = false;
$( document ).mouseup( function() {
mouseHandled = false;
});
$.widget("ui.mouse", {
version: "1.10.3",
options: {
cancel: "input,textarea,button,select,option",
distance: 1,
delay: 0
},
_mouseInit: function() {
var that = this;
this.element
.bind("mousedown."+this.widgetName, function(event) {
return that._mouseDown(event);
})
.bind("click."+this.widgetName, function(event) {
if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
$.removeData(event.target, that.widgetName + ".preventClickEvent");
event.stopImmediatePropagation();
return false;
}
});
this.started = false;
},
// TODO: make sure destroying one instance of mouse doesn't mess with
// other instances of mouse
_mouseDestroy: function() {
this.element.unbind("."+this.widgetName);
if ( this._mouseMoveDelegate ) {
$(document)
.unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
}
},
_mouseDown: function(event) {
// don't let more than one widget handle mouseStart
if( mouseHandled ) { return; }
// we may have missed mouseup (out of window)
(this._mouseStarted && this._mouseUp(event));
this._mouseDownEvent = event;
var that = this,
btnIsLeft = (event.which === 1),
// event.target.nodeName works around a bug in IE 8 with
// disabled inputs (#7620)
elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
return true;
}
this.mouseDelayMet = !this.options.delay;
if (!this.mouseDelayMet) {
this._mouseDelayTimer = setTimeout(function() {
that.mouseDelayMet = true;
}, this.options.delay);
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted = (this._mouseStart(event) !== false);
if (!this._mouseStarted) {
event.preventDefault();
return true;
}
}
// Click event may never have fired (Gecko & Opera)
if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
$.removeData(event.target, this.widgetName + ".preventClickEvent");
}
// these delegates are required to keep context
this._mouseMoveDelegate = function(event) {
return that._mouseMove(event);
};
this._mouseUpDelegate = function(event) {
return that._mouseUp(event);
};
$(document)
.bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.bind("mouseup."+this.widgetName, this._mouseUpDelegate);
event.preventDefault();
mouseHandled = true;
return true;
},
_mouseMove: function(event) {
// IE mouseup check - mouseup happened when mouse was out of window
if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
return this._mouseUp(event);
}
if (this._mouseStarted) {
this._mouseDrag(event);
return event.preventDefault();
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted =
(this._mouseStart(this._mouseDownEvent, event) !== false);
(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
}
return !this._mouseStarted;
},
_mouseUp: function(event) {
$(document)
.unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
if (this._mouseStarted) {
this._mouseStarted = false;
if (event.target === this._mouseDownEvent.target) {
$.data(event.target, this.widgetName + ".preventClickEvent", true);
}
this._mouseStop(event);
}
return false;
},
_mouseDistanceMet: function(event) {
return (Math.max(
Math.abs(this._mouseDownEvent.pageX - event.pageX),
Math.abs(this._mouseDownEvent.pageY - event.pageY)
) >= this.options.distance
);
},
_mouseDelayMet: function(/* event */) {
return this.mouseDelayMet;
},
// These are placeholder methods, to be overriden by extending plugin
_mouseStart: function(/* event */) {},
_mouseDrag: function(/* event */) {},
_mouseStop: function(/* event */) {},
_mouseCapture: function(/* event */) { return true; }
});
})(jQuery);
!jquery.ui.draggable.js
/*!
* jQuery UI Draggable 1.10.3
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/draggable/
*
* Depends:
* jquery.ui.core.js
* jquery.ui.mouse.js
* jquery.ui.widget.js
*/
(function( $, undefined ) {
$.widget("ui.draggable", $.ui.mouse, {
version: "1.10.3",
widgetEventPrefix: "drag",
options: {
addClasses: true,
appendTo: "parent",
axis: false,
connectToSortable: false,
containment: false,
cursor: "auto",
cursorAt: false,
grid: false,
handle: false,
helper: "original",
iframeFix: false,
opacity: false,
refreshPositions: false,
revert: false,
revertDuration: 500,
scope: "default",
scroll: true,
scrollSensitivity: 20,
scrollSpeed: 20,
snap: false,
snapMode: "both",
snapTolerance: 20,
stack: false,
zIndex: false,
// callbacks
drag: null,
start: null,
stop: null
},
_create: function() {
if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
this.element[0].style.position = "relative";
}
if (this.options.addClasses){
this.element.addClass("ui-draggable");
}
if (this.options.disabled){
this.element.addClass("ui-draggable-disabled");
}
this._mouseInit();
},
_destroy: function() {
this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
this._mouseDestroy();
},
_mouseCapture: function(event) {
var o = this.options;
// among others, prevent a drag on a resizable-handle
if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
return false;
}
//Quit if we're not on a valid handle
this.handle = this._getHandle(event);
if (!this.handle) {
return false;
}
$(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
$("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
.css({
width: this.offsetWidth+"px", height: this.offsetHeight+"px",
position: "absolute", opacity: "0.001", zIndex: 1000
})
.css($(this).offset())
.appendTo("body");
});
return true;
},
_mouseStart: function(event) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper(event);
this.helper.addClass("ui-draggable-dragging");
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if($.ui.ddmanager) {
$.ui.ddmanager.current = this;
}
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css( "position" );
this.scrollParent = this.helper.scrollParent();
this.offsetParent = this.helper.offsetParent();
this.offsetParentCssPosition = this.offsetParent.css( "position" );
//The element's absolute position on the page minus margins
this.offset = this.positionAbs = this.element.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
//Reset scroll cache
this.offset.scroll = false;
$.extend(this.offset, {
click: { //Where the click happened, relative to the element
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
},
parent: this._getParentOffset(),
relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});
//Generate the original position
this.originalPosition = this.position = this._generatePosition(event);
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
//Set a containment if given in the options
this._setContainment();
//Trigger event + callbacks
if(this._trigger("start", event) === false) {
this._clear();
return false;
}
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ($.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(this, event);
}
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
if ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStart(this, event);
}
return true;
},
_mouseDrag: function(event, noPropagation) {
// reset any necessary cached properties (see #5009)
if ( this.offsetParentCssPosition === "fixed" ) {
this.offset.parent = this._getParentOffset();
}
//Compute the helpers position
this.position = this._generatePosition(event);
this.positionAbs = this._convertPositionTo("absolute");
//Call plugins and callbacks and use the resulting position if something is returned
if (!noPropagation) {
var ui = this._uiHash();
if(this._trigger("drag", event, ui) === false) {
this._mouseUp({});
return false;
}
this.position = ui.position;
}
if(!this.options.axis || this.options.axis !== "y") {
this.helper[0].style.left = this.position.left+"px";
}
if(!this.options.axis || this.options.axis !== "x") {
this.helper[0].style.top = this.position.top+"px";
}
if($.ui.ddmanager) {
$.ui.ddmanager.drag(this, event);
}
return false;
},
_mouseStop: function(event) {
//If we are using droppables, inform the manager about the drop
var that = this,
dropped = false;
if ($.ui.ddmanager && !this.options.dropBehaviour) {
dropped = $.ui.ddmanager.drop(this, event);
}
//if a drop comes from outside (a sortable)
if(this.dropped) {
dropped = this.dropped;
this.dropped = false;
}
//if the original element is no longer in the DOM don't bother to continue (see #8269)
if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
return false;
}
if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
$(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
if(that._trigger("stop", event) !== false) {
that._clear();
}
});
} else {
if(this._trigger("stop", event) !== false) {
this._clear();
}
}
return false;
},
_mouseUp: function(event) {
//Remove frame helpers
$("div.ui-draggable-iframeFix").each(function() {
this.parentNode.removeChild(this);
});
//If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
if( $.ui.ddmanager ) {
$.ui.ddmanager.dragStop(this, event);
}
return $.ui.mouse.prototype._mouseUp.call(this, event);
},
cancel: function() {
if(this.helper.is(".ui-draggable-dragging")) {
this._mouseUp({});
} else {
this._clear();
}
return this;
},
_getHandle: function(event) {
return this.options.handle ?
!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
true;
},
_createHelper: function(event) {
var o = this.options,
helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
if(!helper.parents("body").length) {
helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
}
if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
helper.css("position", "absolute");
}
return helper;
},
_adjustOffsetFromHelper: function(obj) {
if (typeof obj === "string") {
obj = obj.split(" ");
}
if ($.isArray(obj)) {
obj = {left: +obj[0], top: +obj[1] || 0};
}
if ("left" in obj) {
this.offset.click.left = obj.left + this.margins.left;
}
if ("right" in obj) {
this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
}
if ("top" in obj) {
this.offset.click.top = obj.top + this.margins.top;
}
if ("bottom" in obj) {
this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
}
},
_getParentOffset: function() {
//Get the offsetParent and cache its position
var po = this.offsetParent.offset();
// This is a special case where we need to modify a offset calculated on start, since the following happened:
// 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
// the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
po.left += this.scrollParent.scrollLeft();
po.top += this.scrollParent.scrollTop();
}
//This needs to be actually done for all browsers, since pageX/pageY includes this information
//Ugly IE fix
if((this.offsetParent[0] === document.body) ||
(this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
po = { top: 0, left: 0 };
}
return {
top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
};
},
_getRelativeOffset: function() {
if(this.cssPosition === "relative") {
var p = this.element.position();
return {
top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
};
} else {
return { top: 0, left: 0 };
}
},
_cacheMargins: function() {
this.margins = {
left: (parseInt(this.element.css("marginLeft"),10) || 0),
top: (parseInt(this.element.css("marginTop"),10) || 0),
right: (parseInt(this.element.css("marginRight"),10) || 0),
bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
};
},
_cacheHelperProportions: function() {
this.helperProportions = {
width: this.helper.outerWidth(),
height: this.helper.outerHeight()
};
},
_setContainment: function() {
var over, c, ce,
o = this.options;
if ( !o.containment ) {
this.containment = null;
return;
}
if ( o.containment === "window" ) {
this.containment = [
$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
$( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
$( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
];
return;
}
if ( o.containment === "document") {
this.containment = [
0,
0,
$( document ).width() - this.helperProportions.width - this.margins.left,
( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
];
return;
}
if ( o.containment.constructor === Array ) {
this.containment = o.containment;
return;
}
if ( o.containment === "parent" ) {
o.containment = this.helper[ 0 ].parentNode;
}
c = $( o.containment );
ce = c[ 0 ];
if( !ce ) {
return;
}
over = c.css( "overflow" ) !== "hidden";
this.containment = [
( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
];
this.relative_container = c;
},
_convertPositionTo: function(d, pos) {
if(!pos) {
pos = this.position;
}
var mod = d === "absolute" ? 1 : -1,
scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
//Cache the scroll
if (!this.offset.scroll) {
this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
}
return {
top: (
pos.top + // The absolute mouse position
this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
),
left: (
pos.left + // The absolute mouse position
this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
)
};
},
_generatePosition: function(event) {
var containment, co, top, left,
o = this.options,
scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
pageX = event.pageX,
pageY = event.pageY;
//Cache the scroll
if (!this.offset.scroll) {
this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
}
/*
* - Position constraining -
* Constrain the position to a mix of grid, containment.
*/
// If we are not dragging yet, we won't check for options
if ( this.originalPosition ) {
if ( this.containment ) {
if ( this.relative_container ){
co = this.relative_container.offset();
containment = [
this.containment[ 0 ] + co.left,
this.containment[ 1 ] + co.top,
this.containment[ 2 ] + co.left,
this.containment[ 3 ] + co.top
];
}
else {
containment = this.containment;
}
if(event.pageX - this.offset.click.left < containment[0]) {
pageX = containment[0] + this.offset.click.left;
}
if(event.pageY - this.offset.click.top < containment[1]) {
pageY = containment[1] + this.offset.click.top;
}
if(event.pageX - this.offset.click.left > containment[2]) {
pageX = containment[2] + this.offset.click.left;
}
if(event.pageY - this.offset.click.top > containment[3]) {
pageY = containment[3] + this.offset.click.top;
}
}
if(o.grid) {
//Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
}
}
return {
top: (
pageY - // The absolute mouse position
this.offset.click.top - // Click offset (relative to the element)
this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
),
left: (
pageX - // The absolute mouse position
this.offset.click.left - // Click offset (relative to the element)
this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
)
};
},
_clear: function() {
this.helper.removeClass("ui-draggable-dragging");
if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
this.helper.remove();
}
this.helper = null;
this.cancelHelperRemoval = false;
},
// From now on bulk stuff - mainly helpers
_trigger: function(type, event, ui) {
ui = ui || this._uiHash();
$.ui.plugin.call(this, type, [event, ui]);
//The absolute position has to be recalculated after plugins
if(type === "drag") {
this.positionAbs = this._convertPositionTo("absolute");
}
return $.Widget.prototype._trigger.call(this, type, event, ui);
},
plugins: {},
_uiHash: function() {
return {
helper: this.helper,
position: this.position,
originalPosition: this.originalPosition,
offset: this.positionAbs
};
}
});
$.ui.plugin.add("draggable", "connectToSortable", {
start: function(event, ui) {
var inst = $(this).data("ui-draggable"), o = inst.options,
uiSortable = $.extend({}, ui, { item: inst.element });
inst.sortables = [];
$(o.connectToSortable).each(function() {
var sortable = $.data(this, "ui-sortable");
if (sortable && !sortable.options.disabled) {
inst.sortables.push({
instance: sortable,
shouldRevert: sortable.options.revert
});
sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
sortable._trigger("activate", event, uiSortable);
}
});
},
stop: function(event, ui) {
//If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
var inst = $(this).data("ui-draggable"),
uiSortable = $.extend({}, ui, { item: inst.element });
$.each(inst.sortables, function() {
if(this.instance.isOver) {
this.instance.isOver = 0;
inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
//The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
if(this.shouldRevert) {
this.instance.options.revert = this.shouldRevert;
}
//Trigger the stop of the sortable
this.instance._mouseStop(event);
this.instance.options.helper = this.instance.options._helper;
//If the helper has been the original item, restore properties in the sortable
if(inst.options.helper === "original") {
this.instance.currentItem.css({ top: "auto", left: "auto" });
}
} else {
this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
this.instance._trigger("deactivate", event, uiSortable);
}
});
},
drag: function(event, ui) {
var inst = $(this).data("ui-draggable"), that = this;
$.each(inst.sortables, function() {
var innermostIntersecting = false,
thisSortable = this;
//Copy over some variables to allow calling the sortable's native _intersectsWith
this.instance.positionAbs = inst.positionAbs;
this.instance.helperProportions = inst.helperProportions;
this.instance.offset.click = inst.offset.click;
if(this.instance._intersectsWith(this.instance.containerCache)) {
innermostIntersecting = true;
$.each(inst.sortables, function () {
this.instance.positionAbs = inst.positionAbs;
this.instance.helperProportions = inst.helperProportions;
this.instance.offset.click = inst.offset.click;
if (this !== thisSortable &&
this.instance._intersectsWith(this.instance.containerCache) &&
$.contains(thisSortable.instance.element[0], this.instance.element[0])
) {
innermostIntersecting = false;
}
return innermostIntersecting;
});
}
if(innermostIntersecting) {
//If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
if(!this.instance.isOver) {
this.instance.isOver = 1;
//Now we fake the start of dragging for the sortable instance,
//by cloning the list group item, appending it to the sortable and using it as inst.currentItem
//We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
this.instance.options.helper = function() { return ui.helper[0]; };
event.target = this.instance.currentItem[0];
this.instance._mouseCapture(event, true);
this.instance._mouseStart(event, true, true);
//Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
this.instance.offset.click.top = inst.offset.click.top;
this.instance.offset.click.left = inst.offset.click.left;
this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
inst._trigger("toSortable", event);
inst.dropped = this.instance.element; //draggable revert needs that
//hack so receive/update callbacks work (mostly)
inst.currentItem = inst.element;
this.instance.fromOutside = inst;
}
//Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
if(this.instance.currentItem) {
this.instance._mouseDrag(event);
}
} else {
//If it doesn't intersect with the sortable, and it intersected before,
//we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
if(this.instance.isOver) {
this.instance.isOver = 0;
this.instance.cancelHelperRemoval = true;
//Prevent reverting on this forced stop
this.instance.options.revert = false;
// The out event needs to be triggered independently
this.instance._trigger("out", event, this.instance._uiHash(this.instance));
this.instance._mouseStop(event, true);
this.instance.options.helper = this.instance.options._helper;
//Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
this.instance.currentItem.remove();
if(this.instance.placeholder) {
this.instance.placeholder.remove();
}
inst._trigger("fromSortable", event);
inst.dropped = false; //draggable revert needs that
}
}
});
}
});
$.ui.plugin.add("draggable", "cursor", {
start: function() {
var t = $("body"), o = $(this).data("ui-draggable").options;
if (t.css("cursor")) {
o._cursor = t.css("cursor");
}
t.css("cursor", o.cursor);
},
stop: function() {
var o = $(this).data("ui-draggable").options;
if (o._cursor) {
$("body").css("cursor", o._cursor);
}
}
});
$.ui.plugin.add("draggable", "opacity", {
start: function(event, ui) {
var t = $(ui.helper), o = $(this).data("ui-draggable").options;
if(t.css("opacity")) {
o._opacity = t.css("opacity");
}
t.css("opacity", o.opacity);
},
stop: function(event, ui) {
var o = $(this).data("ui-draggable").options;
if(o._opacity) {
$(ui.helper).css("opacity", o._opacity);
}
}
});
$.ui.plugin.add("draggable", "scroll", {
start: function() {
var i = $(this).data("ui-draggable");
if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
i.overflowOffset = i.scrollParent.offset();
}
},
drag: function( event ) {
var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
if(!o.axis || o.axis !== "x") {
if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
} else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
}
}
if(!o.axis || o.axis !== "y") {
if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
} else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
}
}
} else {
if(!o.axis || o.axis !== "x") {
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
} else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
}
}
if(!o.axis || o.axis !== "y") {
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
} else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
}
}
}
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(i, event);
}
}
});
$.ui.plugin.add("draggable", "snap", {
start: function() {
var i = $(this).data("ui-draggable"),
o = i.options;
i.snapElements = [];
$(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
var $t = $(this),
$o = $t.offset();
if(this !== i.element[0]) {
i.snapElements.push({
item: this,
width: $t.outerWidth(), height: $t.outerHeight(),
top: $o.top, left: $o.left
});
}
});
},
drag: function(event, ui) {
var ts, bs, ls, rs, l, r, t, b, i, first,
inst = $(this).data("ui-draggable"),
o = inst.options,
d = o.snapTolerance,
x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
for (i = inst.snapElements.length - 1; i >= 0; i--){
l = inst.snapElements[i].left;
r = l + inst.snapElements[i].width;
t = inst.snapElements[i].top;
b = t + inst.snapElements[i].height;
if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
if(inst.snapElements[i].snapping) {
(inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
}
inst.snapElements[i].snapping = false;
continue;
}
if(o.snapMode !== "inner") {
ts = Math.abs(t - y2) <= d;
bs = Math.abs(b - y1) <= d;
ls = Math.abs(l - x2) <= d;
rs = Math.abs(r - x1) <= d;
if(ts) {
ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
}
if(bs) {
ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
}
if(ls) {
ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
}
if(rs) {
ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
}
}
first = (ts || bs || ls || rs);
if(o.snapMode !== "outer") {
ts = Math.abs(t - y1) <= d;
bs = Math.abs(b - y2) <= d;
ls = Math.abs(l - x1) <= d;
rs = Math.abs(r - x2) <= d;
if(ts) {
ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
}
if(bs) {
ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
}
if(ls) {
ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
}
if(rs) {
ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
}
}
if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
(inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
}
inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
}
}
});
$.ui.plugin.add("draggable", "stack", {
start: function() {
var min,
o = this.data("ui-draggable").options,
group = $.makeArray($(o.stack)).sort(function(a,b) {
return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
});
if (!group.length) { return; }
min = parseInt($(group[0]).css("zIndex"), 10) || 0;
$(group).each(function(i) {
$(this).css("zIndex", min + i);
});
this.css("zIndex", (min + group.length));
}
});
$.ui.plugin.add("draggable", "zIndex", {
start: function(event, ui) {
var t = $(ui.helper), o = $(this).data("ui-draggable").options;
if(t.css("zIndex")) {
o._zIndex = t.css("zIndex");
}
t.css("zIndex", o.zIndex);
},
stop: function(event, ui) {
var o = $(this).data("ui-draggable").options;
if(o._zIndex) {
$(ui.helper).css("zIndex", o._zIndex);
}
}
});
})(jQuery);
!jquery.ui.resizable.js
/*!
* jQuery UI Resizable 1.10.3
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/resizable/
*
* Depends:
* jquery.ui.core.js
* jquery.ui.mouse.js
* jquery.ui.widget.js
*/
(function( $, undefined ) {
function num(v) {
return parseInt(v, 10) || 0;
}
function isNumber(value) {
return !isNaN(parseInt(value, 10));
}
$.widget("ui.resizable", $.ui.mouse, {
version: "1.10.3",
widgetEventPrefix: "resize",
options: {
alsoResize: false,
animate: false,
animateDuration: "slow",
animateEasing: "swing",
aspectRatio: false,
autoHide: false,
containment: false,
ghost: false,
grid: false,
handles: "e,s,se",
helper: false,
maxHeight: null,
maxWidth: null,
minHeight: 10,
minWidth: 10,
// See #7960
zIndex: 90,
// callbacks
resize: null,
start: null,
stop: null
},
_create: function() {
var n, i, handle, axis, hname,
that = this,
o = this.options;
this.element.addClass("ui-resizable");
$.extend(this, {
_aspectRatio: !!(o.aspectRatio),
aspectRatio: o.aspectRatio,
originalElement: this.element,
_proportionallyResizeElements: [],
_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
});
//Wrap the element if it cannot hold child nodes
if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
//Create a wrapper element and set the wrapper to the new current internal element
this.element.wrap(
$("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
position: this.element.css("position"),
width: this.element.outerWidth(),
height: this.element.outerHeight(),
top: this.element.css("top"),
left: this.element.css("left")
})
);
//Overwrite the original this.element
this.element = this.element.parent().data(
"ui-resizable", this.element.data("ui-resizable")
);
this.elementIsWrapper = true;
//Move margins to the wrapper
this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
//Prevent Safari textarea resize
this.originalResizeStyle = this.originalElement.css("resize");
this.originalElement.css("resize", "none");
//Push the actual element to our proportionallyResize internal array
this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
// avoid IE jump (hard set the margin)
this.originalElement.css({ margin: this.originalElement.css("margin") });
// fix handlers offset
this._proportionallyResize();
}
this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
if(this.handles.constructor === String) {
if ( this.handles === "all") {
this.handles = "n,e,s,w,se,sw,ne,nw";
}
n = this.handles.split(",");
this.handles = {};
for(i = 0; i < n.length; i++) {
handle = $.trim(n[i]);
hname = "ui-resizable-"+handle;
axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
// Apply zIndex to all handles - see #7960
axis.css({ zIndex: o.zIndex });
//TODO : What's going on here?
if ("se" === handle) {
axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
}
//Insert into internal handles object and append to element
this.handles[handle] = ".ui-resizable-"+handle;
this.element.append(axis);
}
}
this._renderAxis = function(target) {
var i, axis, padPos, padWrapper;
target = target || this.element;
for(i in this.handles) {
if(this.handles[i].constructor === String) {
this.handles[i] = $(this.handles[i], this.element).show();
}
//Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
axis = $(this.handles[i], this.element);
//Checking the correct pad and border
padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
//The padding type i have to apply...
padPos = [ "padding",
/ne|nw|n/.test(i) ? "Top" :
/se|sw|s/.test(i) ? "Bottom" :
/^e$/.test(i) ? "Right" : "Left" ].join("");
target.css(padPos, padWrapper);
this._proportionallyResize();
}
//TODO: What's that good for? There's not anything to be executed left
if(!$(this.handles[i]).length) {
continue;
}
}
};
//TODO: make renderAxis a prototype function
this._renderAxis(this.element);
this._handles = $(".ui-resizable-handle", this.element)
.disableSelection();
//Matching axis name
this._handles.mouseover(function() {
if (!that.resizing) {
if (this.className) {
axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
}
//Axis, default = se
that.axis = axis && axis[1] ? axis[1] : "se";
}
});
//If we want to auto hide the elements
if (o.autoHide) {
this._handles.hide();
$(this.element)
.addClass("ui-resizable-autohide")
.mouseenter(function() {
if (o.disabled) {
return;
}
$(this).removeClass("ui-resizable-autohide");
that._handles.show();
})
.mouseleave(function(){
if (o.disabled) {
return;
}
if (!that.resizing) {
$(this).addClass("ui-resizable-autohide");
that._handles.hide();
}
});
}
//Initialize the mouse interaction
this._mouseInit();
},
_destroy: function() {
this._mouseDestroy();
var wrapper,
_destroy = function(exp) {
$(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
.removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
};
//TODO: Unwrap at same DOM position
if (this.elementIsWrapper) {
_destroy(this.element);
wrapper = this.element;
this.originalElement.css({
position: wrapper.css("position"),
width: wrapper.outerWidth(),
height: wrapper.outerHeight(),
top: wrapper.css("top"),
left: wrapper.css("left")
}).insertAfter( wrapper );
wrapper.remove();
}
this.originalElement.css("resize", this.originalResizeStyle);
_destroy(this.originalElement);
return this;
},
_mouseCapture: function(event) {
var i, handle,
capture = false;
for (i in this.handles) {
handle = $(this.handles[i])[0];
if (handle === event.target || $.contains(handle, event.target)) {
capture = true;
}
}
return !this.options.disabled && capture;
},
_mouseStart: function(event) {
var curleft, curtop, cursor,
o = this.options,
iniPos = this.element.position(),
el = this.element;
this.resizing = true;
// bugfix for http://dev.jquery.com/ticket/1749
if ( (/absolute/).test( el.css("position") ) ) {
el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
} else if (el.is(".ui-draggable")) {
el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
}
this._renderProxy();
curleft = num(this.helper.css("left"));
curtop = num(this.helper.css("top"));
if (o.containment) {
curleft += $(o.containment).scrollLeft() || 0;
curtop += $(o.containment).scrollTop() || 0;
}
//Store needed variables
this.offset = this.helper.offset();
this.position = { left: curleft, top: curtop };
this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
this.originalPosition = { left: curleft, top: curtop };
this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
this.originalMousePosition = { left: event.pageX, top: event.pageY };
//Aspect Ratio
this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
cursor = $(".ui-resizable-" + this.axis).css("cursor");
$("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
el.addClass("ui-resizable-resizing");
this._propagate("start", event);
return true;
},
_mouseDrag: function(event) {
//Increase performance, avoid regex
var data,
el = this.helper, props = {},
smp = this.originalMousePosition,
a = this.axis,
prevTop = this.position.top,
prevLeft = this.position.left,
prevWidth = this.size.width,
prevHeight = this.size.height,
dx = (event.pageX-smp.left)||0,
dy = (event.pageY-smp.top)||0,
trigger = this._change[a];
if (!trigger) {
return false;
}
// Calculate the attrs that will be change
data = trigger.apply(this, [event, dx, dy]);
// Put this in the mouseDrag handler since the user can start pressing shift while resizing
this._updateVirtualBoundaries(event.shiftKey);
if (this._aspectRatio || event.shiftKey) {
data = this._updateRatio(data, event);
}
data = this._respectSize(data, event);
this._updateCache(data);
// plugins callbacks need to be called first
this._propagate("resize", event);
if (this.position.top !== prevTop) {
props.top = this.position.top + "px";
}
if (this.position.left !== prevLeft) {
props.left = this.position.left + "px";
}
if (this.size.width !== prevWidth) {
props.width = this.size.width + "px";
}
if (this.size.height !== prevHeight) {
props.height = this.size.height + "px";
}
el.css(props);
if (!this._helper && this._proportionallyResizeElements.length) {
this._proportionallyResize();
}
// Call the user callback if the element was resized
if ( ! $.isEmptyObject(props) ) {
this._trigger("resize", event, this.ui());
}
return false;
},
_mouseStop: function(event) {
this.resizing = false;
var pr, ista, soffseth, soffsetw, s, left, top,
o = this.options, that = this;
if(this._helper) {
pr = this._proportionallyResizeElements;
ista = pr.length && (/textarea/i).test(pr[0].nodeName);
soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
soffsetw = ista ? 0 : that.sizeDiff.width;
s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
if (!o.animate) {
this.element.css($.extend(s, { top: top, left: left }));
}
that.helper.height(that.size.height);
that.helper.width(that.size.width);
if (this._helper && !o.animate) {
this._proportionallyResize();
}
}
$("body").css("cursor", "auto");
this.element.removeClass("ui-resizable-resizing");
this._propagate("stop", event);
if (this._helper) {
this.helper.remove();
}
return false;
},
_updateVirtualBoundaries: function(forceAspectRatio) {
var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
o = this.options;
b = {
minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
};
if(this._aspectRatio || forceAspectRatio) {
// We want to create an enclosing box whose aspect ration is the requested one
// First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
pMinWidth = b.minHeight * this.aspectRatio;
pMinHeight = b.minWidth / this.aspectRatio;
pMaxWidth = b.maxHeight * this.aspectRatio;
pMaxHeight = b.maxWidth / this.aspectRatio;
if(pMinWidth > b.minWidth) {
b.minWidth = pMinWidth;
}
if(pMinHeight > b.minHeight) {
b.minHeight = pMinHeight;
}
if(pMaxWidth < b.maxWidth) {
b.maxWidth = pMaxWidth;
}
if(pMaxHeight < b.maxHeight) {
b.maxHeight = pMaxHeight;
}
}
this._vBoundaries = b;
},
_updateCache: function(data) {
this.offset = this.helper.offset();
if (isNumber(data.left)) {
this.position.left = data.left;
}
if (isNumber(data.top)) {
this.position.top = data.top;
}
if (isNumber(data.height)) {
this.size.height = data.height;
}
if (isNumber(data.width)) {
this.size.width = data.width;
}
},
_updateRatio: function( data ) {
var cpos = this.position,
csize = this.size,
a = this.axis;
if (isNumber(data.height)) {
data.width = (data.height * this.aspectRatio);
} else if (isNumber(data.width)) {
data.height = (data.width / this.aspectRatio);
}
if (a === "sw") {
data.left = cpos.left + (csize.width - data.width);
data.top = null;
}
if (a === "nw") {
data.top = cpos.top + (csize.height - data.height);
data.left = cpos.left + (csize.width - data.width);
}
return data;
},
_respectSize: function( data ) {
var o = this._vBoundaries,
a = this.axis,
ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
dw = this.originalPosition.left + this.originalSize.width,
dh = this.position.top + this.size.height,
cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
if (isminw) {
data.width = o.minWidth;
}
if (isminh) {
data.height = o.minHeight;
}
if (ismaxw) {
data.width = o.maxWidth;
}
if (ismaxh) {
data.height = o.maxHeight;
}
if (isminw && cw) {
data.left = dw - o.minWidth;
}
if (ismaxw && cw) {
data.left = dw - o.maxWidth;
}
if (isminh && ch) {
data.top = dh - o.minHeight;
}
if (ismaxh && ch) {
data.top = dh - o.maxHeight;
}
// fixing jump error on top/left - bug #2330
if (!data.width && !data.height && !data.left && data.top) {
data.top = null;
} else if (!data.width && !data.height && !data.top && data.left) {
data.left = null;
}
return data;
},
_proportionallyResize: function() {
if (!this._proportionallyResizeElements.length) {
return;
}
var i, j, borders, paddings, prel,
element = this.helper || this.element;
for ( i=0; i < this._proportionallyResizeElements.length; i++) {
prel = this._proportionallyResizeElements[i];
if (!this.borderDif) {
this.borderDif = [];
borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
for ( j = 0; j < borders.length; j++ ) {
this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
}
}
prel.css({
height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
});
}
},
_renderProxy: function() {
var el = this.element, o = this.options;
this.elementOffset = el.offset();
if(this._helper) {
this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
this.helper.addClass(this._helper).css({
width: this.element.outerWidth() - 1,
height: this.element.outerHeight() - 1,
position: "absolute",
left: this.elementOffset.left +"px",
top: this.elementOffset.top +"px",
zIndex: ++o.zIndex //TODO: Don't modify option
});
this.helper
.appendTo("body")
.disableSelection();
} else {
this.helper = this.element;
}
},
_change: {
e: function(event, dx) {
return { width: this.originalSize.width + dx };
},
w: function(event, dx) {
var cs = this.originalSize, sp = this.originalPosition;
return { left: sp.left + dx, width: cs.width - dx };
},
n: function(event, dx, dy) {
var cs = this.originalSize, sp = this.originalPosition;
return { top: sp.top + dy, height: cs.height - dy };
},
s: function(event, dx, dy) {
return { height: this.originalSize.height + dy };
},
se: function(event, dx, dy) {
return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
},
sw: function(event, dx, dy) {
return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
},
ne: function(event, dx, dy) {
return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
},
nw: function(event, dx, dy) {
return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
}
},
_propagate: function(n, event) {
$.ui.plugin.call(this, n, [event, this.ui()]);
(n !== "resize" && this._trigger(n, event, this.ui()));
},
plugins: {},
ui: function() {
return {
originalElement: this.originalElement,
element: this.element,
helper: this.helper,
position: this.position,
size: this.size,
originalSize: this.originalSize,
originalPosition: this.originalPosition
};
}
});
/*
* Resizable Extensions
*/
$.ui.plugin.add("resizable", "animate", {
stop: function( event ) {
var that = $(this).data("ui-resizable"),
o = that.options,
pr = that._proportionallyResizeElements,
ista = pr.length && (/textarea/i).test(pr[0].nodeName),
soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
soffsetw = ista ? 0 : that.sizeDiff.width,
style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
that.element.animate(
$.extend(style, top && left ? { top: top, left: left } : {}), {
duration: o.animateDuration,
easing: o.animateEasing,
step: function() {
var data = {
width: parseInt(that.element.css("width"), 10),
height: parseInt(that.element.css("height"), 10),
top: parseInt(that.element.css("top"), 10),
left: parseInt(that.element.css("left"), 10)
};
if (pr && pr.length) {
$(pr[0]).css({ width: data.width, height: data.height });
}
// propagating resize, and updating values for each animation step
that._updateCache(data);
that._propagate("resize", event);
}
}
);
}
});
$.ui.plugin.add("resizable", "containment", {
start: function() {
var element, p, co, ch, cw, width, height,
that = $(this).data("ui-resizable"),
o = that.options,
el = that.element,
oc = o.containment,
ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
if (!ce) {
return;
}
that.containerElement = $(ce);
if (/document/.test(oc) || oc === document) {
that.containerOffset = { left: 0, top: 0 };
that.containerPosition = { left: 0, top: 0 };
that.parentData = {
element: $(document), left: 0, top: 0,
width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
};
}
// i'm a node, so compute top, left, right, bottom
else {
element = $(ce);
p = [];
$([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
that.containerOffset = element.offset();
that.containerPosition = element.position();
that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
co = that.containerOffset;
ch = that.containerSize.height;
cw = that.containerSize.width;
width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
that.parentData = {
element: ce, left: co.left, top: co.top, width: width, height: height
};
}
},
resize: function( event ) {
var woset, hoset, isParent, isOffsetRelative,
that = $(this).data("ui-resizable"),
o = that.options,
co = that.containerOffset, cp = that.position,
pRatio = that._aspectRatio || event.shiftKey,
cop = { top:0, left:0 }, ce = that.containerElement;
if (ce[0] !== document && (/static/).test(ce.css("position"))) {
cop = co;
}
if (cp.left < (that._helper ? co.left : 0)) {
that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
if (pRatio) {
that.size.height = that.size.width / that.aspectRatio;
}
that.position.left = o.helper ? co.left : 0;
}
if (cp.top < (that._helper ? co.top : 0)) {
that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
if (pRatio) {
that.size.width = that.size.height * that.aspectRatio;
}
that.position.top = that._helper ? co.top : 0;
}
that.offset.left = that.parentData.left+that.position.left;
that.offset.top = that.parentData.top+that.position.top;
woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
isParent = that.containerElement.get(0) === that.element.parent().get(0);
isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
if(isParent && isOffsetRelative) {
woset -= that.parentData.left;
}
if (woset + that.size.width >= that.parentData.width) {
that.size.width = that.parentData.width - woset;
if (pRatio) {
that.size.height = that.size.width / that.aspectRatio;
}
}
if (hoset + that.size.height >= that.parentData.height) {
that.size.height = that.parentData.height - hoset;
if (pRatio) {
that.size.width = that.size.height * that.aspectRatio;
}
}
},
stop: function(){
var that = $(this).data("ui-resizable"),
o = that.options,
co = that.containerOffset,
cop = that.containerPosition,
ce = that.containerElement,
helper = $(that.helper),
ho = helper.offset(),
w = helper.outerWidth() - that.sizeDiff.width,
h = helper.outerHeight() - that.sizeDiff.height;
if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
}
if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
$(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
}
}
});
$.ui.plugin.add("resizable", "alsoResize", {
start: function () {
var that = $(this).data("ui-resizable"),
o = that.options,
_store = function (exp) {
$(exp).each(function() {
var el = $(this);
el.data("ui-resizable-alsoresize", {
width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
});
});
};
if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
}else{
_store(o.alsoResize);
}
},
resize: function (event, ui) {
var that = $(this).data("ui-resizable"),
o = that.options,
os = that.originalSize,
op = that.originalPosition,
delta = {
height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
},
_alsoResize = function (exp, c) {
$(exp).each(function() {
var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
$.each(css, function (i, prop) {
var sum = (start[prop]||0) + (delta[prop]||0);
if (sum && sum >= 0) {
style[prop] = sum || null;
}
});
el.css(style);
});
};
if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
$.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
}else{
_alsoResize(o.alsoResize);
}
},
stop: function () {
$(this).removeData("resizable-alsoresize");
}
});
$.ui.plugin.add("resizable", "ghost", {
start: function() {
var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
that.ghost = that.originalElement.clone();
that.ghost
.css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
.addClass("ui-resizable-ghost")
.addClass(typeof o.ghost === "string" ? o.ghost : "");
that.ghost.appendTo(that.helper);
},
resize: function(){
var that = $(this).data("ui-resizable");
if (that.ghost) {
that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
}
},
stop: function() {
var that = $(this).data("ui-resizable");
if (that.ghost && that.helper) {
that.helper.get(0).removeChild(that.ghost.get(0));
}
}
});
$.ui.plugin.add("resizable", "grid", {
resize: function() {
var that = $(this).data("ui-resizable"),
o = that.options,
cs = that.size,
os = that.originalSize,
op = that.originalPosition,
a = that.axis,
grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
gridX = (grid[0]||1),
gridY = (grid[1]||1),
ox = Math.round((cs.width - os.width) / gridX) * gridX,
oy = Math.round((cs.height - os.height) / gridY) * gridY,
newWidth = os.width + ox,
newHeight = os.height + oy,
isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
isMinWidth = o.minWidth && (o.minWidth > newWidth),
isMinHeight = o.minHeight && (o.minHeight > newHeight);
o.grid = grid;
if (isMinWidth) {
newWidth = newWidth + gridX;
}
if (isMinHeight) {
newHeight = newHeight + gridY;
}
if (isMaxWidth) {
newWidth = newWidth - gridX;
}
if (isMaxHeight) {
newHeight = newHeight - gridY;
}
if (/^(se|s|e)$/.test(a)) {
that.size.width = newWidth;
that.size.height = newHeight;
} else if (/^(ne)$/.test(a)) {
that.size.width = newWidth;
that.size.height = newHeight;
that.position.top = op.top - oy;
} else if (/^(sw)$/.test(a)) {
that.size.width = newWidth;
that.size.height = newHeight;
that.position.left = op.left - ox;
} else {
that.size.width = newWidth;
that.size.height = newHeight;
that.position.top = op.top - oy;
that.position.left = op.left - ox;
}
}
});
})(jQuery);
//}}}
!jquery.ui.touch-punch.js
http://touchpunch.furf.com
https://raw.github.com/furf/jquery-ui-touch-punch/master/jquery.ui.touch-punch.js
//{{{
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 2011–2014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
(function ($) {
// Detect touch support
$.support.touch = 'ontouchend' in document;
// Ignore browsers without touch support
if (!$.support.touch) {
return;
}
var mouseProto = $.ui.mouse.prototype,
_mouseInit = mouseProto._mouseInit,
_mouseDestroy = mouseProto._mouseDestroy,
touchHandled;
/**
* Simulate a mouse event based on a corresponding touch event
* @param {Object} event A touch event
* @param {String} simulatedType The corresponding mouse event
*/
function simulateMouseEvent (event, simulatedType) {
// Ignore multi-touch events
if (event.originalEvent.touches.length > 1) {
return;
}
event.preventDefault();
var touch = event.originalEvent.changedTouches[0],
simulatedEvent = document.createEvent('MouseEvents');
// Initialize the simulated mouse event using the touch event's coordinates
simulatedEvent.initMouseEvent(
simulatedType, // type
true, // bubbles
true, // cancelable
window, // view
1, // detail
touch.screenX, // screenX
touch.screenY, // screenY
touch.clientX, // clientX
touch.clientY, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// Dispatch the simulated event to the target element
event.target.dispatchEvent(simulatedEvent);
}
/**
* Handle the jQuery UI widget's touchstart events
* @param {Object} event The widget element's touchstart event
*/
mouseProto._touchStart = function (event) {
var self = this;
// Ignore the event if another widget is already being handled
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
return;
}
// Set the flag to prevent other widgets from inheriting the touch event
touchHandled = true;
// Track movement to determine if interaction was a click
self._touchMoved = false;
// Simulate the mouseover event
simulateMouseEvent(event, 'mouseover');
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
// Simulate the mousedown event
simulateMouseEvent(event, 'mousedown');
};
/**
* Handle the jQuery UI widget's touchmove events
* @param {Object} event The document's touchmove event
*/
mouseProto._touchMove = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Interaction was not a click
this._touchMoved = true;
// Simulate the mousemove event
simulateMouseEvent(event, 'mousemove');
};
/**
* Handle the jQuery UI widget's touchend events
* @param {Object} event The document's touchend event
*/
mouseProto._touchEnd = function (event) {
// Ignore event if not handled
if (!touchHandled) {
return;
}
// Simulate the mouseup event
simulateMouseEvent(event, 'mouseup');
// Simulate the mouseout event
simulateMouseEvent(event, 'mouseout');
// If the touch interaction did not move, it should trigger a click
if (!this._touchMoved) {
// Simulate the click event
simulateMouseEvent(event, 'click');
}
// Unset the flag to allow other widgets to inherit the touch event
touchHandled = false;
};
/**
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
* This method extends the widget with bound touch event handlers that
* translate touch events to mouse events and pass them to the widget's
* original mouse event handling methods.
*/
mouseProto._mouseInit = function () {
var self = this;
// Delegate the touch handlers to the widget's element
self.element.bind({
touchstart: $.proxy(self, '_touchStart'),
touchmove: $.proxy(self, '_touchMove'),
touchend: $.proxy(self, '_touchEnd')
});
// Call the original $.ui.mouse init method
_mouseInit.call(self);
};
/**
* Remove the touch event handlers
*/
mouseProto._mouseDestroy = function () {
var self = this;
// Delegate the touch handlers to the widget's element
self.element.unbind({
touchstart: $.proxy(self, '_touchStart'),
touchmove: $.proxy(self, '_touchMove'),
touchend: $.proxy(self, '_touchEnd')
});
// Call the original $.ui.mouse destroy method
_mouseDestroy.call(self);
};
})(jQuery);
//}}}
/***
|Name|GUIDPlugin|
|License|[[TW Notes License]]|
|Requires|[[CopyTiddlerPlugin]]|
<<guid check>>
<<guid>>
<<guid add>>
!!!!!Code
***/
//{{{
if (config.options.chkAutoAddGUID === undefined) {
config.options.chkAutoAddGUID = true;
}
merge(config.optionsDesc, {
chkAutoAddGUID: "Automatically add a GUID field to tiddlers"
});
//}}}
//{{{
config.macros.guid = {
excludedReservedCheck: [],
generate: function () {
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
},
generateUniqueForTiddler: function (title, tiddlerList) {
var reservedGUIDs = null;
var generate = function () {
if (!reservedGUIDs) {
reservedGUIDs = store.getTiddlerText("ReservedTiddlerGUIDs").split(/\r?\n/g);
for (var i = 0; i < reservedGUIDs.length; i++) {
reservedGUIDs[i] = reservedGUIDs[i].trim();
}
if (!tiddlerList) {
tiddlerList = store.getTiddlers();
}
for (var i = 0; i < tiddlerList.length; i++) {
if (tiddlerList[i].fields && tiddlerList[i].fields["guid"]) {
reservedGUIDs[reservedGUIDs.length] = tiddlerList[i].fields["guid"];
}
}
}
var guid = "";
do {
guid = config.macros.guid.generate();
} while (!guid || (reservedGUIDs.indexOf(guid) > -1));
return guid;
};
var originalGuid = null;
var tiddler = null;
if (title) {
tiddler = (title instanceof Tiddler) ? title : store.getTiddler(title);
if (tiddler && tiddler.fields) {
originalGuid = tiddler.fields["guid"];
}
}
var guid = originalGuid ? originalGuid : generate();
return guid;
},
addMissing: function (skipLinked) {
var tiddlers = [];
store.forEachTiddler(function (title, tiddler) {
var skip = false;
if (skipLinked && tiddler && tiddler.fields) {
for (var k in tiddler.fields) {
if (k.indexOf("server.") === 0) {
skip = true;
break;
}
}
}
if (!skip && tiddler && (!tiddler.fields || !tiddler.fields["guid"])) {
if (!tiddler.fields) {
tiddler.fields = {};
}
tiddler.fields["guid"] = config.macros.guid.generateUniqueForTiddler();
tiddlers.push(tiddler);
}
});
return tiddlers;
},
findTiddler: function (guid) {
var t = null;
if (guid) {
store.forEachTiddler(function (title, tiddler) {
t = (!t && tiddler.fields && (tiddler.fields.guid === guid)) ? tiddler : t;
});
}
return t;
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var buildReservedList = function () {
var guids = {};
var reservedTiddlerGUIDs = store.getTiddlerText("ReservedTiddlerGUIDs");
if (reservedTiddlerGUIDs) {
var lines = reservedTiddlerGUIDs.split("\n");
var source = "";
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if (line) {
if (/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/.test(line)) {
if (guids[line]) {
alert("Duplicate reserved GUID: " + line);
} else {
guids[line] = source;
}
} else {
source = line;
}
}
}
};
return guids;
}
if (params[0] == "add") {
createTiddlyButton(place, "Complete", "Add GUIDs to tiddlers that don't have one", function (e) {
var tiddlers = config.macros.guid.addMissing(false);
displayMessage("Updated " + tiddlers.length + ((tiddlers.length === 1) ? " tiddler" : " tiddlers"));
});
} else if (params[0] == "check") {
var panel = createTiddlyElement(null, "div", null, "sliderPanel");
panel.style.display = "block";
createTiddlyButton(place, "Validate", "List tiddlers that have identical, reserved, or undefined GUIDs, as well as remote tiddlers without a reserved GUID", function (e) {
panel.empty();
var reservedGuids = buildReservedList();
var guids = {};
var result = "";
var tiddlers = store.getTiddlers();
for (var i = 0; i < tiddlers.length; i++) {
if (!tiddlers[i].fields || !tiddlers[i].fields["guid"]) {
result += "undefined : " + tiddlers[i].title + "\n";
} else if (tiddlers[i].fields["guid"] in guids) {
if (typeof guids[tiddlers[i].fields["guid"]] === "string") {
result += tiddlers[i].fields["guid"] + " : " + guids[tiddlers[i].fields["guid"]] + "\n";
guids[tiddlers[i].fields["guid"]] = {};
}
result += tiddlers[i].fields["guid"] + " : " + tiddlers[i].title + "\n";
} else {
var guid = tiddlers[i].fields["guid"];
guids[guid] = tiddlers[i].title;
var server = tiddlers[i].fields["server.host"];
if (reservedGuids[guid] && (config.macros.guid.excludedReservedCheck && (config.macros.guid.excludedReservedCheck.indexOf(reservedGuids[guid]) === -1)) && (tiddlers[i].title != reservedGuids[guid])) {
if (!server || (server.lastIndexOf(reservedGuids[guid]) + reservedGuids[guid].length !== server.length)) {
result += guid + " (reserved " + reservedGuids[guid] + ") : " + tiddlers[i].title + "\n";
}
} else if (server && !reservedGuids[guid]) {
result += guid + " (remote tiddler without a reserved GUID) : " + tiddlers[i].title + "\n";
}
}
}
if (result) {
panel.append("<pre>\n" + result + "</pre>");
} else {
panel.append("<span>no errors</span>");
}
});
place.appendChild(panel);
panel = jQuery(panel);
} else {
var panel = createTiddlyElement(null, "div", null, "sliderPanel");
panel.style.display = "block";
createTiddlyButton(place, "List", "List all tiddler and reserved GUIDs", function (e) {
panel.empty();
var reservedGuids = buildReservedList();
var result = "";
var missing = "";
var tiddlers = store.getTiddlers();
for (var i = 0; i < tiddlers.length; i++) {
if (tiddlers[i].fields && tiddlers[i].fields["guid"]) {
var guid = tiddlers[i].fields["guid"];
result += guid + " : " + tiddlers[i].title + "\n";
if (reservedGuids[guid]) {
delete reservedGuids[guid];
}
} else {
missing += "undefined : " + tiddlers[i].title + "\n";
}
}
var reserved = "";
for (var g in reservedGuids) {
reserved += g + " : " + reservedGuids[g] + "\n"
}
if (result) {
panel.append("<pre>\n" + result + "</pre>");
}
if (reserved) {
panel.append((result ? "<br>" : "") + "Missing reserved GUID tiddlers<br><pre>\n" + reserved + "</pre>");
}
if (missing) {
panel.append(((result || reserved) ? "<br>" : "") + "Undefined GUIDs<br><pre>\n" + missing + "</pre>");
}
});
place.appendChild(panel);
panel = jQuery(panel);
}
}
};
//}}}
//{{{
store.namedNotifications.unshift({
name: null,
notify: function (title) {
if (config.options.chkAutoAddGUID && (typeof title === "string") && store.tiddlerExists(title)) {
var tiddler = store.fetchTiddler(title);
tiddler.fields = tiddler.fields || {};
tiddler.fields["guid"] = config.macros.guid.generateUniqueForTiddler(title);
}
}
});
config.macros.guid.saveChanges = window.saveChanges;
window.saveChanges = function (onlyIfDirty, tiddlers) {
if (config.options.chkAutoAddGUID) {
config.macros.guid.addMissing(true);
}
config.macros.guid.saveChanges.apply(this, arguments);
};
//}}}
//{{{
(function () {
if (config.commands.copyTiddler) {
var copyTiddlerClickCode = eval("config.commands.copyTiddler.click").toString();
copyTiddlerClickCode = copyTiddlerClickCode.replace(/(forEachField[\s\S]*?function[\s\S]*?)(newfields)/, "$1if (!config.options.chkAutoAddGUID || f != 'guid') $2");
eval("config.commands.copyTiddler.click = function click" + copyTiddlerClickCode.substring(copyTiddlerClickCode.indexOf("(")));
}
})();
//}}}
//{{{
config.shadowTiddlers.ReservedTiddlerGUIDs =
"games.html\n" +
"\n" +
"fa911848-1a3d-4a79-a653-6e114711d1f6\n" +
"93c1755f-80af-4326-bca9-c5f1cbed83ef\n" +
"558253e8-0e81-4420-9fb2-23cad4809eab\n" +
"aa726bea-d583-4fd4-a6cf-b324373959fb\n" +
"51d19fa1-dd9d-4d69-9246-9aafa63a0dc2\n" +
"2ed8eb0a-c2f2-44c8-a32d-f673a477b54b\n" +
"6bab1485-b0d7-4394-9d32-e2c9fc4ea0c2\n" +
"80e93f5d-e6a4-403c-a01d-15678b369bed\n" +
"11d25f40-0b77-4f4b-9b51-ec1bf999956e\n" +
"d3128e05-4a96-48ec-be4a-2c0d3d8fb33a\n" +
"\n" +
"gpl.html\n" +
"\n" +
"34fc0268-59fb-4c3d-b412-ac49caf7912e\n" +
"999388ae-15f6-4266-8e2e-5d3315f92126\n" +
"b900254f-2a82-4f96-9f42-3d2414caca5d\n" +
"\n" +
"itw.html\n" +
"\n" +
"35055b4d-80f1-4821-9c3c-26e757d4c3a7\n" +
"a51e4feb-a4cf-40b6-9970-60e9c006c0cc\n" +
"6ff1ef1f-9bbc-439e-b820-5595f87dc3f7\n" +
"2059f8f6-e88b-4d5f-b990-e010c58197d8\n" +
"6616bff2-4051-4d59-88ff-0d37b89ce9c6\n" +
"d3c5d8c6-eb31-4b66-b658-b0bb49290d38\n" +
"5624ff5e-ca09-4df3-a682-e6d8e901e89b\n" +
"8a490858-e5ab-4873-b180-0574a4df34f6\n" +
"20068ee5-fd5e-4980-814e-406d6fb87e43\n" +
"d4033488-a3c6-4853-b942-1ede7976ec16\n" +
"3cbfcd8d-4ff3-456f-890f-bb9b08c713b6\n" +
"65a5b493-2302-436b-a976-0a612655e93f\n" +
"74dfcb01-9f35-4b9f-b665-e7ea8feab903\n" +
"5c246fe5-eaa2-4099-ad8d-d461e2b951c6\n" +
"7aa8c154-358c-4c59-9093-2cb4e8623d4c\n" +
"0fb8484d-92f0-41c0-8c2a-a74ab9f4f75f\n" +
"0e83ed1a-80be-4863-9256-39bb1d8d9181\n" +
"1379e2c3-9147-487e-8d85-45beea0d8a39\n" +
"96820ede-d2b6-430e-9b75-6957d9e23304\n" +
"aabbd405-b917-41b9-85b1-af27db1ec519\n" +
"19f4fb69-6aa4-4a8f-80fe-cdb17743c2c7\n" +
"e0504356-e890-476a-9abf-deb0f187dd3a\n" +
"f2fd0abe-39ef-4702-a2bb-b3ea9a2f0235\n" +
"bcc6651e-d62e-435a-b24c-bb601a283bf3\n" +
"a517ca8f-bc71-48ca-b3b3-7f9e3eb6562b\n" +
"e0d6873e-7bb8-400b-bd87-d947b65aeb36\n" +
"fa169a0e-f56d-4719-8bf7-c4281dd662fe\n" +
"d932f80f-4a64-4b5a-a61f-4a93ba2bb8a7\n" +
"d5456c31-595a-464e-b85a-1255e9179d51\n" +
"2e9b197e-7ca8-41da-bd83-c96d6ec153dd\n" +
"\n" +
"itwnotes.html\n" +
"\n" +
"87f9a47c-063c-46ef-a5b7-ff55abc67888\n" +
"acdd3ca5-1852-4d1f-8cab-7afc0552233d\n" +
"8f34d737-215d-4cb7-843a-894a70fc0bf0\n" +
"8233edc6-d288-4ee2-8608-ff4b5f546991\n" +
"2db1ace9-68a9-422c-8125-eb1c15a8ef5f\n" +
"247f64c4-624b-4e1a-9ff1-2b07c71d6aa0\n" +
"a855f06e-7204-4a23-b54f-f0361e23fc90\n" +
"19849c21-1f6a-4af9-b2fb-39bc39f20ac7\n" +
"e833c6eb-5af0-4b47-8d6b-2b9aac9d3c3b\n" +
"58d8af24-9645-4c49-a2dd-433c9a4a43dc\n" +
"ac4ecea0-2ee6-4b1f-a8b0-8380209d5ae3\n" +
"cd3aac61-dd4e-4140-a5f9-9d573936767a\n" +
"ddf3c706-aed7-48ab-9f4b-84f02fbb4e1e\n" +
"1c5b3d19-18bf-4745-a129-ca0db8871c85\n" +
"5e594dbe-f665-4f1e-ac7e-f1d4e3fe9c4d\n" +
"f104518b-1620-4a19-afa8-51e60295a2ca\n" +
"48eedc95-877c-4f51-8511-74db5cb8bfdd\n" +
"1610bb82-d684-4634-a63c-904c5d89a539\n" +
"890ca4a7-608f-429a-86d6-6ca47a3c0679\n" +
"97d87a18-613d-45e6-9ef9-9dff32b353a1\n" +
"1b62ffd2-127d-42bb-bbc5-b74396694c55\n" +
"21a69bb7-2545-4c27-b394-d693a7ef492b\n" +
"0907bf43-3d98-485e-b94e-6ff5a6a302d8\n" +
"\n" +
"notes.html\n" +
"\n" +
"0198716d-cc24-4aab-a2d6-5ae511a821fb\n" +
"59ef7965-16f6-48b9-9774-e6c204612c33\n" +
"c29a83fb-6205-410a-8b5c-5fc6a16e4aec\n" +
"5b0920b0-ca7b-49cf-ab1e-2b61be77a302\n" +
"51117d39-3cd8-42c7-8d94-fbbd37767a0d\n" +
"9a524955-9a5a-4c7c-b5f6-12766488b4bd\n" +
"8b4add8f-be29-47ac-990b-720400982f82\n" +
"0ed15970-ef50-4ba2-9a97-23e3ac30b1d9\n" +
"6c48ea60-61d0-4841-b174-035cbee42e7a\n" +
"477bd0c1-75fe-4dc9-b4b7-384651484e1e\n" +
"53f1e7e8-68c0-4636-a4f1-14de6918b117\n" +
"2aa0b101-640c-492b-a2a5-a5be37482991\n" +
"7391006f-56a4-4b50-b4b8-f510c61dcdfc\n" +
"ebed9207-8bcf-447b-ade5-5f7c9790199d\n" +
"75824ff2-056d-4d18-bccc-2b179e5f70c8\n" +
"a4448e63-4d2b-4dfe-a598-5efe6e3d5f53\n" +
"10aee81d-25c6-453f-b72e-ad642a49a8b5\n" +
"659891d8-619c-4252-a983-fc83fd327b23\n" +
"6fd4d154-3ae5-4dd7-a68d-9f0dd3d1afaa\n" +
"479fb448-6249-47f7-8342-9d0d92331150\n" +
"f51ce670-98c8-49cc-9bea-0f81f275686f\n" +
"480034c1-0eec-4263-beae-3a23b265adb1\n" +
"3e379eef-51c7-40bd-8a0d-fef36bed59bb\n" +
"bbf9a480-8978-4d39-be05-434b124713a7\n" +
"dfc3d520-6fb4-47f8-b50c-6ca1a6c00a65\n" +
"170abd39-ffb6-45df-89df-acdc0372f2d6\n" +
"ffc11dbf-828a-45d1-986a-0be5ba556364\n" +
"c71dd7f9-7530-4edf-80e0-a5c7f7ba288b\n" +
"29fa1c62-6dc2-4b18-870c-f4c04a71eef4\n" +
"0f01bb48-da38-43bb-bec3-7dd9efa0a3f9\n" +
"0143df28-19d9-4473-9e2e-2f0665ab143e\n" +
"89c6f4c4-d6ff-4526-b0fe-dfb211737f22\n" +
"3a155e60-0504-4437-8860-7c511bbde12b\n" +
"09a1a626-d921-462b-92a9-6f3c3142fdc1\n" +
"a995e1aa-c189-49da-a680-c7255f39f9c3\n" +
"cf4ac14e-265c-4e95-96ed-49345c5c8d07\n" +
"a7f8d6c6-7b2b-4a4f-b53f-079bde71a8bf\n" +
"0373cd25-ca66-48a3-9479-fa292ea6a2e4\n" +
"0a304fe2-64c8-4e9f-9bda-4d30a0f822fb\n" +
"7b678ec1-acf9-472f-990a-63675eb62924\n" +
"89753d53-7402-4e15-88f3-56fd77cc68e4\n" +
"1f3ff777-9279-4e56-87ce-3dcd67e63110\n" +
"be86b959-0234-4b15-87ff-efff2bcd81db\n" +
"47ca82ee-34d8-4632-ab6b-32fe8007abc9\n" +
"dbc34da9-fe04-4e61-b88f-a5220f458058\n" +
"381b9f0c-10f9-4460-86b6-970a721d889e\n" +
"2a584638-ecf8-4473-b9c4-1c36f83e350b\n" +
"f7cf2363-0a40-449e-98ce-b58180333199\n" +
"298571e6-a841-4137-8715-f597100586fe\n" +
"87de36b2-901e-46f3-b59e-9bd289a292ba\n" +
"16ed5f70-0745-4964-9401-ba7800b48cb1\n" +
"1bb6006c-cebe-44b3-b89c-d700d23575e8\n" +
"26fee147-29e2-4e8a-a980-a687d288f688\n" +
"80a27dce-8504-4a66-b832-b80a90f0f187\n" +
"ebc57d6e-c7a3-493b-a4b3-816eee97c24b\n" +
"233bf59e-ed85-49a2-9928-c1f3cdde170e\n" +
"badc0aef-1481-4d28-bb7f-51d79f366e9e\n" +
"12d3d491-37b3-49ad-b8e8-e1fe10dbb830\n" +
"bb85e47f-059f-404d-92d0-838f07625dda\n" +
"4c93aca6-9e1e-4e1a-afca-fd48e962c953\n" +
"d5a50b8d-00d1-47a2-800c-141fb3e03af8\n" +
"d2d7f1a6-1d66-4c78-bf83-b2e5dcbcc0a9\n" +
"ef907d2b-e7e4-4c97-ba0d-b2049b421a2e\n" +
"ea6c68c5-d61b-4548-88b3-335d20ea3726\n" +
"e10a04f5-786e-4041-a97b-da28b5614417\n" +
"868a5ecd-10f8-4340-97f8-cd9f096ee9ea\n" +
"145c5ceb-ffae-447a-b31e-3ad57abf887f\n" +
"baeef698-7366-408e-b821-c478536ff673\n" +
"365be189-488b-48bb-9515-db8261f9a333\n" +
"b4bcd0c1-e855-485b-82b3-af31f872f0e3\n" +
"53409b81-be76-43f8-a52d-ec485f8c9fb1\n" +
"ca1a2041-e759-46c0-8755-2091885082d2\n" +
"0023b005-29ce-4244-8f72-1f6651d38df7\n" +
"c6069460-59f4-4499-b481-40e3e4ef01d8\n" +
"08fc59fe-35f3-444d-bfd4-d5159d8ae42c\n" +
"383de760-944c-4ba3-bd99-9ff97fa60051\n" +
"0d789f9a-acd7-49a5-953e-b272f7404b36\n" +
"72a619e2-872c-41da-8d40-6ddcf0db41ad\n" +
"16121cc2-df0f-4653-b377-9403df63ec60\n" +
"f4236eb8-2f87-45e6-bc30-f31d4ab70e26\n" +
"0e83a6bd-c728-42ad-abb3-0f81410e1fbd\n" +
"9a15d670-f94a-43f2-aa8f-9acf9bf590ff\n" +
"ca619482-07bf-437f-84b8-20a16ae74bfc\n" +
"4610123f-ffc4-4cbc-8d97-1f2766de0bcb\n" +
"325da458-193a-4c6f-8c8b-9d11d71a924d\n" +
"e49361a9-62a0-48ba-8d65-701ce23336b3\n" +
"31c06044-4ab9-4ee2-a2b3-9d2974b63cbe\n" +
"a636f543-58cc-44f0-a619-98ad4827cf6e\n" +
"0b15a509-b2c2-445d-8d73-186aee5a096b\n" +
"a2a2a7ee-f925-4c21-97e6-b97dcb6a91dd\n" +
"2208c690-18b0-4188-b48b-efaaec90124b\n" +
"593df86e-75f7-4082-ad2d-0d70e0b04cf9\n" +
"3f9f45d3-1bf0-4f7f-9a92-ee04aef1775b\n" +
"185b3329-5df7-44df-8495-62365e924e57\n" +
"c0cc0f51-e2fe-4522-8e4a-59a20ef181dd\n" +
"b61d1421-b78c-4201-94dd-c164af881748\n" +
"3b79ef3f-c417-4698-94b5-09e6e2dd2f9f\n" +
"b93a0417-2984-45ba-8b28-0a9bc529abed\n" +
"b84303fc-1415-44c8-8fa0-ca8a772967be\n" +
"89f365ef-9224-44db-b5f3-bf370d235c8c\n" +
"f63f99c2-54f9-4c09-b33b-5281780aa3db\n" +
"f4fa02bc-b486-4f0a-ad6c-7740cfd77ca3\n" +
"90e94a09-a41b-474b-90e3-56b80db0d67a\n" +
"44ca1e81-64e1-4273-a3d3-31e10775282c\n" +
"918ea6ea-cdb7-43a5-b3de-ecb8b8554801\n" +
"dc7d51b7-cf73-4b59-a1a3-f0357b7c6cac\n" +
"3802c60c-91be-46b4-b666-94589810d4db\n" +
"10d4a3f9-23d4-4550-ae4f-1e1a58e30402\n" +
"c73ca4dd-03bf-4993-861f-63721ff16dcb\n" +
"17c842a8-58ba-48ab-8e33-58b85151216a\n" +
"16b5416c-38f0-4bc0-8d7f-dab5c6525823\n" +
"170b4e09-5f53-41b4-bb6c-14726b941297\n" +
"2920e809-b849-4bbd-b18f-833a6eb29e49\n" +
"58365302-7537-4195-817a-840287ec4a79\n" +
"d2328aa1-a712-427d-83a5-be9e6bf405d6\n" +
"c0df230b-9918-4bfd-a6b8-351c66b57d46\n" +
"a91cedd6-888e-4972-8ea7-741d7d9cb5fb\n" +
"5cc98f9e-6abd-4530-89c6-4eacf2c92020\n" +
"779a0f47-f41d-43eb-842e-2e943d9a048d\n" +
"3120c47d-e2c9-4c5a-bc5d-4549cbb5c470\n" +
"10ff94af-4619-4e80-b274-d92ed192d455\n" +
"e932c918-4fa4-4160-90ef-93e2f73a5bec\n" +
"745d3a54-d6f8-4fc6-9377-567ec51dc343\n" +
"35af63b3-18e5-4c7c-a6c8-5af761cb06a8\n" +
"b3954e69-56e1-44db-8ee4-a7e07fc5fdce\n" +
"f32eddf7-5a23-4055-a798-f4d4991fedc9\n" +
"4090e1c2-6e2b-4a77-98ea-b593640f92aa\n" +
"6070c048-c92e-4d83-89a6-3ca2f75b5da8\n" +
"806467e5-d339-41eb-b916-d3a88950212d\n" +
"9c9c2399-356b-48c0-b138-92a3533c2b4a\n" +
"0783f31e-c6fe-4f6a-bd22-540cf7e0541a\n" +
"2ed1470d-4434-4d7b-822c-3f6c17d1069b\n" +
"5c66d97a-6192-4d76-89d1-e5a96daf9468\n" +
"559e69b7-eb85-4ca2-8cb1-2f66eabcb258\n" +
"ea7e23dd-8abf-4893-80cf-d401c63bf1ba\n" +
"f4bc5ef8-44c5-43f7-9f13-9468c3fc60bf\n" +
"7547281a-dba7-4e36-8074-3bdbb6446b1c\n" +
"f381d103-04e1-4eca-82b2-5e294fcbfb45\n" +
"c5834800-c501-45f2-ac9d-06b3ae215e62\n" +
"507a271f-9099-47ae-8b52-4e729314a01d\n" +
"4706ceab-b375-425c-a1d8-c02aa54eded7\n" +
"74629d2b-ccb4-47f6-86da-b72495673fb3\n" +
"e271710c-ed9f-4fd3-a6d4-2a158b7aa56c\n" +
"6668835b-d3a2-4a7a-a1a3-14149c7ecb53\n" +
"9596f9a7-84e1-4986-bbc1-19499fba0146\n" +
"aed2cd8d-5861-4e97-b3df-c7ecd7dc883a\n" +
"f58d0a62-c50b-4ecd-9ae6-90162050dd87\n" +
"109dc203-900c-4056-a817-de2100029910\n" +
"db849155-d0ea-45e1-98d2-5bbc8bb4d23e\n" +
"0d0bacb2-45bb-47fc-8eae-ce15f9412057\n" +
"1099cccd-71e8-4c1d-867d-1e0bc1f5e1a2\n" +
"8bee031d-cec5-47d0-9694-c4923d0d0176\n" +
"f892c68d-13ab-4112-b8c1-41f3ef974c85\n" +
"2536484e-4b40-406b-9b82-4b2e76937f3d\n" +
"48bec9e7-b057-4ce1-bf62-cce75853d646\n" +
"29303e61-7e7c-4676-bd4d-c087eed74b76\n" +
"cf3c98aa-5620-415a-b72d-45783c59bb3b\n" +
"6fd66796-74f1-4e32-b78c-bd116dc3389d\n" +
"94afd1b4-c51e-4e5e-b219-47422b9517d0\n" +
"84485e54-2ee2-4a9a-ba58-6347a7e3537a\n" +
"741b3936-cc15-4825-a6f3-9a017424c985\n" +
"5b1b8213-b879-45a4-bf11-aa941f6cc4ab\n" +
"7a408ff5-ba17-422c-874d-3d8cb1593dd6\n" +
"6a0bf23a-9846-4b05-9e8a-f450b12872c3\n" +
"455400a3-01fa-44ba-a724-99a1a6e21e95\n" +
"\n" +
"notes-ext.html\n" +
"\n" +
"682f8726-0954-459d-a355-d0cc45d594e7\n" +
"099cbe7d-75c7-4dce-ac03-eae7bd649bab\n" +
"039724c2-5897-4428-ab48-f87a1a6fad1a\n" +
"8e57949f-fe77-4725-b2a2-9dc3d7ce962f\n" +
"62b14c85-e5b9-4deb-949e-997d9a2ff99c\n" +
"2aac16da-6561-4c67-9f16-49bfea0e31ef\n" +
"a07ef8ae-4d57-4b64-ab9b-25fbd379f3dc\n" +
"0f66eef7-e75a-44dd-bd22-34d1888c15fe\n" +
"630fb6c5-b137-4af8-b240-aa33290f1187\n" +
"88ee22ca-d58e-43b3-a21f-2f4feb4570e7\n" +
"2469994a-703f-4c81-be32-e1f56cc58e63\n" +
"4a55b614-3897-4140-aa14-f3b2409d39bd\n" +
"8153d9a1-a44c-4acf-a517-e412a24a3bda\n" +
"446bc808-15b4-4a51-8911-a0faec9fb6dd\n" +
"fcecfad4-e48b-4769-b5a6-26010ed777c7\n" +
"6bad9a82-a5cb-413b-81c6-b7b141f6b672\n" +
"bebe1b15-d17d-49a9-aa0e-ecc6306f40bd\n" +
"bfe8dc99-4f47-405c-aabe-4470681b35bc\n" +
"40553f03-5468-4cb0-b61d-47cbaad7346c\n" +
"00ab19bd-22f4-4bff-99de-aebab7746a0c\n" +
"5b595c21-d73e-468e-baaa-d785c4101c6f\n" +
"c7956362-847f-482f-a5a1-e34aa86dd793\n" +
"30933f3f-f077-402c-a0c8-2d8661660aaf\n" +
"95417ba0-775d-4ee6-9f7a-ef57678adbcf\n" +
"bdf05506-0ca1-41a6-92e8-407e4c0e6186\n" +
"78880774-5fba-4127-9510-57339ec227f3\n" +
"6eb74328-1f7a-4846-85bb-6c1106125145\n" +
"04c8a06d-45ae-4e85-bc7a-56480194e02e\n" +
"f32d7d72-b8ea-4bb3-8a45-ebaa65d14fc9\n" +
"6ec3fc1f-bf26-49ef-a4de-8f8df3d95d61\n" +
"a316020a-17d3-46e3-9108-3f4421de1817\n" +
"7f90c3a7-554d-4b55-bf6e-638a6e1facc9\n" +
"acbdf356-6f94-4066-bd1c-ab6452417052\n" +
"8d6abfc1-9359-43b3-b928-adb1b61d6f93\n" +
"d702e420-57b0-4491-9aa0-5f7d21ac0247\n" +
"305753ce-e149-4460-a134-2338fba95f82\n" +
"d507a0e7-90ff-4725-9cd1-a89ad3e5c49c\n" +
"985fb27d-18ae-4c30-b475-3e223897694b\n" +
"ecede8fb-dece-4e18-af1d-9c7111585ac7\n" +
"920e94e0-e97d-4d0a-a082-b5d2bf64aa54\n" +
"5765267b-bbd0-401e-b690-0781598c8f6a\n" +
"d6759f01-0238-4682-b366-9aed21b68cf3\n" +
"2892ee1a-dd7c-47af-ad91-fb6e008f9ab8\n" +
"949a4229-5ae3-42a0-89b4-74467ae0765e\n"
//}}}
//{{{
config.macros.goToTiddler = {
handler: function (place, macroName, params, wikifier, paramString) {
var cmnt = config.macros.newTiddler;
params = paramString.parseParams("anon", null, true, false, false);
var title = params[1] && params[1].name == "anon" ? params[1].value : cmnt.title;
title = getParam(params, "title", title);
var tiddler = store.getTiddler(title);
if (tiddler || readOnly) {
var label = getParam(params, "label", cmnt.label);
createTiddlyText(createTiddlyLink(place, title, false, "button", false, tiddler), label);
} else {
cmnt.createNewTiddlerButton(place, title, params, cmnt.label, cmnt.prompt, cmnt.accessKey, "title", false);
}
}
}
//}}}
http://www.htmlhelp.com/reference/html40/entities
Character entity references, or //entities// for short, provide a method of entering characters that cannot be expressed in the document's character encoding or that cannot easily be entered on a keyboard. Entities are case-sensitive and take the form ''&//name//;''. Examples of entities include ''&copy;'' for the copyright symbol and ''&Alpha;'' for the Greek capital letter alpha.
In addition to entities, authors can use //numeric character references//. While entities are limited to a subset of [[Unicode characters|http://www.unicode.org/charts/]], numeric character references can specify any character. Numeric character references may be given in decimal or hexadecimal, though browser support is stronger for decimal references. Decimal references are of the form ''&#//number//;'' while hexadecimal references take the case-insensitive form ''&#x//number//;''. Examples of numeric character references include ''&#169;'' or ''&#xA9;'' for the copyright symbol, ''&#913;'' or ''&#x391;'' for the Greek capital letter alpha, and ''&#1575;'' or ''&#x627;'' for the Arabic letter alef.
The following documents feature tables of the character entity references in HTML 4, along with the numeric character reference in decimal and hexadecimal. A rendering of each character reference is provided so that users may check their browsers' compliance.
!Latin-1 Entities
The following table gives the character entity reference, decimal character reference, and hexadecimal character reference for 8-bit characters in the Latin-1 (ISO-8859-1) character set, as well as the rendering of each in your browser. [[Glyphs|http://www.unicode.org/charts/]] of the characters are available at the [[Unicode Consortium|http://www.unicode.org/]].
| !Character | !Entity | !Decimal | !Hex |>|>| !Rendering in Your Browser |
|~|~|~|~| !Entity | !Decimal | !Hex |
|no-break space = non-breaking space |&nbsp; |&#160; |&#xA0; | |  |  |
|inverted exclamation mark |&iexcl; |&#161; |&#xA1; |¡ |¡ |¡ |
|cent sign |&cent; |&#162; |&#xA2; |¢ |¢ |¢ |
|pound sign |&pound; |&#163; |&#xA3; |£ |£ |£ |
|currency sign |&curren; |&#164; |&#xA4; |¤ |¤ |¤ |
|yen sign = yuan sign |&yen; |&#165; |&#xA5; |¥ |¥ |¥ |
|broken bar = broken vertical bar |&brvbar; |&#166; |&#xA6; |¦ |¦ |¦ |
|section sign |&sect; |&#167; |&#xA7; |§ |§ |§ |
|diaeresis = spacing diaeresis |&uml; |&#168; |&#xA8; |¨ |¨ |¨ |
|copyright sign |&copy; |&#169; |&#xA9; |© |© |© |
|feminine ordinal indicator |&ordf; |&#170; |&#xAA; |ª |ª |ª |
|left-pointing double angle quotation mark = left pointing guillemet |&laquo; |&#171; |&#xAB; |« |« |« |
|not sign |&not; |&#172; |&#xAC; |¬ |¬ |¬ |
|soft hyphen = discretionary hyphen |&shy; |&#173; |&#xAD; |­ |­ |­ |
|registered sign = registered trade mark sign |&reg; |&#174; |&#xAE; |® |® |® |
|macron = spacing macron = overline = APL overbar |&macr; |&#175; |&#xAF; |¯ |¯ |¯ |
|degree sign |&deg; |&#176; |&#xB0; |° |° |° |
|plus-minus sign = plus-or-minus sign |&plusmn; |&#177; |&#xB1; |± |± |± |
|superscript two = superscript digit two = squared |&sup2; |&#178; |&#xB2; |² |² |² |
|superscript three = superscript digit three = cubed |&sup3; |&#179; |&#xB3; |³ |³ |³ |
|acute accent = spacing acute |&acute; |&#180; |&#xB4; |´ |´ |´ |
|micro sign |&micro; |&#181; |&#xB5; |µ |µ |µ |
|pilcrow sign = paragraph sign |&para; |&#182; |&#xB6; |¶ |¶ |¶ |
|middle dot = Georgian comma = Greek middle dot |&middot; |&#183; |&#xB7; |· |· |· |
|cedilla = spacing cedilla |&cedil; |&#184; |&#xB8; |¸ |¸ |¸ |
|superscript one = superscript digit one |&sup1; |&#185; |&#xB9; |¹ |¹ |¹ |
|masculine ordinal indicator |&ordm; |&#186; |&#xBA; |º |º |º |
|right-pointing double angle quotation mark = right pointing guillemet |&raquo; |&#187; |&#xBB; |» |» |» |
|vulgar fraction one quarter = fraction one quarter |&frac14; |&#188; |&#xBC; |¼ |¼ |¼ |
|vulgar fraction one half = fraction one half |&frac12; |&#189; |&#xBD; |½ |½ |½ |
|vulgar fraction three quarters = fraction three quarters |&frac34; |&#190; |&#xBE; |¾ |¾ |¾ |
|inverted question mark = turned question mark |&iquest; |&#191; |&#xBF; |¿ |¿ |¿ |
|Latin capital letter A with grave = Latin capital letter A grave |&Agrave; |&#192; |&#xC0; |À |À |À |
|Latin capital letter A with acute |&Aacute; |&#193; |&#xC1; |Á |Á |Á |
|Latin capital letter A with circumflex |&Acirc; |&#194; |&#xC2; |Â |Â |Â |
|Latin capital letter A with tilde |&Atilde; |&#195; |&#xC3; |Ã |Ã |Ã |
|Latin capital letter A with diaeresis |&Auml; |&#196; |&#xC4; |Ä |Ä |Ä |
|Latin capital letter A with ring above = Latin capital letter A ring |&Aring; |&#197; |&#xC5; |Å |Å |Å |
|Latin capital letter AE = Latin capital ligature AE |&AElig; |&#198; |&#xC6; |Æ |Æ |Æ |
|Latin capital letter C with cedilla |&Ccedil; |&#199; |&#xC7; |Ç |Ç |Ç |
|Latin capital letter E with grave |&Egrave; |&#200; |&#xC8; |È |È |È |
|Latin capital letter E with acute |&Eacute; |&#201; |&#xC9; |É |É |É |
|Latin capital letter E with circumflex |&Ecirc; |&#202; |&#xCA; |Ê |Ê |Ê |
|Latin capital letter E with diaeresis |&Euml; |&#203; |&#xCB; |Ë |Ë |Ë |
|Latin capital letter I with grave |&Igrave; |&#204; |&#xCC; |Ì |Ì |Ì |
|Latin capital letter I with acute |&Iacute; |&#205; |&#xCD; |Í |Í |Í |
|Latin capital letter I with circumflex |&Icirc; |&#206; |&#xCE; |Î |Î |Î |
|Latin capital letter I with diaeresis |&Iuml; |&#207; |&#xCF; |Ï |Ï |Ï |
|Latin capital letter ETH |&ETH; |&#208; |&#xD0; |Ð |Ð |Ð |
|Latin capital letter N with tilde |&Ntilde; |&#209; |&#xD1; |Ñ |Ñ |Ñ |
|Latin capital letter O with grave |&Ograve; |&#210; |&#xD2; |Ò |Ò |Ò |
|Latin capital letter O with acute |&Oacute; |&#211; |&#xD3; |Ó |Ó |Ó |
|Latin capital letter O with circumflex |&Ocirc; |&#212; |&#xD4; |Ô |Ô |Ô |
|Latin capital letter O with tilde |&Otilde; |&#213; |&#xD5; |Õ |Õ |Õ |
|Latin capital letter O with diaeresis |&Ouml; |&#214; |&#xD6; |Ö |Ö |Ö |
|multiplication sign |&times; |&#215; |&#xD7; |× |× |× |
|Latin capital letter O with stroke = Latin capital letter O slash |&Oslash; |&#216; |&#xD8; |Ø |Ø |Ø |
|Latin capital letter U with grave |&Ugrave; |&#217; |&#xD9; |Ù |Ù |Ù |
|Latin capital letter U with acute |&Uacute; |&#218; |&#xDA; |Ú |Ú |Ú |
|Latin capital letter U with circumflex |&Ucirc; |&#219; |&#xDB; |Û |Û |Û |
|Latin capital letter U with diaeresis |&Uuml; |&#220; |&#xDC; |Ü |Ü |Ü |
|Latin capital letter Y with acute |&Yacute; |&#221; |&#xDD; |Ý |Ý |Ý |
|Latin capital letter THORN |&THORN; |&#222; |&#xDE; |Þ |Þ |Þ |
|Latin small letter sharp s = ess-zed |&szlig; |&#223; |&#xDF; |ß |ß |ß |
|Latin small letter a with grave = Latin small letter a grave |&agrave; |&#224; |&#xE0; |à |à |à |
|Latin small letter a with acute |&aacute; |&#225; |&#xE1; |á |á |á |
|Latin small letter a with circumflex |&acirc; |&#226; |&#xE2; |â |â |â |
|Latin small letter a with tilde |&atilde; |&#227; |&#xE3; |ã |ã |ã |
|Latin small letter a with diaeresis |&auml; |&#228; |&#xE4; |ä |ä |ä |
|Latin small letter a with ring above = Latin small letter a ring |&aring; |&#229; |&#xE5; |å |å |å |
|Latin small letter ae = Latin small ligature ae |&aelig; |&#230; |&#xE6; |æ |æ |æ |
|Latin small letter c with cedilla |&ccedil; |&#231; |&#xE7; |ç |ç |ç |
|Latin small letter e with grave |&egrave; |&#232; |&#xE8; |è |è |è |
|Latin small letter e with acute |&eacute; |&#233; |&#xE9; |é |é |é |
|Latin small letter e with circumflex |&ecirc; |&#234; |&#xEA; |ê |ê |ê |
|Latin small letter e with diaeresis |&euml; |&#235; |&#xEB; |ë |ë |ë |
|Latin small letter i with grave |&igrave; |&#236; |&#xEC; |ì |ì |ì |
|Latin small letter i with acute |&iacute; |&#237; |&#xED; |í |í |í |
|Latin small letter i with circumflex |&icirc; |&#238; |&#xEE; |î |î |î |
|Latin small letter i with diaeresis |&iuml; |&#239; |&#xEF; |ï |ï |ï |
|Latin small letter eth |&eth; |&#240; |&#xF0; |ð |ð |ð |
|Latin small letter n with tilde |&ntilde; |&#241; |&#xF1; |ñ |ñ |ñ |
|Latin small letter o with grave |&ograve; |&#242; |&#xF2; |ò |ò |ò |
|Latin small letter o with acute |&oacute; |&#243; |&#xF3; |ó |ó |ó |
|Latin small letter o with circumflex |&ocirc; |&#244; |&#xF4; |ô |ô |ô |
|Latin small letter o with tilde |&otilde; |&#245; |&#xF5; |õ |õ |õ |
|Latin small letter o with diaeresis |&ouml; |&#246; |&#xF6; |ö |ö |ö |
|division sign |&divide; |&#247; |&#xF7; |÷ |÷ |÷ |
|Latin small letter o with stroke = Latin small letter o slash |&oslash; |&#248; |&#xF8; |ø |ø |ø |
|Latin small letter u with grave |&ugrave; |&#249; |&#xF9; |ù |ù |ù |
|Latin small letter u with acute |&uacute; |&#250; |&#xFA; |ú |ú |ú |
|Latin small letter u with circumflex |&ucirc; |&#251; |&#xFB; |û |û |û |
|Latin small letter u with diaeresis |&uuml; |&#252; |&#xFC; |ü |ü |ü |
|Latin small letter y with acute |&yacute; |&#253; |&#xFD; |ý |ý |ý |
|Latin small letter thorn |&thorn; |&#254; |&#xFE; |þ |þ |þ |
|Latin small letter y with diaeresis |&yuml; |&#255; |&#xFF; |ÿ |ÿ |ÿ |
!Entities for Symbols and Greek Letters
The following table gives the character entity reference, decimal character reference, and hexadecimal character reference for symbols and Greek letters, as well as the rendering of each in your browser. [[Glyphs|http://www.unicode.org/charts/]] of the characters are available at the [[Unicode Consortium|http://www.unicode.org/]].
These entities are all new in HTML 4.0 and may not be supported by old browsers. Support in recent browsers is good.
| !Character | !Entity | !Decimal | !Hex |>|>| !Rendering in Your Browser |
|~|~|~|~| !Entity | !Decimal | !Hex |
|Latin small f with hook = function = florin |&fnof; |&#402; |&#x192; |ƒ |ƒ |ƒ |
|Greek capital letter alpha |&Alpha; |&#913; |&#x391; |Α |Α |Α |
|Greek capital letter beta |&Beta; |&#914; |&#x392; |Β |Β |Β |
|Greek capital letter gamma |&Gamma; |&#915; |&#x393; |Γ |Γ |Γ |
|Greek capital letter delta |&Delta; |&#916; |&#x394; |Δ |Δ |Δ |
|Greek capital letter epsilon |&Epsilon; |&#917; |&#x395; |Ε |Ε |Ε |
|Greek capital letter zeta |&Zeta; |&#918; |&#x396; |Ζ |Ζ |Ζ |
|Greek capital letter eta |&Eta; |&#919; |&#x397; |Η |Η |Η |
|Greek capital letter theta |&Theta; |&#920; |&#x398; |Θ |Θ |Θ |
|Greek capital letter iota |&Iota; |&#921; |&#x399; |Ι |Ι |Ι |
|Greek capital letter kappa |&Kappa; |&#922; |&#x39A; |Κ |Κ |Κ |
|Greek capital letter lambda |&Lambda; |&#923; |&#x39B; |Λ |Λ |Λ |
|Greek capital letter mu |&Mu; |&#924; |&#x39C; |Μ |Μ |Μ |
|Greek capital letter nu |&Nu; |&#925; |&#x39D; |Ν |Ν |Ν |
|Greek capital letter xi |&Xi; |&#926; |&#x39E; |Ξ |Ξ |Ξ |
|Greek capital letter omicron |&Omicron; |&#927; |&#x39F; |Ο |Ο |Ο |
|Greek capital letter pi |&Pi; |&#928; |&#x3A0; |Π |Π |Π |
|Greek capital letter rho |&Rho; |&#929; |&#x3A1; |Ρ |Ρ |Ρ |
|Greek capital letter sigma |&Sigma; |&#931; |&#x3A3; |Σ |Σ |Σ |
|Greek capital letter tau |&Tau; |&#932; |&#x3A4; |Τ |Τ |Τ |
|Greek capital letter upsilon |&Upsilon; |&#933; |&#x3A5; |Υ |Υ |Υ |
|Greek capital letter phi |&Phi; |&#934; |&#x3A6; |Φ |Φ |Φ |
|Greek capital letter chi |&Chi; |&#935; |&#x3A7; |Χ |Χ |Χ |
|Greek capital letter psi |&Psi; |&#936; |&#x3A8; |Ψ |Ψ |Ψ |
|Greek capital letter omega |&Omega; |&#937; |&#x3A9; |Ω |Ω |Ω |
|Greek small letter alpha |&alpha; |&#945; |&#x3B1; |α |α |α |
|Greek small letter beta |&beta; |&#946; |&#x3B2; |β |β |β |
|Greek small letter gamma |&gamma; |&#947; |&#x3B3; |γ |γ |γ |
|Greek small letter delta |&delta; |&#948; |&#x3B4; |δ |δ |δ |
|Greek small letter epsilon |&epsilon; |&#949; |&#x3B5; |ε |ε |ε |
|Greek small letter zeta |&zeta; |&#950; |&#x3B6; |ζ |ζ |ζ |
|Greek small letter eta |&eta; |&#951; |&#x3B7; |η |η |η |
|Greek small letter theta |&theta; |&#952; |&#x3B8; |θ |θ |θ |
|Greek small letter iota |&iota; |&#953; |&#x3B9; |ι |ι |ι |
|Greek small letter kappa |&kappa; |&#954; |&#x3BA; |κ |κ |κ |
|Greek small letter lambda |&lambda; |&#955; |&#x3BB; |λ |λ |λ |
|Greek small letter mu |&mu; |&#956; |&#x3BC; |μ |μ |μ |
|Greek small letter nu |&nu; |&#957; |&#x3BD; |ν |ν |ν |
|Greek small letter xi |&xi; |&#958; |&#x3BE; |ξ |ξ |ξ |
|Greek small letter omicron |&omicron; |&#959; |&#x3BF; |ο |ο |ο |
|Greek small letter pi |&pi; |&#960; |&#x3C0; |π |π |π |
|Greek small letter rho |&rho; |&#961; |&#x3C1; |ρ |ρ |ρ |
|Greek small letter final sigma |&sigmaf; |&#962; |&#x3C2; |ς |ς |ς |
|Greek small letter sigma |&sigma; |&#963; |&#x3C3; |σ |σ |σ |
|Greek small letter tau |&tau; |&#964; |&#x3C4; |τ |τ |τ |
|Greek small letter upsilon |&upsilon; |&#965; |&#x3C5; |υ |υ |υ |
|Greek small letter phi |&phi; |&#966; |&#x3C6; |φ |φ |φ |
|Greek small letter chi |&chi; |&#967; |&#x3C7; |χ |χ |χ |
|Greek small letter psi |&psi; |&#968; |&#x3C8; |ψ |ψ |ψ |
|Greek small letter omega |&omega; |&#969; |&#x3C9; |ω |ω |ω |
|Greek small letter theta symbol |&thetasym; |&#977; |&#x3D1; |ϑ |ϑ |ϑ |
|Greek upsilon with hook symbol |&upsih; |&#978; |&#x3D2; |ϒ |ϒ |ϒ |
|Greek pi symbol |&piv; |&#982; |&#x3D6; |ϖ |ϖ |ϖ |
|bullet = black small circle |&bull; |&#8226; |&#x2022; |• |• |• |
|horizontal ellipsis = three dot leader |&hellip; |&#8230; |&#x2026; |… |… |… |
|prime = minutes = feet |&prime; |&#8242; |&#x2032; |′ |′ |′ |
|double prime = seconds = inches |&Prime; |&#8243; |&#x2033; |″ |″ |″ |
|overline = spacing overscore |&oline; |&#8254; |&#x203E; |‾ |‾ |‾ |
|fraction slash |&frasl; |&#8260; |&#x2044; |⁄ |⁄ |⁄ |
|script capital P = power set = Weierstrass p |&weierp; |&#8472; |&#x2118; |℘ |℘ |℘ |
|blackletter capital I = imaginary part |&image; |&#8465; |&#x2111; |ℑ |ℑ |ℑ |
|blackletter capital R = real part symbol |&real; |&#8476; |&#x211C; |ℜ |ℜ |ℜ |
|trade mark sign |&trade; |&#8482; |&#x2122; |™ |™ |™ |
|alef symbol = first transfinite cardinal |&alefsym; |&#8501; |&#x2135; |ℵ |ℵ |ℵ |
|leftwards arrow |&larr; |&#8592; |&#x2190; |← |← |← |
|upwards arrow |&uarr; |&#8593; |&#x2191; |↑ |↑ |↑ |
|rightwards arrow |&rarr; |&#8594; |&#x2192; |→ |→ |→ |
|downwards arrow |&darr; |&#8595; |&#x2193; |↓ |↓ |↓ |
|left right arrow |&harr; |&#8596; |&#x2194; |↔ |↔ |↔ |
|downwards arrow with corner leftwards = carriage return |&crarr; |&#8629; |&#x21B5; |↵ |↵ |↵ |
|leftwards double arrow |&lArr; |&#8656; |&#x21D0; |⇐ |⇐ |⇐ |
|upwards double arrow |&uArr; |&#8657; |&#x21D1; |⇑ |⇑ |⇑ |
|rightwards double arrow |&rArr; |&#8658; |&#x21D2; |⇒ |⇒ |⇒ |
|downwards double arrow |&dArr; |&#8659; |&#x21D3; |⇓ |⇓ |⇓ |
|left right double arrow |&hArr; |&#8660; |&#x21D4; |⇔ |⇔ |⇔ |
|for all |&forall; |&#8704; |&#x2200; |∀ |∀ |∀ |
|partial differential |&part; |&#8706; |&#x2202; |∂ |∂ |∂ |
|there exists |&exist; |&#8707; |&#x2203; |∃ |∃ |∃ |
|empty set = null set = diameter |&empty; |&#8709; |&#x2205; |∅ |∅ |∅ |
|nabla = backward difference |&nabla; |&#8711; |&#x2207; |∇ |∇ |∇ |
|element of |&isin; |&#8712; |&#x2208; |∈ |∈ |∈ |
|not an element of |&notin; |&#8713; |&#x2209; |∉ |∉ |∉ |
|contains as member |&ni; |&#8715; |&#x220B; |∋ |∋ |∋ |
|n-ary product = product sign |&prod; |&#8719; |&#x220F; |∏ |∏ |∏ |
|n-ary sumation |&sum; |&#8721; |&#x2211; |∑ |∑ |∑ |
|minus sign |&minus; |&#8722; |&#x2212; |− |− |− |
|asterisk operator |&lowast; |&#8727; |&#x2217; |∗ |∗ |∗ |
|square root = radical sign |&radic; |&#8730; |&#x221A; |√ |√ |√ |
|proportional to |&prop; |&#8733; |&#x221D; |∝ |∝ |∝ |
|infinity |&infin; |&#8734; |&#x221E; |∞ |∞ |∞ |
|angle |&ang; |&#8736; |&#x2220; |∠ |∠ |∠ |
|logical and = wedge |&and; |&#8743; |&#x2227; |∧ |∧ |∧ |
|logical or = vee |&or; |&#8744; |&#x2228; |∨ |∨ |∨ |
|intersection = cap |&cap; |&#8745; |&#x2229; |∩ |∩ |∩ |
|union = cup |&cup; |&#8746; |&#x222A; |∪ |∪ |∪ |
|integral |&int; |&#8747; |&#x222B; |∫ |∫ |∫ |
|therefore |&there4; |&#8756; |&#x2234; |∴ |∴ |∴ |
|tilde operator = varies with = similar to |&sim; |&#8764; |&#x223C; |∼ |∼ |∼ |
|approximately equal to |&cong; |&#8773; |&#x2245; |≅ |≅ |≅ |
|almost equal to = asymptotic to |&asymp; |&#8776; |&#x2248; |≈ |≈ |≈ |
|not equal to |&ne; |&#8800; |&#x2260; |≠ |≠ |≠ |
|identical to |&equiv; |&#8801; |&#x2261; |≡ |≡ |≡ |
|less-than or equal to |&le; |&#8804; |&#x2264; |≤ |≤ |≤ |
|greater-than or equal to |&ge; |&#8805; |&#x2265; |≥ |≥ |≥ |
|subset of |&sub; |&#8834; |&#x2282; |⊂ |⊂ |⊂ |
|superset of |&sup; |&#8835; |&#x2283; |⊃ |⊃ |⊃ |
|not a subset of |&nsub; |&#8836; |&#x2284; |⊄ |⊄ |⊄ |
|subset of or equal to |&sube; |&#8838; |&#x2286; |⊆ |⊆ |⊆ |
|superset of or equal to |&supe; |&#8839; |&#x2287; |⊇ |⊇ |⊇ |
|circled plus = direct sum |&oplus; |&#8853; |&#x2295; |⊕ |⊕ |⊕ |
|circled times = vector product |&otimes; |&#8855; |&#x2297; |⊗ |⊗ |⊗ |
|up tack = orthogonal to = perpendicular |&perp; |&#8869; |&#x22A5; |⊥ |⊥ |⊥ |
|dot operator |&sdot; |&#8901; |&#x22C5; |⋅ |⋅ |⋅ |
|left ceiling = APL upstile |&lceil; |&#8968; |&#x2308; |⌈ |⌈ |⌈ |
|right ceiling |&rceil; |&#8969; |&#x2309; |⌉ |⌉ |⌉ |
|left floor = APL downstile |&lfloor; |&#8970; |&#x230A; |⌊ |⌊ |⌊ |
|right floor |&rfloor; |&#8971; |&#x230B; |⌋ |⌋ |⌋ |
|left-pointing angle bracket = bra |&lang; |&#9001; |&#x2329; |⟨ |〈 |〈 |
|right-pointing angle bracket = ket |&rang; |&#9002; |&#x232A; |⟩ |〉 |〉 |
|lozenge |&loz; |&#9674; |&#x25CA; |◊ |◊ |◊ |
|black spade suit |&spades; |&#9824; |&#x2660; |♠ |♠ |♠ |
|black club suit = shamrock |&clubs; |&#9827; |&#x2663; |♣ |♣ |♣ |
|black heart suit = valentine |&hearts; |&#9829; |&#x2665; |♥ |♥ |♥ |
|black diamond suit |&diams; |&#9830; |&#x2666; |♦ |♦ |♦ |
!Special Entities
The following table gives the character entity reference, decimal character reference, and hexadecimal character reference for markup-significant and internationalization characters, as well as the rendering of each in your browser. [[Glyphs|http://www.unicode.org/charts/]] of the characters are available at the [[Unicode Consortium|http://www.unicode.org/]].
With the exception of [[HTML 2.0|http://www.w3.org/MarkUp/html-spec/]]'s ''&quot;'', ''&amp;'', ''&lt;'', and ''&gt;'', these entities are all new in HTML 4.0 and may not be supported by old browsers. Support in recent browsers is good.
| !Character | !Entity | !Decimal | !Hex |>|>| !Rendering in Your Browser |
|~|~|~|~| !Entity | !Decimal | !Hex |
|quotation mark = APL quote |&quot; |&#34; |&#x22; |" |" |" |
|ampersand |&amp; |&#38; |&#x26; |& |& |& |
|less-than sign |&lt; |&#60; |&#x3C; |< |< |< |
|greater-than sign |&gt; |&#62; |&#x3E; |> |> |> |
|Latin capital ligature OE |&OElig; |&#338; |&#x152; |Œ |Œ |Œ |
|Latin small ligature oe |&oelig; |&#339; |&#x153; |œ |œ |œ |
|Latin capital letter S with caron |&Scaron; |&#352; |&#x160; |Š |Š |Š |
|Latin small letter s with caron |&scaron; |&#353; |&#x161; |š |š |š |
|Latin capital letter Y with diaeresis |&Yuml; |&#376; |&#x178; |Ÿ |Ÿ |Ÿ |
|modifier letter circumflex accent |&circ; |&#710; |&#x2C6; |ˆ |ˆ |ˆ |
|small tilde |&tilde; |&#732; |&#x2DC; |˜ |˜ |˜ |
|en space |&ensp; |&#8194; |&#x2002; |  |  |  |
|em space |&emsp; |&#8195; |&#x2003; |  |  |  |
|thin space |&thinsp; |&#8201; |&#x2009; |  |  |  |
|zero width non-joiner |&zwnj; |&#8204; |&#x200C; |‌ |‌ |‌ |
|zero width joiner |&zwj; |&#8205; |&#x200D; |‍ |‍ |‍ |
|left-to-right mark |&lrm; |&#8206; |&#x200E; |‎ |‎ |‎ |
|right-to-left mark |&rlm; |&#8207; |&#x200F; |‏ |‏ |‏ |
|en dash |&ndash; |&#8211; |&#x2013; |– |– |– |
|em dash |&mdash; |&#8212; |&#x2014; |— |— |— |
|left single quotation mark |&lsquo; |&#8216; |&#x2018; |‘ |‘ |‘ |
|right single quotation mark |&rsquo; |&#8217; |&#x2019; |’ |’ |’ |
|single low-9 quotation mark |&sbquo; |&#8218; |&#x201A; |‚ |‚ |‚ |
|left double quotation mark |&ldquo; |&#8220; |&#x201C; |“ |“ |“ |
|right double quotation mark |&rdquo; |&#8221; |&#x201D; |” |” |” |
|double low-9 quotation mark |&bdquo; |&#8222; |&#x201E; |„ |„ |„ |
|dagger |&dagger; |&#8224; |&#x2020; |† |† |† |
|double dagger |&Dagger; |&#8225; |&#x2021; |‡ |‡ |‡ |
|per mille sign |&permil; |&#8240; |&#x2030; |‰ |‰ |‰ |
|single left-pointing angle quotation mark |&lsaquo; |&#8249; |&#x2039; |‹ |‹ |‹ |
|single right-pointing angle quotation mark |&rsaquo; |&#8250; |&#x203A; |› |› |› |
|euro sign |&euro; |&#8364; |&#x20AC; |€ |€ |€ |
http://www.tiddlywiki.com/#HtmlEntities
Entities in HTML documents allow characters to be entered that can't easily be typed on an ordinary keyboard. They take the form of an ampersand (&), an identifying string, and a terminating semi-colon (;). There's a complete reference [[here|HTML 4 Entities]]; some of the more common and useful ones are shown below.
|>|>|>|>|>|>| !HTML Entities |
| &nbsp; | | no-break space | | &apos; | ' | single quote, apostrophe |
| &ndash; | – | en dash |~| &quot; | " | quotation mark |
| &mdash; | — | em dash |~| &prime; | ′ | prime; minutes; feet |
| &hellip; | … | horizontal ellipsis |~| &Prime; | ″ | double prime; seconds; inches |
| &copy; | © | Copyright symbol |~| &lsquo; | ‘ | left single quote |
| &reg; | ® | Registered symbol |~| &rsquo; | ’ | right single quote |
| &trade; | ™ | Trademark symbol |~| &ldquo; | “ | left double quote |
| &dagger; | † | dagger |~| &rdquo; | ” | right double quote |
| &Dagger; | ‡ | double dagger |~| &laquo; | « | left angle quote |
| &para; | ¶ | paragraph sign |~| &raquo; | » | right angle quote |
| &sect; | § | section sign |~| &times; | × | multiplication symbol |
| &uarr; | ↑ | up arrow |~| &darr; | ↓ | down arrow |
| &larr; | ← | left arrow |~| &rarr; | → | right arrow |
| &lArr; | ⇐ | double left arrow |~| &rArr; | ⇒ | double right arrow |
| &harr; | ↔ | left right arrow |~| &hArr; | ⇔ | double left right arrow |
The table below shows how accented characters can be built up by subsituting a base character into the various accent entities in place of the underscore ('_'):
|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>| !Accented Characters |
| grave accent | &_grave; | À | à | È | è | Ì | ì | Ò | ò | Ù | ù | | | | | | |
| acute accent | &_acute; | Á | á | É | é | Í | í | Ó | ó | Ú | ú | | | Ý | ý | | |
| circumflex accent | &_circ; | Â | â | Ê | ê | Î | î | Ô | ô | Û | û | | | | | | |
| umlaut mark | &_uml; | Ä | ä | Ë | ë | Ï | ï | Ö | ö | Ü | ü | | | Ÿ | ÿ | | |
| tilde | &_tilde; | Ã | ã | | | | | Õ | õ | | | Ñ | ñ | | | | |
| ring | &_ring; | Å | å | | | | | | | | | | | | | |
| slash | &_slash; | | | | | | | Ø | ø | | | | | | | |
| cedilla | &_cedil; | | | | | | | | | | | | | | | Ç | ç |
!Simple Images
{{{
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
Displays as:
[img[fractalveg.jpg]]
!Tooltips for Images
{{{
[img[tooltip|http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
Displays as:
[img[tooltip|fractalveg.jpg]]
!Image Links
{{{
[img[http://wikitext.tiddlyspace.com/fractalveg.jpg][http://www.flickr.com/photos/jermy/10134618/]]
}}}
Displays as:
[img[fractalveg.jpg][http://www.flickr.com/photos/jermy/10134618/]]
!Floating Images with Text
{{{
[<img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
[<img[fractalveg.jpg]]Displays as.
{{{
@@clear:both;display:block; @@
}}}
Will then clear the float.
@@clear:both;display:block;Like this@@
{{{
[>img[http://wikitext.tiddlyspace.com/fractalveg.jpg]]
}}}
[>img[fractalveg.jpg]]Displays as.@@clear:both;display:block; @@
!See Also
[[Image Macro]]
/***
|Name|ImagePastePlugin|
|License|[[TW Notes License]]|
|Requires|[[paste.js]]|
!!!!!Code
***/
//{{{
if (config.options.chkPasteImages === undefined) {
config.options.chkPasteImages = true;
}
merge(config.optionsDesc, {
chkPasteImages: "Enable image pasting when editing tiddlers"
});
//}}}
//{{{
config.macros.imagePaste = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
if (config.options.chkPasteImages) {
var editTextarea = null;
if (paramString) {
try {
editTextarea = eval(paramString);
} catch (e) {}
}
if (!editTextarea) {
editTextarea = jQuery(place).closest("[template='EditTemplate']").find("textarea").eq(0);
}
if (jQuery.isFunction(editTextarea.pastableTextarea)) {
var insertAtCaret = function (txtarea, text) {
// http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery
var scrollPos = txtarea.scrollTop;
var caretPos = txtarea.selectionStart;
var front = (txtarea.value).substring(0, caretPos);
var back = (txtarea.value).substring(txtarea.selectionEnd, txtarea.value.length);
txtarea.value = front + text + back;
caretPos = caretPos + text.length;
txtarea.selectionStart = caretPos;
txtarea.selectionEnd = caretPos;
txtarea.focus();
txtarea.scrollTop = scrollPos;
};
editTextarea.pastableTextarea();
editTextarea.on("pasteImage", function(e, data) {
insertAtCaret(this, "[img(" + data.width + "px+,)[" + data.dataURL + "]]");
});
}
}
}
}
//}}}
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.2.3|
|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
<<<
2011.09.03 [1.2.3] bypass addStretchHandlers() if no '+' suffix is used (i.e., not resizable)
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: 3, date: new Date(2011,9,3)};
//}}}
//{{{
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();
if (stretchW||stretchH) 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|ImageSizePluginOverride|
|License|[[TW Notes License]]|
|Requires|[[ImageSizePlugin]]|
!!!!!Code
***/
//{{{
(function () {
var f = config.formatters[config.formatters.findByField("name", "image")];
if (f) {
var code;
code = f.handler.toString();
code = code.replace('var img = createTiddlyElement(e,"img");',
'var img = createTiddlyElement(e,"img");\n' +
'if (link) {\n' +
' (function () {\n' +
' var $img = jQuery(img);\n' +
' $img.data("resized", false);\n' +
' var action = e.onclick || function () {\n' +
' return true;\n' +
' };\n' +
' e.onclick = function () {\n' +
' if ($img.data("resized") === false) {\n' +
' return action();\n' +
' }\n' +
' return false;\n' +
' };\n' +
' })();\n' +
'}\n'
);
eval('f.handler = function handler' + code.substring(code.indexOf('(')));
code = eval('config.formatterHelpers.addStretchHandlers').toString();
code = code.replace('this.sizing=true;', 'jQuery(this).data("resized", false); this.sizing=true;');
code = code.replace('var s=this.style;', 'jQuery(this).data("resized", true); var s=this.style;');
code = code.replace('if (ev.shiftKey)', 'if (ev.shiftKey || ev.ctrlKey) { jQuery(this).data("resized", true); } if (ev.shiftKey)');
eval('config.formatterHelpers.addStretchHandlers = function addStretchHandlers' + code.substring(code.indexOf('(')));
}
})();
//}}}
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.6.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|interactive controls for import/export with filtering.|
Combine tiddlers from any two TiddlyWiki documents. Interactively select and copy tiddlers from another TiddlyWiki source document. Includes prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles. When done, a list of all imported tiddlers is written into [[ImportedTiddlers]].
!!!!!Documentation
<<<
see [[ImportTiddlersPluginInfo]] for details
<<<
!!!!!interactive control panel
<<<
<<importTiddlers inline>>
{{clear{
^^(see also: [[ImportTiddlers]] shadow tiddler)^^}}}
<<<
!!!!!Revisions
<<<
2011.02.14 4.6.2 fix OSX error: use picker.file.path
2009.10.10 4.6.1 in createImportPanel, Use {{{window.Components}}} instead of {{{config.browser.isGecko}}} to avoid applying FF3 'file browse' fixup in Chrome.
2009.10.06 4.6.0 added createTiddlerFromFile (import text files)
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 1.0.0 Initial Release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImportTiddlersPlugin= {major: 4, minor: 6, revision: 2, date: new Date(2011,2,14)};
// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;
// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;
// default shadow definition
config.shadowTiddlers.ImportTiddlers='<<importTiddlers inline>>';
// use shadow tiddler content in backstage panel
if (config.tasks) config.tasks.importTask.content='<<tiddler ImportTiddlers>>' // TW2.2 or above
//}}}
//{{{
// backward-compatiblity for TW2.0.x and TW1.2.x
if (config.macros.importTiddlers==undefined) config.macros.importTiddlers={};
if (typeof merge=='undefined') {
function merge(dst,src,preserveExisting) {
for(var i in src) { if(!preserveExisting || dst[i] === undefined) dst[i] = src[i]; }
return dst;
}
}
if (config.browser.isGecko===undefined)
config.browser.isGecko=(config.userAgent.indexOf('gecko')!=-1);
//}}}
//{{{
merge(config.macros.importTiddlers,{
$: function(id) { return document.getElementById(id); }, // abbreviation
label: 'import tiddlers',
prompt: 'Copy tiddlers from another document',
openMsg: 'Opening %0',
openErrMsg: 'Could not open %0 - error=%1',
readMsg: 'Read %0 bytes from %1',
foundMsg: 'Found %0 tiddlers in %1',
filterMsg: "Filtered %0 tiddlers matching '%1'",
summaryMsg: '%0 tiddler%1 in the list',
summaryFilteredMsg: '%0 of %1 tiddler%2 in the list',
plural: 's are',
single: ' is',
countMsg: '%0 tiddlers selected for import',
processedMsg: 'Processed %0 tiddlers',
importedMsg: 'Imported %0 of %1 tiddlers from %2',
loadText: 'please load a document...',
closeText: 'close',
doneText: 'done',
startText: 'import',
stopText: 'stop',
local: true, // default to import from local file
src: '', // path/filename or URL of document to import (retrieved from SiteUrl)
proxy: '', // URL for remote proxy script (retrieved from SiteProxy)
useProxy: false, // use specific proxy script in front of remote URL
inbound: null, // hash-indexed array of tiddlers from other document
newTags: '', // text of tags added to imported tiddlers
addTags: true, // add new tags to imported tiddlers
listsize: 10, // # of lines to show in imported tiddler list
importTags: true, // include tags from remote source document when importing a tiddler
keepTags: true, // retain existing tags when replacing a tiddler
sync: false, // add 'server' fields to imported tiddlers (for sync function)
lastFilter: '', // most recent filter (URL hash) applied
lastAction: null, // most recent collision button performed
index: 0, // current processing index in import list
sort: '' // sort order for imported tiddler listbox
});
//}}}
//{{{
// hijack core macro handler
if (config.macros.importTiddlers.coreHandler==undefined)
config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler;
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
if (config.macros.importTiddlers.coreHandler)
config.macros.importTiddlers.coreHandler.apply(this,arguments);
else
createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
} else if (params[0]=='link') { // show link to floating panel
createTiddlyButton(place,params[1]||this.label,params[2]||this.prompt,onClickImportMenu);
} else if (params[0]=='inline') {// show panel as INLINE tiddler content
createImportPanel(place);
this.$('importPanel').style.position='static';
this.$('importPanel').style.display='block';
} else if (config.macros.loadTiddlers)
config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
//}}}
//{{{
// Handle link click to create/show/hide control panel
function onClickImportMenu(e) { var e=e||window.event;
var parent=resolveTarget(e).parentNode;
var panel=document.getElementById('importPanel');
if (panel==undefined || panel.parentNode!=parent) panel=createImportPanel(parent);
var isOpen=panel.style.display=='block';
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,false,'none'));
else
panel.style.display=isOpen?'none':'block';
e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return(false);
}
//}}}
//{{{
// Create control panel: HTML, CSS
function createImportPanel(place) {
var cmi=config.macros.importTiddlers; // abbrev
var panel=cmi.$('importPanel');
if (panel) { panel.parentNode.removeChild(panel); }
setStylesheet(store.getTiddlerText('ImportTiddlersPlugin##css'),'importTiddlers');
panel=createTiddlyElement(place,'span','importPanel',null,null)
panel.innerHTML=store.getTiddlerText('ImportTiddlersPlugin##html');
refreshImportList();
if (!cmi.src.length) cmi.src=store.getTiddlerText('SiteUrl')||'';
cmi.$('importSourceURL').value=cmi.src;
if (!cmi.proxy.length) cmi.proxy=store.getTiddlerText('SiteProxy')||'SiteProxy';
cmi.$('importSiteProxy').value=cmi.proxy;
if (window.Components) { // FF3 FIXUP
cmi.$('fileImportSource').style.display='none';
cmi.$('importLocalPanelFix').style.display='block';
}
cmi.$('chkSync').checked=cmi.sync;
cmi.$('chkImportTags').checked=cmi.importTags;
cmi.$('chkKeepTags').checked=cmi.keepTags;
cmi.$('chkAddTags').checked=cmi.addTags;
cmi.$('txtNewTags').value=cmi.newTags;
cmi.$('txtNewTags').style.display=cmi.addTags?'block':'none';
cmi.$('chkSync').checked=cmi.sync;
cmi.$('chkImportReport').checked=config.options.chkImportReport;
return panel;
}
//}}}
//{{{
// process control interactions
function onClickImportButton(which,event) {
var cmi=config.macros.importTiddlers; // abbreviation
var list=cmi.$('importList'); if (!list) return false;
var thePanel=cmi.$('importPanel');
var theCollisionPanel=cmi.$('importCollisionPanel');
var theNewTitle=cmi.$('importNewTitle');
var count=0;
switch (which.id)
{
case 'importFromFile': // show local panel
case 'importFromWeb': // show HTTP panel
cmi.local=(which.id=='importFromFile');
cmi.showPanel('importLocalPanel',cmi.local);
cmi.showPanel('importHTTPPanel',!cmi.local);
break;
case 'importOptions': // show/hide options panel
cmi.showPanel('importOptionsPanel',cmi.$('importOptionsPanel').style.display=='none');
break;
case 'fileImportSource':
case 'importLoad': // load import source into hidden frame
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
if (cmi.src=='') break;
// Load document, read it's DOM and fill the list
cmi.loadRemoteFile(cmi.src,cmi.filterTiddlerList);
break;
case 'importSelectFeed': // select a pre-defined systemServer feed URL
var p=Popup.create(which); if (!p) return false;
var tids=store.getTaggedTiddlers('systemServer');
if (!tids.length)
createTiddlyText(createTiddlyElement(p,'li'),'no pre-defined server feeds');
for (var t=0; t<tids.length; t++) {
var u=store.getTiddlerSlice(tids[t].title,'URL');
var d=store.getTiddlerSlice(tids[t].title,'Description');
if (!d||!d.length) d=store.getTiddlerSlice(tids[t].title,'description');
if (!d||!d.length) d=u;
createTiddlyButton(createTiddlyElement(p,'li'),tids[t].title,d,
function(){
var u=this.getAttribute('url');
document.getElementById('importSourceURL').value=u;
config.macros.importTiddlers.src=u;
document.getElementById('importLoad').onclick();
},
null,null,null,{url:u});
}
Popup.show();
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation();
return false;
// create popup with feed list
// onselect, insert feed URL into input field.
break;
case 'importSelectAll': // select all tiddler list items (i.e., not headings)
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
if (list.options[t].value=='') continue;
list.options[t].selected=true;
count++;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectNew': // select tiddlers not in current document
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=='') continue;
list.options[t].selected=!store.tiddlerExists(list.options[t].value);
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectChanges': // select tiddlers that are updated from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value==''||!store.tiddlerExists(list.options[t].value)) continue;
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified>0); // updated tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectDifferences': // select tiddlers that are new or different from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=='') continue;
if (!store.tiddlerExists(list.options[t].value)) { list.options[t].selected=true; count++; continue; }
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified!=0); // changed tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importApplyFilter': // filter list to include only matching tiddlers
importReport(); // if an import was in progress, generate a report
clearMessage();
if (!cmi.all) // no tiddlers loaded = '0 selected'
{ displayMessage(cmi.countMsg.format([0])); return false; }
var hash=cmi.$('importLastFilter').value;
cmi.inbound=cmi.filterByHash('#'+hash,cmi.all);
refreshImportList(); // reset/resize the listbox
break;
case 'importStart': // initiate the import processing
importReport(); // if an import was in progress, generate a report
cmi.$('importApplyToAll').checked=false;
cmi.$('importStart').value=cmi.stopText;
if (cmi.index>0) cmi.index=-1; // stop processing
else cmi.index=importTiddlers(0); // or begin processing
importStopped();
break;
case 'importClose': // unload imported tiddlers or hide the import control panel
// if imported tiddlers not loaded, close the import control panel
if (!cmi.inbound) { thePanel.style.display='none'; break; }
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
break;
case 'importSkip': // don't import the tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status='skipped after asking'; // mark item as skipped
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index+1); // resume with NEXT item
importStopped();
break;
case 'importRename': // change name of imported tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status = 'renamed from '+theImported.title; // mark item as renamed
theImported.set(theNewTitle.value,null,null,null,null); // change the tiddler title
theItem.value = theNewTitle.value; // change the listbox item text
theItem.text = theNewTitle.value; // change the listbox item text
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importMerge': // join existing and imported tiddler content
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
var theText = theExisting.text+'\n----\n^^merged from: ';
theText +='[['+cmi.src+'#'+theItem.value+'|'+cmi.src+'#'+theItem.value+']]^^\n';
theText +='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
var theDate = new Date();
var theTags = theExisting.getTags()+' '+theImported.getTags();
theImported.set(null,theText,null,theDate,theTags);
theImported.status = 'merged with '+theExisting.title; // mark item as merged
theImported.status += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with this item
importStopped();
break;
case 'importReplace': // substitute imported tiddler for existing tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
theImported.status = 'replaces '+theExisting.title; // mark item for replace
theImported.status += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importListSmaller': // decrease current listbox size, minimum=5
if (list.options.length==1) break;
list.size-=(list.size>5)?1:0;
cmi.listsize=list.size;
break;
case 'importListLarger': // increase current listbox size, maximum=number of items in list
if (list.options.length==1) break;
list.size+=(list.size<list.options.length)?1:0;
cmi.listsize=list.size;
break;
case 'importListMaximize': // toggle listbox size between current and maximum
if (list.options.length==1) break;
list.size=(list.size==list.options.length)?cmi.listsize:list.options.length;
break;
}
}
//}}}
//{{{
config.macros.importTiddlers.showPanel=function(place,show,skipAnim) {
if (typeof place=='string') var place=document.getElementById(place);
if (!place||!place.style) return;
if(!skipAnim && anim && config.options.chkAnimate) anim.startAnimating(new Slider(place,show,false,'none'));
else place.style.display=show?'block':'none';
}
//}}}
//{{{
function refreshImportList(selectedIndex) {
var cmi=config.macros.importTiddlers; // abbrev
var list=cmi.$('importList'); if (!list) return;
// if nothing to show, reset list content and size
if (!cmi.inbound) {
while (list.length > 0) { list.options[0] = null; }
list.options[0]=new Option(cmi.loadText,'',false,false);
list.size=cmi.listsize;
cmi.$('importLoad').disabled=false;
cmi.$('importLoad').style.display='inline';
cmi.$('importStart').disabled=true;
cmi.$('importOptions').disabled=true;
cmi.$('importOptions').style.display='none';
cmi.$('fileImportSource').disabled=false;
cmi.$('importFromFile').disabled=false;
cmi.$('importFromWeb').disabled=false;
cmi.$('importStart').value=cmi.startText;
cmi.$('importClose').value=cmi.doneText;
cmi.$('importSelectPanel').style.display='none';
cmi.$('importOptionsPanel').style.display='none';
return;
}
// there are inbound tiddlers loaded...
cmi.$('importLoad').disabled=true;
cmi.$('importLoad').style.display='none';
cmi.$('importOptions').style.display='inline';
cmi.$('importOptions').disabled=false;
cmi.$('fileImportSource').disabled=true;
cmi.$('importFromFile').disabled=true;
cmi.$('importFromWeb').disabled=true;
cmi.$('importClose').value=cmi.closeText;
if (cmi.$('importSelectPanel').style.display=='none')
cmi.showPanel('importSelectPanel',true);
// get the sort order
if (!selectedIndex) selectedIndex=0;
if (selectedIndex==0) cmi.sort='title'; // heading
if (selectedIndex==1) cmi.sort='title';
if (selectedIndex==2) cmi.sort='modified';
if (selectedIndex==3) cmi.sort='tags';
if (selectedIndex>3) {
// display selected tiddler count
for (var t=0,count=0; t < list.options.length; t++) {
if (!list.options[t].selected) continue;
if (list.options[t].value!='')
count+=1;
else { // if heading is selected, deselect it, and then select and count all in section
list.options[t].selected=false;
for ( t++; t<list.options.length && list.options[t].value!=''; t++) {
list.options[t].selected=true;
count++;
}
}
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
}
cmi.$('importStart').disabled=!count;
if (selectedIndex>3) return; // no refresh needed
// get the alphasorted list of tiddlers
var tiddlers=cmi.inbound;
tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
// clear current list contents
while (list.length > 0) { list.options[0] = null; }
// add heading and control items to list
var i=0;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
if (cmi.all.length==tiddlers.length)
var summary=cmi.summaryMsg.format([tiddlers.length,(tiddlers.length!=1)?cmi.plural:cmi.single]);
else
var summary=cmi.summaryFilteredMsg.format([tiddlers.length,cmi.all.length,(cmi.all.length!=1)?cmi.plural:cmi.single]);
list.options[i++]=new Option(summary,'',false,false);
list.options[i++]=new Option(((cmi.sort=='title' )?'>':indent)+' [by title]','',false,false);
list.options[i++]=new Option(((cmi.sort=='modified')?'>':indent)+' [by date]','',false,false);
list.options[i++]=new Option(((cmi.sort=='tags')?'>':indent)+' [by tags]','',false,false);
// output the tiddler list
switch(cmi.sort) {
case 'title':
for(var t = 0; t < tiddlers.length; t++)
list.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
break;
case 'modified':
// sort descending for newest date first
tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
var lastSection = '';
for(var t = 0; t < tiddlers.length; t++) {
var tiddler = tiddlers[t];
var theSection = tiddler.modified.toLocaleDateString();
if (theSection != lastSection) {
list.options[i++] = new Option(theSection,'',false,false);
lastSection = theSection;
}
list.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
}
break;
case 'tags':
var theTitles = {}; // all tiddler titles, hash indexed by tag value
var theTags = new Array();
for(var t=0; t<tiddlers.length; t++) {
var title=tiddlers[t].title;
var tags=tiddlers[t].tags;
if (!tags || !tags.length) {
if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
theTitles['untagged'].push(title);
}
else for(var s=0; s<tags.length; s++) {
if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
theTitles[tags[s]].push(title);
}
}
theTags.sort();
for(var tagindex=0; tagindex<theTags.length; tagindex++) {
var theTag=theTags[tagindex];
list.options[i++]=new Option(theTag,'',false,false);
for(var t=0; t<theTitles[theTag].length; t++)
list.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
}
break;
}
list.selectedIndex=selectedIndex; // select current control item
if (list.size<cmi.listsize) list.size=cmi.listsize;
if (list.size>list.options.length) list.size=list.options.length;
}
//}}}
//{{{
// re-entrant processing for handling import with interactive collision prompting
function importTiddlers(startIndex) {
var cmi=config.macros.importTiddlers; // abbrev
if (!cmi.inbound) return -1;
var list=cmi.$('importList'); if (!list) return;
var t;
// if starting new import, reset import status flags
if (startIndex==0)
for (var t=0;t<cmi.inbound.length;t++)
cmi.inbound[t].status='';
for (var i=startIndex; i<list.options.length; i++) {
// if list item is not selected or is a heading (i.e., has no value), skip it
if ((!list.options[i].selected) || ((t=list.options[i].value)==''))
continue;
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==t) break;
var inbound = cmi.inbound[j];
var theExisting = store.getTiddler(inbound.title);
// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
if (inbound.status=='added')
continue;
// don't import the 'ImportedTiddlers' history from the other document...
if (inbound.title=='ImportedTiddlers')
continue;
// if tiddler exists and import not marked for replace or merge, stop importing
if (theExisting && (inbound.status.substr(0,7)!='replace') && (inbound.status.substr(0,5)!='merge'))
return i;
// assemble tags (remote + existing + added)
var newTags = '';
if (cmi.importTags)
newTags+=inbound.getTags() // import remote tags
if (cmi.keepTags && theExisting)
newTags+=' '+theExisting.getTags(); // keep existing tags
if (cmi.addTags && cmi.newTags.trim().length)
newTags+=' '+cmi.newTags; // add new tags
inbound.set(null,null,null,null,newTags.trim());
// set the status to 'added' (if not already set by the 'ask the user' UI)
inbound.status=(inbound.status=='')?'added':inbound.status;
// set sync fields
if (cmi.sync) {
if (!inbound.fields) inbound.fields={}; // for TW2.1.x backward-compatibility
inbound.fields['server.page.revision']=inbound.modified.convertToYYYYMMDDHHMM();
inbound.fields['server.type']='file';
inbound.fields['server.host']=(cmi.local&&!cmi.src.startsWith('file:')?'file:///':'')+cmi.src;
}
// do the import!
store.suspendNotifications();
store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
store.resumeNotifications();
}
return(-1); // signals that we really finished the entire list
}
function importStopped() {
var cmi=config.macros.importTiddlers; // abbrev
var list=cmi.$('importList'); if (!list) return;
var theNewTitle=cmi.$('importNewTitle');
if (cmi.index==-1){
cmi.$('importStart').value=cmi.startText;
importReport(); // import finished... generate the report
} else {
// import collision...
// show the collision panel and set the title edit field
cmi.$('importStart').value=cmi.stopText;
cmi.showPanel('importCollisionPanel',true);
theNewTitle.value=list.options[cmi.index].value;
if (cmi.$('importApplyToAll').checked && cmi.lastAction && cmi.lastAction.id!='importRename')
onClickImportButton(cmi.lastAction);
}
}
//}}}
//{{{
function importReport() {
var cmi=config.macros.importTiddlers; // abbrev
if (!cmi.inbound) return;
// if import was not completed, the collision panel will still be open... close it now.
var panel=cmi.$('importCollisionPanel'); if (panel) panel.style.display='none';
// get the alphasorted list of tiddlers
var tiddlers = cmi.inbound;
// gather the statistics
var count=0; var total=0;
for (var t=0; t<tiddlers.length; t++) {
if (!tiddlers[t].status || !tiddlers[t].status.trim().length) continue;
if (tiddlers[t].status.substr(0,7)!='skipped') count++;
total++;
}
// generate a report
if (total) displayMessage(cmi.processedMsg.format([total]));
if (count && config.options.chkImportReport) {
// get/create the report tiddler
var theReport = store.getTiddler('ImportedTiddlers');
if (!theReport) { theReport=new Tiddler(); theReport.title='ImportedTiddlers'; theReport.text=''; }
// format the report content
var now = new Date();
var newText = 'On '+now.toLocaleString()+', '+config.options.txtUserName
newText +=' imported '+count+' tiddler'+(count==1?'':'s')+' from\n[['+cmi.src+'|'+cmi.src+']]:\n';
if (cmi.addTags && cmi.newTags.trim().length)
newText += 'imported tiddlers were tagged with: "'+cmi.newTags+'"\n';
newText += '<<<\n';
for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status)
newText += '#[['+tiddlers[t].title+']] - '+tiddlers[t].status+'\n';
newText += '<<<\n';
// update the ImportedTiddlers content and show the tiddler
theReport.text = newText+((theReport.text!='')?'\n----\n':'')+theReport.text;
theReport.modifier = config.options.txtUserName;
theReport.modified = new Date();
store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
story.displayTiddler(null,theReport.title,1,null,null,false);
story.refreshTiddler(theReport.title,1,true);
}
// reset status flags
for (var t=0; t<cmi.inbound.length; t++) cmi.inbound[t].status='';
// mark document as dirty and let display update as needed
if (count) { store.setDirty(true); store.notifyAll(); }
// always show final message when tiddlers were actually loaded
if (count) displayMessage(cmi.importedMsg.format([count,tiddlers.length,cmi.src.replace(/%20/g,' ')]));
}
//}}}
//{{{
// // File and XMLHttpRequest I/O
config.macros.importTiddlers.askForFilename=function(here) {
var msg=here.title; // use tooltip as dialog box message
var path=getLocalPath(document.location.href);
var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\');
if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
var file='';
var result='';
if(window.Components) { // moz
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, nsIFilePicker.modeOpen);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension='html';
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.path;
}
catch(e) { alert('error during local file access: '+e.toString()) }
}
else { // IE
try { // XPSP2 IE only
var s = new ActiveXObject('UserAccounts.CommonDialog');
s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
s.FilterIndex=3; // default to HTML files;
s.InitialDir=path;
s.FileName=file;
if (s.showOpen()) var result=s.FileName;
}
catch(e) { // fallback
var result=prompt(msg,path+file);
}
}
return result;
}
config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
if (src==undefined || !src.length) return null; // filename is required
var original=src; // URL as specified
var hashpos=src.indexOf('#'); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
clearMessage();
displayMessage(this.openMsg.format([src.replace(/%20/g,' ')]));
if (src.substr(0,5)!='http:' && src.substr(0,5)!='file:') { // if not a URL, read from local filesystem
var txt=loadFile(src);
if (!txt) { // file didn't load, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf('/'); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf('\\');
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!='http:') src=getLocalPath(src);
var txt=loadFile(src);
}
if (!txt) { // file still didn't load, report error
displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g,' '),'(filesystem error)']));
} else {
displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g,' ')]));
if (version.major+version.minor*.1+version.revision*.01!=2.52) txt=convertUTF8ToUnicode(txt);
if (callback) callback(true,original,txt,src,null);
}
} else {
doHttp('GET',src,null,null,config.options.txtRemoteUsername,config.options.txtRemotePassword,callback,original,null);
}
}
config.macros.importTiddlers.readTiddlersFromHTML=function(html){
var remoteStore=new TiddlyWiki();
remoteStore.importTiddlyWiki(html);
return remoteStore.getTiddlers('title');
}
config.macros.importTiddlers.readTiddlersFromCSV=function(CSV){
var remoteStore=new TiddlyWiki();
// GET NAMES
var lines=CSV.replace(/\r/g,'').split('\n');
var names=lines.shift().replace(/"/g,'').split(',');
CSV=lines.join('\n');
// ENCODE commas and newlines within quoted values
var comma='!~comma~!'; var commaRE=new RegExp(comma,'g');
var newline='!~newline~!'; var newlineRE=new RegExp(newline,'g');
CSV=CSV.replace(/"([^"]*?)"/g,
function(x){ return x.replace(/\,/g,comma).replace(/\n/g,newline); });
// PARSE lines
var lines=CSV.split('\n');
for (var i=0; i<lines.length; i++) { if (!lines[i].length) continue;
var values=lines[i].split(',');
// DECODE commas, newlines, and doubled-quotes, and remove enclosing quotes (if any)
for (var v=0; v<values.length; v++)
values[v]=values[v].replace(commaRE,',').replace(newlineRE,'\n')
.replace(/^"|"$/g,'').replace(/""/g,'"');
// EXTRACT tiddler values
var title=''; var text=''; var tags=[]; var fields={};
var created=null; var when=new Date(); var who=config.options.txtUserName;
for (var v=0; v<values.length; v++) { var val=values[v];
if (names[v]) switch(names[v].toLowerCase()) {
case 'title': title=val.replace(/\[\]\|/g,'_'); break;
case 'created': created=new Date(val); break;
case 'modified':when=new Date(val); break;
case 'modifier':who=val; break;
case 'text': text=val; break;
case 'tags': tags=val.readBracketedList(); break;
default: fields[names[v].toLowerCase()]=val; break;
}
}
// CREATE tiddler in temporary store
if (title.length)
remoteStore.saveTiddler(title,title,text,who,when,tags,fields,true,created||when);
}
return remoteStore.getTiddlers('title');
}
config.macros.importTiddlers.createTiddlerFromFile=function(src,txt) {
var t=new Tiddler();
var pos=src.lastIndexOf("/"); if (pos==-1) pos=src.lastIndexOf("\\");
t.title=pos==-1?src:src.substr(pos+1);
t.text=txt;
t.created=t.modified=new Date();
t.modifier=config.options.txtUserName;
if (src.substr(src.length-3,3)=='.js') t.tags=['systemConfig'];
return [t];
}
config.macros.importTiddlers.filterTiddlerList=function(success,params,txt,src,xhr){
var cmi=config.macros.importTiddlers; // abbreviation
var src=src.replace(/%20/g,' ');
if (!success) { displayMessage(cmi.openErrMsg.format([src,xhr.status])); return; }
cmi.all=cmi.readTiddlersFromHTML(txt);
if (!cmi.all||!cmi.all.length) cmi.all=cmi.readTiddlersFromCSV(txt)
if (!cmi.all||!cmi.all.length) cmi.all=cmi.createTiddlerFromFile(src,txt)
var count=cmi.all?cmi.all.length:0;
var querypos=src.lastIndexOf('?'); if (querypos!=-1) src=src.substr(0,querypos);
displayMessage(cmi.foundMsg.format([count,src]));
cmi.inbound=cmi.filterByHash(params,cmi.all); // use full URL including hash (if any)
cmi.$('importLastFilter').value=cmi.lastFilter;
window.refreshImportList(0);
}
config.macros.importTiddlers.filterByHash=function(src,tiddlers){
var hashpos=src.lastIndexOf('#'); if (hashpos==-1) return tiddlers;
var hash=src.substr(hashpos+1); if (!hash.length) return tiddlers;
var tids=[];
var params=hash.parseParams('anon',null,true,false,false);
for (var p=1; p<params.length; p++) {
switch (params[p].name) {
case 'anon':
case 'open':
tids.pushUnique(params[p].value);
break;
case 'tag':
if (store.getMatchingTiddlers) { // for boolean expressions - see MatchTagsPlugin
var r=store.getMatchingTiddlers(params[p].value,null,tiddlers);
for (var t=0; t<r.length; t++) tids.pushUnique(r[t].title);
} else for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].isTagged(params[p].value))
tids.pushUnique(tiddlers[t].title);
break;
case 'story':
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].title==params[p].value) {
tiddlers[t].changed();
for (var s=0; s<tiddlers[t].links.length; s++)
tids.pushUnique(tiddlers[t].links[s]);
break;
}
break;
case 'search':
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].text.indexOf(params[p].value)!=-1)
tids.pushUnique(tiddlers[t].title);
break;
}
}
var matches=[];
for (var t=0; t<tiddlers.length; t++)
if (tids.contains(tiddlers[t].title))
matches.push(tiddlers[t]);
displayMessage(config.macros.importTiddlers.filterMsg.format([matches.length,hash]));
config.macros.importTiddlers.lastFilter=hash;
return matches;
}
//}}}
/***
!!!Control panel CSS
//{{{
!css
#importPanel {
display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;
padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;
}
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }
#importPanel select { width:100%;margin:0px;font-size:8pt;line-height:110%;}
#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}
#importPanel .box { border:1px solid #000; background-color:#eee; padding:3px 5px; margin-bottom:5px; -moz-border-radius:5px;-webkit-border-radius:5px;}
#importPanel .topline { border-top:1px solid #999; padding-top:2px; margin-top:2px; }
#importPanel .rad { width:auto; }
#importPanel .chk { width:auto; margin:1px;border:0; }
#importPanel .btn { width:auto; }
#importPanel .btn1 { width:98%; }
#importPanel .btn2 { width:48%; }
#importPanel .btn3 { width:32%; }
#importPanel .btn4 { width:23%; }
#importPanel .btn5 { width:19%; }
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }
#backstagePanel #importPanel { left:10%; right:auto; }
!end
//}}}
!!!Control panel HTML
//{{{
!html
<!-- source and report -->
<table><tr><td align=left>
import from
<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED
onclick="onClickImportButton(this,event)" title="show file controls"> local file
<input type="radio" class="rad" name="importFrom" id="importFromWeb" value="http"
onclick="onClickImportButton(this,event)" title="show web controls"> web server
</td><td align=right>
<input type=checkbox class="chk" id="chkImportReport"
onClick="config.options['chkImportReport']=this.checked;"> create report
</td></tr></table>
<div class="box" id="importSourcePanel" style="margin:.5em">
<div id="importLocalPanel" style="display:block;margin-bottom:2px;"><!-- import from local file -->
enter or browse for source path/filename<br>
<input type="file" id="fileImportSource" size=57 style="width:100%"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;document.getElementById('importLoad').onclick()">
<div id="importLocalPanelFix" style="display:none"><!-- FF3 FIXUP -->
<input type="text" id="fileImportSourceFix" style="width:90%"
title="Enter a path/file to import"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;document.getElementById('importLoad').onclick()">
<input type="button" id="fileImportSourceFixButton" style="width:7%" value="..."
title="Select a path/file to import"
onClick="var r=config.macros.importTiddlers.askForFilename(this); if (!r||!r.length) return;
document.getElementById('fileImportSourceFix').value=r;
config.macros.importTiddlers.src=r;
document.getElementById('importLoad').onclick()">
</div><!--end FF3 FIXUP-->
</div><!--end local-->
<div id="importHTTPPanel" style="display:none;margin-bottom:2px;"><!-- import from http server -->
<table><tr><td align=left>
enter a URL or <a href="javascript:;" id="importSelectFeed"
onclick="return onClickImportButton(this,event)" title="select a pre-defined 'systemServer' URL">
select a server</a><br>
</td><td align=right>
<input type="checkbox" class="chk" id="importUsePassword"
onClick="config.macros.importTiddlers.usePassword=this.checked;
config.macros.importTiddlers.showPanel('importIDPWPanel',this.checked,true);">password
<input type="checkbox" class="chk" id="importUseProxy"
onClick="config.macros.importTiddlers.useProxy=this.checked;
config.macros.importTiddlers.showPanel('importSiteProxy',this.checked,true);">proxy
</td></tr></table>
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"
onKeyUp="config.macros.importTiddlers.proxy=this.value"
onChange="config.macros.importTiddlers.proxy=this.value;">
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;">
<div id="importIDPWPanel" style="text-align:center;margin-top:2px;display:none";>
username: <input type=text id="txtImportID" style="width:25%"
onChange="config.options.txtRemoteUsername=this.value;">
password: <input type=password id="txtImportPW" style="width:25%"
onChange="config.options.txtRemotePassword=this.value;">
</div><!--end idpw-->
</div><!--end http-->
</div><!--end source-->
<div class="box" id="importSelectPanel" style="display:none;margin:.5em;">
<table><tr><td align=left>
select:
<a href="javascript:;" id="importSelectAll"
onclick="return onClickImportButton(this)" title="SELECT all tiddlers">
all</a>
<a href="javascript:;" id="importSelectNew"
onclick="return onClickImportButton(this)" title="SELECT tiddlers not already in destination document">
added</a>
<a href="javascript:;" id="importSelectChanges"
onclick="return onClickImportButton(this)" title="SELECT tiddlers that have been updated in source document">
changes</a>
<a href="javascript:;" id="importSelectDifferences"
onclick="return onClickImportButton(this)" title="SELECT tiddlers that have been added or are different from existing tiddlers">
differences</a>
</td><td align=right>
<a href="javascript:;" id="importListSmaller"
onclick="return onClickImportButton(this)" title="SHRINK list size">
– </a>
<a href="javascript:;" id="importListLarger"
onclick="return onClickImportButton(this)" title="GROW list size">
+ </a>
<a href="javascript:;" id="importListMaximize"
onclick="return onClickImportButton(this)" title="MAXIMIZE/RESTORE list size">
= </a>
</td></tr></table>
<select id="importList" size=8 multiple
onchange="setTimeout('refreshImportList('+this.selectedIndex+')',1)">
<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->
</select>
<div style="text-align:center">
<a href="javascript:;"
title="click for help using filters..."
onclick="alert('A filter consists of one or more space-separated combinations of: tiddlertitle, tag:[[tagvalue]], tag:[[tag expression]] (requires MatchTagsPlugin), story:[[TiddlerName]], and/or search:[[searchtext]]. Use a blank filter to restore the list of all tiddlers.'); return false;"
>filter</a>
<input type="text" id="importLastFilter" style="margin-bottom:1px; width:65%"
title="Enter a combination of one or more filters. Use a blank filter for all tiddlers."
onfocus="this.select()" value=""
onKeyUp="config.macros.importTiddlers.lastFilter=this.value"
onChange="config.macros.importTiddlers.lastFilter=this.value;">
<input type="button" id="importApplyFilter" style="width:20%" value="apply"
title="filter list of tiddlers to include only those that match certain criteria"
onclick="return onClickImportButton(this)">
</div>
</div><!--end select-->
<div class="box" id="importOptionsPanel" style="text-align:center;margin:.5em;display:none;">
apply tags: <input type=checkbox class="chk" id="chkImportTags" checked
onClick="config.macros.importTiddlers.importTags=this.checked;">from source
<input type=checkbox class="chk" id="chkKeepTags" checked
onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing
<input type=checkbox class="chk" id="chkAddTags"
onClick="config.macros.importTiddlers.addTags=this.checked;
config.macros.importTiddlers.showPanel('txtNewTags',this.checked,false);
if (this.checked) document.getElementById('txtNewTags').focus();">add tags<br>
<input type=text id="txtNewTags" style="margin-top:4px;display:none;" size=15 onfocus="this.select()"
title="enter tags to be added to imported tiddlers"
onKeyUp="config.macros.importTiddlers.newTags=this.value;
document.getElementById('chkAddTags').checked=this.value.length>0;" autocomplete=off>
<nobr><input type=checkbox class="chk" id="chkSync"
onClick="config.macros.importTiddlers.sync=this.checked;">
link tiddlers to source document (for sync later)</nobr>
</div><!--end options-->
<div id="importButtonPanel" style="text-align:center">
<input type=button id="importLoad" class="importButton btn3" value="open"
title="load listbox with tiddlers from source document"
onclick="onClickImportButton(this)">
<input type=button id="importOptions" class="importButton btn3" value="options..."
title="set options for tags, sync, etc."
onclick="onClickImportButton(this)">
<input type=button id="importStart" class="importButton btn3" value="import"
title="start/stop import of selected source tiddlers into current document"
onclick="onClickImportButton(this)">
<input type=button id="importClose" class="importButton btn3" value="done"
title="clear listbox or hide control panel"
onclick="onClickImportButton(this)">
</div>
<div class="none" id="importCollisionPanel" style="display:none;margin:.5em 0 .5em .5em;">
<table><tr><td style="width:65%" align="left">
<table><tr><td align=left>
tiddler already exists:
</td><td align=right>
<input type=checkbox class="chk" id="importApplyToAll"
onclick="document.getElementById('importRename').disabled=this.checked;"
checked>apply to all
</td></tr></table>
<input type=text id="importNewTitle" size=15 autocomplete=off">
</td><td style="width:34%" align="center">
<input type=button id="importMerge"
class="importButton" style="width:47%" value="merge"
title="append the incoming tiddler to the existing tiddler"
onclick="onClickImportButton(this)"><!--
--><input type=button id="importSkip"
class="importButton" style="width:47%" value="skip"
title="do not import this tiddler"
onclick="onClickImportButton(this)"><!--
--><br><input type=button id="importRename"
class="importButton" style="width:47%" value="rename"
title="rename the incoming tiddler"
onclick="onClickImportButton(this)"><!--
--><input type=button id="importReplace"
class="importButton" style="width:47%" value="replace"
title="discard the existing tiddler"
onclick="onClickImportButton(this)">
</td></tr></table>
</div><!--end collision-->
!end
//}}}
***/
/***
|Name|ImportTiddlersPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[CollatorPlugin]] [[ImportTiddlersPlugin]]|
!!!!!Code
***/
//{{{
config.macros.importTiddlers.addTags = true;
config.macros.importTiddlers.importTags = true;
config.macros.importTiddlers.keepTags = false;
config.macros.importTiddlers.sync = true;
//}}}
//{{{
(function () {
var code = eval("httpReq").toString();
var newCode = null;
var startPosition = code.indexOf('xhr.setRequestHeader("X-Requested-With"');
if (startPosition > -1) {
var endPosition = code.indexOf(';', startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + code.substring(endPosition + 1);
code = newCode;
}
}
var startPosition = code.indexOf("window.netscape.security.PrivilegeManager.enablePrivilege");
if (startPosition > -1) {
var endPosition = code.indexOf(';', startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + " try { " + code.substring(startPosition, endPosition + 1) + " } catch (e) {} " + code.substring(endPosition + 1);
code = newCode;
}
}
if (newCode != null) {
eval("httpReq = function httpReq" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
(function () {
if ((typeof netscape === "undefined") || (!netscape.security || !netscape.security.PrivilegeManager || !netscape.security.PrivilegeManager.enablePrivilege)) {
config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
if (src==undefined || !src.length) return null; // filename is required
var original=src; // URL as specified
var hashpos=src.indexOf('#'); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
clearMessage();
displayMessage(this.openMsg.format([src.replace(/%20/g,' ')]));
if (src.substr(0,5)!='http:' && src.substr(0,6)!='https:' && src.substr(0,5)!='file:') { // if not a URL, read from local filesystem
var reader = new FileReader;
reader.onload = function (e) {
var txt = e.target.result;
if (!txt) { // file still didn't load, report error
displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g,' '),'(filesystem error)']));
} else {
displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g,' ')]));
if (version.major+version.minor*.1+version.revision*.01!=2.52) {
try {
var textTemp = '';
while (txt.length > 0) {
var index = txt.indexOf(' ', 10000);
index = (index > -1) ? index : txt.length;
textTemp = textTemp + convertUTF8ToUnicode(txt.substring(0, index));
txt = txt.substring(index);
}
txt = textTemp;
} catch (err) {
displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g,' '),'(' + err + ')']));
return false;
}
}
if (callback) callback(true,original,txt,src,null);
}
};
reader.readAsBinaryString(config.macros.importTiddlers.$('fileImportSource').files[0]);
} else {
doHttp('GET',src,null,null,config.options.txtRemoteUsername,config.options.txtRemotePassword,callback,original,null);
}
}
var code = eval("createImportPanel").toString();
var newCode = null;
var startPosition = code.indexOf("window.Components");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "false" + code.substring(startPosition + "window.Components".length);
code = newCode;
}
var startPosition = code.indexOf("ImportTiddlersPlugin##html");
if (startPosition > -1) {
var startPosition = code.indexOf(";", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ".replace('57', '52')" + code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval("createImportPanel = function createImportPanel" + newCode.substring(newCode.indexOf("(")));
}
}
})();
//}}}
//{{{
(function () {
config.macros.importTiddlers.filterByHash_contains = function (tids, title) {
if (tids) {
var lowerCaseTitle = null;
for (var i = 0; i < tids.length; i++) {
if ((typeof tids[i] == "string") || (tids[i] instanceof String)) {
if (tids[i] == title) {
return true;
}
} else {
lowerCaseTitle = (lowerCaseTitle === null) ? title.toLowerCase() : lowerCaseTitle;
if (lowerCaseTitle.indexOf(tids[i][0].toLowerCase()) > -1) {
return true;
}
}
}
}
return false;
}
var code = eval("config.macros.importTiddlers.filterByHash").toString();
var newCode = null;
var startPosition = code.indexOf("pushUnique");
if (startPosition > -1) {
newCode = code.substring(0, code.lastIndexOf("t", startPosition)) + "tids.pushUnique([params[p].value])" + code.substring(code.indexOf(")", startPosition) + 1);
code = newCode;
}
var startPosition = code.indexOf("contains");
if (startPosition > -1) {
newCode = code.substring(0, code.lastIndexOf("t", startPosition)) + "config.macros.importTiddlers.filterByHash_contains(tids, " + code.substring(code.indexOf("(", startPosition) + 1);
code = newCode;
}
if (newCode != null) {
eval("config.macros.importTiddlers.filterByHash = function filterByHash" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
(function () {
var code = eval("importTiddlers").toString();
code = code.replace("store.fetchTiddler(inbound.title).created",
'if (!cmi.sync && inbound.fields && inbound.fields["changecount"]) { var importedTiddler = store.fetchTiddler(inbound.title); if (!importedTiddler.fields) importedTiddler.fields = {}; importedTiddler.fields["changecount"] = inbound.fields["changecount"]; } ' +
"if (inbound.creator) store.fetchTiddler(inbound.title).creator = inbound.creator; " +
"if (!inbound.creator) delete store.fetchTiddler(inbound.title).creator; " +
"store.fetchTiddler(inbound.title).created");
eval("importTiddlers = function importTiddlers" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
var code = eval("refreshImportList").toString();
code = code.replace("if (!selectedIndex)", "var refreshList = false; if (list.options.length < 4) { refreshList = true; } else { if ((selectedIndex == 0) || (selectedIndex == 1)) { if (cmi.sort == 'title') { list.options[0].selected = false; list.options[1].selected = false; } else { refreshList = true; } } if (selectedIndex == 2) { if (cmi.sort == 'modified') { list.options[2].selected = false; } else { refreshList = true; } } if (selectedIndex == 3) { if (cmi.sort == 'tags') { list.options[3].selected = false; } else { refreshList = true; } } } if (!selectedIndex)");
code = code.replace("if (selectedIndex>3)", "if (!refreshList)");
code = code.replace("if (selectedIndex>3)", "if (!refreshList)");
code = code.replace("tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });", "tiddlers.sort(function (a, b) { return config.macros.collator.compare(a['title'], b['title']); });");
eval("refreshImportList = function refreshImportList" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name|ImportTiddlersPluginPatch|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPluginPatch|
|Version|4.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|ImportTiddlersPlugin|
|Description|backward-compatible function patches for use with ImportTiddlersPlugin and TW2.1.x or earlier|
!!!!!Usage
<<<
The current version ImportTiddlersPlugin is compatible with the TW2.2.x core functions. This "patch" plugin provides additional functions needed to enable the current version of ImportTiddlersPlugin to operate correctly under TW2.1.x or earlier.
{{medium{You do not need to install this plugin if you are using TW2.2.0 or above}}}
(though it won't hurt anything if you do... it will just take up more space).
<<<
!!!!!Revisions
<<<
2008.09.30 [4.4.0] added safety check for TW21Loader object and forward-compatible loadFromDiv() prototype to permit use with TW2.0.x and TW1.2.x.
2008.08.05 [4.3.2] rewrote loadRemoteFile to eliminate use of platform-specific fileExists() function
2008.01.03 [3.6.0] added support for passing txtRemoteUsername and txtRemotePassword for accessing password-protected remote servers
2007.06.27 [3.5.5] compatibility functions split from ImportTiddlersPlugin
|please see [[ImportTiddlersPlugin]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
//{{{
// these functions are only defined when installed in TW2.1.x and earlier...
if (version.major+version.minor/10 <= 2.1) {
// Version
version.extensions.ImportTiddlersPluginPatch= {major: 4, minor: 4, revision: 0, date: new Date(2008,9,30)};
// fixups for TW2.0.x and earlier
if (window.merge==undefined) window.merge=function(dst,src,preserveExisting)
{ for (p in src) if (!preserveExisting||dst[p]===undefined) dst[p]=src[p]; return dst; }
if (config.macros.importTiddlers==undefined) config.macros.importTiddlers={ };
config.macros.importTiddlers.loadRemoteFile = function(src,callback,quiet) {
if (src==undefined || !src.length) return null; // filename is required
if (!quiet) clearMessage();
if (!quiet) displayMessage(this.openMsg.format([src]));
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
var txt=loadFile(src);
if (!txt) { // file didn't load, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\");
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
var txt=loadFile(src);
}
if (!txt) { // file still didn't load, report error
if (!quiet) displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"]));
} else {
if (!quiet) displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g," ")]));
if (callback) callback(true,src,convertUTF8ToUnicode(txt),src,null);
}
} else {
var x; // get an request object
try {x = new XMLHttpRequest()} // moz
catch(e) {
try {x = new ActiveXObject("Msxml2.XMLHTTP")} // IE 6
catch (e) {
try {x = new ActiveXObject("Microsoft.XMLHTTP")} // IE 5
catch (e) { return }
}
}
// setup callback function to handle server response(s)
x.onreadystatechange = function() {
if (x.readyState == 4) {
if (x.status==0 || x.status == 200) {
if (!quiet) displayMessage(config.macros.importTiddlers.readMsg.format([x.responseText.length,src]));
if (callback) callback(true,src,x.responseText,src,x);
}
else {
if (!quiet) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,x.status]));
}
}
}
// get privileges to read another document's DOM via http:// or file:// (moz-only)
if (typeof(netscape)!="undefined") {
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }
catch (e) { if (!quiet) displayMessage(e.description?e.description:e.toString()); }
}
// send the HTTP request
try {
var url=src+(src.indexOf('?')<0?'?':'&')+'nocache='+Math.random();
x.open("GET",src,true,config.options.txtRemoteUsername,config.options.txtRemotePassword);
if (x.overrideMimeType) x.overrideMimeType('text/html');
x.send(null);
}
catch (e) {
if (!quiet) {
displayMessage(config.macros.importTiddlers.openErrMsg.format([src,"(unknown)"]));
displayMessage(e.description?e.description:e.toString());
}
}
}
}
config.macros.importTiddlers.readTiddlersFromHTML=function(html) {
// for TW2.1 and earlier
// extract store area from html
var start=html.indexOf('<div id="storeArea">');
var end=html.indexOf("<!--POST-BODY-START--"+">",start);
if (end==-1) var end=html.indexOf("</body"+">",start); // backward-compatibility for older documents
var sa="<html><body>"+html.substring(start,end)+"</body></html>";
// load html into iframe document
var f=document.getElementById("loaderFrame"); if (f) document.body.removeChild(f);
f=document.createElement("iframe"); f.id="loaderFrame";
f.style.width="0px"; f.style.height="0px"; f.style.border="0px";
document.body.appendChild(f);
var d=f.document;
if (f.contentDocument) d=f.contentDocument; // For NS6
else if (f.contentWindow) d=f.contentWindow.document; // For IE5.5 and IE6
d.open(); d.writeln(sa); d.close();
// read tiddler DIVs from storeArea DOM element
var sa = d.getElementById("storeArea");
if (!sa) return null;
sa.normalize();
var nodes = sa.childNodes;
if (!nodes || !nodes.length) return null;
var tiddlers = [];
for(var t = 0; t < nodes.length; t++) {
var title = null;
if(nodes[t].getAttribute)
title = nodes[t].getAttribute("title"); // TW 2.2+
if(!title && nodes[t].getAttribute)
title = nodes[t].getAttribute("tiddler"); // TW 2.1.x
if(!title && nodes[t].id && (nodes[t].id.substr(0,5) == "store"))
title = nodes[t].id.substr(5); // TW 1.2.x
if(title && title != "")
tiddlers.push((new Tiddler()).loadFromDiv(nodes[t],title));
}
return tiddlers;
}
// // FORWARD-COMPATIBLE SUPPORT FOR TW2.1.x
// // enables reading tiddler definitions using TW2.2+ storeArea format, even when plugin is running under TW2.1.x
if (typeof TW21Loader!="undefined") {
TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node) {
var e = node.firstChild;
var text = null;
if(node.getAttribute("tiddler"))
text = getNodeText(e).unescapeLineBreaks();
else {
while(e.nodeName!="PRE" && e.nodeName!="pre") e = e.nextSibling;
text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
}
var modifier = node.getAttribute("modifier");
var c = node.getAttribute("created");
var m = node.getAttribute("modified");
var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
var tags = node.getAttribute("tags");
var fields = {};
var attrs = node.attributes;
for(var i = attrs.length-1; i >= 0; i--) {
var name = attrs[i].name;
if (attrs[i].specified && !TiddlyWiki.isStandardField(name))
fields[name] = attrs[i].value.unescapeLineBreaks();
}
tiddler.assign(title,text,modifier,modified,tags,created,fields);
return tiddler;
};
}
// FORWARD-COMPATIBLE SUPPORT FOR TW2.0.x and TW1.2.x
// enables reading tiddler definitions using TW2.2+ storeArea format, even when plugin is running under TW2.0.x or TW1.2.x
if (typeof Tiddler.prototype.loadFromDiv!="undefined") {
Tiddler.prototype.loadFromDiv = function(node,title) { // Load a tiddler from an HTML DIV
var e = node.firstChild;
var text = null;
if(node.getAttribute("tiddler")) {
// get merged text from adjacent text nodes
var t=""; while(e&&e.nodeName=="#text") { t+=e.nodeValue; e=e.nextSibling; }
text = Tiddler.unescapeLineBreaks(t);
} else {
while(e.nodeName!="PRE" && e.nodeName!="pre") e = e.nextSibling;
text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
}
var modifier = node.getAttribute("modifier");
var c = node.getAttribute("created");
var m = node.getAttribute("modified");
var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
var tags = node.getAttribute("tags");
this.set(title,text,modifier,modified,tags,created);
return this;
}
}
} // END OF pre-TW2.2 backward-compatibility functions
//}}}
/***
|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(/^#/,'')); } }
//}}}
/***
|Name|jszip.js|
|Source|https://github.com/Stuk/jszip/blob/main/dist/jszip.js|
|Documentation|https://stuk.github.io/jszip|
|Version|3.10.1|
|License|[[MIT License|https://opensource.org/licenses/MIT]] or [[GPLv3 license|https://opensource.org/license/gpl-3-0]]|
|Description|A library for creating, reading and editing .zip files with JavaScript, with a lovely and simple API|
!!!!!Code
***/
//{{{
/*!
JSZip v3.10.1 - A JavaScript class for generating and reading zip files
<http://stuartk.com/jszip>
(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
JSZip uses the library pako released under the MIT license :
https://github.com/nodeca/pako/blob/main/LICENSE
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSZip = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
var utils = require("./utils");
var support = require("./support");
// private property
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
exports.encode = function(input) {
var output = [];
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0, len = input.length, remainingBytes = len;
var isArray = utils.getTypeOf(input) !== "string";
while (i < input.length) {
remainingBytes = len - i;
if (!isArray) {
chr1 = input.charCodeAt(i++);
chr2 = i < len ? input.charCodeAt(i++) : 0;
chr3 = i < len ? input.charCodeAt(i++) : 0;
} else {
chr1 = input[i++];
chr2 = i < len ? input[i++] : 0;
chr3 = i < len ? input[i++] : 0;
}
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64;
enc4 = remainingBytes > 2 ? (chr3 & 63) : 64;
output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4));
}
return output.join("");
};
// public method for decoding
exports.decode = function(input) {
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0, resultIndex = 0;
var dataUrlPrefix = "data:";
if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) {
// This is a common error: people give a data url
// (data:image/png;base64,iVBOR...) with a {base64: true} and
// wonders why things don't work.
// We can detect that the string input looks like a data url but we
// *can't* be sure it is one: removing everything up to the comma would
// be too dangerous.
throw new Error("Invalid base64 input, it looks like a data url.");
}
input = input.replace(/[^A-Za-z0-9+/=]/g, "");
var totalLength = input.length * 3 / 4;
if(input.charAt(input.length - 1) === _keyStr.charAt(64)) {
totalLength--;
}
if(input.charAt(input.length - 2) === _keyStr.charAt(64)) {
totalLength--;
}
if (totalLength % 1 !== 0) {
// totalLength is not an integer, the length does not match a valid
// base64 content. That can happen if:
// - the input is not a base64 content
// - the input is *almost* a base64 content, with a extra chars at the
// beginning or at the end
// - the input uses a base64 variant (base64url for example)
throw new Error("Invalid base64 input, bad content length.");
}
var output;
if (support.uint8array) {
output = new Uint8Array(totalLength|0);
} else {
output = new Array(totalLength|0);
}
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output[resultIndex++] = chr1;
if (enc3 !== 64) {
output[resultIndex++] = chr2;
}
if (enc4 !== 64) {
output[resultIndex++] = chr3;
}
}
return output;
};
},{"./support":30,"./utils":32}],2:[function(require,module,exports){
"use strict";
var external = require("./external");
var DataWorker = require("./stream/DataWorker");
var Crc32Probe = require("./stream/Crc32Probe");
var DataLengthProbe = require("./stream/DataLengthProbe");
/**
* Represent a compressed object, with everything needed to decompress it.
* @constructor
* @param {number} compressedSize the size of the data compressed.
* @param {number} uncompressedSize the size of the data after decompression.
* @param {number} crc32 the crc32 of the decompressed file.
* @param {object} compression the type of compression, see lib/compressions.js.
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data.
*/
function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) {
this.compressedSize = compressedSize;
this.uncompressedSize = uncompressedSize;
this.crc32 = crc32;
this.compression = compression;
this.compressedContent = data;
}
CompressedObject.prototype = {
/**
* Create a worker to get the uncompressed content.
* @return {GenericWorker} the worker.
*/
getContentWorker: function () {
var worker = new DataWorker(external.Promise.resolve(this.compressedContent))
.pipe(this.compression.uncompressWorker())
.pipe(new DataLengthProbe("data_length"));
var that = this;
worker.on("end", function () {
if (this.streamInfo["data_length"] !== that.uncompressedSize) {
throw new Error("Bug : uncompressed data size mismatch");
}
});
return worker;
},
/**
* Create a worker to get the compressed content.
* @return {GenericWorker} the worker.
*/
getCompressedWorker: function () {
return new DataWorker(external.Promise.resolve(this.compressedContent))
.withStreamInfo("compressedSize", this.compressedSize)
.withStreamInfo("uncompressedSize", this.uncompressedSize)
.withStreamInfo("crc32", this.crc32)
.withStreamInfo("compression", this.compression)
;
}
};
/**
* Chain the given worker with other workers to compress the content with the
* given compression.
* @param {GenericWorker} uncompressedWorker the worker to pipe.
* @param {Object} compression the compression object.
* @param {Object} compressionOptions the options to use when compressing.
* @return {GenericWorker} the new worker compressing the content.
*/
CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) {
return uncompressedWorker
.pipe(new Crc32Probe())
.pipe(new DataLengthProbe("uncompressedSize"))
.pipe(compression.compressWorker(compressionOptions))
.pipe(new DataLengthProbe("compressedSize"))
.withStreamInfo("compression", compression);
};
module.exports = CompressedObject;
},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){
"use strict";
var GenericWorker = require("./stream/GenericWorker");
exports.STORE = {
magic: "\x00\x00",
compressWorker : function () {
return new GenericWorker("STORE compression");
},
uncompressWorker : function () {
return new GenericWorker("STORE decompression");
}
};
exports.DEFLATE = require("./flate");
},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){
"use strict";
var utils = require("./utils");
/**
* The following functions come from pako, from pako/lib/zlib/crc32.js
* released under the MIT license, see pako https://github.com/nodeca/pako/
*/
// Use ordinary array, since untyped makes no boost here
function makeTable() {
var c, table = [];
for(var n =0; n < 256; n++){
c = n;
for(var k =0; k < 8; k++){
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
table[n] = c;
}
return table;
}
// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable, end = pos + len;
crc = crc ^ (-1);
for (var i = pos; i < end; i++ ) {
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;
}
// That's all for the pako functions.
/**
* Compute the crc32 of a string.
* This is almost the same as the function crc32, but for strings. Using the
* same function for the two use cases leads to horrible performances.
* @param {Number} crc the starting value of the crc.
* @param {String} str the string to use.
* @param {Number} len the length of the string.
* @param {Number} pos the starting position for the crc32 computation.
* @return {Number} the computed crc32.
*/
function crc32str(crc, str, len, pos) {
var t = crcTable, end = pos + len;
crc = crc ^ (-1);
for (var i = pos; i < end; i++ ) {
crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;
}
module.exports = function crc32wrapper(input, crc) {
if (typeof input === "undefined" || !input.length) {
return 0;
}
var isArray = utils.getTypeOf(input) !== "string";
if(isArray) {
return crc32(crc|0, input, input.length, 0);
} else {
return crc32str(crc|0, input, input.length, 0);
}
};
},{"./utils":32}],5:[function(require,module,exports){
"use strict";
exports.base64 = false;
exports.binary = false;
exports.dir = false;
exports.createFolders = true;
exports.date = null;
exports.compression = null;
exports.compressionOptions = null;
exports.comment = null;
exports.unixPermissions = null;
exports.dosPermissions = null;
},{}],6:[function(require,module,exports){
"use strict";
// load the global object first:
// - it should be better integrated in the system (unhandledRejection in node)
// - the environment may have a custom Promise implementation (see zone.js)
var ES6Promise = null;
if (typeof Promise !== "undefined") {
ES6Promise = Promise;
} else {
ES6Promise = require("lie");
}
/**
* Let the user use/change some implementations.
*/
module.exports = {
Promise: ES6Promise
};
},{"lie":37}],7:[function(require,module,exports){
"use strict";
var USE_TYPEDARRAY = (typeof Uint8Array !== "undefined") && (typeof Uint16Array !== "undefined") && (typeof Uint32Array !== "undefined");
var pako = require("pako");
var utils = require("./utils");
var GenericWorker = require("./stream/GenericWorker");
var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array";
exports.magic = "\x08\x00";
/**
* Create a worker that uses pako to inflate/deflate.
* @constructor
* @param {String} action the name of the pako function to call : either "Deflate" or "Inflate".
* @param {Object} options the options to use when (de)compressing.
*/
function FlateWorker(action, options) {
GenericWorker.call(this, "FlateWorker/" + action);
this._pako = null;
this._pakoAction = action;
this._pakoOptions = options;
// the `meta` object from the last chunk received
// this allow this worker to pass around metadata
this.meta = {};
}
utils.inherits(FlateWorker, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
FlateWorker.prototype.processChunk = function (chunk) {
this.meta = chunk.meta;
if (this._pako === null) {
this._createPako();
}
this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false);
};
/**
* @see GenericWorker.flush
*/
FlateWorker.prototype.flush = function () {
GenericWorker.prototype.flush.call(this);
if (this._pako === null) {
this._createPako();
}
this._pako.push([], true);
};
/**
* @see GenericWorker.cleanUp
*/
FlateWorker.prototype.cleanUp = function () {
GenericWorker.prototype.cleanUp.call(this);
this._pako = null;
};
/**
* Create the _pako object.
* TODO: lazy-loading this object isn't the best solution but it's the
* quickest. The best solution is to lazy-load the worker list. See also the
* issue #446.
*/
FlateWorker.prototype._createPako = function () {
this._pako = new pako[this._pakoAction]({
raw: true,
level: this._pakoOptions.level || -1 // default compression
});
var self = this;
this._pako.onData = function(data) {
self.push({
data : data,
meta : self.meta
});
};
};
exports.compressWorker = function (compressionOptions) {
return new FlateWorker("Deflate", compressionOptions);
};
exports.uncompressWorker = function () {
return new FlateWorker("Inflate", {});
};
},{"./stream/GenericWorker":28,"./utils":32,"pako":38}],8:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var GenericWorker = require("../stream/GenericWorker");
var utf8 = require("../utf8");
var crc32 = require("../crc32");
var signature = require("../signature");
/**
* Transform an integer into a string in hexadecimal.
* @private
* @param {number} dec the number to convert.
* @param {number} bytes the number of bytes to generate.
* @returns {string} the result.
*/
var decToHex = function(dec, bytes) {
var hex = "", i;
for (i = 0; i < bytes; i++) {
hex += String.fromCharCode(dec & 0xff);
dec = dec >>> 8;
}
return hex;
};
/**
* Generate the UNIX part of the external file attributes.
* @param {Object} unixPermissions the unix permissions or null.
* @param {Boolean} isDir true if the entry is a directory, false otherwise.
* @return {Number} a 32 bit integer.
*
* adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
*
* TTTTsstrwxrwxrwx0000000000ADVSHR
* ^^^^____________________________ file type, see zipinfo.c (UNX_*)
* ^^^_________________________ setuid, setgid, sticky
* ^^^^^^^^^________________ permissions
* ^^^^^^^^^^______ not used ?
* ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
*/
var generateUnixExternalFileAttr = function (unixPermissions, isDir) {
var result = unixPermissions;
if (!unixPermissions) {
// I can't use octal values in strict mode, hence the hexa.
// 040775 => 0x41fd
// 0100664 => 0x81b4
result = isDir ? 0x41fd : 0x81b4;
}
return (result & 0xFFFF) << 16;
};
/**
* Generate the DOS part of the external file attributes.
* @param {Object} dosPermissions the dos permissions or null.
* @param {Boolean} isDir true if the entry is a directory, false otherwise.
* @return {Number} a 32 bit integer.
*
* Bit 0 Read-Only
* Bit 1 Hidden
* Bit 2 System
* Bit 3 Volume Label
* Bit 4 Directory
* Bit 5 Archive
*/
var generateDosExternalFileAttr = function (dosPermissions) {
// the dir flag is already set for compatibility
return (dosPermissions || 0) & 0x3F;
};
/**
* Generate the various parts used in the construction of the final zip file.
* @param {Object} streamInfo the hash with information about the compressed file.
* @param {Boolean} streamedContent is the content streamed ?
* @param {Boolean} streamingEnded is the stream finished ?
* @param {number} offset the current offset from the start of the zip file.
* @param {String} platform let's pretend we are this platform (change platform dependents fields)
* @param {Function} encodeFileName the function to encode the file name / comment.
* @return {Object} the zip parts.
*/
var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) {
var file = streamInfo["file"],
compression = streamInfo["compression"],
useCustomEncoding = encodeFileName !== utf8.utf8encode,
encodedFileName = utils.transformTo("string", encodeFileName(file.name)),
utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
comment = file.comment,
encodedComment = utils.transformTo("string", encodeFileName(comment)),
utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
useUTF8ForComment = utfEncodedComment.length !== comment.length,
dosTime,
dosDate,
extraFields = "",
unicodePathExtraField = "",
unicodeCommentExtraField = "",
dir = file.dir,
date = file.date;
var dataInfo = {
crc32 : 0,
compressedSize : 0,
uncompressedSize : 0
};
// if the content is streamed, the sizes/crc32 are only available AFTER
// the end of the stream.
if (!streamedContent || streamingEnded) {
dataInfo.crc32 = streamInfo["crc32"];
dataInfo.compressedSize = streamInfo["compressedSize"];
dataInfo.uncompressedSize = streamInfo["uncompressedSize"];
}
var bitflag = 0;
if (streamedContent) {
// Bit 3: the sizes/crc32 are set to zero in the local header.
// The correct values are put in the data descriptor immediately
// following the compressed data.
bitflag |= 0x0008;
}
if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) {
// Bit 11: Language encoding flag (EFS).
bitflag |= 0x0800;
}
var extFileAttr = 0;
var versionMadeBy = 0;
if (dir) {
// dos or unix, we set the dos dir flag
extFileAttr |= 0x00010;
}
if(platform === "UNIX") {
versionMadeBy = 0x031E; // UNIX, version 3.0
extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
} else { // DOS or other, fallback to DOS
versionMadeBy = 0x0014; // DOS, version 2.0
extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
}
// date
// @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
// @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
// @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
dosTime = date.getUTCHours();
dosTime = dosTime << 6;
dosTime = dosTime | date.getUTCMinutes();
dosTime = dosTime << 5;
dosTime = dosTime | date.getUTCSeconds() / 2;
dosDate = date.getUTCFullYear() - 1980;
dosDate = dosDate << 4;
dosDate = dosDate | (date.getUTCMonth() + 1);
dosDate = dosDate << 5;
dosDate = dosDate | date.getUTCDate();
if (useUTF8ForFileName) {
// set the unicode path extra field. unzip needs at least one extra
// field to correctly handle unicode path, so using the path is as good
// as any other information. This could improve the situation with
// other archive managers too.
// This field is usually used without the utf8 flag, with a non
// unicode path in the header (winrar, winzip). This helps (a bit)
// with the messy Windows' default compressed folders feature but
// breaks on p7zip which doesn't seek the unicode path extra field.
// So for now, UTF-8 everywhere !
unicodePathExtraField =
// Version
decToHex(1, 1) +
// NameCRC32
decToHex(crc32(encodedFileName), 4) +
// UnicodeName
utfEncodedFileName;
extraFields +=
// Info-ZIP Unicode Path Extra Field
"\x75\x70" +
// size
decToHex(unicodePathExtraField.length, 2) +
// content
unicodePathExtraField;
}
if(useUTF8ForComment) {
unicodeCommentExtraField =
// Version
decToHex(1, 1) +
// CommentCRC32
decToHex(crc32(encodedComment), 4) +
// UnicodeName
utfEncodedComment;
extraFields +=
// Info-ZIP Unicode Path Extra Field
"\x75\x63" +
// size
decToHex(unicodeCommentExtraField.length, 2) +
// content
unicodeCommentExtraField;
}
var header = "";
// version needed to extract
header += "\x0A\x00";
// general purpose bit flag
header += decToHex(bitflag, 2);
// compression method
header += compression.magic;
// last mod file time
header += decToHex(dosTime, 2);
// last mod file date
header += decToHex(dosDate, 2);
// crc-32
header += decToHex(dataInfo.crc32, 4);
// compressed size
header += decToHex(dataInfo.compressedSize, 4);
// uncompressed size
header += decToHex(dataInfo.uncompressedSize, 4);
// file name length
header += decToHex(encodedFileName.length, 2);
// extra field length
header += decToHex(extraFields.length, 2);
var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields;
var dirRecord = signature.CENTRAL_FILE_HEADER +
// version made by (00: DOS)
decToHex(versionMadeBy, 2) +
// file header (common to file and central directory)
header +
// file comment length
decToHex(encodedComment.length, 2) +
// disk number start
"\x00\x00" +
// internal file attributes TODO
"\x00\x00" +
// external file attributes
decToHex(extFileAttr, 4) +
// relative offset of local header
decToHex(offset, 4) +
// file name
encodedFileName +
// extra field
extraFields +
// file comment
encodedComment;
return {
fileRecord: fileRecord,
dirRecord: dirRecord
};
};
/**
* Generate the EOCD record.
* @param {Number} entriesCount the number of entries in the zip file.
* @param {Number} centralDirLength the length (in bytes) of the central dir.
* @param {Number} localDirLength the length (in bytes) of the local dir.
* @param {String} comment the zip file comment as a binary string.
* @param {Function} encodeFileName the function to encode the comment.
* @return {String} the EOCD record.
*/
var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) {
var dirEnd = "";
var encodedComment = utils.transformTo("string", encodeFileName(comment));
// end of central dir signature
dirEnd = signature.CENTRAL_DIRECTORY_END +
// number of this disk
"\x00\x00" +
// number of the disk with the start of the central directory
"\x00\x00" +
// total number of entries in the central directory on this disk
decToHex(entriesCount, 2) +
// total number of entries in the central directory
decToHex(entriesCount, 2) +
// size of the central directory 4 bytes
decToHex(centralDirLength, 4) +
// offset of start of central directory with respect to the starting disk number
decToHex(localDirLength, 4) +
// .ZIP file comment length
decToHex(encodedComment.length, 2) +
// .ZIP file comment
encodedComment;
return dirEnd;
};
/**
* Generate data descriptors for a file entry.
* @param {Object} streamInfo the hash generated by a worker, containing information
* on the file entry.
* @return {String} the data descriptors.
*/
var generateDataDescriptors = function (streamInfo) {
var descriptor = "";
descriptor = signature.DATA_DESCRIPTOR +
// crc-32 4 bytes
decToHex(streamInfo["crc32"], 4) +
// compressed size 4 bytes
decToHex(streamInfo["compressedSize"], 4) +
// uncompressed size 4 bytes
decToHex(streamInfo["uncompressedSize"], 4);
return descriptor;
};
/**
* A worker to concatenate other workers to create a zip file.
* @param {Boolean} streamFiles `true` to stream the content of the files,
* `false` to accumulate it.
* @param {String} comment the comment to use.
* @param {String} platform the platform to use, "UNIX" or "DOS".
* @param {Function} encodeFileName the function to encode file names and comments.
*/
function ZipFileWorker(streamFiles, comment, platform, encodeFileName) {
GenericWorker.call(this, "ZipFileWorker");
// The number of bytes written so far. This doesn't count accumulated chunks.
this.bytesWritten = 0;
// The comment of the zip file
this.zipComment = comment;
// The platform "generating" the zip file.
this.zipPlatform = platform;
// the function to encode file names and comments.
this.encodeFileName = encodeFileName;
// Should we stream the content of the files ?
this.streamFiles = streamFiles;
// If `streamFiles` is false, we will need to accumulate the content of the
// files to calculate sizes / crc32 (and write them *before* the content).
// This boolean indicates if we are accumulating chunks (it will change a lot
// during the lifetime of this worker).
this.accumulate = false;
// The buffer receiving chunks when accumulating content.
this.contentBuffer = [];
// The list of generated directory records.
this.dirRecords = [];
// The offset (in bytes) from the beginning of the zip file for the current source.
this.currentSourceOffset = 0;
// The total number of entries in this zip file.
this.entriesCount = 0;
// the name of the file currently being added, null when handling the end of the zip file.
// Used for the emitted metadata.
this.currentFile = null;
this._sources = [];
}
utils.inherits(ZipFileWorker, GenericWorker);
/**
* @see GenericWorker.push
*/
ZipFileWorker.prototype.push = function (chunk) {
var currentFilePercent = chunk.meta.percent || 0;
var entriesCount = this.entriesCount;
var remainingFiles = this._sources.length;
if(this.accumulate) {
this.contentBuffer.push(chunk);
} else {
this.bytesWritten += chunk.data.length;
GenericWorker.prototype.push.call(this, {
data : chunk.data,
meta : {
currentFile : this.currentFile,
percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100
}
});
}
};
/**
* The worker started a new source (an other worker).
* @param {Object} streamInfo the streamInfo object from the new source.
*/
ZipFileWorker.prototype.openedSource = function (streamInfo) {
this.currentSourceOffset = this.bytesWritten;
this.currentFile = streamInfo["file"].name;
var streamedContent = this.streamFiles && !streamInfo["file"].dir;
// don't stream folders (because they don't have any content)
if(streamedContent) {
var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
this.push({
data : record.fileRecord,
meta : {percent:0}
});
} else {
// we need to wait for the whole file before pushing anything
this.accumulate = true;
}
};
/**
* The worker finished a source (an other worker).
* @param {Object} streamInfo the streamInfo object from the finished source.
*/
ZipFileWorker.prototype.closedSource = function (streamInfo) {
this.accumulate = false;
var streamedContent = this.streamFiles && !streamInfo["file"].dir;
var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
this.dirRecords.push(record.dirRecord);
if(streamedContent) {
// after the streamed file, we put data descriptors
this.push({
data : generateDataDescriptors(streamInfo),
meta : {percent:100}
});
} else {
// the content wasn't streamed, we need to push everything now
// first the file record, then the content
this.push({
data : record.fileRecord,
meta : {percent:0}
});
while(this.contentBuffer.length) {
this.push(this.contentBuffer.shift());
}
}
this.currentFile = null;
};
/**
* @see GenericWorker.flush
*/
ZipFileWorker.prototype.flush = function () {
var localDirLength = this.bytesWritten;
for(var i = 0; i < this.dirRecords.length; i++) {
this.push({
data : this.dirRecords[i],
meta : {percent:100}
});
}
var centralDirLength = this.bytesWritten - localDirLength;
var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName);
this.push({
data : dirEnd,
meta : {percent:100}
});
};
/**
* Prepare the next source to be read.
*/
ZipFileWorker.prototype.prepareNextSource = function () {
this.previous = this._sources.shift();
this.openedSource(this.previous.streamInfo);
if (this.isPaused) {
this.previous.pause();
} else {
this.previous.resume();
}
};
/**
* @see GenericWorker.registerPrevious
*/
ZipFileWorker.prototype.registerPrevious = function (previous) {
this._sources.push(previous);
var self = this;
previous.on("data", function (chunk) {
self.processChunk(chunk);
});
previous.on("end", function () {
self.closedSource(self.previous.streamInfo);
if(self._sources.length) {
self.prepareNextSource();
} else {
self.end();
}
});
previous.on("error", function (e) {
self.error(e);
});
return this;
};
/**
* @see GenericWorker.resume
*/
ZipFileWorker.prototype.resume = function () {
if(!GenericWorker.prototype.resume.call(this)) {
return false;
}
if (!this.previous && this._sources.length) {
this.prepareNextSource();
return true;
}
if (!this.previous && !this._sources.length && !this.generatedError) {
this.end();
return true;
}
};
/**
* @see GenericWorker.error
*/
ZipFileWorker.prototype.error = function (e) {
var sources = this._sources;
if(!GenericWorker.prototype.error.call(this, e)) {
return false;
}
for(var i = 0; i < sources.length; i++) {
try {
sources[i].error(e);
} catch(e) {
// the `error` exploded, nothing to do
}
}
return true;
};
/**
* @see GenericWorker.lock
*/
ZipFileWorker.prototype.lock = function () {
GenericWorker.prototype.lock.call(this);
var sources = this._sources;
for(var i = 0; i < sources.length; i++) {
sources[i].lock();
}
};
module.exports = ZipFileWorker;
},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){
"use strict";
var compressions = require("../compressions");
var ZipFileWorker = require("./ZipFileWorker");
/**
* Find the compression to use.
* @param {String} fileCompression the compression defined at the file level, if any.
* @param {String} zipCompression the compression defined at the load() level.
* @return {Object} the compression object to use.
*/
var getCompression = function (fileCompression, zipCompression) {
var compressionName = fileCompression || zipCompression;
var compression = compressions[compressionName];
if (!compression) {
throw new Error(compressionName + " is not a valid compression method !");
}
return compression;
};
/**
* Create a worker to generate a zip file.
* @param {JSZip} zip the JSZip instance at the right root level.
* @param {Object} options to generate the zip file.
* @param {String} comment the comment to use.
*/
exports.generateWorker = function (zip, options, comment) {
var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName);
var entriesCount = 0;
try {
zip.forEach(function (relativePath, file) {
entriesCount++;
var compression = getCompression(file.options.compression, options.compression);
var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
var dir = file.dir, date = file.date;
file._compressWorker(compression, compressionOptions)
.withStreamInfo("file", {
name : relativePath,
dir : dir,
date : date,
comment : file.comment || "",
unixPermissions : file.unixPermissions,
dosPermissions : file.dosPermissions
})
.pipe(zipFileWorker);
});
zipFileWorker.entriesCount = entriesCount;
} catch (e) {
zipFileWorker.error(e);
}
return zipFileWorker;
};
},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){
"use strict";
/**
* Representation a of zip file in js
* @constructor
*/
function JSZip() {
// if this constructor is used without `new`, it adds `new` before itself:
if(!(this instanceof JSZip)) {
return new JSZip();
}
if(arguments.length) {
throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
}
// object containing the files :
// {
// "folder/" : {...},
// "folder/data.txt" : {...}
// }
// NOTE: we use a null prototype because we do not
// want filenames like "toString" coming from a zip file
// to overwrite methods and attributes in a normal Object.
this.files = Object.create(null);
this.comment = null;
// Where we are in the hierarchy
this.root = "";
this.clone = function() {
var newObj = new JSZip();
for (var i in this) {
if (typeof this[i] !== "function") {
newObj[i] = this[i];
}
}
return newObj;
};
}
JSZip.prototype = require("./object");
JSZip.prototype.loadAsync = require("./load");
JSZip.support = require("./support");
JSZip.defaults = require("./defaults");
// TODO find a better way to handle this version,
// a require('package.json').version doesn't work with webpack, see #327
JSZip.version = "3.10.1";
JSZip.loadAsync = function (content, options) {
return new JSZip().loadAsync(content, options);
};
JSZip.external = require("./external");
module.exports = JSZip;
},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){
"use strict";
var utils = require("./utils");
var external = require("./external");
var utf8 = require("./utf8");
var ZipEntries = require("./zipEntries");
var Crc32Probe = require("./stream/Crc32Probe");
var nodejsUtils = require("./nodejsUtils");
/**
* Check the CRC32 of an entry.
* @param {ZipEntry} zipEntry the zip entry to check.
* @return {Promise} the result.
*/
function checkEntryCRC32(zipEntry) {
return new external.Promise(function (resolve, reject) {
var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
worker.on("error", function (e) {
reject(e);
})
.on("end", function () {
if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
reject(new Error("Corrupted zip : CRC32 mismatch"));
} else {
resolve();
}
})
.resume();
});
}
module.exports = function (data, options) {
var zip = this;
options = utils.extend(options || {}, {
base64: false,
checkCRC32: false,
optimizedBinaryString: false,
createFolders: false,
decodeFileName: utf8.utf8decode
});
if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file."));
}
return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64)
.then(function (data) {
var zipEntries = new ZipEntries(options);
zipEntries.load(data);
return zipEntries;
}).then(function checkCRC32(zipEntries) {
var promises = [external.Promise.resolve(zipEntries)];
var files = zipEntries.files;
if (options.checkCRC32) {
for (var i = 0; i < files.length; i++) {
promises.push(checkEntryCRC32(files[i]));
}
}
return external.Promise.all(promises);
}).then(function addFiles(results) {
var zipEntries = results.shift();
var files = zipEntries.files;
for (var i = 0; i < files.length; i++) {
var input = files[i];
var unsafeName = input.fileNameStr;
var safeName = utils.resolve(input.fileNameStr);
zip.file(safeName, input.decompressed, {
binary: true,
optimizedBinaryString: true,
date: input.date,
dir: input.dir,
comment: input.fileCommentStr.length ? input.fileCommentStr : null,
unixPermissions: input.unixPermissions,
dosPermissions: input.dosPermissions,
createFolders: options.createFolders
});
if (!input.dir) {
zip.file(safeName).unsafeOriginalName = unsafeName;
}
}
if (zipEntries.zipComment.length) {
zip.comment = zipEntries.zipComment;
}
return zip;
});
};
},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var GenericWorker = require("../stream/GenericWorker");
/**
* A worker that use a nodejs stream as source.
* @constructor
* @param {String} filename the name of the file entry for this stream.
* @param {Readable} stream the nodejs stream.
*/
function NodejsStreamInputAdapter(filename, stream) {
GenericWorker.call(this, "Nodejs stream input adapter for " + filename);
this._upstreamEnded = false;
this._bindStream(stream);
}
utils.inherits(NodejsStreamInputAdapter, GenericWorker);
/**
* Prepare the stream and bind the callbacks on it.
* Do this ASAP on node 0.10 ! A lazy binding doesn't always work.
* @param {Stream} stream the nodejs stream to use.
*/
NodejsStreamInputAdapter.prototype._bindStream = function (stream) {
var self = this;
this._stream = stream;
stream.pause();
stream
.on("data", function (chunk) {
self.push({
data: chunk,
meta : {
percent : 0
}
});
})
.on("error", function (e) {
if(self.isPaused) {
this.generatedError = e;
} else {
self.error(e);
}
})
.on("end", function () {
if(self.isPaused) {
self._upstreamEnded = true;
} else {
self.end();
}
});
};
NodejsStreamInputAdapter.prototype.pause = function () {
if(!GenericWorker.prototype.pause.call(this)) {
return false;
}
this._stream.pause();
return true;
};
NodejsStreamInputAdapter.prototype.resume = function () {
if(!GenericWorker.prototype.resume.call(this)) {
return false;
}
if(this._upstreamEnded) {
this.end();
} else {
this._stream.resume();
}
return true;
};
module.exports = NodejsStreamInputAdapter;
},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){
"use strict";
var Readable = require("readable-stream").Readable;
var utils = require("../utils");
utils.inherits(NodejsStreamOutputAdapter, Readable);
/**
* A nodejs stream using a worker as source.
* @see the SourceWrapper in http://nodejs.org/api/stream.html
* @constructor
* @param {StreamHelper} helper the helper wrapping the worker
* @param {Object} options the nodejs stream options
* @param {Function} updateCb the update callback.
*/
function NodejsStreamOutputAdapter(helper, options, updateCb) {
Readable.call(this, options);
this._helper = helper;
var self = this;
helper.on("data", function (data, meta) {
if (!self.push(data)) {
self._helper.pause();
}
if(updateCb) {
updateCb(meta);
}
})
.on("error", function(e) {
self.emit("error", e);
})
.on("end", function () {
self.push(null);
});
}
NodejsStreamOutputAdapter.prototype._read = function() {
this._helper.resume();
};
module.exports = NodejsStreamOutputAdapter;
},{"../utils":32,"readable-stream":16}],14:[function(require,module,exports){
"use strict";
module.exports = {
/**
* True if this is running in Nodejs, will be undefined in a browser.
* In a browser, browserify won't include this file and the whole module
* will be resolved an empty object.
*/
isNode : typeof Buffer !== "undefined",
/**
* Create a new nodejs Buffer from an existing content.
* @param {Object} data the data to pass to the constructor.
* @param {String} encoding the encoding to use.
* @return {Buffer} a new Buffer.
*/
newBufferFrom: function(data, encoding) {
if (Buffer.from && Buffer.from !== Uint8Array.from) {
return Buffer.from(data, encoding);
} else {
if (typeof data === "number") {
// Safeguard for old Node.js versions. On newer versions,
// Buffer.from(number) / Buffer(number, encoding) already throw.
throw new Error("The \"data\" argument must not be a number");
}
return new Buffer(data, encoding);
}
},
/**
* Create a new nodejs Buffer with the specified size.
* @param {Integer} size the size of the buffer.
* @return {Buffer} a new Buffer.
*/
allocBuffer: function (size) {
if (Buffer.alloc) {
return Buffer.alloc(size);
} else {
var buf = new Buffer(size);
buf.fill(0);
return buf;
}
},
/**
* Find out if an object is a Buffer.
* @param {Object} b the object to test.
* @return {Boolean} true if the object is a Buffer, false otherwise.
*/
isBuffer : function(b){
return Buffer.isBuffer(b);
},
isStream : function (obj) {
return obj &&
typeof obj.on === "function" &&
typeof obj.pause === "function" &&
typeof obj.resume === "function";
}
};
},{}],15:[function(require,module,exports){
"use strict";
var utf8 = require("./utf8");
var utils = require("./utils");
var GenericWorker = require("./stream/GenericWorker");
var StreamHelper = require("./stream/StreamHelper");
var defaults = require("./defaults");
var CompressedObject = require("./compressedObject");
var ZipObject = require("./zipObject");
var generate = require("./generate");
var nodejsUtils = require("./nodejsUtils");
var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter");
/**
* Add a file in the current folder.
* @private
* @param {string} name the name of the file
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
* @param {Object} originalOptions the options of the file
* @return {Object} the new file.
*/
var fileAdd = function(name, data, originalOptions) {
// be sure sub folders exist
var dataType = utils.getTypeOf(data),
parent;
/*
* Correct options.
*/
var o = utils.extend(originalOptions || {}, defaults);
o.date = o.date || new Date();
if (o.compression !== null) {
o.compression = o.compression.toUpperCase();
}
if (typeof o.unixPermissions === "string") {
o.unixPermissions = parseInt(o.unixPermissions, 8);
}
// UNX_IFDIR 0040000 see zipinfo.c
if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
o.dir = true;
}
// Bit 4 Directory
if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
o.dir = true;
}
if (o.dir) {
name = forceTrailingSlash(name);
}
if (o.createFolders && (parent = parentFolder(name))) {
folderAdd.call(this, parent, true);
}
var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false;
if (!originalOptions || typeof originalOptions.binary === "undefined") {
o.binary = !isUnicodeString;
}
var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0;
if (isCompressedEmpty || o.dir || !data || data.length === 0) {
o.base64 = false;
o.binary = true;
data = "";
o.compression = "STORE";
dataType = "string";
}
/*
* Convert content to fit.
*/
var zipObjectContent = null;
if (data instanceof CompressedObject || data instanceof GenericWorker) {
zipObjectContent = data;
} else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
zipObjectContent = new NodejsStreamInputAdapter(name, data);
} else {
zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64);
}
var object = new ZipObject(name, zipObjectContent, o);
this.files[name] = object;
/*
TODO: we can't throw an exception because we have async promises
(we can have a promise of a Date() for example) but returning a
promise is useless because file(name, data) returns the JSZip
object for chaining. Should we break that to allow the user
to catch the error ?
return external.Promise.resolve(zipObjectContent)
.then(function () {
return object;
});
*/
};
/**
* Find the parent folder of the path.
* @private
* @param {string} path the path to use
* @return {string} the parent folder, or ""
*/
var parentFolder = function (path) {
if (path.slice(-1) === "/") {
path = path.substring(0, path.length - 1);
}
var lastSlash = path.lastIndexOf("/");
return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
};
/**
* Returns the path with a slash at the end.
* @private
* @param {String} path the path to check.
* @return {String} the path with a trailing slash.
*/
var forceTrailingSlash = function(path) {
// Check the name ends with a /
if (path.slice(-1) !== "/") {
path += "/"; // IE doesn't like substr(-1)
}
return path;
};
/**
* Add a (sub) folder in the current folder.
* @private
* @param {string} name the folder's name
* @param {boolean=} [createFolders] If true, automatically create sub
* folders. Defaults to false.
* @return {Object} the new folder.
*/
var folderAdd = function(name, createFolders) {
createFolders = (typeof createFolders !== "undefined") ? createFolders : defaults.createFolders;
name = forceTrailingSlash(name);
// Does this folder already exist?
if (!this.files[name]) {
fileAdd.call(this, name, null, {
dir: true,
createFolders: createFolders
});
}
return this.files[name];
};
/**
* Cross-window, cross-Node-context regular expression detection
* @param {Object} object Anything
* @return {Boolean} true if the object is a regular expression,
* false otherwise
*/
function isRegExp(object) {
return Object.prototype.toString.call(object) === "[object RegExp]";
}
// return the actual prototype of JSZip
var out = {
/**
* @see loadAsync
*/
load: function() {
throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
},
/**
* Call a callback function for each entry at this folder level.
* @param {Function} cb the callback function:
* function (relativePath, file) {...}
* It takes 2 arguments : the relative path and the file.
*/
forEach: function(cb) {
var filename, relativePath, file;
// ignore warning about unwanted properties because this.files is a null prototype object
/* eslint-disable-next-line guard-for-in */
for (filename in this.files) {
file = this.files[filename];
relativePath = filename.slice(this.root.length, filename.length);
if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root
cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn...
}
}
},
/**
* Filter nested files/folders with the specified function.
* @param {Function} search the predicate to use :
* function (relativePath, file) {...}
* It takes 2 arguments : the relative path and the file.
* @return {Array} An array of matching elements.
*/
filter: function(search) {
var result = [];
this.forEach(function (relativePath, entry) {
if (search(relativePath, entry)) { // the file matches the function
result.push(entry);
}
});
return result;
},
/**
* Add a file to the zip file, or search a file.
* @param {string|RegExp} name The name of the file to add (if data is defined),
* the name of the file to find (if no data) or a regex to match files.
* @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded
* @param {Object} o File options
* @return {JSZip|Object|Array} this JSZip object (when adding a file),
* a file (when searching by string) or an array of files (when searching by regex).
*/
file: function(name, data, o) {
if (arguments.length === 1) {
if (isRegExp(name)) {
var regexp = name;
return this.filter(function(relativePath, file) {
return !file.dir && regexp.test(relativePath);
});
}
else { // text
var obj = this.files[this.root + name];
if (obj && !obj.dir) {
return obj;
} else {
return null;
}
}
}
else { // more than one argument : we have data !
name = this.root + name;
fileAdd.call(this, name, data, o);
}
return this;
},
/**
* Add a directory to the zip file, or search.
* @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
* @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
*/
folder: function(arg) {
if (!arg) {
return this;
}
if (isRegExp(arg)) {
return this.filter(function(relativePath, file) {
return file.dir && arg.test(relativePath);
});
}
// else, name is a new folder
var name = this.root + arg;
var newFolder = folderAdd.call(this, name);
// Allow chaining by returning a new object with this folder as the root
var ret = this.clone();
ret.root = newFolder.name;
return ret;
},
/**
* Delete a file, or a directory and all sub-files, from the zip
* @param {string} name the name of the file to delete
* @return {JSZip} this JSZip object
*/
remove: function(name) {
name = this.root + name;
var file = this.files[name];
if (!file) {
// Look for any folders
if (name.slice(-1) !== "/") {
name += "/";
}
file = this.files[name];
}
if (file && !file.dir) {
// file
delete this.files[name];
} else {
// maybe a folder, delete recursively
var kids = this.filter(function(relativePath, file) {
return file.name.slice(0, name.length) === name;
});
for (var i = 0; i < kids.length; i++) {
delete this.files[kids[i].name];
}
}
return this;
},
/**
* @deprecated This method has been removed in JSZip 3.0, please check the upgrade guide.
*/
generate: function() {
throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
},
/**
* Generate the complete zip file as an internal stream.
* @param {Object} options the options to generate the zip file :
* - compression, "STORE" by default.
* - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
* @return {StreamHelper} the streamed zip file.
*/
generateInternalStream: function(options) {
var worker, opts = {};
try {
opts = utils.extend(options || {}, {
streamFiles: false,
compression: "STORE",
compressionOptions : null,
type: "",
platform: "DOS",
comment: null,
mimeType: "application/zip",
encodeFileName: utf8.utf8encode
});
opts.type = opts.type.toLowerCase();
opts.compression = opts.compression.toUpperCase();
// "binarystring" is preferred but the internals use "string".
if(opts.type === "binarystring") {
opts.type = "string";
}
if (!opts.type) {
throw new Error("No output type specified.");
}
utils.checkSupport(opts.type);
// accept nodejs `process.platform`
if(
opts.platform === "darwin" ||
opts.platform === "freebsd" ||
opts.platform === "linux" ||
opts.platform === "sunos"
) {
opts.platform = "UNIX";
}
if (opts.platform === "win32") {
opts.platform = "DOS";
}
var comment = opts.comment || this.comment || "";
worker = generate.generateWorker(this, opts, comment);
} catch (e) {
worker = new GenericWorker("error");
worker.error(e);
}
return new StreamHelper(worker, opts.type || "string", opts.mimeType);
},
/**
* Generate the complete zip file asynchronously.
* @see generateInternalStream
*/
generateAsync: function(options, onUpdate) {
return this.generateInternalStream(options).accumulate(onUpdate);
},
/**
* Generate the complete zip file asynchronously.
* @see generateInternalStream
*/
generateNodeStream: function(options, onUpdate) {
options = options || {};
if (!options.type) {
options.type = "nodebuffer";
}
return this.generateInternalStream(options).toNodejsStream(onUpdate);
}
};
module.exports = out;
},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){
"use strict";
/*
* This file is used by module bundlers (browserify/webpack/etc) when
* including a stream implementation. We use "readable-stream" to get a
* consistent behavior between nodejs versions but bundlers often have a shim
* for "stream". Using this shim greatly improve the compatibility and greatly
* reduce the final size of the bundle (only one stream implementation, not
* two).
*/
module.exports = require("stream");
},{"stream":undefined}],17:[function(require,module,exports){
"use strict";
var DataReader = require("./DataReader");
var utils = require("../utils");
function ArrayReader(data) {
DataReader.call(this, data);
for(var i = 0; i < this.data.length; i++) {
data[i] = data[i] & 0xFF;
}
}
utils.inherits(ArrayReader, DataReader);
/**
* @see DataReader.byteAt
*/
ArrayReader.prototype.byteAt = function(i) {
return this.data[this.zero + i];
};
/**
* @see DataReader.lastIndexOfSignature
*/
ArrayReader.prototype.lastIndexOfSignature = function(sig) {
var sig0 = sig.charCodeAt(0),
sig1 = sig.charCodeAt(1),
sig2 = sig.charCodeAt(2),
sig3 = sig.charCodeAt(3);
for (var i = this.length - 4; i >= 0; --i) {
if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) {
return i - this.zero;
}
}
return -1;
};
/**
* @see DataReader.readAndCheckSignature
*/
ArrayReader.prototype.readAndCheckSignature = function (sig) {
var sig0 = sig.charCodeAt(0),
sig1 = sig.charCodeAt(1),
sig2 = sig.charCodeAt(2),
sig3 = sig.charCodeAt(3),
data = this.readData(4);
return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3];
};
/**
* @see DataReader.readData
*/
ArrayReader.prototype.readData = function(size) {
this.checkOffset(size);
if(size === 0) {
return [];
}
var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
this.index += size;
return result;
};
module.exports = ArrayReader;
},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){
"use strict";
var utils = require("../utils");
function DataReader(data) {
this.data = data; // type : see implementation
this.length = data.length;
this.index = 0;
this.zero = 0;
}
DataReader.prototype = {
/**
* Check that the offset will not go too far.
* @param {string} offset the additional offset to check.
* @throws {Error} an Error if the offset is out of bounds.
*/
checkOffset: function(offset) {
this.checkIndex(this.index + offset);
},
/**
* Check that the specified index will not be too far.
* @param {string} newIndex the index to check.
* @throws {Error} an Error if the index is out of bounds.
*/
checkIndex: function(newIndex) {
if (this.length < this.zero + newIndex || newIndex < 0) {
throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?");
}
},
/**
* Change the index.
* @param {number} newIndex The new index.
* @throws {Error} if the new index is out of the data.
*/
setIndex: function(newIndex) {
this.checkIndex(newIndex);
this.index = newIndex;
},
/**
* Skip the next n bytes.
* @param {number} n the number of bytes to skip.
* @throws {Error} if the new index is out of the data.
*/
skip: function(n) {
this.setIndex(this.index + n);
},
/**
* Get the byte at the specified index.
* @param {number} i the index to use.
* @return {number} a byte.
*/
byteAt: function() {
// see implementations
},
/**
* Get the next number with a given byte size.
* @param {number} size the number of bytes to read.
* @return {number} the corresponding number.
*/
readInt: function(size) {
var result = 0,
i;
this.checkOffset(size);
for (i = this.index + size - 1; i >= this.index; i--) {
result = (result << 8) + this.byteAt(i);
}
this.index += size;
return result;
},
/**
* Get the next string with a given byte size.
* @param {number} size the number of bytes to read.
* @return {string} the corresponding string.
*/
readString: function(size) {
return utils.transformTo("string", this.readData(size));
},
/**
* Get raw data without conversion, <size> bytes.
* @param {number} size the number of bytes to read.
* @return {Object} the raw data, implementation specific.
*/
readData: function() {
// see implementations
},
/**
* Find the last occurrence of a zip signature (4 bytes).
* @param {string} sig the signature to find.
* @return {number} the index of the last occurrence, -1 if not found.
*/
lastIndexOfSignature: function() {
// see implementations
},
/**
* Read the signature (4 bytes) at the current position and compare it with sig.
* @param {string} sig the expected signature
* @return {boolean} true if the signature matches, false otherwise.
*/
readAndCheckSignature: function() {
// see implementations
},
/**
* Get the next date.
* @return {Date} the date.
*/
readDate: function() {
var dostime = this.readInt(4);
return new Date(Date.UTC(
((dostime >> 25) & 0x7f) + 1980, // year
((dostime >> 21) & 0x0f) - 1, // month
(dostime >> 16) & 0x1f, // day
(dostime >> 11) & 0x1f, // hour
(dostime >> 5) & 0x3f, // minute
(dostime & 0x1f) << 1)); // second
}
};
module.exports = DataReader;
},{"../utils":32}],19:[function(require,module,exports){
"use strict";
var Uint8ArrayReader = require("./Uint8ArrayReader");
var utils = require("../utils");
function NodeBufferReader(data) {
Uint8ArrayReader.call(this, data);
}
utils.inherits(NodeBufferReader, Uint8ArrayReader);
/**
* @see DataReader.readData
*/
NodeBufferReader.prototype.readData = function(size) {
this.checkOffset(size);
var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
this.index += size;
return result;
};
module.exports = NodeBufferReader;
},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){
"use strict";
var DataReader = require("./DataReader");
var utils = require("../utils");
function StringReader(data) {
DataReader.call(this, data);
}
utils.inherits(StringReader, DataReader);
/**
* @see DataReader.byteAt
*/
StringReader.prototype.byteAt = function(i) {
return this.data.charCodeAt(this.zero + i);
};
/**
* @see DataReader.lastIndexOfSignature
*/
StringReader.prototype.lastIndexOfSignature = function(sig) {
return this.data.lastIndexOf(sig) - this.zero;
};
/**
* @see DataReader.readAndCheckSignature
*/
StringReader.prototype.readAndCheckSignature = function (sig) {
var data = this.readData(4);
return sig === data;
};
/**
* @see DataReader.readData
*/
StringReader.prototype.readData = function(size) {
this.checkOffset(size);
// this will work because the constructor applied the "& 0xff" mask.
var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
this.index += size;
return result;
};
module.exports = StringReader;
},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){
"use strict";
var ArrayReader = require("./ArrayReader");
var utils = require("../utils");
function Uint8ArrayReader(data) {
ArrayReader.call(this, data);
}
utils.inherits(Uint8ArrayReader, ArrayReader);
/**
* @see DataReader.readData
*/
Uint8ArrayReader.prototype.readData = function(size) {
this.checkOffset(size);
if(size === 0) {
// in IE10, when using subarray(idx, idx), we get the array [0x00] instead of [].
return new Uint8Array(0);
}
var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size);
this.index += size;
return result;
};
module.exports = Uint8ArrayReader;
},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var support = require("../support");
var ArrayReader = require("./ArrayReader");
var StringReader = require("./StringReader");
var NodeBufferReader = require("./NodeBufferReader");
var Uint8ArrayReader = require("./Uint8ArrayReader");
/**
* Create a reader adapted to the data.
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read.
* @return {DataReader} the data reader.
*/
module.exports = function (data) {
var type = utils.getTypeOf(data);
utils.checkSupport(type);
if (type === "string" && !support.uint8array) {
return new StringReader(data);
}
if (type === "nodebuffer") {
return new NodeBufferReader(data);
}
if (support.uint8array) {
return new Uint8ArrayReader(utils.transformTo("uint8array", data));
}
return new ArrayReader(utils.transformTo("array", data));
};
},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){
"use strict";
exports.LOCAL_FILE_HEADER = "PK\x03\x04";
exports.CENTRAL_FILE_HEADER = "PK\x01\x02";
exports.CENTRAL_DIRECTORY_END = "PK\x05\x06";
exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07";
exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06";
exports.DATA_DESCRIPTOR = "PK\x07\x08";
},{}],24:[function(require,module,exports){
"use strict";
var GenericWorker = require("./GenericWorker");
var utils = require("../utils");
/**
* A worker which convert chunks to a specified type.
* @constructor
* @param {String} destType the destination type.
*/
function ConvertWorker(destType) {
GenericWorker.call(this, "ConvertWorker to " + destType);
this.destType = destType;
}
utils.inherits(ConvertWorker, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
ConvertWorker.prototype.processChunk = function (chunk) {
this.push({
data : utils.transformTo(this.destType, chunk.data),
meta : chunk.meta
});
};
module.exports = ConvertWorker;
},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){
"use strict";
var GenericWorker = require("./GenericWorker");
var crc32 = require("../crc32");
var utils = require("../utils");
/**
* A worker which calculate the crc32 of the data flowing through.
* @constructor
*/
function Crc32Probe() {
GenericWorker.call(this, "Crc32Probe");
this.withStreamInfo("crc32", 0);
}
utils.inherits(Crc32Probe, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
Crc32Probe.prototype.processChunk = function (chunk) {
this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0);
this.push(chunk);
};
module.exports = Crc32Probe;
},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var GenericWorker = require("./GenericWorker");
/**
* A worker which calculate the total length of the data flowing through.
* @constructor
* @param {String} propName the name used to expose the length
*/
function DataLengthProbe(propName) {
GenericWorker.call(this, "DataLengthProbe for " + propName);
this.propName = propName;
this.withStreamInfo(propName, 0);
}
utils.inherits(DataLengthProbe, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
DataLengthProbe.prototype.processChunk = function (chunk) {
if(chunk) {
var length = this.streamInfo[this.propName] || 0;
this.streamInfo[this.propName] = length + chunk.data.length;
}
GenericWorker.prototype.processChunk.call(this, chunk);
};
module.exports = DataLengthProbe;
},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var GenericWorker = require("./GenericWorker");
// the size of the generated chunks
// TODO expose this as a public variable
var DEFAULT_BLOCK_SIZE = 16 * 1024;
/**
* A worker that reads a content and emits chunks.
* @constructor
* @param {Promise} dataP the promise of the data to split
*/
function DataWorker(dataP) {
GenericWorker.call(this, "DataWorker");
var self = this;
this.dataIsReady = false;
this.index = 0;
this.max = 0;
this.data = null;
this.type = "";
this._tickScheduled = false;
dataP.then(function (data) {
self.dataIsReady = true;
self.data = data;
self.max = data && data.length || 0;
self.type = utils.getTypeOf(data);
if(!self.isPaused) {
self._tickAndRepeat();
}
}, function (e) {
self.error(e);
});
}
utils.inherits(DataWorker, GenericWorker);
/**
* @see GenericWorker.cleanUp
*/
DataWorker.prototype.cleanUp = function () {
GenericWorker.prototype.cleanUp.call(this);
this.data = null;
};
/**
* @see GenericWorker.resume
*/
DataWorker.prototype.resume = function () {
if(!GenericWorker.prototype.resume.call(this)) {
return false;
}
if (!this._tickScheduled && this.dataIsReady) {
this._tickScheduled = true;
utils.delay(this._tickAndRepeat, [], this);
}
return true;
};
/**
* Trigger a tick a schedule an other call to this function.
*/
DataWorker.prototype._tickAndRepeat = function() {
this._tickScheduled = false;
if(this.isPaused || this.isFinished) {
return;
}
this._tick();
if(!this.isFinished) {
utils.delay(this._tickAndRepeat, [], this);
this._tickScheduled = true;
}
};
/**
* Read and push a chunk.
*/
DataWorker.prototype._tick = function() {
if(this.isPaused || this.isFinished) {
return false;
}
var size = DEFAULT_BLOCK_SIZE;
var data = null, nextIndex = Math.min(this.max, this.index + size);
if (this.index >= this.max) {
// EOF
return this.end();
} else {
switch(this.type) {
case "string":
data = this.data.substring(this.index, nextIndex);
break;
case "uint8array":
data = this.data.subarray(this.index, nextIndex);
break;
case "array":
case "nodebuffer":
data = this.data.slice(this.index, nextIndex);
break;
}
this.index = nextIndex;
return this.push({
data : data,
meta : {
percent : this.max ? this.index / this.max * 100 : 0
}
});
}
};
module.exports = DataWorker;
},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){
"use strict";
/**
* A worker that does nothing but passing chunks to the next one. This is like
* a nodejs stream but with some differences. On the good side :
* - it works on IE 6-9 without any issue / polyfill
* - it weights less than the full dependencies bundled with browserify
* - it forwards errors (no need to declare an error handler EVERYWHERE)
*
* A chunk is an object with 2 attributes : `meta` and `data`. The former is an
* object containing anything (`percent` for example), see each worker for more
* details. The latter is the real data (String, Uint8Array, etc).
*
* @constructor
* @param {String} name the name of the stream (mainly used for debugging purposes)
*/
function GenericWorker(name) {
// the name of the worker
this.name = name || "default";
// an object containing metadata about the workers chain
this.streamInfo = {};
// an error which happened when the worker was paused
this.generatedError = null;
// an object containing metadata to be merged by this worker into the general metadata
this.extraStreamInfo = {};
// true if the stream is paused (and should not do anything), false otherwise
this.isPaused = true;
// true if the stream is finished (and should not do anything), false otherwise
this.isFinished = false;
// true if the stream is locked to prevent further structure updates (pipe), false otherwise
this.isLocked = false;
// the event listeners
this._listeners = {
"data":[],
"end":[],
"error":[]
};
// the previous worker, if any
this.previous = null;
}
GenericWorker.prototype = {
/**
* Push a chunk to the next workers.
* @param {Object} chunk the chunk to push
*/
push : function (chunk) {
this.emit("data", chunk);
},
/**
* End the stream.
* @return {Boolean} true if this call ended the worker, false otherwise.
*/
end : function () {
if (this.isFinished) {
return false;
}
this.flush();
try {
this.emit("end");
this.cleanUp();
this.isFinished = true;
} catch (e) {
this.emit("error", e);
}
return true;
},
/**
* End the stream with an error.
* @param {Error} e the error which caused the premature end.
* @return {Boolean} true if this call ended the worker with an error, false otherwise.
*/
error : function (e) {
if (this.isFinished) {
return false;
}
if(this.isPaused) {
this.generatedError = e;
} else {
this.isFinished = true;
this.emit("error", e);
// in the workers chain exploded in the middle of the chain,
// the error event will go downward but we also need to notify
// workers upward that there has been an error.
if(this.previous) {
this.previous.error(e);
}
this.cleanUp();
}
return true;
},
/**
* Add a callback on an event.
* @param {String} name the name of the event (data, end, error)
* @param {Function} listener the function to call when the event is triggered
* @return {GenericWorker} the current object for chainability
*/
on : function (name, listener) {
this._listeners[name].push(listener);
return this;
},
/**
* Clean any references when a worker is ending.
*/
cleanUp : function () {
this.streamInfo = this.generatedError = this.extraStreamInfo = null;
this._listeners = [];
},
/**
* Trigger an event. This will call registered callback with the provided arg.
* @param {String} name the name of the event (data, end, error)
* @param {Object} arg the argument to call the callback with.
*/
emit : function (name, arg) {
if (this._listeners[name]) {
for(var i = 0; i < this._listeners[name].length; i++) {
this._listeners[name][i].call(this, arg);
}
}
},
/**
* Chain a worker with an other.
* @param {Worker} next the worker receiving events from the current one.
* @return {worker} the next worker for chainability
*/
pipe : function (next) {
return next.registerPrevious(this);
},
/**
* Same as `pipe` in the other direction.
* Using an API with `pipe(next)` is very easy.
* Implementing the API with the point of view of the next one registering
* a source is easier, see the ZipFileWorker.
* @param {Worker} previous the previous worker, sending events to this one
* @return {Worker} the current worker for chainability
*/
registerPrevious : function (previous) {
if (this.isLocked) {
throw new Error("The stream '" + this + "' has already been used.");
}
// sharing the streamInfo...
this.streamInfo = previous.streamInfo;
// ... and adding our own bits
this.mergeStreamInfo();
this.previous = previous;
var self = this;
previous.on("data", function (chunk) {
self.processChunk(chunk);
});
previous.on("end", function () {
self.end();
});
previous.on("error", function (e) {
self.error(e);
});
return this;
},
/**
* Pause the stream so it doesn't send events anymore.
* @return {Boolean} true if this call paused the worker, false otherwise.
*/
pause : function () {
if(this.isPaused || this.isFinished) {
return false;
}
this.isPaused = true;
if(this.previous) {
this.previous.pause();
}
return true;
},
/**
* Resume a paused stream.
* @return {Boolean} true if this call resumed the worker, false otherwise.
*/
resume : function () {
if(!this.isPaused || this.isFinished) {
return false;
}
this.isPaused = false;
// if true, the worker tried to resume but failed
var withError = false;
if(this.generatedError) {
this.error(this.generatedError);
withError = true;
}
if(this.previous) {
this.previous.resume();
}
return !withError;
},
/**
* Flush any remaining bytes as the stream is ending.
*/
flush : function () {},
/**
* Process a chunk. This is usually the method overridden.
* @param {Object} chunk the chunk to process.
*/
processChunk : function(chunk) {
this.push(chunk);
},
/**
* Add a key/value to be added in the workers chain streamInfo once activated.
* @param {String} key the key to use
* @param {Object} value the associated value
* @return {Worker} the current worker for chainability
*/
withStreamInfo : function (key, value) {
this.extraStreamInfo[key] = value;
this.mergeStreamInfo();
return this;
},
/**
* Merge this worker's streamInfo into the chain's streamInfo.
*/
mergeStreamInfo : function () {
for(var key in this.extraStreamInfo) {
if (!Object.prototype.hasOwnProperty.call(this.extraStreamInfo, key)) {
continue;
}
this.streamInfo[key] = this.extraStreamInfo[key];
}
},
/**
* Lock the stream to prevent further updates on the workers chain.
* After calling this method, all calls to pipe will fail.
*/
lock: function () {
if (this.isLocked) {
throw new Error("The stream '" + this + "' has already been used.");
}
this.isLocked = true;
if (this.previous) {
this.previous.lock();
}
},
/**
*
* Pretty print the workers chain.
*/
toString : function () {
var me = "Worker " + this.name;
if (this.previous) {
return this.previous + " -> " + me;
} else {
return me;
}
}
};
module.exports = GenericWorker;
},{}],29:[function(require,module,exports){
"use strict";
var utils = require("../utils");
var ConvertWorker = require("./ConvertWorker");
var GenericWorker = require("./GenericWorker");
var base64 = require("../base64");
var support = require("../support");
var external = require("../external");
var NodejsStreamOutputAdapter = null;
if (support.nodestream) {
try {
NodejsStreamOutputAdapter = require("../nodejs/NodejsStreamOutputAdapter");
} catch(e) {
// ignore
}
}
/**
* Apply the final transformation of the data. If the user wants a Blob for
* example, it's easier to work with an U8intArray and finally do the
* ArrayBuffer/Blob conversion.
* @param {String} type the name of the final type
* @param {String|Uint8Array|Buffer} content the content to transform
* @param {String} mimeType the mime type of the content, if applicable.
* @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format.
*/
function transformZipOutput(type, content, mimeType) {
switch(type) {
case "blob" :
return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType);
case "base64" :
return base64.encode(content);
default :
return utils.transformTo(type, content);
}
}
/**
* Concatenate an array of data of the given type.
* @param {String} type the type of the data in the given array.
* @param {Array} dataArray the array containing the data chunks to concatenate
* @return {String|Uint8Array|Buffer} the concatenated data
* @throws Error if the asked type is unsupported
*/
function concat (type, dataArray) {
var i, index = 0, res = null, totalLength = 0;
for(i = 0; i < dataArray.length; i++) {
totalLength += dataArray[i].length;
}
switch(type) {
case "string":
return dataArray.join("");
case "array":
return Array.prototype.concat.apply([], dataArray);
case "uint8array":
res = new Uint8Array(totalLength);
for(i = 0; i < dataArray.length; i++) {
res.set(dataArray[i], index);
index += dataArray[i].length;
}
return res;
case "nodebuffer":
return Buffer.concat(dataArray);
default:
throw new Error("concat : unsupported type '" + type + "'");
}
}
/**
* Listen a StreamHelper, accumulate its content and concatenate it into a
* complete block.
* @param {StreamHelper} helper the helper to use.
* @param {Function} updateCallback a callback called on each update. Called
* with one arg :
* - the metadata linked to the update received.
* @return Promise the promise for the accumulation.
*/
function accumulate(helper, updateCallback) {
return new external.Promise(function (resolve, reject){
var dataArray = [];
var chunkType = helper._internalType,
resultType = helper._outputType,
mimeType = helper._mimeType;
helper
.on("data", function (data, meta) {
dataArray.push(data);
if(updateCallback) {
updateCallback(meta);
}
})
.on("error", function(err) {
dataArray = [];
reject(err);
})
.on("end", function (){
try {
var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
resolve(result);
} catch (e) {
reject(e);
}
dataArray = [];
})
.resume();
});
}
/**
* An helper to easily use workers outside of JSZip.
* @constructor
* @param {Worker} worker the worker to wrap
* @param {String} outputType the type of data expected by the use
* @param {String} mimeType the mime type of the content, if applicable.
*/
function StreamHelper(worker, outputType, mimeType) {
var internalType = outputType;
switch(outputType) {
case "blob":
case "arraybuffer":
internalType = "uint8array";
break;
case "base64":
internalType = "string";
break;
}
try {
// the type used internally
this._internalType = internalType;
// the type used to output results
this._outputType = outputType;
// the mime type
this._mimeType = mimeType;
utils.checkSupport(internalType);
this._worker = worker.pipe(new ConvertWorker(internalType));
// the last workers can be rewired without issues but we need to
// prevent any updates on previous workers.
worker.lock();
} catch(e) {
this._worker = new GenericWorker("error");
this._worker.error(e);
}
}
StreamHelper.prototype = {
/**
* Listen a StreamHelper, accumulate its content and concatenate it into a
* complete block.
* @param {Function} updateCb the update callback.
* @return Promise the promise for the accumulation.
*/
accumulate : function (updateCb) {
return accumulate(this, updateCb);
},
/**
* Add a listener on an event triggered on a stream.
* @param {String} evt the name of the event
* @param {Function} fn the listener
* @return {StreamHelper} the current helper.
*/
on : function (evt, fn) {
var self = this;
if(evt === "data") {
this._worker.on(evt, function (chunk) {
fn.call(self, chunk.data, chunk.meta);
});
} else {
this._worker.on(evt, function () {
utils.delay(fn, arguments, self);
});
}
return this;
},
/**
* Resume the flow of chunks.
* @return {StreamHelper} the current helper.
*/
resume : function () {
utils.delay(this._worker.resume, [], this._worker);
return this;
},
/**
* Pause the flow of chunks.
* @return {StreamHelper} the current helper.
*/
pause : function () {
this._worker.pause();
return this;
},
/**
* Return a nodejs stream for this helper.
* @param {Function} updateCb the update callback.
* @return {NodejsStreamOutputAdapter} the nodejs stream.
*/
toNodejsStream : function (updateCb) {
utils.checkSupport("nodestream");
if (this._outputType !== "nodebuffer") {
// an object stream containing blob/arraybuffer/uint8array/string
// is strange and I don't know if it would be useful.
// I you find this comment and have a good usecase, please open a
// bug report !
throw new Error(this._outputType + " is not supported by this method");
}
return new NodejsStreamOutputAdapter(this, {
objectMode : this._outputType !== "nodebuffer"
}, updateCb);
}
};
module.exports = StreamHelper;
},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){
"use strict";
exports.base64 = true;
exports.array = true;
exports.string = true;
exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
exports.nodebuffer = typeof Buffer !== "undefined";
// contains true if JSZip can read/generate Uint8Array, false otherwise.
exports.uint8array = typeof Uint8Array !== "undefined";
if (typeof ArrayBuffer === "undefined") {
exports.blob = false;
}
else {
var buffer = new ArrayBuffer(0);
try {
exports.blob = new Blob([buffer], {
type: "application/zip"
}).size === 0;
}
catch (e) {
try {
var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
var builder = new Builder();
builder.append(buffer);
exports.blob = builder.getBlob("application/zip").size === 0;
}
catch (e) {
exports.blob = false;
}
}
}
try {
exports.nodestream = !!require("readable-stream").Readable;
} catch(e) {
exports.nodestream = false;
}
},{"readable-stream":16}],31:[function(require,module,exports){
"use strict";
var utils = require("./utils");
var support = require("./support");
var nodejsUtils = require("./nodejsUtils");
var GenericWorker = require("./stream/GenericWorker");
/**
* The following functions come from pako, from pako/lib/utils/strings
* released under the MIT license, see pako https://github.com/nodeca/pako/
*/
// Table with utf8 lengths (calculated by first byte of sequence)
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
// because max possible codepoint is 0x10ffff
var _utf8len = new Array(256);
for (var i=0; i<256; i++) {
_utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1);
}
_utf8len[254]=_utf8len[254]=1; // Invalid sequence start
// convert string to array (typed, when possible)
var string2buf = function (str) {
var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
// count binary size
for (m_pos = 0; m_pos < str_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
c2 = str.charCodeAt(m_pos+1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
}
// allocate buffer
if (support.uint8array) {
buf = new Uint8Array(buf_len);
} else {
buf = new Array(buf_len);
}
// convert
for (i=0, m_pos = 0; i < buf_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
c2 = str.charCodeAt(m_pos+1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
if (c < 0x80) {
/* one byte */
buf[i++] = c;
} else if (c < 0x800) {
/* two bytes */
buf[i++] = 0xC0 | (c >>> 6);
buf[i++] = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
/* three bytes */
buf[i++] = 0xE0 | (c >>> 12);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
} else {
/* four bytes */
buf[i++] = 0xf0 | (c >>> 18);
buf[i++] = 0x80 | (c >>> 12 & 0x3f);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
}
}
return buf;
};
// Calculate max possible position in utf8 buffer,
// that will not break sequence. If that's not possible
// - (very small limits) return max size as is.
//
// buf[] - utf8 bytes array
// max - length limit (mandatory);
var utf8border = function(buf, max) {
var pos;
max = max || buf.length;
if (max > buf.length) { max = buf.length; }
// go back from last position, until start of sequence found
pos = max-1;
while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
// Fuckup - very small and broken sequence,
// return max, because we should return something anyway.
if (pos < 0) { return max; }
// If we came to start of buffer - that means vuffer is too small,
// return max too.
if (pos === 0) { return max; }
return (pos + _utf8len[buf[pos]] > max) ? pos : max;
};
// convert array to string
var buf2string = function (buf) {
var i, out, c, c_len;
var len = buf.length;
// Reserve max possible length (2 words per char)
// NB: by unknown reasons, Array is significantly faster for
// String.fromCharCode.apply than Uint16Array.
var utf16buf = new Array(len*2);
for (out=0, i=0; i<len;) {
c = buf[i++];
// quick process ascii
if (c < 0x80) { utf16buf[out++] = c; continue; }
c_len = _utf8len[c];
// skip 5 & 6 byte codes
if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; }
// apply mask on first byte
c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
// join the rest
while (c_len > 1 && i < len) {
c = (c << 6) | (buf[i++] & 0x3f);
c_len--;
}
// terminated by end of string?
if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
if (c < 0x10000) {
utf16buf[out++] = c;
} else {
c -= 0x10000;
utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
utf16buf[out++] = 0xdc00 | (c & 0x3ff);
}
}
// shrinkBuf(utf16buf, out)
if (utf16buf.length !== out) {
if(utf16buf.subarray) {
utf16buf = utf16buf.subarray(0, out);
} else {
utf16buf.length = out;
}
}
// return String.fromCharCode.apply(null, utf16buf);
return utils.applyFromCharCode(utf16buf);
};
// That's all for the pako functions.
/**
* Transform a javascript string into an array (typed if possible) of bytes,
* UTF-8 encoded.
* @param {String} str the string to encode
* @return {Array|Uint8Array|Buffer} the UTF-8 encoded string.
*/
exports.utf8encode = function utf8encode(str) {
if (support.nodebuffer) {
return nodejsUtils.newBufferFrom(str, "utf-8");
}
return string2buf(str);
};
/**
* Transform a bytes array (or a representation) representing an UTF-8 encoded
* string into a javascript string.
* @param {Array|Uint8Array|Buffer} buf the data de decode
* @return {String} the decoded string.
*/
exports.utf8decode = function utf8decode(buf) {
if (support.nodebuffer) {
return utils.transformTo("nodebuffer", buf).toString("utf-8");
}
buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf);
return buf2string(buf);
};
/**
* A worker to decode utf8 encoded binary chunks into string chunks.
* @constructor
*/
function Utf8DecodeWorker() {
GenericWorker.call(this, "utf-8 decode");
// the last bytes if a chunk didn't end with a complete codepoint.
this.leftOver = null;
}
utils.inherits(Utf8DecodeWorker, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
Utf8DecodeWorker.prototype.processChunk = function (chunk) {
var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data);
// 1st step, re-use what's left of the previous chunk
if (this.leftOver && this.leftOver.length) {
if(support.uint8array) {
var previousData = data;
data = new Uint8Array(previousData.length + this.leftOver.length);
data.set(this.leftOver, 0);
data.set(previousData, this.leftOver.length);
} else {
data = this.leftOver.concat(data);
}
this.leftOver = null;
}
var nextBoundary = utf8border(data);
var usableData = data;
if (nextBoundary !== data.length) {
if (support.uint8array) {
usableData = data.subarray(0, nextBoundary);
this.leftOver = data.subarray(nextBoundary, data.length);
} else {
usableData = data.slice(0, nextBoundary);
this.leftOver = data.slice(nextBoundary, data.length);
}
}
this.push({
data : exports.utf8decode(usableData),
meta : chunk.meta
});
};
/**
* @see GenericWorker.flush
*/
Utf8DecodeWorker.prototype.flush = function () {
if(this.leftOver && this.leftOver.length) {
this.push({
data : exports.utf8decode(this.leftOver),
meta : {}
});
this.leftOver = null;
}
};
exports.Utf8DecodeWorker = Utf8DecodeWorker;
/**
* A worker to endcode string chunks into utf8 encoded binary chunks.
* @constructor
*/
function Utf8EncodeWorker() {
GenericWorker.call(this, "utf-8 encode");
}
utils.inherits(Utf8EncodeWorker, GenericWorker);
/**
* @see GenericWorker.processChunk
*/
Utf8EncodeWorker.prototype.processChunk = function (chunk) {
this.push({
data : exports.utf8encode(chunk.data),
meta : chunk.meta
});
};
exports.Utf8EncodeWorker = Utf8EncodeWorker;
},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){
"use strict";
var support = require("./support");
var base64 = require("./base64");
var nodejsUtils = require("./nodejsUtils");
var external = require("./external");
require("setimmediate");
/**
* Convert a string that pass as a "binary string": it should represent a byte
* array but may have > 255 char codes. Be sure to take only the first byte
* and returns the byte array.
* @param {String} str the string to transform.
* @return {Array|Uint8Array} the string in a binary format.
*/
function string2binary(str) {
var result = null;
if (support.uint8array) {
result = new Uint8Array(str.length);
} else {
result = new Array(str.length);
}
return stringToArrayLike(str, result);
}
/**
* Create a new blob with the given content and the given type.
* @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use
* an Uint8Array because the stock browser of android 4 won't accept it (it
* will be silently converted to a string, "[object Uint8Array]").
*
* Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge:
* when a large amount of Array is used to create the Blob, the amount of
* memory consumed is nearly 100 times the original data amount.
*
* @param {String} type the mime type of the blob.
* @return {Blob} the created blob.
*/
exports.newBlob = function(part, type) {
exports.checkSupport("blob");
try {
// Blob constructor
return new Blob([part], {
type: type
});
}
catch (e) {
try {
// deprecated, browser only, old way
var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
var builder = new Builder();
builder.append(part);
return builder.getBlob(type);
}
catch (e) {
// well, fuck ?!
throw new Error("Bug : can't construct the Blob.");
}
}
};
/**
* The identity function.
* @param {Object} input the input.
* @return {Object} the same input.
*/
function identity(input) {
return input;
}
/**
* Fill in an array with a string.
* @param {String} str the string to use.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
* @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
*/
function stringToArrayLike(str, array) {
for (var i = 0; i < str.length; ++i) {
array[i] = str.charCodeAt(i) & 0xFF;
}
return array;
}
/**
* An helper for the function arrayLikeToString.
* This contains static information and functions that
* can be optimized by the browser JIT compiler.
*/
var arrayToStringHelper = {
/**
* Transform an array of int into a string, chunk by chunk.
* See the performances notes on arrayLikeToString.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
* @param {String} type the type of the array.
* @param {Integer} chunk the chunk size.
* @return {String} the resulting string.
* @throws Error if the chunk is too big for the stack.
*/
stringifyByChunk: function(array, type, chunk) {
var result = [], k = 0, len = array.length;
// shortcut
if (len <= chunk) {
return String.fromCharCode.apply(null, array);
}
while (k < len) {
if (type === "array" || type === "nodebuffer") {
result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
}
else {
result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
}
k += chunk;
}
return result.join("");
},
/**
* Call String.fromCharCode on every item in the array.
* This is the naive implementation, which generate A LOT of intermediate string.
* This should be used when everything else fail.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
* @return {String} the result.
*/
stringifyByChar: function(array){
var resultStr = "";
for(var i = 0; i < array.length; i++) {
resultStr += String.fromCharCode(array[i]);
}
return resultStr;
},
applyCanBeUsed : {
/**
* true if the browser accepts to use String.fromCharCode on Uint8Array
*/
uint8array : (function () {
try {
return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1;
} catch (e) {
return false;
}
})(),
/**
* true if the browser accepts to use String.fromCharCode on nodejs Buffer.
*/
nodebuffer : (function () {
try {
return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1;
} catch (e) {
return false;
}
})()
}
};
/**
* Transform an array-like object to a string.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
* @return {String} the result.
*/
function arrayLikeToString(array) {
// Performances notes :
// --------------------
// String.fromCharCode.apply(null, array) is the fastest, see
// see http://jsperf.com/converting-a-uint8array-to-a-string/2
// but the stack is limited (and we can get huge arrays !).
//
// result += String.fromCharCode(array[i]); generate too many strings !
//
// This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
// TODO : we now have workers that split the work. Do we still need that ?
var chunk = 65536,
type = exports.getTypeOf(array),
canUseApply = true;
if (type === "uint8array") {
canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array;
} else if (type === "nodebuffer") {
canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer;
}
if (canUseApply) {
while (chunk > 1) {
try {
return arrayToStringHelper.stringifyByChunk(array, type, chunk);
} catch (e) {
chunk = Math.floor(chunk / 2);
}
}
}
// no apply or chunk error : slow and painful algorithm
// default browser on android 4.*
return arrayToStringHelper.stringifyByChar(array);
}
exports.applyFromCharCode = arrayLikeToString;
/**
* Copy the data from an array-like to an other array-like.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
* @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
* @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
*/
function arrayLikeToArrayLike(arrayFrom, arrayTo) {
for (var i = 0; i < arrayFrom.length; i++) {
arrayTo[i] = arrayFrom[i];
}
return arrayTo;
}
// a matrix containing functions to transform everything into everything.
var transform = {};
// string to ?
transform["string"] = {
"string": identity,
"array": function(input) {
return stringToArrayLike(input, new Array(input.length));
},
"arraybuffer": function(input) {
return transform["string"]["uint8array"](input).buffer;
},
"uint8array": function(input) {
return stringToArrayLike(input, new Uint8Array(input.length));
},
"nodebuffer": function(input) {
return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length));
}
};
// array to ?
transform["array"] = {
"string": arrayLikeToString,
"array": identity,
"arraybuffer": function(input) {
return (new Uint8Array(input)).buffer;
},
"uint8array": function(input) {
return new Uint8Array(input);
},
"nodebuffer": function(input) {
return nodejsUtils.newBufferFrom(input);
}
};
// arraybuffer to ?
transform["arraybuffer"] = {
"string": function(input) {
return arrayLikeToString(new Uint8Array(input));
},
"array": function(input) {
return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
},
"arraybuffer": identity,
"uint8array": function(input) {
return new Uint8Array(input);
},
"nodebuffer": function(input) {
return nodejsUtils.newBufferFrom(new Uint8Array(input));
}
};
// uint8array to ?
transform["uint8array"] = {
"string": arrayLikeToString,
"array": function(input) {
return arrayLikeToArrayLike(input, new Array(input.length));
},
"arraybuffer": function(input) {
return input.buffer;
},
"uint8array": identity,
"nodebuffer": function(input) {
return nodejsUtils.newBufferFrom(input);
}
};
// nodebuffer to ?
transform["nodebuffer"] = {
"string": arrayLikeToString,
"array": function(input) {
return arrayLikeToArrayLike(input, new Array(input.length));
},
"arraybuffer": function(input) {
return transform["nodebuffer"]["uint8array"](input).buffer;
},
"uint8array": function(input) {
return arrayLikeToArrayLike(input, new Uint8Array(input.length));
},
"nodebuffer": identity
};
/**
* Transform an input into any type.
* The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
* If no output type is specified, the unmodified input will be returned.
* @param {String} outputType the output type.
* @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
* @throws {Error} an Error if the browser doesn't support the requested output type.
*/
exports.transformTo = function(outputType, input) {
if (!input) {
// undefined, null, etc
// an empty string won't harm.
input = "";
}
if (!outputType) {
return input;
}
exports.checkSupport(outputType);
var inputType = exports.getTypeOf(input);
var result = transform[inputType][outputType](input);
return result;
};
/**
* Resolve all relative path components, "." and "..", in a path. If these relative components
* traverse above the root then the resulting path will only contain the final path component.
*
* All empty components, e.g. "//", are removed.
* @param {string} path A path with / or \ separators
* @returns {string} The path with all relative path components resolved.
*/
exports.resolve = function(path) {
var parts = path.split("/");
var result = [];
for (var index = 0; index < parts.length; index++) {
var part = parts[index];
// Allow the first and last component to be empty for trailing slashes.
if (part === "." || (part === "" && index !== 0 && index !== parts.length - 1)) {
continue;
} else if (part === "..") {
result.pop();
} else {
result.push(part);
}
}
return result.join("/");
};
/**
* Return the type of the input.
* The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
* @param {Object} input the input to identify.
* @return {String} the (lowercase) type of the input.
*/
exports.getTypeOf = function(input) {
if (typeof input === "string") {
return "string";
}
if (Object.prototype.toString.call(input) === "[object Array]") {
return "array";
}
if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
return "nodebuffer";
}
if (support.uint8array && input instanceof Uint8Array) {
return "uint8array";
}
if (support.arraybuffer && input instanceof ArrayBuffer) {
return "arraybuffer";
}
};
/**
* Throw an exception if the type is not supported.
* @param {String} type the type to check.
* @throws {Error} an Error if the browser doesn't support the requested type.
*/
exports.checkSupport = function(type) {
var supported = support[type.toLowerCase()];
if (!supported) {
throw new Error(type + " is not supported by this platform");
}
};
exports.MAX_VALUE_16BITS = 65535;
exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1
/**
* Prettify a string read as binary.
* @param {string} str the string to prettify.
* @return {string} a pretty string.
*/
exports.pretty = function(str) {
var res = "",
code, i;
for (i = 0; i < (str || "").length; i++) {
code = str.charCodeAt(i);
res += "\\x" + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
}
return res;
};
/**
* Defer the call of a function.
* @param {Function} callback the function to call asynchronously.
* @param {Array} args the arguments to give to the callback.
*/
exports.delay = function(callback, args, self) {
setImmediate(function () {
callback.apply(self || null, args || []);
});
};
/**
* Extends a prototype with an other, without calling a constructor with
* side effects. Inspired by nodejs' `utils.inherits`
* @param {Function} ctor the constructor to augment
* @param {Function} superCtor the parent constructor to use
*/
exports.inherits = function (ctor, superCtor) {
var Obj = function() {};
Obj.prototype = superCtor.prototype;
ctor.prototype = new Obj();
};
/**
* Merge the objects passed as parameters into a new one.
* @private
* @param {...Object} var_args All objects to merge.
* @return {Object} a new object with the data of the others.
*/
exports.extend = function() {
var result = {}, i, attr;
for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
for (attr in arguments[i]) {
if (Object.prototype.hasOwnProperty.call(arguments[i], attr) && typeof result[attr] === "undefined") {
result[attr] = arguments[i][attr];
}
}
}
return result;
};
/**
* Transform arbitrary content into a Promise.
* @param {String} name a name for the content being processed.
* @param {Object} inputData the content to process.
* @param {Boolean} isBinary true if the content is not an unicode string
* @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character.
* @param {Boolean} isBase64 true if the string content is encoded with base64.
* @return {Promise} a promise in a format usable by JSZip.
*/
exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) {
// if inputData is already a promise, this flatten it.
var promise = external.Promise.resolve(inputData).then(function(data) {
var isBlob = support.blob && (data instanceof Blob || ["[object File]", "[object Blob]"].indexOf(Object.prototype.toString.call(data)) !== -1);
if (isBlob && typeof FileReader !== "undefined") {
return new external.Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function(e) {
resolve(e.target.result);
};
reader.onerror = function(e) {
reject(e.target.error);
};
reader.readAsArrayBuffer(data);
});
} else {
return data;
}
});
return promise.then(function(data) {
var dataType = exports.getTypeOf(data);
if (!dataType) {
return external.Promise.reject(
new Error("Can't read the data of '" + name + "'. Is it " +
"in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?")
);
}
// special case : it's way easier to work with Uint8Array than with ArrayBuffer
if (dataType === "arraybuffer") {
data = exports.transformTo("uint8array", data);
} else if (dataType === "string") {
if (isBase64) {
data = base64.decode(data);
}
else if (isBinary) {
// optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask
if (isOptimizedBinaryString !== true) {
// this is a string, not in a base64 format.
// Be sure that this is a correct "binary string"
data = string2binary(data);
}
}
}
return data;
});
};
},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"setimmediate":54}],33:[function(require,module,exports){
"use strict";
var readerFor = require("./reader/readerFor");
var utils = require("./utils");
var sig = require("./signature");
var ZipEntry = require("./zipEntry");
var support = require("./support");
// class ZipEntries {{{
/**
* All the entries in the zip file.
* @constructor
* @param {Object} loadOptions Options for loading the stream.
*/
function ZipEntries(loadOptions) {
this.files = [];
this.loadOptions = loadOptions;
}
ZipEntries.prototype = {
/**
* Check that the reader is on the specified signature.
* @param {string} expectedSignature the expected signature.
* @throws {Error} if it is an other signature.
*/
checkSignature: function(expectedSignature) {
if (!this.reader.readAndCheckSignature(expectedSignature)) {
this.reader.index -= 4;
var signature = this.reader.readString(4);
throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")");
}
},
/**
* Check if the given signature is at the given index.
* @param {number} askedIndex the index to check.
* @param {string} expectedSignature the signature to expect.
* @return {boolean} true if the signature is here, false otherwise.
*/
isSignature: function(askedIndex, expectedSignature) {
var currentIndex = this.reader.index;
this.reader.setIndex(askedIndex);
var signature = this.reader.readString(4);
var result = signature === expectedSignature;
this.reader.setIndex(currentIndex);
return result;
},
/**
* Read the end of the central directory.
*/
readBlockEndOfCentral: function() {
this.diskNumber = this.reader.readInt(2);
this.diskWithCentralDirStart = this.reader.readInt(2);
this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
this.centralDirRecords = this.reader.readInt(2);
this.centralDirSize = this.reader.readInt(4);
this.centralDirOffset = this.reader.readInt(4);
this.zipCommentLength = this.reader.readInt(2);
// warning : the encoding depends of the system locale
// On a linux machine with LANG=en_US.utf8, this field is utf8 encoded.
// On a windows machine, this field is encoded with the localized windows code page.
var zipComment = this.reader.readData(this.zipCommentLength);
var decodeParamType = support.uint8array ? "uint8array" : "array";
// To get consistent behavior with the generation part, we will assume that
// this is utf8 encoded unless specified otherwise.
var decodeContent = utils.transformTo(decodeParamType, zipComment);
this.zipComment = this.loadOptions.decodeFileName(decodeContent);
},
/**
* Read the end of the Zip 64 central directory.
* Not merged with the method readEndOfCentral :
* The end of central can coexist with its Zip64 brother,
* I don't want to read the wrong number of bytes !
*/
readBlockZip64EndOfCentral: function() {
this.zip64EndOfCentralSize = this.reader.readInt(8);
this.reader.skip(4);
// this.versionMadeBy = this.reader.readString(2);
// this.versionNeeded = this.reader.readInt(2);
this.diskNumber = this.reader.readInt(4);
this.diskWithCentralDirStart = this.reader.readInt(4);
this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
this.centralDirRecords = this.reader.readInt(8);
this.centralDirSize = this.reader.readInt(8);
this.centralDirOffset = this.reader.readInt(8);
this.zip64ExtensibleData = {};
var extraDataSize = this.zip64EndOfCentralSize - 44,
index = 0,
extraFieldId,
extraFieldLength,
extraFieldValue;
while (index < extraDataSize) {
extraFieldId = this.reader.readInt(2);
extraFieldLength = this.reader.readInt(4);
extraFieldValue = this.reader.readData(extraFieldLength);
this.zip64ExtensibleData[extraFieldId] = {
id: extraFieldId,
length: extraFieldLength,
value: extraFieldValue
};
}
},
/**
* Read the end of the Zip 64 central directory locator.
*/
readBlockZip64EndOfCentralLocator: function() {
this.diskWithZip64CentralDirStart = this.reader.readInt(4);
this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
this.disksCount = this.reader.readInt(4);
if (this.disksCount > 1) {
throw new Error("Multi-volumes zip are not supported");
}
},
/**
* Read the local files, based on the offset read in the central part.
*/
readLocalFiles: function() {
var i, file;
for (i = 0; i < this.files.length; i++) {
file = this.files[i];
this.reader.setIndex(file.localHeaderOffset);
this.checkSignature(sig.LOCAL_FILE_HEADER);
file.readLocalPart(this.reader);
file.handleUTF8();
file.processAttributes();
}
},
/**
* Read the central directory.
*/
readCentralDir: function() {
var file;
this.reader.setIndex(this.centralDirOffset);
while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) {
file = new ZipEntry({
zip64: this.zip64
}, this.loadOptions);
file.readCentralPart(this.reader);
this.files.push(file);
}
if (this.centralDirRecords !== this.files.length) {
if (this.centralDirRecords !== 0 && this.files.length === 0) {
// We expected some records but couldn't find ANY.
// This is really suspicious, as if something went wrong.
throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
} else {
// We found some records but not all.
// Something is wrong but we got something for the user: no error here.
// console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length);
}
}
},
/**
* Read the end of central directory.
*/
readEndOfCentral: function() {
var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END);
if (offset < 0) {
// Check if the content is a truncated zip or complete garbage.
// A "LOCAL_FILE_HEADER" is not required at the beginning (auto
// extractible zip for example) but it can give a good hint.
// If an ajax request was used without responseType, we will also
// get unreadable data.
var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER);
if (isGarbage) {
throw new Error("Can't find end of central directory : is this a zip file ? " +
"If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html");
} else {
throw new Error("Corrupted zip: can't find end of central directory");
}
}
this.reader.setIndex(offset);
var endOfCentralDirOffset = offset;
this.checkSignature(sig.CENTRAL_DIRECTORY_END);
this.readBlockEndOfCentral();
/* extract from the zip spec :
4) If one of the fields in the end of central directory
record is too small to hold required data, the field
should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
ZIP64 format record should be created.
5) The end of central directory record and the
Zip64 end of central directory locator record must
reside on the same disk when splitting or spanning
an archive.
*/
if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) {
this.zip64 = true;
/*
Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents
all numbers as 64-bit double precision IEEE 754 floating point numbers.
So, we have 53bits for integers and bitwise operations treat everything as 32bits.
see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
*/
// should look for a zip64 EOCD locator
offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
if (offset < 0) {
throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator");
}
this.reader.setIndex(offset);
this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
this.readBlockZip64EndOfCentralLocator();
// now the zip64 EOCD record
if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) {
// console.warn("ZIP64 end of central directory not where expected.");
this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
if (this.relativeOffsetEndOfZip64CentralDir < 0) {
throw new Error("Corrupted zip: can't find the ZIP64 end of central directory");
}
}
this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
this.readBlockZip64EndOfCentral();
}
var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize;
if (this.zip64) {
expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator
expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize;
}
var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset;
if (extraBytes > 0) {
// console.warn(extraBytes, "extra bytes at beginning or within zipfile");
if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) {
// The offsets seem wrong, but we have something at the specified offset.
// So… we keep it.
} else {
// the offset is wrong, update the "zero" of the reader
// this happens if data has been prepended (crx files for example)
this.reader.zero = extraBytes;
}
} else if (extraBytes < 0) {
throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes.");
}
},
prepareReader: function(data) {
this.reader = readerFor(data);
},
/**
* Read a zip file and create ZipEntries.
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file.
*/
load: function(data) {
this.prepareReader(data);
this.readEndOfCentral();
this.readCentralDir();
this.readLocalFiles();
}
};
// }}} end of ZipEntries
module.exports = ZipEntries;
},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){
"use strict";
var readerFor = require("./reader/readerFor");
var utils = require("./utils");
var CompressedObject = require("./compressedObject");
var crc32fn = require("./crc32");
var utf8 = require("./utf8");
var compressions = require("./compressions");
var support = require("./support");
var MADE_BY_DOS = 0x00;
var MADE_BY_UNIX = 0x03;
/**
* Find a compression registered in JSZip.
* @param {string} compressionMethod the method magic to find.
* @return {Object|null} the JSZip compression object, null if none found.
*/
var findCompression = function(compressionMethod) {
for (var method in compressions) {
if (!Object.prototype.hasOwnProperty.call(compressions, method)) {
continue;
}
if (compressions[method].magic === compressionMethod) {
return compressions[method];
}
}
return null;
};
// class ZipEntry {{{
/**
* An entry in the zip file.
* @constructor
* @param {Object} options Options of the current file.
* @param {Object} loadOptions Options for loading the stream.
*/
function ZipEntry(options, loadOptions) {
this.options = options;
this.loadOptions = loadOptions;
}
ZipEntry.prototype = {
/**
* say if the file is encrypted.
* @return {boolean} true if the file is encrypted, false otherwise.
*/
isEncrypted: function() {
// bit 1 is set
return (this.bitFlag & 0x0001) === 0x0001;
},
/**
* say if the file has utf-8 filename/comment.
* @return {boolean} true if the filename/comment is in utf-8, false otherwise.
*/
useUTF8: function() {
// bit 11 is set
return (this.bitFlag & 0x0800) === 0x0800;
},
/**
* Read the local part of a zip file and add the info in this object.
* @param {DataReader} reader the reader to use.
*/
readLocalPart: function(reader) {
var compression, localExtraFieldsLength;
// we already know everything from the central dir !
// If the central dir data are false, we are doomed.
// On the bright side, the local part is scary : zip64, data descriptors, both, etc.
// The less data we get here, the more reliable this should be.
// Let's skip the whole header and dash to the data !
reader.skip(22);
// in some zip created on windows, the filename stored in the central dir contains \ instead of /.
// Strangely, the filename here is OK.
// I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
// or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
// Search "unzip mismatching "local" filename continuing with "central" filename version" on
// the internet.
//
// I think I see the logic here : the central directory is used to display
// content and the local directory is used to extract the files. Mixing / and \
// may be used to display \ to windows users and use / when extracting the files.
// Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394
this.fileNameLength = reader.readInt(2);
localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
// the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.
this.fileName = reader.readData(this.fileNameLength);
reader.skip(localExtraFieldsLength);
if (this.compressedSize === -1 || this.uncompressedSize === -1) {
throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");
}
compression = findCompression(this.compressionMethod);
if (compression === null) { // no compression found
throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");
}
this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));
},
/**
* Read the central part of a zip file and add the info in this object.
* @param {DataReader} reader the reader to use.
*/
readCentralPart: function(reader) {
this.versionMadeBy = reader.readInt(2);
reader.skip(2);
// this.versionNeeded = reader.readInt(2);
this.bitFlag = reader.readInt(2);
this.compressionMethod = reader.readString(2);
this.date = reader.readDate();
this.crc32 = reader.readInt(4);
this.compressedSize = reader.readInt(4);
this.uncompressedSize = reader.readInt(4);
var fileNameLength = reader.readInt(2);
this.extraFieldsLength = reader.readInt(2);
this.fileCommentLength = reader.readInt(2);
this.diskNumberStart = reader.readInt(2);
this.internalFileAttributes = reader.readInt(2);
this.externalFileAttributes = reader.readInt(4);
this.localHeaderOffset = reader.readInt(4);
if (this.isEncrypted()) {
throw new Error("Encrypted zip are not supported");
}
// will be read in the local part, see the comments there
reader.skip(fileNameLength);
this.readExtraFields(reader);
this.parseZIP64ExtraField(reader);
this.fileComment = reader.readData(this.fileCommentLength);
},
/**
* Parse the external file attributes and get the unix/dos permissions.
*/
processAttributes: function () {
this.unixPermissions = null;
this.dosPermissions = null;
var madeBy = this.versionMadeBy >> 8;
// Check if we have the DOS directory flag set.
// We look for it in the DOS and UNIX permissions
// but some unknown platform could set it as a compatibility flag.
this.dir = this.externalFileAttributes & 0x0010 ? true : false;
if(madeBy === MADE_BY_DOS) {
// first 6 bits (0 to 5)
this.dosPermissions = this.externalFileAttributes & 0x3F;
}
if(madeBy === MADE_BY_UNIX) {
this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF;
// the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);
}
// fail safe : if the name ends with a / it probably means a folder
if (!this.dir && this.fileNameStr.slice(-1) === "/") {
this.dir = true;
}
},
/**
* Parse the ZIP64 extra field and merge the info in the current ZipEntry.
* @param {DataReader} reader the reader to use.
*/
parseZIP64ExtraField: function() {
if (!this.extraFields[0x0001]) {
return;
}
// should be something, preparing the extra reader
var extraReader = readerFor(this.extraFields[0x0001].value);
// I really hope that these 64bits integer can fit in 32 bits integer, because js
// won't let us have more.
if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {
this.uncompressedSize = extraReader.readInt(8);
}
if (this.compressedSize === utils.MAX_VALUE_32BITS) {
this.compressedSize = extraReader.readInt(8);
}
if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {
this.localHeaderOffset = extraReader.readInt(8);
}
if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {
this.diskNumberStart = extraReader.readInt(4);
}
},
/**
* Read the central part of a zip file and add the info in this object.
* @param {DataReader} reader the reader to use.
*/
readExtraFields: function(reader) {
var end = reader.index + this.extraFieldsLength,
extraFieldId,
extraFieldLength,
extraFieldValue;
if (!this.extraFields) {
this.extraFields = {};
}
while (reader.index + 4 < end) {
extraFieldId = reader.readInt(2);
extraFieldLength = reader.readInt(2);
extraFieldValue = reader.readData(extraFieldLength);
this.extraFields[extraFieldId] = {
id: extraFieldId,
length: extraFieldLength,
value: extraFieldValue
};
}
reader.setIndex(end);
},
/**
* Apply an UTF8 transformation if needed.
*/
handleUTF8: function() {
var decodeParamType = support.uint8array ? "uint8array" : "array";
if (this.useUTF8()) {
this.fileNameStr = utf8.utf8decode(this.fileName);
this.fileCommentStr = utf8.utf8decode(this.fileComment);
} else {
var upath = this.findExtraFieldUnicodePath();
if (upath !== null) {
this.fileNameStr = upath;
} else {
// ASCII text or unsupported code page
var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);
this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);
}
var ucomment = this.findExtraFieldUnicodeComment();
if (ucomment !== null) {
this.fileCommentStr = ucomment;
} else {
// ASCII text or unsupported code page
var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);
this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);
}
}
},
/**
* Find the unicode path declared in the extra field, if any.
* @return {String} the unicode path, null otherwise.
*/
findExtraFieldUnicodePath: function() {
var upathField = this.extraFields[0x7075];
if (upathField) {
var extraReader = readerFor(upathField.value);
// wrong version
if (extraReader.readInt(1) !== 1) {
return null;
}
// the crc of the filename changed, this field is out of date.
if (crc32fn(this.fileName) !== extraReader.readInt(4)) {
return null;
}
return utf8.utf8decode(extraReader.readData(upathField.length - 5));
}
return null;
},
/**
* Find the unicode comment declared in the extra field, if any.
* @return {String} the unicode comment, null otherwise.
*/
findExtraFieldUnicodeComment: function() {
var ucommentField = this.extraFields[0x6375];
if (ucommentField) {
var extraReader = readerFor(ucommentField.value);
// wrong version
if (extraReader.readInt(1) !== 1) {
return null;
}
// the crc of the comment changed, this field is out of date.
if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {
return null;
}
return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));
}
return null;
}
};
module.exports = ZipEntry;
},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){
"use strict";
var StreamHelper = require("./stream/StreamHelper");
var DataWorker = require("./stream/DataWorker");
var utf8 = require("./utf8");
var CompressedObject = require("./compressedObject");
var GenericWorker = require("./stream/GenericWorker");
/**
* A simple object representing a file in the zip file.
* @constructor
* @param {string} name the name of the file
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
* @param {Object} options the options of the file
*/
var ZipObject = function(name, data, options) {
this.name = name;
this.dir = options.dir;
this.date = options.date;
this.comment = options.comment;
this.unixPermissions = options.unixPermissions;
this.dosPermissions = options.dosPermissions;
this._data = data;
this._dataBinary = options.binary;
// keep only the compression
this.options = {
compression : options.compression,
compressionOptions : options.compressionOptions
};
};
ZipObject.prototype = {
/**
* Create an internal stream for the content of this object.
* @param {String} type the type of each chunk.
* @return StreamHelper the stream.
*/
internalStream: function (type) {
var result = null, outputType = "string";
try {
if (!type) {
throw new Error("No output type specified.");
}
outputType = type.toLowerCase();
var askUnicodeString = outputType === "string" || outputType === "text";
if (outputType === "binarystring" || outputType === "text") {
outputType = "string";
}
result = this._decompressWorker();
var isUnicodeString = !this._dataBinary;
if (isUnicodeString && !askUnicodeString) {
result = result.pipe(new utf8.Utf8EncodeWorker());
}
if (!isUnicodeString && askUnicodeString) {
result = result.pipe(new utf8.Utf8DecodeWorker());
}
} catch (e) {
result = new GenericWorker("error");
result.error(e);
}
return new StreamHelper(result, outputType, "");
},
/**
* Prepare the content in the asked type.
* @param {String} type the type of the result.
* @param {Function} onUpdate a function to call on each internal update.
* @return Promise the promise of the result.
*/
async: function (type, onUpdate) {
return this.internalStream(type).accumulate(onUpdate);
},
/**
* Prepare the content as a nodejs stream.
* @param {String} type the type of each chunk.
* @param {Function} onUpdate a function to call on each internal update.
* @return Stream the stream.
*/
nodeStream: function (type, onUpdate) {
return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate);
},
/**
* Return a worker for the compressed content.
* @private
* @param {Object} compression the compression object to use.
* @param {Object} compressionOptions the options to use when compressing.
* @return Worker the worker.
*/
_compressWorker: function (compression, compressionOptions) {
if (
this._data instanceof CompressedObject &&
this._data.compression.magic === compression.magic
) {
return this._data.getCompressedWorker();
} else {
var result = this._decompressWorker();
if(!this._dataBinary) {
result = result.pipe(new utf8.Utf8EncodeWorker());
}
return CompressedObject.createWorkerFrom(result, compression, compressionOptions);
}
},
/**
* Return a worker for the decompressed content.
* @private
* @return Worker the worker.
*/
_decompressWorker : function () {
if (this._data instanceof CompressedObject) {
return this._data.getContentWorker();
} else if (this._data instanceof GenericWorker) {
return this._data;
} else {
return new DataWorker(this._data);
}
}
};
var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"];
var removedFn = function () {
throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
};
for(var i = 0; i < removedMethods.length; i++) {
ZipObject.prototype[removedMethods[i]] = removedFn;
}
module.exports = ZipObject;
},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){
(function (global){
'use strict';
var Mutation = global.MutationObserver || global.WebKitMutationObserver;
var scheduleDrain;
{
if (Mutation) {
var called = 0;
var observer = new Mutation(nextTick);
var element = global.document.createTextNode('');
observer.observe(element, {
characterData: true
});
scheduleDrain = function () {
element.data = (called = ++called % 2);
};
} else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
var channel = new global.MessageChannel();
channel.port1.onmessage = nextTick;
scheduleDrain = function () {
channel.port2.postMessage(0);
};
} else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
scheduleDrain = function () {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = global.document.createElement('script');
scriptEl.onreadystatechange = function () {
nextTick();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
};
} else {
scheduleDrain = function () {
setTimeout(nextTick, 0);
};
}
}
var draining;
var queue = [];
//named nextTick for less confusing stack traces
function nextTick() {
draining = true;
var i, oldQueue;
var len = queue.length;
while (len) {
oldQueue = queue;
queue = [];
i = -1;
while (++i < len) {
oldQueue[i]();
}
len = queue.length;
}
draining = false;
}
module.exports = immediate;
function immediate(task) {
if (queue.push(task) === 1 && !draining) {
scheduleDrain();
}
}
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],37:[function(require,module,exports){
'use strict';
var immediate = require('immediate');
/* istanbul ignore next */
function INTERNAL() {}
var handlers = {};
var REJECTED = ['REJECTED'];
var FULFILLED = ['FULFILLED'];
var PENDING = ['PENDING'];
module.exports = Promise;
function Promise(resolver) {
if (typeof resolver !== 'function') {
throw new TypeError('resolver must be a function');
}
this.state = PENDING;
this.queue = [];
this.outcome = void 0;
if (resolver !== INTERNAL) {
safelyResolveThenable(this, resolver);
}
}
Promise.prototype["finally"] = function (callback) {
if (typeof callback !== 'function') {
return this;
}
var p = this.constructor;
return this.then(resolve, reject);
function resolve(value) {
function yes () {
return value;
}
return p.resolve(callback()).then(yes);
}
function reject(reason) {
function no () {
throw reason;
}
return p.resolve(callback()).then(no);
}
};
Promise.prototype["catch"] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
typeof onRejected !== 'function' && this.state === REJECTED) {
return this;
}
var promise = new this.constructor(INTERNAL);
if (this.state !== PENDING) {
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
unwrap(promise, resolver, this.outcome);
} else {
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
}
return promise;
};
function QueueItem(promise, onFulfilled, onRejected) {
this.promise = promise;
if (typeof onFulfilled === 'function') {
this.onFulfilled = onFulfilled;
this.callFulfilled = this.otherCallFulfilled;
}
if (typeof onRejected === 'function') {
this.onRejected = onRejected;
this.callRejected = this.otherCallRejected;
}
}
QueueItem.prototype.callFulfilled = function (value) {
handlers.resolve(this.promise, value);
};
QueueItem.prototype.otherCallFulfilled = function (value) {
unwrap(this.promise, this.onFulfilled, value);
};
QueueItem.prototype.callRejected = function (value) {
handlers.reject(this.promise, value);
};
QueueItem.prototype.otherCallRejected = function (value) {
unwrap(this.promise, this.onRejected, value);
};
function unwrap(promise, func, value) {
immediate(function () {
var returnValue;
try {
returnValue = func(value);
} catch (e) {
return handlers.reject(promise, e);
}
if (returnValue === promise) {
handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
} else {
handlers.resolve(promise, returnValue);
}
});
}
handlers.resolve = function (self, value) {
var result = tryCatch(getThen, value);
if (result.status === 'error') {
return handlers.reject(self, result.value);
}
var thenable = result.value;
if (thenable) {
safelyResolveThenable(self, thenable);
} else {
self.state = FULFILLED;
self.outcome = value;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callFulfilled(value);
}
}
return self;
};
handlers.reject = function (self, error) {
self.state = REJECTED;
self.outcome = error;
var i = -1;
var len = self.queue.length;
while (++i < len) {
self.queue[i].callRejected(error);
}
return self;
};
function getThen(obj) {
// Make sure we only access the accessor once as required by the spec
var then = obj && obj.then;
if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
return function appyThen() {
then.apply(obj, arguments);
};
}
}
function safelyResolveThenable(self, thenable) {
// Either fulfill, reject or reject with error
var called = false;
function onError(value) {
if (called) {
return;
}
called = true;
handlers.reject(self, value);
}
function onSuccess(value) {
if (called) {
return;
}
called = true;
handlers.resolve(self, value);
}
function tryToUnwrap() {
thenable(onSuccess, onError);
}
var result = tryCatch(tryToUnwrap);
if (result.status === 'error') {
onError(result.value);
}
}
function tryCatch(func, value) {
var out = {};
try {
out.value = func(value);
out.status = 'success';
} catch (e) {
out.status = 'error';
out.value = e;
}
return out;
}
Promise.resolve = resolve;
function resolve(value) {
if (value instanceof this) {
return value;
}
return handlers.resolve(new this(INTERNAL), value);
}
Promise.reject = reject;
function reject(reason) {
var promise = new this(INTERNAL);
return handlers.reject(promise, reason);
}
Promise.all = all;
function all(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}
var len = iterable.length;
var called = false;
if (!len) {
return this.resolve([]);
}
var values = new Array(len);
var resolved = 0;
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) {
allResolver(iterable[i], i);
}
return promise;
function allResolver(value, i) {
self.resolve(value).then(resolveFromAll, function (error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
function resolveFromAll(outValue) {
values[i] = outValue;
if (++resolved === len && !called) {
called = true;
handlers.resolve(promise, values);
}
}
}
}
Promise.race = race;
function race(iterable) {
var self = this;
if (Object.prototype.toString.call(iterable) !== '[object Array]') {
return this.reject(new TypeError('must be an array'));
}
var len = iterable.length;
var called = false;
if (!len) {
return this.resolve([]);
}
var i = -1;
var promise = new this(INTERNAL);
while (++i < len) {
resolver(iterable[i]);
}
return promise;
function resolver(value) {
self.resolve(value).then(function (response) {
if (!called) {
called = true;
handlers.resolve(promise, response);
}
}, function (error) {
if (!called) {
called = true;
handlers.reject(promise, error);
}
});
}
}
},{"immediate":36}],38:[function(require,module,exports){
// Top level file is just a mixin of submodules & constants
'use strict';
var assign = require('./lib/utils/common').assign;
var deflate = require('./lib/deflate');
var inflate = require('./lib/inflate');
var constants = require('./lib/zlib/constants');
var pako = {};
assign(pako, deflate, inflate, constants);
module.exports = pako;
},{"./lib/deflate":39,"./lib/inflate":40,"./lib/utils/common":41,"./lib/zlib/constants":44}],39:[function(require,module,exports){
'use strict';
var zlib_deflate = require('./zlib/deflate');
var utils = require('./utils/common');
var strings = require('./utils/strings');
var msg = require('./zlib/messages');
var ZStream = require('./zlib/zstream');
var toString = Object.prototype.toString;
/* Public constants ==========================================================*/
/* ===========================================================================*/
var Z_NO_FLUSH = 0;
var Z_FINISH = 4;
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_SYNC_FLUSH = 2;
var Z_DEFAULT_COMPRESSION = -1;
var Z_DEFAULT_STRATEGY = 0;
var Z_DEFLATED = 8;
/* ===========================================================================*/
/**
* class Deflate
*
* Generic JS-style wrapper for zlib calls. If you don't need
* streaming behaviour - use more simple functions: [[deflate]],
* [[deflateRaw]] and [[gzip]].
**/
/* internal
* Deflate.chunks -> Array
*
* Chunks of output data, if [[Deflate#onData]] not overriden.
**/
/**
* Deflate.result -> Uint8Array|Array
*
* Compressed result, generated by default [[Deflate#onData]]
* and [[Deflate#onEnd]] handlers. Filled after you push last chunk
* (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you
* push a chunk with explicit flush (call [[Deflate#push]] with
* `Z_SYNC_FLUSH` param).
**/
/**
* Deflate.err -> Number
*
* Error code after deflate finished. 0 (Z_OK) on success.
* You will not need it in real life, because deflate errors
* are possible only on wrong options or bad `onData` / `onEnd`
* custom handlers.
**/
/**
* Deflate.msg -> String
*
* Error message, if [[Deflate.err]] != 0
**/
/**
* new Deflate(options)
* - options (Object): zlib deflate options.
*
* Creates new deflator instance with specified params. Throws exception
* on bad params. Supported options:
*
* - `level`
* - `windowBits`
* - `memLevel`
* - `strategy`
* - `dictionary`
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Additional options, for internal needs:
*
* - `chunkSize` - size of generated data chunks (16K by default)
* - `raw` (Boolean) - do raw deflate
* - `gzip` (Boolean) - create gzip wrapper
* - `to` (String) - if equal to 'string', then result will be "binary string"
* (each char code [0..255])
* - `header` (Object) - custom header for gzip
* - `text` (Boolean) - true if compressed data believed to be text
* - `time` (Number) - modification time, unix timestamp
* - `os` (Number) - operation system code
* - `extra` (Array) - array of bytes with extra data (max 65536)
* - `name` (String) - file name (binary string)
* - `comment` (String) - comment (binary string)
* - `hcrc` (Boolean) - true if header crc should be added
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
* , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
*
* var deflate = new pako.Deflate({ level: 3});
*
* deflate.push(chunk1, false);
* deflate.push(chunk2, true); // true -> last chunk
*
* if (deflate.err) { throw new Error(deflate.err); }
*
* console.log(deflate.result);
* ```
**/
function Deflate(options) {
if (!(this instanceof Deflate)) return new Deflate(options);
this.options = utils.assign({
level: Z_DEFAULT_COMPRESSION,
method: Z_DEFLATED,
chunkSize: 16384,
windowBits: 15,
memLevel: 8,
strategy: Z_DEFAULT_STRATEGY,
to: ''
}, options || {});
var opt = this.options;
if (opt.raw && (opt.windowBits > 0)) {
opt.windowBits = -opt.windowBits;
}
else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
opt.windowBits += 16;
}
this.err = 0; // error code, if happens (0 = Z_OK)
this.msg = ''; // error message
this.ended = false; // used to avoid multiple onEnd() calls
this.chunks = []; // chunks of compressed data
this.strm = new ZStream();
this.strm.avail_out = 0;
var status = zlib_deflate.deflateInit2(
this.strm,
opt.level,
opt.method,
opt.windowBits,
opt.memLevel,
opt.strategy
);
if (status !== Z_OK) {
throw new Error(msg[status]);
}
if (opt.header) {
zlib_deflate.deflateSetHeader(this.strm, opt.header);
}
if (opt.dictionary) {
var dict;
// Convert data if needed
if (typeof opt.dictionary === 'string') {
// If we need to compress text, change encoding to utf8.
dict = strings.string2buf(opt.dictionary);
} else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
dict = new Uint8Array(opt.dictionary);
} else {
dict = opt.dictionary;
}
status = zlib_deflate.deflateSetDictionary(this.strm, dict);
if (status !== Z_OK) {
throw new Error(msg[status]);
}
this._dict_set = true;
}
}
/**
* Deflate#push(data[, mode]) -> Boolean
* - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
* converted to utf8 byte sequence.
* - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
*
* Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
* new compressed chunks. Returns `true` on success. The last data block must have
* mode Z_FINISH (or `true`). That will flush internal pending buffers and call
* [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
* can use mode Z_SYNC_FLUSH, keeping the compression context.
*
* On fail call [[Deflate#onEnd]] with error code and return false.
*
* We strongly recommend to use `Uint8Array` on input for best speed (output
* array format is detected automatically). Also, don't skip last param and always
* use the same type in your code (boolean or number). That will improve JS speed.
*
* For regular `Array`-s make sure all elements are [0..255].
*
* ##### Example
*
* ```javascript
* push(chunk, false); // push one of data chunks
* ...
* push(chunk, true); // push last chunk
* ```
**/
Deflate.prototype.push = function (data, mode) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var status, _mode;
if (this.ended) { return false; }
_mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);
// Convert data if needed
if (typeof data === 'string') {
// If we need to compress text, change encoding to utf8.
strm.input = strings.string2buf(data);
} else if (toString.call(data) === '[object ArrayBuffer]') {
strm.input = new Uint8Array(data);
} else {
strm.input = data;
}
strm.next_in = 0;
strm.avail_in = strm.input.length;
do {
if (strm.avail_out === 0) {
strm.output = new utils.Buf8(chunkSize);
strm.next_out = 0;
strm.avail_out = chunkSize;
}
status = zlib_deflate.deflate(strm, _mode); /* no bad return value */
if (status !== Z_STREAM_END && status !== Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
if (this.options.to === 'string') {
this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
} else {
this.onData(utils.shrinkBuf(strm.output, strm.next_out));
}
}
} while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);
// Finalize on the last chunk.
if (_mode === Z_FINISH) {
status = zlib_deflate.deflateEnd(this.strm);
this.onEnd(status);
this.ended = true;
return status === Z_OK;
}
// callback interim results if Z_SYNC_FLUSH.
if (_mode === Z_SYNC_FLUSH) {
this.onEnd(Z_OK);
strm.avail_out = 0;
return true;
}
return true;
};
/**
* Deflate#onData(chunk) -> Void
* - chunk (Uint8Array|Array|String): ouput data. Type of array depends
* on js engine support. When string output requested, each chunk
* will be string.
*
* By default, stores data blocks in `chunks[]` property and glue
* those in `onEnd`. Override this handler, if you need another behaviour.
**/
Deflate.prototype.onData = function (chunk) {
this.chunks.push(chunk);
};
/**
* Deflate#onEnd(status) -> Void
* - status (Number): deflate status. 0 (Z_OK) on success,
* other if not.
*
* Called once after you tell deflate that the input stream is
* complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
* or if an error happened. By default - join collected chunks,
* free memory and fill `results` / `err` properties.
**/
Deflate.prototype.onEnd = function (status) {
// On success - join
if (status === Z_OK) {
if (this.options.to === 'string') {
this.result = this.chunks.join('');
} else {
this.result = utils.flattenChunks(this.chunks);
}
}
this.chunks = [];
this.err = status;
this.msg = this.strm.msg;
};
/**
* deflate(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* Compress `data` with deflate algorithm and `options`.
*
* Supported options are:
*
* - level
* - windowBits
* - memLevel
* - strategy
* - dictionary
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Sugar (options):
*
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
* negative windowBits implicitly.
* - `to` (String) - if equal to 'string', then result will be "binary string"
* (each char code [0..255])
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
*
* console.log(pako.deflate(data));
* ```
**/
function deflate(input, options) {
var deflator = new Deflate(options);
deflator.push(input, true);
// That will never happens, if you don't cheat with options :)
if (deflator.err) { throw deflator.msg || msg[deflator.err]; }
return deflator.result;
}
/**
* deflateRaw(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [[deflate]], but creates raw data, without wrapper
* (header and adler32 crc).
**/
function deflateRaw(input, options) {
options = options || {};
options.raw = true;
return deflate(input, options);
}
/**
* gzip(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [[deflate]], but create gzip wrapper instead of
* deflate one.
**/
function gzip(input, options) {
options = options || {};
options.gzip = true;
return deflate(input, options);
}
exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;
},{"./utils/common":41,"./utils/strings":42,"./zlib/deflate":46,"./zlib/messages":51,"./zlib/zstream":53}],40:[function(require,module,exports){
'use strict';
var zlib_inflate = require('./zlib/inflate');
var utils = require('./utils/common');
var strings = require('./utils/strings');
var c = require('./zlib/constants');
var msg = require('./zlib/messages');
var ZStream = require('./zlib/zstream');
var GZheader = require('./zlib/gzheader');
var toString = Object.prototype.toString;
/**
* class Inflate
*
* Generic JS-style wrapper for zlib calls. If you don't need
* streaming behaviour - use more simple functions: [[inflate]]
* and [[inflateRaw]].
**/
/* internal
* inflate.chunks -> Array
*
* Chunks of output data, if [[Inflate#onData]] not overriden.
**/
/**
* Inflate.result -> Uint8Array|Array|String
*
* Uncompressed result, generated by default [[Inflate#onData]]
* and [[Inflate#onEnd]] handlers. Filled after you push last chunk
* (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you
* push a chunk with explicit flush (call [[Inflate#push]] with
* `Z_SYNC_FLUSH` param).
**/
/**
* Inflate.err -> Number
*
* Error code after inflate finished. 0 (Z_OK) on success.
* Should be checked if broken data possible.
**/
/**
* Inflate.msg -> String
*
* Error message, if [[Inflate.err]] != 0
**/
/**
* new Inflate(options)
* - options (Object): zlib inflate options.
*
* Creates new inflator instance with specified params. Throws exception
* on bad params. Supported options:
*
* - `windowBits`
* - `dictionary`
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Additional options, for internal needs:
*
* - `chunkSize` - size of generated data chunks (16K by default)
* - `raw` (Boolean) - do raw inflate
* - `to` (String) - if equal to 'string', then result will be converted
* from utf8 to utf16 (javascript) string. When string output requested,
* chunk length can differ from `chunkSize`, depending on content.
*
* By default, when no options set, autodetect deflate/gzip data format via
* wrapper header.
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
* , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
*
* var inflate = new pako.Inflate({ level: 3});
*
* inflate.push(chunk1, false);
* inflate.push(chunk2, true); // true -> last chunk
*
* if (inflate.err) { throw new Error(inflate.err); }
*
* console.log(inflate.result);
* ```
**/
function Inflate(options) {
if (!(this instanceof Inflate)) return new Inflate(options);
this.options = utils.assign({
chunkSize: 16384,
windowBits: 0,
to: ''
}, options || {});
var opt = this.options;
// Force window size for `raw` data, if not set directly,
// because we have no header for autodetect.
if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
opt.windowBits = -opt.windowBits;
if (opt.windowBits === 0) { opt.windowBits = -15; }
}
// If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
!(options && options.windowBits)) {
opt.windowBits += 32;
}
// Gzip header has no info about windows size, we can do autodetect only
// for deflate. So, if window size not set, force it to max when gzip possible
if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
// bit 3 (16) -> gzipped data
// bit 4 (32) -> autodetect gzip/deflate
if ((opt.windowBits & 15) === 0) {
opt.windowBits |= 15;
}
}
this.err = 0; // error code, if happens (0 = Z_OK)
this.msg = ''; // error message
this.ended = false; // used to avoid multiple onEnd() calls
this.chunks = []; // chunks of compressed data
this.strm = new ZStream();
this.strm.avail_out = 0;
var status = zlib_inflate.inflateInit2(
this.strm,
opt.windowBits
);
if (status !== c.Z_OK) {
throw new Error(msg[status]);
}
this.header = new GZheader();
zlib_inflate.inflateGetHeader(this.strm, this.header);
}
/**
* Inflate#push(data[, mode]) -> Boolean
* - data (Uint8Array|Array|ArrayBuffer|String): input data
* - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
*
* Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
* new output chunks. Returns `true` on success. The last data block must have
* mode Z_FINISH (or `true`). That will flush internal pending buffers and call
* [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you
* can use mode Z_SYNC_FLUSH, keeping the decompression context.
*
* On fail call [[Inflate#onEnd]] with error code and return false.
*
* We strongly recommend to use `Uint8Array` on input for best speed (output
* format is detected automatically). Also, don't skip last param and always
* use the same type in your code (boolean or number). That will improve JS speed.
*
* For regular `Array`-s make sure all elements are [0..255].
*
* ##### Example
*
* ```javascript
* push(chunk, false); // push one of data chunks
* ...
* push(chunk, true); // push last chunk
* ```
**/
Inflate.prototype.push = function (data, mode) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var dictionary = this.options.dictionary;
var status, _mode;
var next_out_utf8, tail, utf8str;
var dict;
// Flag to properly process Z_BUF_ERROR on testing inflate call
// when we check that all output data was flushed.
var allowBufError = false;
if (this.ended) { return false; }
_mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
// Convert data if needed
if (typeof data === 'string') {
// Only binary strings can be decompressed on practice
strm.input = strings.binstring2buf(data);
} else if (toString.call(data) === '[object ArrayBuffer]') {
strm.input = new Uint8Array(data);
} else {
strm.input = data;
}
strm.next_in = 0;
strm.avail_in = strm.input.length;
do {
if (strm.avail_out === 0) {
strm.output = new utils.Buf8(chunkSize);
strm.next_out = 0;
strm.avail_out = chunkSize;
}
status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */
if (status === c.Z_NEED_DICT && dictionary) {
// Convert data if needed
if (typeof dictionary === 'string') {
dict = strings.string2buf(dictionary);
} else if (toString.call(dictionary) === '[object ArrayBuffer]') {
dict = new Uint8Array(dictionary);
} else {
dict = dictionary;
}
status = zlib_inflate.inflateSetDictionary(this.strm, dict);
}
if (status === c.Z_BUF_ERROR && allowBufError === true) {
status = c.Z_OK;
allowBufError = false;
}
if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if (strm.next_out) {
if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {
if (this.options.to === 'string') {
next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
tail = strm.next_out - next_out_utf8;
utf8str = strings.buf2string(strm.output, next_out_utf8);
// move tail
strm.next_out = tail;
strm.avail_out = chunkSize - tail;
if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }
this.onData(utf8str);
} else {
this.onData(utils.shrinkBuf(strm.output, strm.next_out));
}
}
}
// When no more input data, we should check that internal inflate buffers
// are flushed. The only way to do it when avail_out = 0 - run one more
// inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.
// Here we set flag to process this error properly.
//
// NOTE. Deflate does not return error in this case and does not needs such
// logic.
if (strm.avail_in === 0 && strm.avail_out === 0) {
allowBufError = true;
}
} while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END);
if (status === c.Z_STREAM_END) {
_mode = c.Z_FINISH;
}
// Finalize on the last chunk.
if (_mode === c.Z_FINISH) {
status = zlib_inflate.inflateEnd(this.strm);
this.onEnd(status);
this.ended = true;
return status === c.Z_OK;
}
// callback interim results if Z_SYNC_FLUSH.
if (_mode === c.Z_SYNC_FLUSH) {
this.onEnd(c.Z_OK);
strm.avail_out = 0;
return true;
}
return true;
};
/**
* Inflate#onData(chunk) -> Void
* - chunk (Uint8Array|Array|String): ouput data. Type of array depends
* on js engine support. When string output requested, each chunk
* will be string.
*
* By default, stores data blocks in `chunks[]` property and glue
* those in `onEnd`. Override this handler, if you need another behaviour.
**/
Inflate.prototype.onData = function (chunk) {
this.chunks.push(chunk);
};
/**
* Inflate#onEnd(status) -> Void
* - status (Number): inflate status. 0 (Z_OK) on success,
* other if not.
*
* Called either after you tell inflate that the input stream is
* complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
* or if an error happened. By default - join collected chunks,
* free memory and fill `results` / `err` properties.
**/
Inflate.prototype.onEnd = function (status) {
// On success - join
if (status === c.Z_OK) {
if (this.options.to === 'string') {
// Glue & convert here, until we teach pako to send
// utf8 alligned strings to onData
this.result = this.chunks.join('');
} else {
this.result = utils.flattenChunks(this.chunks);
}
}
this.chunks = [];
this.err = status;
this.msg = this.strm.msg;
};
/**
* inflate(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* Decompress `data` with inflate/ungzip and `options`. Autodetect
* format via wrapper header by default. That's why we don't provide
* separate `ungzip` method.
*
* Supported options are:
*
* - windowBits
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information.
*
* Sugar (options):
*
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
* negative windowBits implicitly.
* - `to` (String) - if equal to 'string', then result will be converted
* from utf8 to utf16 (javascript) string. When string output requested,
* chunk length can differ from `chunkSize`, depending on content.
*
*
* ##### Example:
*
* ```javascript
* var pako = require('pako')
* , input = pako.deflate([1,2,3,4,5,6,7,8,9])
* , output;
*
* try {
* output = pako.inflate(input);
* } catch (err)
* console.log(err);
* }
* ```
**/
function inflate(input, options) {
var inflator = new Inflate(options);
inflator.push(input, true);
// That will never happens, if you don't cheat with options :)
if (inflator.err) { throw inflator.msg || msg[inflator.err]; }
return inflator.result;
}
/**
* inflateRaw(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* The same as [[inflate]], but creates raw data, without wrapper
* (header and adler32 crc).
**/
function inflateRaw(input, options) {
options = options || {};
options.raw = true;
return inflate(input, options);
}
/**
* ungzip(data[, options]) -> Uint8Array|Array|String
* - data (Uint8Array|Array|String): input data to decompress.
* - options (Object): zlib inflate options.
*
* Just shortcut to [[inflate]], because it autodetects format
* by header.content. Done for convenience.
**/
exports.Inflate = Inflate;
exports.inflate = inflate;
exports.inflateRaw = inflateRaw;
exports.ungzip = inflate;
},{"./utils/common":41,"./utils/strings":42,"./zlib/constants":44,"./zlib/gzheader":47,"./zlib/inflate":49,"./zlib/messages":51,"./zlib/zstream":53}],41:[function(require,module,exports){
'use strict';
var TYPED_OK = (typeof Uint8Array !== 'undefined') &&
(typeof Uint16Array !== 'undefined') &&
(typeof Int32Array !== 'undefined');
exports.assign = function (obj /*from1, from2, from3, ...*/) {
var sources = Array.prototype.slice.call(arguments, 1);
while (sources.length) {
var source = sources.shift();
if (!source) { continue; }
if (typeof source !== 'object') {
throw new TypeError(source + 'must be non-object');
}
for (var p in source) {
if (source.hasOwnProperty(p)) {
obj[p] = source[p];
}
}
}
return obj;
};
// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
if (buf.length === size) { return buf; }
if (buf.subarray) { return buf.subarray(0, size); }
buf.length = size;
return buf;
};
var fnTyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
if (src.subarray && dest.subarray) {
dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
return;
}
// Fallback to ordinary array
for (var i = 0; i < len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function (chunks) {
var i, l, len, pos, chunk, result;
// calculate data length
len = 0;
for (i = 0, l = chunks.length; i < l; i++) {
len += chunks[i].length;
}
// join chunks
result = new Uint8Array(len);
pos = 0;
for (i = 0, l = chunks.length; i < l; i++) {
chunk = chunks[i];
result.set(chunk, pos);
pos += chunk.length;
}
return result;
}
};
var fnUntyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
for (var i = 0; i < len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function (chunks) {
return [].concat.apply([], chunks);
}
};
// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
if (on) {
exports.Buf8 = Uint8Array;
exports.Buf16 = Uint16Array;
exports.Buf32 = Int32Array;
exports.assign(exports, fnTyped);
} else {
exports.Buf8 = Array;
exports.Buf16 = Array;
exports.Buf32 = Array;
exports.assign(exports, fnUntyped);
}
};
exports.setTyped(TYPED_OK);
},{}],42:[function(require,module,exports){
// String encode/decode helpers
'use strict';
var utils = require('./common');
// Quick check if we can use fast array to bin string conversion
//
// - apply(Array) can fail on Android 2.2
// - apply(Uint8Array) can fail on iOS 5.1 Safary
//
var STR_APPLY_OK = true;
var STR_APPLY_UIA_OK = true;
try { String.fromCharCode.apply(null, [ 0 ]); } catch (__) { STR_APPLY_OK = false; }
try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; }
// Table with utf8 lengths (calculated by first byte of sequence)
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
// because max possible codepoint is 0x10ffff
var _utf8len = new utils.Buf8(256);
for (var q = 0; q < 256; q++) {
_utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);
}
_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start
// convert string to array (typed, when possible)
exports.string2buf = function (str) {
var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
// count binary size
for (m_pos = 0; m_pos < str_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
c2 = str.charCodeAt(m_pos + 1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
}
// allocate buffer
buf = new utils.Buf8(buf_len);
// convert
for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
c = str.charCodeAt(m_pos);
if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
c2 = str.charCodeAt(m_pos + 1);
if ((c2 & 0xfc00) === 0xdc00) {
c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
m_pos++;
}
}
if (c < 0x80) {
/* one byte */
buf[i++] = c;
} else if (c < 0x800) {
/* two bytes */
buf[i++] = 0xC0 | (c >>> 6);
buf[i++] = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
/* three bytes */
buf[i++] = 0xE0 | (c >>> 12);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
} else {
/* four bytes */
buf[i++] = 0xf0 | (c >>> 18);
buf[i++] = 0x80 | (c >>> 12 & 0x3f);
buf[i++] = 0x80 | (c >>> 6 & 0x3f);
buf[i++] = 0x80 | (c & 0x3f);
}
}
return buf;
};
// Helper (used in 2 places)
function buf2binstring(buf, len) {
// use fallback for big arrays to avoid stack overflow
if (len < 65537) {
if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
}
}
var result = '';
for (var i = 0; i < len; i++) {
result += String.fromCharCode(buf[i]);
}
return result;
}
// Convert byte array to binary string
exports.buf2binstring = function (buf) {
return buf2binstring(buf, buf.length);
};
// Convert binary string (typed, when possible)
exports.binstring2buf = function (str) {
var buf = new utils.Buf8(str.length);
for (var i = 0, len = buf.length; i < len; i++) {
buf[i] = str.charCodeAt(i);
}
return buf;
};
// convert array to string
exports.buf2string = function (buf, max) {
var i, out, c, c_len;
var len = max || buf.length;
// Reserve max possible length (2 words per char)
// NB: by unknown reasons, Array is significantly faster for
// String.fromCharCode.apply than Uint16Array.
var utf16buf = new Array(len * 2);
for (out = 0, i = 0; i < len;) {
c = buf[i++];
// quick process ascii
if (c < 0x80) { utf16buf[out++] = c; continue; }
c_len = _utf8len[c];
// skip 5 & 6 byte codes
if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; }
// apply mask on first byte
c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
// join the rest
while (c_len > 1 && i < len) {
c = (c << 6) | (buf[i++] & 0x3f);
c_len--;
}
// terminated by end of string?
if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
if (c < 0x10000) {
utf16buf[out++] = c;
} else {
c -= 0x10000;
utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
utf16buf[out++] = 0xdc00 | (c & 0x3ff);
}
}
return buf2binstring(utf16buf, out);
};
// Calculate max possible position in utf8 buffer,
// that will not break sequence. If that's not possible
// - (very small limits) return max size as is.
//
// buf[] - utf8 bytes array
// max - length limit (mandatory);
exports.utf8border = function (buf, max) {
var pos;
max = max || buf.length;
if (max > buf.length) { max = buf.length; }
// go back from last position, until start of sequence found
pos = max - 1;
while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
// Fuckup - very small and broken sequence,
// return max, because we should return something anyway.
if (pos < 0) { return max; }
// If we came to start of buffer - that means vuffer is too small,
// return max too.
if (pos === 0) { return max; }
return (pos + _utf8len[buf[pos]] > max) ? pos : max;
};
},{"./common":41}],43:[function(require,module,exports){
'use strict';
// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It doesn't worth to make additional optimizationa as in original.
// Small size is preferable.
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
function adler32(adler, buf, len, pos) {
var s1 = (adler & 0xffff) |0,
s2 = ((adler >>> 16) & 0xffff) |0,
n = 0;
while (len !== 0) {
// Set limit ~ twice less than 5552, to keep
// s2 in 31-bits, because we force signed ints.
// in other case %= will fail.
n = len > 2000 ? 2000 : len;
len -= n;
do {
s1 = (s1 + buf[pos++]) |0;
s2 = (s2 + s1) |0;
} while (--n);
s1 %= 65521;
s2 %= 65521;
}
return (s1 | (s2 << 16)) |0;
}
module.exports = adler32;
},{}],44:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
module.exports = {
/* Allowed flush values; see deflate() and inflate() below for details */
Z_NO_FLUSH: 0,
Z_PARTIAL_FLUSH: 1,
Z_SYNC_FLUSH: 2,
Z_FULL_FLUSH: 3,
Z_FINISH: 4,
Z_BLOCK: 5,
Z_TREES: 6,
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
Z_OK: 0,
Z_STREAM_END: 1,
Z_NEED_DICT: 2,
Z_ERRNO: -1,
Z_STREAM_ERROR: -2,
Z_DATA_ERROR: -3,
//Z_MEM_ERROR: -4,
Z_BUF_ERROR: -5,
//Z_VERSION_ERROR: -6,
/* compression levels */
Z_NO_COMPRESSION: 0,
Z_BEST_SPEED: 1,
Z_BEST_COMPRESSION: 9,
Z_DEFAULT_COMPRESSION: -1,
Z_FILTERED: 1,
Z_HUFFMAN_ONLY: 2,
Z_RLE: 3,
Z_FIXED: 4,
Z_DEFAULT_STRATEGY: 0,
/* Possible values of the data_type field (though see inflate()) */
Z_BINARY: 0,
Z_TEXT: 1,
//Z_ASCII: 1, // = Z_TEXT (deprecated)
Z_UNKNOWN: 2,
/* The deflate compression method */
Z_DEFLATED: 8
//Z_NULL: null // Use -1 or null inline, depending on var type
};
},{}],45:[function(require,module,exports){
'use strict';
// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
// Use ordinary array, since untyped makes no boost here
function makeTable() {
var c, table = [];
for (var n = 0; n < 256; n++) {
c = n;
for (var k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
table[n] = c;
}
return table;
}
// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable,
end = pos + len;
crc ^= -1;
for (var i = pos; i < end; i++) {
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;
}
module.exports = crc32;
},{}],46:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
var utils = require('../utils/common');
var trees = require('./trees');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var msg = require('./messages');
/* Public constants ==========================================================*/
/* ===========================================================================*/
/* Allowed flush values; see deflate() and inflate() below for details */
var Z_NO_FLUSH = 0;
var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH = 2;
var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_BLOCK = 5;
//var Z_TREES = 6;
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
var Z_OK = 0;
var Z_STREAM_END = 1;
//var Z_NEED_DICT = 2;
//var Z_ERRNO = -1;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
//var Z_MEM_ERROR = -4;
var Z_BUF_ERROR = -5;
//var Z_VERSION_ERROR = -6;
/* compression levels */
//var Z_NO_COMPRESSION = 0;
//var Z_BEST_SPEED = 1;
//var Z_BEST_COMPRESSION = 9;
var Z_DEFAULT_COMPRESSION = -1;
var Z_FILTERED = 1;
var Z_HUFFMAN_ONLY = 2;
var Z_RLE = 3;
var Z_FIXED = 4;
var Z_DEFAULT_STRATEGY = 0;
/* Possible values of the data_type field (though see inflate()) */
//var Z_BINARY = 0;
//var Z_TEXT = 1;
//var Z_ASCII = 1; // = Z_TEXT
var Z_UNKNOWN = 2;
/* The deflate compression method */
var Z_DEFLATED = 8;
/*============================================================================*/
var MAX_MEM_LEVEL = 9;
/* Maximum value for memLevel in deflateInit2 */
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_MEM_LEVEL = 8;
var LENGTH_CODES = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS = 256;
/* number of literal bytes 0..255 */
var L_CODES = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES = 30;
/* number of distance codes */
var BL_CODES = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE = 2 * L_CODES + 1;
/* maximum heap size */
var MAX_BITS = 15;
/* All codes must not exceed MAX_BITS bits */
var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
var PRESET_DICT = 0x20;
var INIT_STATE = 42;
var EXTRA_STATE = 69;
var NAME_STATE = 73;
var COMMENT_STATE = 91;
var HCRC_STATE = 103;
var BUSY_STATE = 113;
var FINISH_STATE = 666;
var BS_NEED_MORE = 1; /* block not completed, need more input or more output */
var BS_BLOCK_DONE = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */
var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
function err(strm, errorCode) {
strm.msg = msg[errorCode];
return errorCode;
}
function rank(f) {
return ((f) << 1) - ((f) > 4 ? 9 : 0);
}
function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
/* =========================================================================
* Flush as much pending output as possible. All deflate() output goes
* through this function so some applications may wish to modify it
* to avoid allocating a large strm->output buffer and copying into it.
* (See also read_buf()).
*/
function flush_pending(strm) {
var s = strm.state;
//_tr_flush_bits(s);
var len = s.pending;
if (len > strm.avail_out) {
len = strm.avail_out;
}
if (len === 0) { return; }
utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
strm.next_out += len;
s.pending_out += len;
strm.total_out += len;
strm.avail_out -= len;
s.pending -= len;
if (s.pending === 0) {
s.pending_out = 0;
}
}
function flush_block_only(s, last) {
trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
s.block_start = s.strstart;
flush_pending(s.strm);
}
function put_byte(s, b) {
s.pending_buf[s.pending++] = b;
}
/* =========================================================================
* Put a short in the pending buffer. The 16-bit value is put in MSB order.
* IN assertion: the stream state is correct and there is enough room in
* pending_buf.
*/
function putShortMSB(s, b) {
// put_byte(s, (Byte)(b >> 8));
// put_byte(s, (Byte)(b & 0xff));
s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
s.pending_buf[s.pending++] = b & 0xff;
}
/* ===========================================================================
* Read a new buffer from the current input stream, update the adler32
* and total number of bytes read. All deflate() input goes through
* this function so some applications may wish to modify it to avoid
* allocating a large strm->input buffer and copying from it.
* (See also flush_pending()).
*/
function read_buf(strm, buf, start, size) {
var len = strm.avail_in;
if (len > size) { len = size; }
if (len === 0) { return 0; }
strm.avail_in -= len;
// zmemcpy(buf, strm->next_in, len);
utils.arraySet(buf, strm.input, strm.next_in, len, start);
if (strm.state.wrap === 1) {
strm.adler = adler32(strm.adler, buf, len, start);
}
else if (strm.state.wrap === 2) {
strm.adler = crc32(strm.adler, buf, len, start);
}
strm.next_in += len;
strm.total_in += len;
return len;
}
/* ===========================================================================
* Set match_start to the longest match starting at the given string and
* return its length. Matches shorter or equal to prev_length are discarded,
* in which case the result is equal to prev_length and match_start is
* garbage.
* IN assertions: cur_match is the head of the hash chain for the current
* string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
* OUT assertion: the match length is not greater than s->lookahead.
*/
function longest_match(s, cur_match) {
var chain_length = s.max_chain_length; /* max hash chain length */
var scan = s.strstart; /* current string */
var match; /* matched string */
var len; /* length of current match */
var best_len = s.prev_length; /* best match length so far */
var nice_match = s.nice_match; /* stop if match long enough */
var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;
var _win = s.window; // shortcut
var wmask = s.w_mask;
var prev = s.prev;
/* Stop when cur_match becomes <= limit. To simplify the code,
* we prevent matches with the string of window index 0.
*/
var strend = s.strstart + MAX_MATCH;
var scan_end1 = _win[scan + best_len - 1];
var scan_end = _win[scan + best_len];
/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
* It is easy to get rid of this optimization if necessary.
*/
// Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
/* Do not waste too much time if we already have a good match: */
if (s.prev_length >= s.good_match) {
chain_length >>= 2;
}
/* Do not look for matches beyond the end of the input. This is necessary
* to make deflate deterministic.
*/
if (nice_match > s.lookahead) { nice_match = s.lookahead; }
// Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
do {
// Assert(cur_match < s->strstart, "no future");
match = cur_match;
/* Skip to next match if the match length cannot increase
* or if the match length is less than 2. Note that the checks below
* for insufficient lookahead only occur occasionally for performance
* reasons. Therefore uninitialized memory will be accessed, and
* conditional jumps will be made that depend on those values.
* However the length of the match is limited to the lookahead, so
* the output of deflate is not affected by the uninitialized values.
*/
if (_win[match + best_len] !== scan_end ||
_win[match + best_len - 1] !== scan_end1 ||
_win[match] !== _win[scan] ||
_win[++match] !== _win[scan + 1]) {
continue;
}
/* The check at best_len-1 can be removed because it will be made
* again later. (This heuristic is not always a win.)
* It is not necessary to compare scan[2] and match[2] since they
* are always equal when the other bytes match, given that
* the hash keys are equal and that HASH_BITS >= 8.
*/
scan += 2;
match++;
// Assert(*scan == *match, "match[2]?");
/* We check for insufficient lookahead only every 8th comparison;
* the 256th check will be made at strstart+258.
*/
do {
/*jshint noempty:false*/
} while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
scan < strend);
// Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
len = MAX_MATCH - (strend - scan);
scan = strend - MAX_MATCH;
if (len > best_len) {
s.match_start = cur_match;
best_len = len;
if (len >= nice_match) {
break;
}
scan_end1 = _win[scan + best_len - 1];
scan_end = _win[scan + best_len];
}
} while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
if (best_len <= s.lookahead) {
return best_len;
}
return s.lookahead;
}
/* ===========================================================================
* Fill the window when the lookahead becomes insufficient.
* Updates strstart and lookahead.
*
* IN assertion: lookahead < MIN_LOOKAHEAD
* OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
* At least one byte has been read, or avail_in == 0; reads are
* performed for at least two bytes (required for the zip translate_eol
* option -- not supported here).
*/
function fill_window(s) {
var _w_size = s.w_size;
var p, n, m, more, str;
//Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
do {
more = s.window_size - s.lookahead - s.strstart;
// JS ints have 32 bit, block below not needed
/* Deal with !@#$% 64K limit: */
//if (sizeof(int) <= 2) {
// if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
// more = wsize;
//
// } else if (more == (unsigned)(-1)) {
// /* Very unlikely, but possible on 16 bit machine if
// * strstart == 0 && lookahead == 1 (input done a byte at time)
// */
// more--;
// }
//}
/* If the window is almost full and there is insufficient lookahead,
* move the upper half to the lower one to make room in the upper half.
*/
if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
s.match_start -= _w_size;
s.strstart -= _w_size;
/* we now have strstart >= MAX_DIST */
s.block_start -= _w_size;
/* Slide the hash table (could be avoided with 32 bit values
at the expense of memory usage). We slide even when level == 0
to keep the hash table consistent if we switch back to level > 0
later. (Using level 0 permanently is not an optimal usage of
zlib, so we don't care about this pathological case.)
*/
n = s.hash_size;
p = n;
do {
m = s.head[--p];
s.head[p] = (m >= _w_size ? m - _w_size : 0);
} while (--n);
n = _w_size;
p = n;
do {
m = s.prev[--p];
s.prev[p] = (m >= _w_size ? m - _w_size : 0);
/* If n is not on any hash chain, prev[n] is garbage but
* its value will never be used.
*/
} while (--n);
more += _w_size;
}
if (s.strm.avail_in === 0) {
break;
}
/* If there was no sliding:
* strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
* more == window_size - lookahead - strstart
* => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
* => more >= window_size - 2*WSIZE + 2
* In the BIG_MEM or MMAP case (not yet supported),
* window_size == input_size + MIN_LOOKAHEAD &&
* strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
* Otherwise, window_size == 2*WSIZE so more >= 2.
* If there was sliding, more >= WSIZE. So in all cases, more >= 2.
*/
//Assert(more >= 2, "more < 2");
n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
s.lookahead += n;
/* Initialize the hash value now that we have some input: */
if (s.lookahead + s.insert >= MIN_MATCH) {
str = s.strstart - s.insert;
s.ins_h = s.window[str];
/* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
// Call update_hash() MIN_MATCH-3 more times
//#endif
while (s.insert) {
/* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;
s.prev[str & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = str;
str++;
s.insert--;
if (s.lookahead + s.insert < MIN_MATCH) {
break;
}
}
}
/* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
* but this is not important since only literal bytes will be emitted.
*/
} while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);
/* If the WIN_INIT bytes after the end of the current data have never been
* written, then zero those bytes in order to avoid memory check reports of
* the use of uninitialized (or uninitialised as Julian writes) bytes by
* the longest match routines. Update the high water mark for the next
* time through here. WIN_INIT is set to MAX_MATCH since the longest match
* routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
*/
// if (s.high_water < s.window_size) {
// var curr = s.strstart + s.lookahead;
// var init = 0;
//
// if (s.high_water < curr) {
// /* Previous high water mark below current data -- zero WIN_INIT
// * bytes or up to end of window, whichever is less.
// */
// init = s.window_size - curr;
// if (init > WIN_INIT)
// init = WIN_INIT;
// zmemzero(s->window + curr, (unsigned)init);
// s->high_water = curr + init;
// }
// else if (s->high_water < (ulg)curr + WIN_INIT) {
// /* High water mark at or above current data, but below current data
// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
// * to end of window, whichever is less.
// */
// init = (ulg)curr + WIN_INIT - s->high_water;
// if (init > s->window_size - s->high_water)
// init = s->window_size - s->high_water;
// zmemzero(s->window + s->high_water, (unsigned)init);
// s->high_water += init;
// }
// }
//
// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
// "not enough room for search");
}
/* ===========================================================================
* Copy without compression as much as possible from the input stream, return
* the current block state.
* This function does not insert new strings in the dictionary since
* uncompressible data is probably not useful. This function is used
* only for the level=0 compression option.
* NOTE: this function should be optimized to avoid extra copying from
* window to pending_buf.
*/
function deflate_stored(s, flush) {
/* Stored blocks are limited to 0xffff bytes, pending_buf is limited
* to pending_buf_size, and each stored block has a 5 byte header:
*/
var max_block_size = 0xffff;
if (max_block_size > s.pending_buf_size - 5) {
max_block_size = s.pending_buf_size - 5;
}
/* Copy as much as possible from input to output: */
for (;;) {
/* Fill the window as much as possible: */
if (s.lookahead <= 1) {
//Assert(s->strstart < s->w_size+MAX_DIST(s) ||
// s->block_start >= (long)s->w_size, "slide too late");
// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
// s.block_start >= s.w_size)) {
// throw new Error("slide too late");
// }
fill_window(s);
if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) {
break;
}
/* flush the current block */
}
//Assert(s->block_start >= 0L, "block gone");
// if (s.block_start < 0) throw new Error("block gone");
s.strstart += s.lookahead;
s.lookahead = 0;
/* Emit a stored block if pending_buf will be full: */
var max_start = s.block_start + max_block_size;
if (s.strstart === 0 || s.strstart >= max_start) {
/* strstart == 0 is possible when wraparound on 16-bit machine */
s.lookahead = s.strstart - max_start;
s.strstart = max_start;
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
/* Flush if we may have to slide, otherwise block_start may become
* negative and the data will be gone:
*/
if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.strstart > s.block_start) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_NEED_MORE;
}
/* ===========================================================================
* Compress as much as possible from the input stream, return the current
* block state.
* This function does not perform lazy evaluation of matches and inserts
* new strings in the dictionary only for unmatched strings or for short
* matches. It is used only for the fast compression options.
*/
function deflate_fast(s, flush) {
var hash_head; /* head of the hash chain */
var bflush; /* set if current block must be flushed */
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the next match, plus MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s.lookahead < MIN_LOOKAHEAD) {
fill_window(s);
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) {
break; /* flush the current block */
}
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = 0/*NIL*/;
if (s.lookahead >= MIN_MATCH) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
/* Find the longest match, discarding those <= prev_length.
* At this point we have always match_length < MIN_MATCH
*/
if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
/* To simplify the code, we prevent matches with the string
* of window index 0 (in particular we have to avoid a match
* of the string with itself at the start of the input file).
*/
s.match_length = longest_match(s, hash_head);
/* longest_match() sets match_start */
}
if (s.match_length >= MIN_MATCH) {
// check_match(s, s.strstart, s.match_start, s.match_length); // for debug only
/*** _tr_tally_dist(s, s.strstart - s.match_start,
s.match_length - MIN_MATCH, bflush); ***/
bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
s.lookahead -= s.match_length;
/* Insert new strings in the hash table only if the match length
* is not too large. This saves time but degrades compression.
*/
if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
s.match_length--; /* string at strstart already in table */
do {
s.strstart++;
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
/* strstart never exceeds WSIZE-MAX_MATCH, so there are
* always MIN_MATCH bytes ahead.
*/
} while (--s.match_length !== 0);
s.strstart++;
} else
{
s.strstart += s.match_length;
s.match_length = 0;
s.ins_h = s.window[s.strstart];
/* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
// Call UPDATE_HASH() MIN_MATCH-3 more times
//#endif
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
* matter since it will be recomputed at next deflate call.
*/
}
} else {
/* No match, output a literal byte */
//Tracevv((stderr,"%c", s.window[s.strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
}
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* Same as above, but achieves better compression. We use a lazy
* evaluation for matches: a match is finally adopted only if there is
* no better match at the next window position.
*/
function deflate_slow(s, flush) {
var hash_head; /* head of hash chain */
var bflush; /* set if current block must be flushed */
var max_insert;
/* Process the input block. */
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the next match, plus MIN_MATCH bytes to insert the
* string following the next match.
*/
if (s.lookahead < MIN_LOOKAHEAD) {
fill_window(s);
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) { break; } /* flush the current block */
}
/* Insert the string window[strstart .. strstart+2] in the
* dictionary, and set hash_head to the head of the hash chain:
*/
hash_head = 0/*NIL*/;
if (s.lookahead >= MIN_MATCH) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
/* Find the longest match, discarding those <= prev_length.
*/
s.prev_length = s.match_length;
s.prev_match = s.match_start;
s.match_length = MIN_MATCH - 1;
if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
/* To simplify the code, we prevent matches with the string
* of window index 0 (in particular we have to avoid a match
* of the string with itself at the start of the input file).
*/
s.match_length = longest_match(s, hash_head);
/* longest_match() sets match_start */
if (s.match_length <= 5 &&
(s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {
/* If prev_match is also MIN_MATCH, match_start is garbage
* but we will ignore the current match anyway.
*/
s.match_length = MIN_MATCH - 1;
}
}
/* If there was a match at the previous step and the current
* match is not better, output the previous match:
*/
if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
max_insert = s.strstart + s.lookahead - MIN_MATCH;
/* Do not insert strings in hash table beyond this. */
//check_match(s, s.strstart-1, s.prev_match, s.prev_length);
/***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
s.prev_length - MIN_MATCH, bflush);***/
bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
/* Insert in hash table all strings up to the end of the match.
* strstart-1 and strstart are already inserted. If there is not
* enough lookahead, the last two strings are not inserted in
* the hash table.
*/
s.lookahead -= s.prev_length - 1;
s.prev_length -= 2;
do {
if (++s.strstart <= max_insert) {
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = s.strstart;
/***/
}
} while (--s.prev_length !== 0);
s.match_available = 0;
s.match_length = MIN_MATCH - 1;
s.strstart++;
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
} else if (s.match_available) {
/* If there was no match at the previous position, output a
* single literal. If there was a match but the current match
* is longer, truncate the previous match to a single literal.
*/
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);
if (bflush) {
/*** FLUSH_BLOCK_ONLY(s, 0) ***/
flush_block_only(s, false);
/***/
}
s.strstart++;
s.lookahead--;
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
} else {
/* There is no previous match to compare with, wait for
* the next step to decide.
*/
s.match_available = 1;
s.strstart++;
s.lookahead--;
}
}
//Assert (flush != Z_NO_FLUSH, "no flush?");
if (s.match_available) {
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);
s.match_available = 0;
}
s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* For Z_RLE, simply look for runs of bytes, generate matches only of distance
* one. Do not maintain a hash table. (It will be regenerated if this run of
* deflate switches away from Z_RLE.)
*/
function deflate_rle(s, flush) {
var bflush; /* set if current block must be flushed */
var prev; /* byte at distance one to match */
var scan, strend; /* scan goes up to strend for length of run */
var _win = s.window;
for (;;) {
/* Make sure that we always have enough lookahead, except
* at the end of the input file. We need MAX_MATCH bytes
* for the longest run, plus one for the unrolled loop.
*/
if (s.lookahead <= MAX_MATCH) {
fill_window(s);
if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
if (s.lookahead === 0) { break; } /* flush the current block */
}
/* See how many times the previous byte repeats */
s.match_length = 0;
if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
scan = s.strstart - 1;
prev = _win[scan];
if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
strend = s.strstart + MAX_MATCH;
do {
/*jshint noempty:false*/
} while (prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
prev === _win[++scan] && prev === _win[++scan] &&
scan < strend);
s.match_length = MAX_MATCH - (strend - scan);
if (s.match_length > s.lookahead) {
s.match_length = s.lookahead;
}
}
//Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
}
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
if (s.match_length >= MIN_MATCH) {
//check_match(s, s.strstart, s.strstart - 1, s.match_length);
/*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);
s.lookahead -= s.match_length;
s.strstart += s.match_length;
s.match_length = 0;
} else {
/* No match, output a literal byte */
//Tracevv((stderr,"%c", s->window[s->strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
}
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* ===========================================================================
* For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
* (It will be regenerated if this run of deflate switches away from Huffman.)
*/
function deflate_huff(s, flush) {
var bflush; /* set if current block must be flushed */
for (;;) {
/* Make sure that we have a literal to write. */
if (s.lookahead === 0) {
fill_window(s);
if (s.lookahead === 0) {
if (flush === Z_NO_FLUSH) {
return BS_NEED_MORE;
}
break; /* flush the current block */
}
}
/* Output a literal byte */
s.match_length = 0;
//Tracevv((stderr,"%c", s->window[s->strstart]));
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
s.lookahead--;
s.strstart++;
if (bflush) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
}
s.insert = 0;
if (flush === Z_FINISH) {
/*** FLUSH_BLOCK(s, 1); ***/
flush_block_only(s, true);
if (s.strm.avail_out === 0) {
return BS_FINISH_STARTED;
}
/***/
return BS_FINISH_DONE;
}
if (s.last_lit) {
/*** FLUSH_BLOCK(s, 0); ***/
flush_block_only(s, false);
if (s.strm.avail_out === 0) {
return BS_NEED_MORE;
}
/***/
}
return BS_BLOCK_DONE;
}
/* Values for max_lazy_match, good_match and max_chain_length, depending on
* the desired pack level (0..9). The values given below have been tuned to
* exclude worst case performance for pathological files. Better values may be
* found for specific files.
*/
function Config(good_length, max_lazy, nice_length, max_chain, func) {
this.good_length = good_length;
this.max_lazy = max_lazy;
this.nice_length = nice_length;
this.max_chain = max_chain;
this.func = func;
}
var configuration_table;
configuration_table = [
/* good lazy nice chain */
new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */
new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */
new Config(4, 5, 16, 8, deflate_fast), /* 2 */
new Config(4, 6, 32, 32, deflate_fast), /* 3 */
new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */
new Config(8, 16, 32, 32, deflate_slow), /* 5 */
new Config(8, 16, 128, 128, deflate_slow), /* 6 */
new Config(8, 32, 128, 256, deflate_slow), /* 7 */
new Config(32, 128, 258, 1024, deflate_slow), /* 8 */
new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */
];
/* ===========================================================================
* Initialize the "longest match" routines for a new zlib stream
*/
function lm_init(s) {
s.window_size = 2 * s.w_size;
/*** CLEAR_HASH(s); ***/
zero(s.head); // Fill with NIL (= 0);
/* Set the default configuration parameters:
*/
s.max_lazy_match = configuration_table[s.level].max_lazy;
s.good_match = configuration_table[s.level].good_length;
s.nice_match = configuration_table[s.level].nice_length;
s.max_chain_length = configuration_table[s.level].max_chain;
s.strstart = 0;
s.block_start = 0;
s.lookahead = 0;
s.insert = 0;
s.match_length = s.prev_length = MIN_MATCH - 1;
s.match_available = 0;
s.ins_h = 0;
}
function DeflateState() {
this.strm = null; /* pointer back to this zlib stream */
this.status = 0; /* as the name implies */
this.pending_buf = null; /* output still pending */
this.pending_buf_size = 0; /* size of pending_buf */
this.pending_out = 0; /* next pending byte to output to the stream */
this.pending = 0; /* nb of bytes in the pending buffer */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
this.gzhead = null; /* gzip header information to write */
this.gzindex = 0; /* where in extra, name, or comment */
this.method = Z_DEFLATED; /* can only be DEFLATED */
this.last_flush = -1; /* value of flush param for previous deflate call */
this.w_size = 0; /* LZ77 window size (32K by default) */
this.w_bits = 0; /* log2(w_size) (8..16) */
this.w_mask = 0; /* w_size - 1 */
this.window = null;
/* Sliding window. Input bytes are read into the second half of the window,
* and move to the first half later to keep a dictionary of at least wSize
* bytes. With this organization, matches are limited to a distance of
* wSize-MAX_MATCH bytes, but this ensures that IO is always
* performed with a length multiple of the block size.
*/
this.window_size = 0;
/* Actual size of window: 2*wSize, except when the user input buffer
* is directly used as sliding window.
*/
this.prev = null;
/* Link to older string with same hash index. To limit the size of this
* array to 64K, this link is maintained only for the last 32K strings.
* An index in this array is thus a window index modulo 32K.
*/
this.head = null; /* Heads of the hash chains or NIL. */
this.ins_h = 0; /* hash index of string to be inserted */
this.hash_size = 0; /* number of elements in hash table */
this.hash_bits = 0; /* log2(hash_size) */
this.hash_mask = 0; /* hash_size-1 */
this.hash_shift = 0;
/* Number of bits by which ins_h must be shifted at each input
* step. It must be such that after MIN_MATCH steps, the oldest
* byte no longer takes part in the hash key, that is:
* hash_shift * MIN_MATCH >= hash_bits
*/
this.block_start = 0;
/* Window position at the beginning of the current output block. Gets
* negative when the window is moved backwards.
*/
this.match_length = 0; /* length of best match */
this.prev_match = 0; /* previous match */
this.match_available = 0; /* set if previous match exists */
this.strstart = 0; /* start of string to insert */
this.match_start = 0; /* start of matching string */
this.lookahead = 0; /* number of valid bytes ahead in window */
this.prev_length = 0;
/* Length of the best match at previous step. Matches not greater than this
* are discarded. This is used in the lazy match evaluation.
*/
this.max_chain_length = 0;
/* To speed up deflation, hash chains are never searched beyond this
* length. A higher limit improves compression ratio but degrades the
* speed.
*/
this.max_lazy_match = 0;
/* Attempt to find a better match only when the current match is strictly
* smaller than this value. This mechanism is used only for compression
* levels >= 4.
*/
// That's alias to max_lazy_match, don't use directly
//this.max_insert_length = 0;
/* Insert new strings in the hash table only if the match length is not
* greater than this length. This saves time but degrades compression.
* max_insert_length is used only for compression levels <= 3.
*/
this.level = 0; /* compression level (1..9) */
this.strategy = 0; /* favor or force Huffman coding*/
this.good_match = 0;
/* Use a faster search when the previous match is longer than this */
this.nice_match = 0; /* Stop searching when current match exceeds this */
/* used by trees.c: */
/* Didn't use ct_data typedef below to suppress compiler warning */
// struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
// struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
// struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
// Use flat array of DOUBLE size, with interleaved fata,
// because JS does not support effective
this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2);
this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2);
this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2);
zero(this.dyn_ltree);
zero(this.dyn_dtree);
zero(this.bl_tree);
this.l_desc = null; /* desc. for literal tree */
this.d_desc = null; /* desc. for distance tree */
this.bl_desc = null; /* desc. for bit length tree */
//ush bl_count[MAX_BITS+1];
this.bl_count = new utils.Buf16(MAX_BITS + 1);
/* number of codes at each bit length for an optimal tree */
//int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
this.heap = new utils.Buf16(2 * L_CODES + 1); /* heap used to build the Huffman trees */
zero(this.heap);
this.heap_len = 0; /* number of elements in the heap */
this.heap_max = 0; /* element of largest frequency */
/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
* The same heap array is used to build all trees.
*/
this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
zero(this.depth);
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
this.l_buf = 0; /* buffer index for literals or lengths */
this.lit_bufsize = 0;
/* Size of match buffer for literals/lengths. There are 4 reasons for
* limiting lit_bufsize to 64K:
* - frequencies can be kept in 16 bit counters
* - if compression is not successful for the first block, all input
* data is still in the window so we can still emit a stored block even
* when input comes from standard input. (This can also be done for
* all blocks if lit_bufsize is not greater than 32K.)
* - if compression is not successful for a file smaller than 64K, we can
* even emit a stored file instead of a stored block (saving 5 bytes).
* This is applicable only for zip (not gzip or zlib).
* - creating new Huffman trees less frequently may not provide fast
* adaptation to changes in the input data statistics. (Take for
* example a binary file with poorly compressible code followed by
* a highly compressible string table.) Smaller buffer sizes give
* fast adaptation but have of course the overhead of transmitting
* trees more frequently.
* - I can't count above 4
*/
this.last_lit = 0; /* running index in l_buf */
this.d_buf = 0;
/* Buffer index for distances. To simplify the code, d_buf and l_buf have
* the same number of elements. To use different lengths, an extra flag
* array would be necessary.
*/
this.opt_len = 0; /* bit length of current block with optimal trees */
this.static_len = 0; /* bit length of current block with static trees */
this.matches = 0; /* number of string matches in current block */
this.insert = 0; /* bytes at end of window left to insert */
this.bi_buf = 0;
/* Output buffer. bits are inserted starting at the bottom (least
* significant bits).
*/
this.bi_valid = 0;
/* Number of valid bits in bi_buf. All bits above the last valid bit
* are always zero.
*/
// Used for window memory init. We safely ignore it for JS. That makes
// sense only for pointers and memory check tools.
//this.high_water = 0;
/* High water mark offset in window for initialized bytes -- bytes above
* this are set to zero in order to avoid memory check warnings when
* longest match routines access bytes past the input. This is then
* updated to the new high water mark.
*/
}
function deflateResetKeep(strm) {
var s;
if (!strm || !strm.state) {
return err(strm, Z_STREAM_ERROR);
}
strm.total_in = strm.total_out = 0;
strm.data_type = Z_UNKNOWN;
s = strm.state;
s.pending = 0;
s.pending_out = 0;
if (s.wrap < 0) {
s.wrap = -s.wrap;
/* was made negative by deflate(..., Z_FINISH); */
}
s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
strm.adler = (s.wrap === 2) ?
0 // crc32(0, Z_NULL, 0)
:
1; // adler32(0, Z_NULL, 0)
s.last_flush = Z_NO_FLUSH;
trees._tr_init(s);
return Z_OK;
}
function deflateReset(strm) {
var ret = deflateResetKeep(strm);
if (ret === Z_OK) {
lm_init(strm.state);
}
return ret;
}
function deflateSetHeader(strm, head) {
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
strm.state.gzhead = head;
return Z_OK;
}
function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
if (!strm) { // === Z_NULL
return Z_STREAM_ERROR;
}
var wrap = 1;
if (level === Z_DEFAULT_COMPRESSION) {
level = 6;
}
if (windowBits < 0) { /* suppress zlib wrapper */
wrap = 0;
windowBits = -windowBits;
}
else if (windowBits > 15) {
wrap = 2; /* write gzip wrapper instead */
windowBits -= 16;
}
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
strategy < 0 || strategy > Z_FIXED) {
return err(strm, Z_STREAM_ERROR);
}
if (windowBits === 8) {
windowBits = 9;
}
/* until 256-byte window bug fixed */
var s = new DeflateState();
strm.state = s;
s.strm = strm;
s.wrap = wrap;
s.gzhead = null;
s.w_bits = windowBits;
s.w_size = 1 << s.w_bits;
s.w_mask = s.w_size - 1;
s.hash_bits = memLevel + 7;
s.hash_size = 1 << s.hash_bits;
s.hash_mask = s.hash_size - 1;
s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
s.window = new utils.Buf8(s.w_size * 2);
s.head = new utils.Buf16(s.hash_size);
s.prev = new utils.Buf16(s.w_size);
// Don't need mem init magic for JS.
//s.high_water = 0; /* nothing written to s->window yet */
s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
s.pending_buf_size = s.lit_bufsize * 4;
//overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
//s->pending_buf = (uchf *) overlay;
s.pending_buf = new utils.Buf8(s.pending_buf_size);
// It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
//s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
s.d_buf = 1 * s.lit_bufsize;
//s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
s.l_buf = (1 + 2) * s.lit_bufsize;
s.level = level;
s.strategy = strategy;
s.method = method;
return deflateReset(strm);
}
function deflateInit(strm, level) {
return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}
function deflate(strm, flush) {
var old_flush, s;
var beg, val; // for gzip header write only
if (!strm || !strm.state ||
flush > Z_BLOCK || flush < 0) {
return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
}
s = strm.state;
if (!strm.output ||
(!strm.input && strm.avail_in !== 0) ||
(s.status === FINISH_STATE && flush !== Z_FINISH)) {
return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
}
s.strm = strm; /* just in case */
old_flush = s.last_flush;
s.last_flush = flush;
/* Write the header */
if (s.status === INIT_STATE) {
if (s.wrap === 2) { // GZIP header
strm.adler = 0; //crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (!s.gzhead) { // s->gzhead == Z_NULL
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, s.level === 9 ? 2 :
(s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
4 : 0));
put_byte(s, OS_CODE);
s.status = BUSY_STATE;
}
else {
put_byte(s, (s.gzhead.text ? 1 : 0) +
(s.gzhead.hcrc ? 2 : 0) +
(!s.gzhead.extra ? 0 : 4) +
(!s.gzhead.name ? 0 : 8) +
(!s.gzhead.comment ? 0 : 16)
);
put_byte(s, s.gzhead.time & 0xff);
put_byte(s, (s.gzhead.time >> 8) & 0xff);
put_byte(s, (s.gzhead.time >> 16) & 0xff);
put_byte(s, (s.gzhead.time >> 24) & 0xff);
put_byte(s, s.level === 9 ? 2 :
(s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
4 : 0));
put_byte(s, s.gzhead.os & 0xff);
if (s.gzhead.extra && s.gzhead.extra.length) {
put_byte(s, s.gzhead.extra.length & 0xff);
put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
}
if (s.gzhead.hcrc) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
}
s.gzindex = 0;
s.status = EXTRA_STATE;
}
}
else // DEFLATE header
{
var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
var level_flags = -1;
if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
level_flags = 0;
} else if (s.level < 6) {
level_flags = 1;
} else if (s.level === 6) {
level_flags = 2;
} else {
level_flags = 3;
}
header |= (level_flags << 6);
if (s.strstart !== 0) { header |= PRESET_DICT; }
header += 31 - (header % 31);
s.status = BUSY_STATE;
putShortMSB(s, header);
/* Save the adler32 of the preset dictionary: */
if (s.strstart !== 0) {
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
strm.adler = 1; // adler32(0L, Z_NULL, 0);
}
}
//#ifdef GZIP
if (s.status === EXTRA_STATE) {
if (s.gzhead.extra/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
break;
}
}
put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
s.gzindex++;
}
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (s.gzindex === s.gzhead.extra.length) {
s.gzindex = 0;
s.status = NAME_STATE;
}
}
else {
s.status = NAME_STATE;
}
}
if (s.status === NAME_STATE) {
if (s.gzhead.name/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
//int val;
do {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.name.length) {
val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (val === 0) {
s.gzindex = 0;
s.status = COMMENT_STATE;
}
}
else {
s.status = COMMENT_STATE;
}
}
if (s.status === COMMENT_STATE) {
if (s.gzhead.comment/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
//int val;
do {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.comment.length) {
val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (val === 0) {
s.status = HCRC_STATE;
}
}
else {
s.status = HCRC_STATE;
}
}
if (s.status === HCRC_STATE) {
if (s.gzhead.hcrc) {
if (s.pending + 2 > s.pending_buf_size) {
flush_pending(strm);
}
if (s.pending + 2 <= s.pending_buf_size) {
put_byte(s, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
strm.adler = 0; //crc32(0L, Z_NULL, 0);
s.status = BUSY_STATE;
}
}
else {
s.status = BUSY_STATE;
}
}
//#endif
/* Flush as much pending output as possible */
if (s.pending !== 0) {
flush_pending(strm);
if (strm.avail_out === 0) {
/* Since avail_out is 0, deflate will be called again with
* more output space, but possibly with both pending and
* avail_in equal to zero. There won't be anything to do,
* but this is not an error situation so make sure we
* return OK instead of BUF_ERROR at next call of deflate:
*/
s.last_flush = -1;
return Z_OK;
}
/* Make sure there is something to do and avoid duplicate consecutive
* flushes. For repeated and useless calls with Z_FINISH, we keep
* returning Z_STREAM_END instead of Z_BUF_ERROR.
*/
} else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
flush !== Z_FINISH) {
return err(strm, Z_BUF_ERROR);
}
/* User must not provide more input after the first FINISH: */
if (s.status === FINISH_STATE && strm.avail_in !== 0) {
return err(strm, Z_BUF_ERROR);
}
/* Start a new block or continue the current one.
*/
if (strm.avail_in !== 0 || s.lookahead !== 0 ||
(flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
(s.strategy === Z_RLE ? deflate_rle(s, flush) :
configuration_table[s.level].func(s, flush));
if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
s.status = FINISH_STATE;
}
if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
if (strm.avail_out === 0) {
s.last_flush = -1;
/* avoid BUF_ERROR next call, see above */
}
return Z_OK;
/* If flush != Z_NO_FLUSH && avail_out == 0, the next call
* of deflate should use the same flush parameter to make sure
* that the flush is complete. So we don't have to output an
* empty block here, this will be done at next call. This also
* ensures that for a very small output buffer, we emit at most
* one empty block.
*/
}
if (bstate === BS_BLOCK_DONE) {
if (flush === Z_PARTIAL_FLUSH) {
trees._tr_align(s);
}
else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
trees._tr_stored_block(s, 0, 0, false);
/* For a full flush, this empty block will be recognized
* as a special marker by inflate_sync().
*/
if (flush === Z_FULL_FLUSH) {
/*** CLEAR_HASH(s); ***/ /* forget history */
zero(s.head); // Fill with NIL (= 0);
if (s.lookahead === 0) {
s.strstart = 0;
s.block_start = 0;
s.insert = 0;
}
}
}
flush_pending(strm);
if (strm.avail_out === 0) {
s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
return Z_OK;
}
}
}
//Assert(strm->avail_out > 0, "bug2");
//if (strm.avail_out <= 0) { throw new Error("bug2");}
if (flush !== Z_FINISH) { return Z_OK; }
if (s.wrap <= 0) { return Z_STREAM_END; }
/* Write the trailer */
if (s.wrap === 2) {
put_byte(s, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
put_byte(s, (strm.adler >> 16) & 0xff);
put_byte(s, (strm.adler >> 24) & 0xff);
put_byte(s, strm.total_in & 0xff);
put_byte(s, (strm.total_in >> 8) & 0xff);
put_byte(s, (strm.total_in >> 16) & 0xff);
put_byte(s, (strm.total_in >> 24) & 0xff);
}
else
{
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
flush_pending(strm);
/* If avail_out is zero, the application will call deflate again
* to flush the rest.
*/
if (s.wrap > 0) { s.wrap = -s.wrap; }
/* write the trailer only once! */
return s.pending !== 0 ? Z_OK : Z_STREAM_END;
}
function deflateEnd(strm) {
var status;
if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
return Z_STREAM_ERROR;
}
status = strm.state.status;
if (status !== INIT_STATE &&
status !== EXTRA_STATE &&
status !== NAME_STATE &&
status !== COMMENT_STATE &&
status !== HCRC_STATE &&
status !== BUSY_STATE &&
status !== FINISH_STATE
) {
return err(strm, Z_STREAM_ERROR);
}
strm.state = null;
return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
}
/* =========================================================================
* Initializes the compression dictionary from the given byte
* sequence without producing any compressed output.
*/
function deflateSetDictionary(strm, dictionary) {
var dictLength = dictionary.length;
var s;
var str, n;
var wrap;
var avail;
var next;
var input;
var tmpDict;
if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
return Z_STREAM_ERROR;
}
s = strm.state;
wrap = s.wrap;
if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {
return Z_STREAM_ERROR;
}
/* when using zlib wrappers, compute Adler-32 for provided dictionary */
if (wrap === 1) {
/* adler32(strm->adler, dictionary, dictLength); */
strm.adler = adler32(strm.adler, dictionary, dictLength, 0);
}
s.wrap = 0; /* avoid computing Adler-32 in read_buf */
/* if dictionary would fill window, just replace the history */
if (dictLength >= s.w_size) {
if (wrap === 0) { /* already empty otherwise */
/*** CLEAR_HASH(s); ***/
zero(s.head); // Fill with NIL (= 0);
s.strstart = 0;
s.block_start = 0;
s.insert = 0;
}
/* use the tail */
// dictionary = dictionary.slice(dictLength - s.w_size);
tmpDict = new utils.Buf8(s.w_size);
utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);
dictionary = tmpDict;
dictLength = s.w_size;
}
/* insert dictionary into window and hash */
avail = strm.avail_in;
next = strm.next_in;
input = strm.input;
strm.avail_in = dictLength;
strm.next_in = 0;
strm.input = dictionary;
fill_window(s);
while (s.lookahead >= MIN_MATCH) {
str = s.strstart;
n = s.lookahead - (MIN_MATCH - 1);
do {
/* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;
s.prev[str & s.w_mask] = s.head[s.ins_h];
s.head[s.ins_h] = str;
str++;
} while (--n);
s.strstart = str;
s.lookahead = MIN_MATCH - 1;
fill_window(s);
}
s.strstart += s.lookahead;
s.block_start = s.strstart;
s.insert = s.lookahead;
s.lookahead = 0;
s.match_length = s.prev_length = MIN_MATCH - 1;
s.match_available = 0;
strm.next_in = next;
strm.input = input;
strm.avail_in = avail;
s.wrap = wrap;
return Z_OK;
}
exports.deflateInit = deflateInit;
exports.deflateInit2 = deflateInit2;
exports.deflateReset = deflateReset;
exports.deflateResetKeep = deflateResetKeep;
exports.deflateSetHeader = deflateSetHeader;
exports.deflate = deflate;
exports.deflateEnd = deflateEnd;
exports.deflateSetDictionary = deflateSetDictionary;
exports.deflateInfo = 'pako deflate (from Nodeca project)';
/* Not implemented
exports.deflateBound = deflateBound;
exports.deflateCopy = deflateCopy;
exports.deflateParams = deflateParams;
exports.deflatePending = deflatePending;
exports.deflatePrime = deflatePrime;
exports.deflateTune = deflateTune;
*/
},{"../utils/common":41,"./adler32":43,"./crc32":45,"./messages":51,"./trees":52}],47:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
function GZheader() {
/* true if compressed data believed to be text */
this.text = 0;
/* modification time */
this.time = 0;
/* extra flags (not used when writing a gzip file) */
this.xflags = 0;
/* operating system */
this.os = 0;
/* pointer to extra field or Z_NULL if none */
this.extra = null;
/* extra field length (valid if extra != Z_NULL) */
this.extra_len = 0; // Actually, we don't need it in JS,
// but leave for few code modifications
//
// Setup limits is not necessary because in js we should not preallocate memory
// for inflate use constant limit in 65536 bytes
//
/* space at extra (only when reading header) */
// this.extra_max = 0;
/* pointer to zero-terminated file name or Z_NULL */
this.name = '';
/* space at name (only when reading header) */
// this.name_max = 0;
/* pointer to zero-terminated comment or Z_NULL */
this.comment = '';
/* space at comment (only when reading header) */
// this.comm_max = 0;
/* true if there was or will be a header crc */
this.hcrc = 0;
/* true when done reading gzip header (not used when writing a gzip file) */
this.done = false;
}
module.exports = GZheader;
},{}],48:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
// See state defs from inflate.js
var BAD = 30; /* got a data error -- remain here until reset */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
/*
Decode literal, length, and distance codes and write out the resulting
literal and match bytes until either not enough input or output is
available, an end-of-block is encountered, or a data error is encountered.
When large enough input and output buffers are supplied to inflate(), for
example, a 16K input buffer and a 64K output buffer, more than 95% of the
inflate execution time is spent in this routine.
Entry assumptions:
state.mode === LEN
strm.avail_in >= 6
strm.avail_out >= 258
start >= strm.avail_out
state.bits < 8
On return, state.mode is one of:
LEN -- ran out of enough output space or enough available input
TYPE -- reached end of block code, inflate() to interpret next block
BAD -- error in block data
Notes:
- The maximum input bits used by a length/distance pair is 15 bits for the
length code, 5 bits for the length extra, 15 bits for the distance code,
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
Therefore if strm.avail_in >= 6, then there is enough input to avoid
checking for available input while decoding.
- The maximum bytes that a single length/distance pair can output is 258
bytes, which is the maximum length that can be coded. inflate_fast()
requires strm.avail_out >= 258 for each loop to avoid checking for
output space.
*/
module.exports = function inflate_fast(strm, start) {
var state;
var _in; /* local strm.input */
var last; /* have enough input while in < last */
var _out; /* local strm.output */
var beg; /* inflate()'s initial strm.output */
var end; /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
var dmax; /* maximum distance from zlib header */
//#endif
var wsize; /* window size or zero if not using window */
var whave; /* valid bytes in the window */
var wnext; /* window write index */
// Use `s_window` instead `window`, avoid conflict with instrumentation tools
var s_window; /* allocated sliding window, if wsize != 0 */
var hold; /* local strm.hold */
var bits; /* local strm.bits */
var lcode; /* local strm.lencode */
var dcode; /* local strm.distcode */
var lmask; /* mask for first level of length codes */
var dmask; /* mask for first level of distance codes */
var here; /* retrieved table entry */
var op; /* code bits, operation, extra bits, or */
/* window position, window bytes to copy */
var len; /* match length, unused bytes */
var dist; /* match distance */
var from; /* where to copy match from */
var from_source;
var input, output; // JS specific, because we have no pointers
/* copy state to local variables */
state = strm.state;
//here = state.here;
_in = strm.next_in;
input = strm.input;
last = _in + (strm.avail_in - 5);
_out = strm.next_out;
output = strm.output;
beg = _out - (start - strm.avail_out);
end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
dmax = state.dmax;
//#endif
wsize = state.wsize;
whave = state.whave;
wnext = state.wnext;
s_window = state.window;
hold = state.hold;
bits = state.bits;
lcode = state.lencode;
dcode = state.distcode;
lmask = (1 << state.lenbits) - 1;
dmask = (1 << state.distbits) - 1;
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
top:
do {
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = lcode[hold & lmask];
dolen:
for (;;) { // Goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op === 0) { /* literal */
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
output[_out++] = here & 0xffff/*here.val*/;
}
else if (op & 16) { /* length base */
len = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (op) {
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
len += hold & ((1 << op) - 1);
hold >>>= op;
bits -= op;
}
//Tracevv((stderr, "inflate: length %u\n", len));
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = dcode[hold & dmask];
dodist:
for (;;) { // goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op & 16) { /* distance base */
dist = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
}
dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
if (dist > dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
//#endif
hold >>>= op;
bits -= op;
//Tracevv((stderr, "inflate: distance %u\n", dist));
op = _out - beg; /* max distance in output */
if (dist > op) { /* see if copy from window */
op = dist - op; /* distance back in window */
if (op > whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// if (len <= op - whave) {
// do {
// output[_out++] = 0;
// } while (--len);
// continue top;
// }
// len -= op - whave;
// do {
// output[_out++] = 0;
// } while (--op > whave);
// if (op === 0) {
// from = _out - dist;
// do {
// output[_out++] = output[from++];
// } while (--len);
// continue top;
// }
//#endif
}
from = 0; // window index
from_source = s_window;
if (wnext === 0) { /* very common case */
from += wsize - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
else if (wnext < op) { /* wrap around window */
from += wsize + wnext - op;
op -= wnext;
if (op < len) { /* some from end of window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = 0;
if (wnext < len) { /* some from start of window */
op = wnext;
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
}
else { /* contiguous in window */
from += wnext - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = s_window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
while (len > 2) {
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
len -= 3;
}
if (len) {
output[_out++] = from_source[from++];
if (len > 1) {
output[_out++] = from_source[from++];
}
}
}
else {
from = _out - dist; /* copy direct from output */
do { /* minimum length is three */
output[_out++] = output[from++];
output[_out++] = output[from++];
output[_out++] = output[from++];
len -= 3;
} while (len > 2);
if (len) {
output[_out++] = output[from++];
if (len > 1) {
output[_out++] = output[from++];
}
}
}
}
else if ((op & 64) === 0) { /* 2nd level distance code */
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist;
}
else {
strm.msg = 'invalid distance code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
}
else if ((op & 64) === 0) { /* 2nd level length code */
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen;
}
else if (op & 32) { /* end-of-block */
//Tracevv((stderr, "inflate: end of block\n"));
state.mode = TYPE;
break top;
}
else {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
} while (_in < last && _out < end);
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
len = bits >> 3;
_in -= len;
bits -= len << 3;
hold &= (1 << bits) - 1;
/* update state and return */
strm.next_in = _in;
strm.next_out = _out;
strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
state.hold = hold;
state.bits = bits;
return;
};
},{}],49:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
var utils = require('../utils/common');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var inflate_fast = require('./inffast');
var inflate_table = require('./inftrees');
var CODES = 0;
var LENS = 1;
var DISTS = 2;
/* Public constants ==========================================================*/
/* ===========================================================================*/
/* Allowed flush values; see deflate() and inflate() below for details */
//var Z_NO_FLUSH = 0;
//var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH = 2;
//var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_BLOCK = 5;
var Z_TREES = 6;
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_NEED_DICT = 2;
//var Z_ERRNO = -1;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
var Z_MEM_ERROR = -4;
var Z_BUF_ERROR = -5;
//var Z_VERSION_ERROR = -6;
/* The deflate compression method */
var Z_DEFLATED = 8;
/* STATES ====================================================================*/
/* ===========================================================================*/
var HEAD = 1; /* i: waiting for magic header */
var FLAGS = 2; /* i: waiting for method and flags (gzip) */
var TIME = 3; /* i: waiting for modification time (gzip) */
var OS = 4; /* i: waiting for extra flags and operating system (gzip) */
var EXLEN = 5; /* i: waiting for extra length (gzip) */
var EXTRA = 6; /* i: waiting for extra bytes (gzip) */
var NAME = 7; /* i: waiting for end of file name (gzip) */
var COMMENT = 8; /* i: waiting for end of comment (gzip) */
var HCRC = 9; /* i: waiting for header crc (gzip) */
var DICTID = 10; /* i: waiting for dictionary check value */
var DICT = 11; /* waiting for inflateSetDictionary() call */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */
var STORED = 14; /* i: waiting for stored size (length and complement) */
var COPY_ = 15; /* i/o: same as COPY below, but only first time in */
var COPY = 16; /* i/o: waiting for input or output to copy stored block */
var TABLE = 17; /* i: waiting for dynamic block table lengths */
var LENLENS = 18; /* i: waiting for code length code lengths */
var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */
var LEN_ = 20; /* i: same as LEN below, but only first time in */
var LEN = 21; /* i: waiting for length/lit/eob code */
var LENEXT = 22; /* i: waiting for length extra bits */
var DIST = 23; /* i: waiting for distance code */
var DISTEXT = 24; /* i: waiting for distance extra bits */
var MATCH = 25; /* o: waiting for output space to copy string */
var LIT = 26; /* o: waiting for output space to write literal */
var CHECK = 27; /* i: waiting for 32-bit check value */
var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */
var DONE = 29; /* finished check, done -- remain here until reset */
var BAD = 30; /* got a data error -- remain here until reset */
var MEM = 31; /* got an inflate() memory error -- remain here until reset */
var SYNC = 32; /* looking for synchronization bytes to restart inflate() */
/* ===========================================================================*/
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_WBITS = MAX_WBITS;
function zswap32(q) {
return (((q >>> 24) & 0xff) +
((q >>> 8) & 0xff00) +
((q & 0xff00) << 8) +
((q & 0xff) << 24));
}
function InflateState() {
this.mode = 0; /* current inflate mode */
this.last = false; /* true if processing last block */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
this.havedict = false; /* true if dictionary provided */
this.flags = 0; /* gzip header method and flags (0 if zlib) */
this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
this.check = 0; /* protected copy of check value */
this.total = 0; /* protected copy of output count */
// TODO: may be {}
this.head = null; /* where to save gzip header information */
/* sliding window */
this.wbits = 0; /* log base 2 of requested window size */
this.wsize = 0; /* window size or zero if not using window */
this.whave = 0; /* valid bytes in the window */
this.wnext = 0; /* window write index */
this.window = null; /* allocated sliding window, if needed */
/* bit accumulator */
this.hold = 0; /* input bit accumulator */
this.bits = 0; /* number of bits in "in" */
/* for string and stored block copying */
this.length = 0; /* literal or length of data to copy */
this.offset = 0; /* distance back to copy string from */
/* for table and code decoding */
this.extra = 0; /* extra bits needed */
/* fixed and dynamic code tables */
this.lencode = null; /* starting table for length/literal codes */
this.distcode = null; /* starting table for distance codes */
this.lenbits = 0; /* index bits for lencode */
this.distbits = 0; /* index bits for distcode */
/* dynamic table building */
this.ncode = 0; /* number of code length code lengths */
this.nlen = 0; /* number of length code lengths */
this.ndist = 0; /* number of distance code lengths */
this.have = 0; /* number of code lengths in lens[] */
this.next = null; /* next available space in codes[] */
this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
this.work = new utils.Buf16(288); /* work area for code table building */
/*
because we don't have pointers in js, we use lencode and distcode directly
as buffers so we don't need codes
*/
//this.codes = new utils.Buf32(ENOUGH); /* space for code tables */
this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
this.distdyn = null; /* dynamic table for distance codes (JS specific) */
this.sane = 0; /* if false, allow invalid distance too far */
this.back = 0; /* bits back of last unprocessed length/lit */
this.was = 0; /* initial length of match */
}
function inflateResetKeep(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
strm.total_in = strm.total_out = state.total = 0;
strm.msg = ''; /*Z_NULL*/
if (state.wrap) { /* to support ill-conceived Java test suite */
strm.adler = state.wrap & 1;
}
state.mode = HEAD;
state.last = 0;
state.havedict = 0;
state.dmax = 32768;
state.head = null/*Z_NULL*/;
state.hold = 0;
state.bits = 0;
//state.lencode = state.distcode = state.next = state.codes;
state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
state.sane = 1;
state.back = -1;
//Tracev((stderr, "inflate: reset\n"));
return Z_OK;
}
function inflateReset(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
state.wsize = 0;
state.whave = 0;
state.wnext = 0;
return inflateResetKeep(strm);
}
function inflateReset2(strm, windowBits) {
var wrap;
var state;
/* get the state */
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
wrap = 0;
windowBits = -windowBits;
}
else {
wrap = (windowBits >> 4) + 1;
if (windowBits < 48) {
windowBits &= 15;
}
}
/* set number of window bits, free window if different */
if (windowBits && (windowBits < 8 || windowBits > 15)) {
return Z_STREAM_ERROR;
}
if (state.window !== null && state.wbits !== windowBits) {
state.window = null;
}
/* update state and reset the rest of it */
state.wrap = wrap;
state.wbits = windowBits;
return inflateReset(strm);
}
function inflateInit2(strm, windowBits) {
var ret;
var state;
if (!strm) { return Z_STREAM_ERROR; }
//strm.msg = Z_NULL; /* in case we return an error */
state = new InflateState();
//if (state === Z_NULL) return Z_MEM_ERROR;
//Tracev((stderr, "inflate: allocated\n"));
strm.state = state;
state.window = null/*Z_NULL*/;
ret = inflateReset2(strm, windowBits);
if (ret !== Z_OK) {
strm.state = null/*Z_NULL*/;
}
return ret;
}
function inflateInit(strm) {
return inflateInit2(strm, DEF_WBITS);
}
/*
Return state with length and distance decoding tables and index sizes set to
fixed code decoding. Normally this returns fixed tables from inffixed.h.
If BUILDFIXED is defined, then instead this routine builds the tables the
first time it's called, and returns those tables the first time and
thereafter. This reduces the size of the code by about 2K bytes, in
exchange for a little execution time. However, BUILDFIXED should not be
used for threaded applications, since the rewriting of the tables and virgin
may not be thread-safe.
*/
var virgin = true;
var lenfix, distfix; // We have no pointers in JS, so keep tables separate
function fixedtables(state) {
/* build fixed huffman tables if first call (may not be thread safe) */
if (virgin) {
var sym;
lenfix = new utils.Buf32(512);
distfix = new utils.Buf32(32);
/* literal/length table */
sym = 0;
while (sym < 144) { state.lens[sym++] = 8; }
while (sym < 256) { state.lens[sym++] = 9; }
while (sym < 280) { state.lens[sym++] = 7; }
while (sym < 288) { state.lens[sym++] = 8; }
inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 });
/* distance table */
sym = 0;
while (sym < 32) { state.lens[sym++] = 5; }
inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 });
/* do this just once */
virgin = false;
}
state.lencode = lenfix;
state.lenbits = 9;
state.distcode = distfix;
state.distbits = 5;
}
/*
Update the window with the last wsize (normally 32K) bytes written before
returning. If window does not exist yet, create it. This is only called
when a window is already in use, or when output has been written during this
inflate call, but the end of the deflate stream has not been reached yet.
It is also called to create a window for dictionary data when a dictionary
is loaded.
Providing output buffers larger than 32K to inflate() should provide a speed
advantage, since only the last 32K of output is copied to the sliding window
upon return from inflate(), and since all distances after the first 32K of
output will fall in the output data, making match copies simpler and faster.
The advantage may be dependent on the size of the processor's data caches.
*/
function updatewindow(strm, src, end, copy) {
var dist;
var state = strm.state;
/* if it hasn't been done already, allocate space for the window */
if (state.window === null) {
state.wsize = 1 << state.wbits;
state.wnext = 0;
state.whave = 0;
state.window = new utils.Buf8(state.wsize);
}
/* copy state->wsize or less output bytes into the circular window */
if (copy >= state.wsize) {
utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0);
state.wnext = 0;
state.whave = state.wsize;
}
else {
dist = state.wsize - state.wnext;
if (dist > copy) {
dist = copy;
}
//zmemcpy(state->window + state->wnext, end - copy, dist);
utils.arraySet(state.window, src, end - copy, dist, state.wnext);
copy -= dist;
if (copy) {
//zmemcpy(state->window, end - copy, copy);
utils.arraySet(state.window, src, end - copy, copy, 0);
state.wnext = copy;
state.whave = state.wsize;
}
else {
state.wnext += dist;
if (state.wnext === state.wsize) { state.wnext = 0; }
if (state.whave < state.wsize) { state.whave += dist; }
}
}
return 0;
}
function inflate(strm, flush) {
var state;
var input, output; // input/output buffers
var next; /* next input INDEX */
var put; /* next output INDEX */
var have, left; /* available input and output */
var hold; /* bit buffer */
var bits; /* bits in bit buffer */
var _in, _out; /* save starting available input and output */
var copy; /* number of stored or match bytes to copy */
var from; /* where to copy match bytes from */
var from_source;
var here = 0; /* current decoding table entry */
var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
//var last; /* parent table entry */
var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
var len; /* length to copy for repeats, bits to drop */
var ret; /* return code */
var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */
var opts;
var n; // temporary var for NEED_BITS
var order = /* permutation of code lengths */
[ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
if (!strm || !strm.state || !strm.output ||
(!strm.input && strm.avail_in !== 0)) {
return Z_STREAM_ERROR;
}
state = strm.state;
if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
_in = have;
_out = left;
ret = Z_OK;
inf_leave: // goto emulation
for (;;) {
switch (state.mode) {
case HEAD:
if (state.wrap === 0) {
state.mode = TYPEDO;
break;
}
//=== NEEDBITS(16);
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */
state.check = 0/*crc32(0L, Z_NULL, 0)*/;
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = FLAGS;
break;
}
state.flags = 0; /* expect zlib header */
if (state.head) {
state.head.done = false;
}
if (!(state.wrap & 1) || /* check if zlib header allowed */
(((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
strm.msg = 'incorrect header check';
state.mode = BAD;
break;
}
if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
len = (hold & 0x0f)/*BITS(4)*/ + 8;
if (state.wbits === 0) {
state.wbits = len;
}
else if (len > state.wbits) {
strm.msg = 'invalid window size';
state.mode = BAD;
break;
}
state.dmax = 1 << len;
//Tracev((stderr, "inflate: zlib header ok\n"));
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = hold & 0x200 ? DICTID : TYPE;
//=== INITBITS();
hold = 0;
bits = 0;
//===//
break;
case FLAGS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.flags = hold;
if ((state.flags & 0xff) !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
if (state.flags & 0xe000) {
strm.msg = 'unknown header flags set';
state.mode = BAD;
break;
}
if (state.head) {
state.head.text = ((hold >> 8) & 1);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = TIME;
/* falls through */
case TIME:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.time = hold;
}
if (state.flags & 0x0200) {
//=== CRC4(state.check, hold)
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
hbuf[2] = (hold >>> 16) & 0xff;
hbuf[3] = (hold >>> 24) & 0xff;
state.check = crc32(state.check, hbuf, 4, 0);
//===
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = OS;
/* falls through */
case OS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.xflags = (hold & 0xff);
state.head.os = (hold >> 8);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = EXLEN;
/* falls through */
case EXLEN:
if (state.flags & 0x0400) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length = hold;
if (state.head) {
state.head.extra_len = hold;
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
else if (state.head) {
state.head.extra = null/*Z_NULL*/;
}
state.mode = EXTRA;
/* falls through */
case EXTRA:
if (state.flags & 0x0400) {
copy = state.length;
if (copy > have) { copy = have; }
if (copy) {
if (state.head) {
len = state.head.extra_len - state.length;
if (!state.head.extra) {
// Use untyped array for more conveniend processing later
state.head.extra = new Array(state.head.extra_len);
}
utils.arraySet(
state.head.extra,
input,
next,
// extra field is limited to 65536 bytes
// - no need for additional size check
copy,
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
len
);
//zmemcpy(state.head.extra + len, next,
// len + copy > state.head.extra_max ?
// state.head.extra_max - len : copy);
}
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
state.length -= copy;
}
if (state.length) { break inf_leave; }
}
state.length = 0;
state.mode = NAME;
/* falls through */
case NAME:
if (state.flags & 0x0800) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
// TODO: 2 or 1 bytes?
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.name_max*/)) {
state.head.name += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.name = null;
}
state.length = 0;
state.mode = COMMENT;
/* falls through */
case COMMENT:
if (state.flags & 0x1000) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.comm_max*/)) {
state.head.comment += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.comment = null;
}
state.mode = HCRC;
/* falls through */
case HCRC:
if (state.flags & 0x0200) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.check & 0xffff)) {
strm.msg = 'header crc mismatch';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
if (state.head) {
state.head.hcrc = ((state.flags >> 9) & 1);
state.head.done = true;
}
strm.adler = state.check = 0;
state.mode = TYPE;
break;
case DICTID:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
strm.adler = state.check = zswap32(hold);
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = DICT;
/* falls through */
case DICT:
if (state.havedict === 0) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
return Z_NEED_DICT;
}
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = TYPE;
/* falls through */
case TYPE:
if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
/* falls through */
case TYPEDO:
if (state.last) {
//--- BYTEBITS() ---//
hold >>>= bits & 7;
bits -= bits & 7;
//---//
state.mode = CHECK;
break;
}
//=== NEEDBITS(3); */
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.last = (hold & 0x01)/*BITS(1)*/;
//--- DROPBITS(1) ---//
hold >>>= 1;
bits -= 1;
//---//
switch ((hold & 0x03)/*BITS(2)*/) {
case 0: /* stored block */
//Tracev((stderr, "inflate: stored block%s\n",
// state.last ? " (last)" : ""));
state.mode = STORED;
break;
case 1: /* fixed block */
fixedtables(state);
//Tracev((stderr, "inflate: fixed codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = LEN_; /* decode codes */
if (flush === Z_TREES) {
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break inf_leave;
}
break;
case 2: /* dynamic block */
//Tracev((stderr, "inflate: dynamic codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = TABLE;
break;
case 3:
strm.msg = 'invalid block type';
state.mode = BAD;
}
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break;
case STORED:
//--- BYTEBITS() ---// /* go to byte boundary */
hold >>>= bits & 7;
bits -= bits & 7;
//---//
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
strm.msg = 'invalid stored block lengths';
state.mode = BAD;
break;
}
state.length = hold & 0xffff;
//Tracev((stderr, "inflate: stored length %u\n",
// state.length));
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = COPY_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case COPY_:
state.mode = COPY;
/* falls through */
case COPY:
copy = state.length;
if (copy) {
if (copy > have) { copy = have; }
if (copy > left) { copy = left; }
if (copy === 0) { break inf_leave; }
//--- zmemcpy(put, next, copy); ---
utils.arraySet(output, input, next, copy, put);
//---//
have -= copy;
next += copy;
left -= copy;
put += copy;
state.length -= copy;
break;
}
//Tracev((stderr, "inflate: stored end\n"));
state.mode = TYPE;
break;
case TABLE:
//=== NEEDBITS(14); */
while (bits < 14) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
//#ifndef PKZIP_BUG_WORKAROUND
if (state.nlen > 286 || state.ndist > 30) {
strm.msg = 'too many length or distance symbols';
state.mode = BAD;
break;
}
//#endif
//Tracev((stderr, "inflate: table sizes ok\n"));
state.have = 0;
state.mode = LENLENS;
/* falls through */
case LENLENS:
while (state.have < state.ncode) {
//=== NEEDBITS(3);
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
while (state.have < 19) {
state.lens[order[state.have++]] = 0;
}
// We have separate tables & no pointers. 2 commented lines below not needed.
//state.next = state.codes;
//state.lencode = state.next;
// Switch to use dynamic table
state.lencode = state.lendyn;
state.lenbits = 7;
opts = { bits: state.lenbits };
ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
state.lenbits = opts.bits;
if (ret) {
strm.msg = 'invalid code lengths set';
state.mode = BAD;
break;
}
//Tracev((stderr, "inflate: code lengths ok\n"));
state.have = 0;
state.mode = CODELENS;
/* falls through */
case CODELENS:
while (state.have < state.nlen + state.ndist) {
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_val < 16) {
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.lens[state.have++] = here_val;
}
else {
if (here_val === 16) {
//=== NEEDBITS(here.bits + 2);
n = here_bits + 2;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
if (state.have === 0) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
len = state.lens[state.have - 1];
copy = 3 + (hold & 0x03);//BITS(2);
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
}
else if (here_val === 17) {
//=== NEEDBITS(here.bits + 3);
n = here_bits + 3;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 3 + (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
else {
//=== NEEDBITS(here.bits + 7);
n = here_bits + 7;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 11 + (hold & 0x7f);//BITS(7);
//--- DROPBITS(7) ---//
hold >>>= 7;
bits -= 7;
//---//
}
if (state.have + copy > state.nlen + state.ndist) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
while (copy--) {
state.lens[state.have++] = len;
}
}
}
/* handle error breaks in while */
if (state.mode === BAD) { break; }
/* check for end-of-block code (better have one) */
if (state.lens[256] === 0) {
strm.msg = 'invalid code -- missing end-of-block';
state.mode = BAD;
break;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state.lenbits = 9;
opts = { bits: state.lenbits };
ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.lenbits = opts.bits;
// state.lencode = state.next;
if (ret) {
strm.msg = 'invalid literal/lengths set';
state.mode = BAD;
break;
}
state.distbits = 6;
//state.distcode.copy(state.codes);
// Switch to use dynamic table
state.distcode = state.distdyn;
opts = { bits: state.distbits };
ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.distbits = opts.bits;
// state.distcode = state.next;
if (ret) {
strm.msg = 'invalid distances set';
state.mode = BAD;
break;
}
//Tracev((stderr, 'inflate: codes ok\n'));
state.mode = LEN_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case LEN_:
state.mode = LEN;
/* falls through */
case LEN:
if (have >= 6 && left >= 258) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
inflate_fast(strm, _out);
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
if (state.mode === TYPE) {
state.back = -1;
}
break;
}
state.back = 0;
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if (here_bits <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_op && (here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.lencode[last_val +
((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
state.length = here_val;
if (here_op === 0) {
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
state.mode = LIT;
break;
}
if (here_op & 32) {
//Tracevv((stderr, "inflate: end of block\n"));
state.back = -1;
state.mode = TYPE;
break;
}
if (here_op & 64) {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break;
}
state.extra = here_op & 15;
state.mode = LENEXT;
/* falls through */
case LENEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//Tracevv((stderr, "inflate: length %u\n", state.length));
state.was = state.length;
state.mode = DIST;
/* falls through */
case DIST:
for (;;) {
here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if ((here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.distcode[last_val +
((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
if (here_op & 64) {
strm.msg = 'invalid distance code';
state.mode = BAD;
break;
}
state.offset = here_val;
state.extra = (here_op) & 15;
state.mode = DISTEXT;
/* falls through */
case DISTEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//#ifdef INFLATE_STRICT
if (state.offset > state.dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
//#endif
//Tracevv((stderr, "inflate: distance %u\n", state.offset));
state.mode = MATCH;
/* falls through */
case MATCH:
if (left === 0) { break inf_leave; }
copy = _out - left;
if (state.offset > copy) { /* copy from window */
copy = state.offset - copy;
if (copy > state.whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// Trace((stderr, "inflate.c too far\n"));
// copy -= state.whave;
// if (copy > state.length) { copy = state.length; }
// if (copy > left) { copy = left; }
// left -= copy;
// state.length -= copy;
// do {
// output[put++] = 0;
// } while (--copy);
// if (state.length === 0) { state.mode = LEN; }
// break;
//#endif
}
if (copy > state.wnext) {
copy -= state.wnext;
from = state.wsize - copy;
}
else {
from = state.wnext - copy;
}
if (copy > state.length) { copy = state.length; }
from_source = state.window;
}
else { /* copy from output */
from_source = output;
from = put - state.offset;
copy = state.length;
}
if (copy > left) { copy = left; }
left -= copy;
state.length -= copy;
do {
output[put++] = from_source[from++];
} while (--copy);
if (state.length === 0) { state.mode = LEN; }
break;
case LIT:
if (left === 0) { break inf_leave; }
output[put++] = state.length;
left--;
state.mode = LEN;
break;
case CHECK:
if (state.wrap) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
// Use '|' insdead of '+' to make sure that result is signed
hold |= input[next++] << bits;
bits += 8;
}
//===//
_out -= left;
strm.total_out += _out;
state.total += _out;
if (_out) {
strm.adler = state.check =
/*UPDATE(state.check, put - _out, _out);*/
(state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
}
_out = left;
// NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
if ((state.flags ? hold : zswap32(hold)) !== state.check) {
strm.msg = 'incorrect data check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: check matches trailer\n"));
}
state.mode = LENGTH;
/* falls through */
case LENGTH:
if (state.wrap && state.flags) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.total & 0xffffffff)) {
strm.msg = 'incorrect length check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: length matches trailer\n"));
}
state.mode = DONE;
/* falls through */
case DONE:
ret = Z_STREAM_END;
break inf_leave;
case BAD:
ret = Z_DATA_ERROR;
break inf_leave;
case MEM:
return Z_MEM_ERROR;
case SYNC:
/* falls through */
default:
return Z_STREAM_ERROR;
}
}
// inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
/*
Return from inflate(), updating the total counts and the check value.
If there was no progress during the inflate() call, return a buffer
error. Call updatewindow() to create and/or update the window state.
Note: a memory error from inflate() is non-recoverable.
*/
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
(state.mode < CHECK || flush !== Z_FINISH))) {
if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
state.mode = MEM;
return Z_MEM_ERROR;
}
}
_in -= strm.avail_in;
_out -= strm.avail_out;
strm.total_in += _in;
strm.total_out += _out;
state.total += _out;
if (state.wrap && _out) {
strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
(state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
}
strm.data_type = state.bits + (state.last ? 64 : 0) +
(state.mode === TYPE ? 128 : 0) +
(state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
ret = Z_BUF_ERROR;
}
return ret;
}
function inflateEnd(strm) {
if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
return Z_STREAM_ERROR;
}
var state = strm.state;
if (state.window) {
state.window = null;
}
strm.state = null;
return Z_OK;
}
function inflateGetHeader(strm, head) {
var state;
/* check state */
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }
/* save header structure */
state.head = head;
head.done = false;
return Z_OK;
}
function inflateSetDictionary(strm, dictionary) {
var dictLength = dictionary.length;
var state;
var dictid;
var ret;
/* check state */
if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; }
state = strm.state;
if (state.wrap !== 0 && state.mode !== DICT) {
return Z_STREAM_ERROR;
}
/* check for correct dictionary identifier */
if (state.mode === DICT) {
dictid = 1; /* adler32(0, null, 0)*/
/* dictid = adler32(dictid, dictionary, dictLength); */
dictid = adler32(dictid, dictionary, dictLength, 0);
if (dictid !== state.check) {
return Z_DATA_ERROR;
}
}
/* copy dictionary to window using updatewindow(), which will amend the
existing dictionary if appropriate */
ret = updatewindow(strm, dictionary, dictLength, dictLength);
if (ret) {
state.mode = MEM;
return Z_MEM_ERROR;
}
state.havedict = 1;
// Tracev((stderr, "inflate: dictionary set\n"));
return Z_OK;
}
exports.inflateReset = inflateReset;
exports.inflateReset2 = inflateReset2;
exports.inflateResetKeep = inflateResetKeep;
exports.inflateInit = inflateInit;
exports.inflateInit2 = inflateInit2;
exports.inflate = inflate;
exports.inflateEnd = inflateEnd;
exports.inflateGetHeader = inflateGetHeader;
exports.inflateSetDictionary = inflateSetDictionary;
exports.inflateInfo = 'pako inflate (from Nodeca project)';
/* Not implemented
exports.inflateCopy = inflateCopy;
exports.inflateGetDictionary = inflateGetDictionary;
exports.inflateMark = inflateMark;
exports.inflatePrime = inflatePrime;
exports.inflateSync = inflateSync;
exports.inflateSyncPoint = inflateSyncPoint;
exports.inflateUndermine = inflateUndermine;
*/
},{"../utils/common":41,"./adler32":43,"./crc32":45,"./inffast":48,"./inftrees":50}],50:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
var utils = require('../utils/common');
var MAXBITS = 15;
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var CODES = 0;
var LENS = 1;
var DISTS = 2;
var lbase = [ /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
];
var lext = [ /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
];
var dbase = [ /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577, 0, 0
];
var dext = [ /* Distance codes 0..29 extra */
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
28, 28, 29, 29, 64, 64
];
module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
{
var bits = opts.bits;
//here = opts.here; /* table entry for duplication */
var len = 0; /* a code's length in bits */
var sym = 0; /* index of code symbols */
var min = 0, max = 0; /* minimum and maximum code lengths */
var root = 0; /* number of index bits for root table */
var curr = 0; /* number of index bits for current table */
var drop = 0; /* code bits to drop for sub-table */
var left = 0; /* number of prefix codes available */
var used = 0; /* code entries in table used */
var huff = 0; /* Huffman code */
var incr; /* for incrementing code, index */
var fill; /* index for replicating entries */
var low; /* low bits for current root entry */
var mask; /* mask for low root bits */
var next; /* next available space in table */
var base = null; /* base value table to use */
var base_index = 0;
// var shoextra; /* extra bits table to use */
var end; /* use base and extra for symbol > end */
var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */
var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */
var extra = null;
var extra_index = 0;
var here_bits, here_op, here_val;
/*
Process a set of code lengths to create a canonical Huffman code. The
code lengths are lens[0..codes-1]. Each length corresponds to the
symbols 0..codes-1. The Huffman code is generated by first sorting the
symbols by length from short to long, and retaining the symbol order
for codes with equal lengths. Then the code starts with all zero bits
for the first code of the shortest length, and the codes are integer
increments for the same length, and zeros are appended as the length
increases. For the deflate format, these bits are stored backwards
from their more natural integer increment ordering, and so when the
decoding tables are built in the large loop below, the integer codes
are incremented backwards.
This routine assumes, but does not check, that all of the entries in
lens[] are in the range 0..MAXBITS. The caller must assure this.
1..MAXBITS is interpreted as that code length. zero means that that
symbol does not occur in this code.
The codes are sorted by computing a count of codes for each length,
creating from that a table of starting indices for each length in the
sorted table, and then entering the symbols in order in the sorted
table. The sorted table is work[], with that space being provided by
the caller.
The length counts are used for other purposes as well, i.e. finding
the minimum and maximum length codes, determining if there are any
codes at all, checking for a valid set of lengths, and looking ahead
at length counts to determine sub-table sizes when building the
decoding tables.
*/
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
for (len = 0; len <= MAXBITS; len++) {
count[len] = 0;
}
for (sym = 0; sym < codes; sym++) {
count[lens[lens_index + sym]]++;
}
/* bound code lengths, force root to be within code lengths */
root = bits;
for (max = MAXBITS; max >= 1; max--) {
if (count[max] !== 0) { break; }
}
if (root > max) {
root = max;
}
if (max === 0) { /* no symbols to code at all */
//table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
//table.bits[opts.table_index] = 1; //here.bits = (var char)1;
//table.val[opts.table_index++] = 0; //here.val = (var short)0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
//table.op[opts.table_index] = 64;
//table.bits[opts.table_index] = 1;
//table.val[opts.table_index++] = 0;
table[table_index++] = (1 << 24) | (64 << 16) | 0;
opts.bits = 1;
return 0; /* no symbols, but wait for decoding to report error */
}
for (min = 1; min < max; min++) {
if (count[min] !== 0) { break; }
}
if (root < min) {
root = min;
}
/* check for an over-subscribed or incomplete set of lengths */
left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= count[len];
if (left < 0) {
return -1;
} /* over-subscribed */
}
if (left > 0 && (type === CODES || max !== 1)) {
return -1; /* incomplete set */
}
/* generate offsets into symbol table for each length for sorting */
offs[1] = 0;
for (len = 1; len < MAXBITS; len++) {
offs[len + 1] = offs[len] + count[len];
}
/* sort symbols by length, by symbol order within each length */
for (sym = 0; sym < codes; sym++) {
if (lens[lens_index + sym] !== 0) {
work[offs[lens[lens_index + sym]]++] = sym;
}
}
/*
Create and fill in decoding tables. In this loop, the table being
filled is at next and has curr index bits. The code being used is huff
with length len. That code is converted to an index by dropping drop
bits off of the bottom. For codes where len is less than drop + curr,
those top drop + curr - len bits are incremented through all values to
fill the table with replicated entries.
root is the number of index bits for the root table. When len exceeds
root, sub-tables are created pointed to by the root entry with an index
of the low root bits of huff. This is saved in low to check for when a
new sub-table should be started. drop is zero when the root table is
being filled, and drop is root when sub-tables are being filled.
When a new sub-table is needed, it is necessary to look ahead in the
code lengths to determine what size sub-table is needed. The length
counts are used for this, and so count[] is decremented as codes are
entered in the tables.
used keeps track of how many table entries have been allocated from the
provided *table space. It is checked for LENS and DIST tables against
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
the initial root table size constants. See the comments in inftrees.h
for more information.
sym increments through all symbols, and the loop terminates when
all codes of length max, i.e. all codes, have been processed. This
routine permits incomplete codes, so another loop after this one fills
in the rest of the decoding tables with invalid code markers.
*/
/* set up for code type */
// poor man optimization - use if-else instead of switch,
// to avoid deopts in old v8
if (type === CODES) {
base = extra = work; /* dummy value--not used */
end = 19;
} else if (type === LENS) {
base = lbase;
base_index -= 257;
extra = lext;
extra_index -= 257;
end = 256;
} else { /* DISTS */
base = dbase;
extra = dext;
end = -1;
}
/* initialize opts for loop */
huff = 0; /* starting code */
sym = 0; /* starting code symbol */
len = min; /* starting code length */
next = table_index; /* current table to fill in */
curr = root; /* current table index bits */
drop = 0; /* current bits to drop from code for index */
low = -1; /* trigger new sub-table when len > root */
used = 1 << root; /* use root table entries */
mask = used - 1; /* mask for comparing low */
/* check available table space */
if ((type === LENS && used > ENOUGH_LENS) ||
(type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
/* process all codes and make table entries */
for (;;) {
/* create table entry */
here_bits = len - drop;
if (work[sym] < end) {
here_op = 0;
here_val = work[sym];
}
else if (work[sym] > end) {
here_op = extra[extra_index + work[sym]];
here_val = base[base_index + work[sym]];
}
else {
here_op = 32 + 64; /* end of block */
here_val = 0;
}
/* replicate for those indices with low len bits equal to huff */
incr = 1 << (len - drop);
fill = 1 << curr;
min = fill; /* save offset to next table */
do {
fill -= incr;
table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
} while (fill !== 0);
/* backwards increment the len-bit code huff */
incr = 1 << (len - 1);
while (huff & incr) {
incr >>= 1;
}
if (incr !== 0) {
huff &= incr - 1;
huff += incr;
} else {
huff = 0;
}
/* go to next symbol, update count, len */
sym++;
if (--count[len] === 0) {
if (len === max) { break; }
len = lens[lens_index + work[sym]];
}
/* create new sub-table if needed */
if (len > root && (huff & mask) !== low) {
/* if first time, transition to sub-tables */
if (drop === 0) {
drop = root;
}
/* increment past last table */
next += min; /* here min is 1 << curr */
/* determine length of next table */
curr = len - drop;
left = 1 << curr;
while (curr + drop < max) {
left -= count[curr + drop];
if (left <= 0) { break; }
curr++;
left <<= 1;
}
/* check for enough space */
used += 1 << curr;
if ((type === LENS && used > ENOUGH_LENS) ||
(type === DISTS && used > ENOUGH_DISTS)) {
return 1;
}
/* point entry in root table to sub-table */
low = huff & mask;
/*table.op[low] = curr;
table.bits[low] = root;
table.val[low] = next - opts.table_index;*/
table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
}
}
/* fill in remaining table entry if code is incomplete (guaranteed to have
at most one remaining entry, since if the code is incomplete, the
maximum code length that was allowed to get this far is one bit) */
if (huff !== 0) {
//table.op[next + huff] = 64; /* invalid code marker */
//table.bits[next + huff] = len - drop;
//table.val[next + huff] = 0;
table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
}
/* set return parameters */
//opts.table_index += used;
opts.bits = root;
return 0;
};
},{"../utils/common":41}],51:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
module.exports = {
2: 'need dictionary', /* Z_NEED_DICT 2 */
1: 'stream end', /* Z_STREAM_END 1 */
0: '', /* Z_OK 0 */
'-1': 'file error', /* Z_ERRNO (-1) */
'-2': 'stream error', /* Z_STREAM_ERROR (-2) */
'-3': 'data error', /* Z_DATA_ERROR (-3) */
'-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */
'-5': 'buffer error', /* Z_BUF_ERROR (-5) */
'-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
};
},{}],52:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
var utils = require('../utils/common');
/* Public constants ==========================================================*/
/* ===========================================================================*/
//var Z_FILTERED = 1;
//var Z_HUFFMAN_ONLY = 2;
//var Z_RLE = 3;
var Z_FIXED = 4;
//var Z_DEFAULT_STRATEGY = 0;
/* Possible values of the data_type field (though see inflate()) */
var Z_BINARY = 0;
var Z_TEXT = 1;
//var Z_ASCII = 1; // = Z_TEXT
var Z_UNKNOWN = 2;
/*============================================================================*/
function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
// From zutil.h
var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES = 2;
/* The three kinds of block type */
var MIN_MATCH = 3;
var MAX_MATCH = 258;
/* The minimum and maximum match lengths */
// From deflate.h
/* ===========================================================================
* Internal compression state.
*/
var LENGTH_CODES = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS = 256;
/* number of literal bytes 0..255 */
var L_CODES = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES = 30;
/* number of distance codes */
var BL_CODES = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE = 2 * L_CODES + 1;
/* maximum heap size */
var MAX_BITS = 15;
/* All codes must not exceed MAX_BITS bits */
var Buf_size = 16;
/* size of bit buffer in bi_buf */
/* ===========================================================================
* Constants
*/
var MAX_BL_BITS = 7;
/* Bit length codes must not exceed MAX_BL_BITS bits */
var END_BLOCK = 256;
/* end of block literal code */
var REP_3_6 = 16;
/* repeat previous bit length 3-6 times (2 bits of repeat count) */
var REPZ_3_10 = 17;
/* repeat a zero length 3-10 times (3 bits of repeat count) */
var REPZ_11_138 = 18;
/* repeat a zero length 11-138 times (7 bits of repeat count) */
/* eslint-disable comma-spacing,array-bracket-spacing */
var extra_lbits = /* extra bits for each length code */
[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
var extra_dbits = /* extra bits for each distance code */
[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
var extra_blbits = /* extra bits for each bit length code */
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];
var bl_order =
[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
/* eslint-enable comma-spacing,array-bracket-spacing */
/* The lengths of the bit length codes are sent in order of decreasing
* probability, to avoid transmitting the lengths for unused bit length codes.
*/
/* ===========================================================================
* Local data. These are initialized only once.
*/
// We pre-fill arrays with 0 to avoid uninitialized gaps
var DIST_CODE_LEN = 512; /* see definition of array dist_code below */
// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1
var static_ltree = new Array((L_CODES + 2) * 2);
zero(static_ltree);
/* The static literal tree. Since the bit lengths are imposed, there is no
* need for the L_CODES extra codes used during heap construction. However
* The codes 286 and 287 are needed to build a canonical tree (see _tr_init
* below).
*/
var static_dtree = new Array(D_CODES * 2);
zero(static_dtree);
/* The static distance tree. (Actually a trivial tree since all codes use
* 5 bits.)
*/
var _dist_code = new Array(DIST_CODE_LEN);
zero(_dist_code);
/* Distance codes. The first 256 values correspond to the distances
* 3 .. 258, the last 256 values correspond to the top 8 bits of
* the 15 bit distances.
*/
var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1);
zero(_length_code);
/* length code for each normalized match length (0 == MIN_MATCH) */
var base_length = new Array(LENGTH_CODES);
zero(base_length);
/* First normalized length for each code (0 = MIN_MATCH) */
var base_dist = new Array(D_CODES);
zero(base_dist);
/* First normalized distance for each code (0 = distance of 1) */
function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {
this.static_tree = static_tree; /* static tree or NULL */
this.extra_bits = extra_bits; /* extra bits for each code or NULL */
this.extra_base = extra_base; /* base index for extra_bits */
this.elems = elems; /* max number of elements in the tree */
this.max_length = max_length; /* max bit length for the codes */
// show if `static_tree` has data or dummy - needed for monomorphic objects
this.has_stree = static_tree && static_tree.length;
}
var static_l_desc;
var static_d_desc;
var static_bl_desc;
function TreeDesc(dyn_tree, stat_desc) {
this.dyn_tree = dyn_tree; /* the dynamic tree */
this.max_code = 0; /* largest code with non zero frequency */
this.stat_desc = stat_desc; /* the corresponding static tree */
}
function d_code(dist) {
return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
}
/* ===========================================================================
* Output a short LSB first on the stream.
* IN assertion: there is enough room in pendingBuf.
*/
function put_short(s, w) {
// put_byte(s, (uch)((w) & 0xff));
// put_byte(s, (uch)((ush)(w) >> 8));
s.pending_buf[s.pending++] = (w) & 0xff;
s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
}
/* ===========================================================================
* Send a value on a given number of bits.
* IN assertion: length <= 16 and value fits in length bits.
*/
function send_bits(s, value, length) {
if (s.bi_valid > (Buf_size - length)) {
s.bi_buf |= (value << s.bi_valid) & 0xffff;
put_short(s, s.bi_buf);
s.bi_buf = value >> (Buf_size - s.bi_valid);
s.bi_valid += length - Buf_size;
} else {
s.bi_buf |= (value << s.bi_valid) & 0xffff;
s.bi_valid += length;
}
}
function send_code(s, c, tree) {
send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
}
/* ===========================================================================
* Reverse the first len bits of a code, using straightforward code (a faster
* method would use a table)
* IN assertion: 1 <= len <= 15
*/
function bi_reverse(code, len) {
var res = 0;
do {
res |= code & 1;
code >>>= 1;
res <<= 1;
} while (--len > 0);
return res >>> 1;
}
/* ===========================================================================
* Flush the bit buffer, keeping at most 7 bits in it.
*/
function bi_flush(s) {
if (s.bi_valid === 16) {
put_short(s, s.bi_buf);
s.bi_buf = 0;
s.bi_valid = 0;
} else if (s.bi_valid >= 8) {
s.pending_buf[s.pending++] = s.bi_buf & 0xff;
s.bi_buf >>= 8;
s.bi_valid -= 8;
}
}
/* ===========================================================================
* Compute the optimal bit lengths for a tree and update the total bit length
* for the current block.
* IN assertion: the fields freq and dad are set, heap[heap_max] and
* above are the tree nodes sorted by increasing frequency.
* OUT assertions: the field len is set to the optimal bit length, the
* array bl_count contains the frequencies for each bit length.
* The length opt_len is updated; static_len is also updated if stree is
* not null.
*/
function gen_bitlen(s, desc)
// deflate_state *s;
// tree_desc *desc; /* the tree descriptor */
{
var tree = desc.dyn_tree;
var max_code = desc.max_code;
var stree = desc.stat_desc.static_tree;
var has_stree = desc.stat_desc.has_stree;
var extra = desc.stat_desc.extra_bits;
var base = desc.stat_desc.extra_base;
var max_length = desc.stat_desc.max_length;
var h; /* heap index */
var n, m; /* iterate over the tree elements */
var bits; /* bit length */
var xbits; /* extra bits */
var f; /* frequency */
var overflow = 0; /* number of elements with bit length too large */
for (bits = 0; bits <= MAX_BITS; bits++) {
s.bl_count[bits] = 0;
}
/* In a first pass, compute the optimal bit lengths (which may
* overflow in the case of the bit length tree).
*/
tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */
for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
n = s.heap[h];
bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
if (bits > max_length) {
bits = max_length;
overflow++;
}
tree[n * 2 + 1]/*.Len*/ = bits;
/* We overwrite tree[n].Dad which is no longer needed */
if (n > max_code) { continue; } /* not a leaf node */
s.bl_count[bits]++;
xbits = 0;
if (n >= base) {
xbits = extra[n - base];
}
f = tree[n * 2]/*.Freq*/;
s.opt_len += f * (bits + xbits);
if (has_stree) {
s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
}
}
if (overflow === 0) { return; }
// Trace((stderr,"\nbit length overflow\n"));
/* This happens for example on obj2 and pic of the Calgary corpus */
/* Find the first bit length which could increase: */
do {
bits = max_length - 1;
while (s.bl_count[bits] === 0) { bits--; }
s.bl_count[bits]--; /* move one leaf down the tree */
s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
s.bl_count[max_length]--;
/* The brother of the overflow item also moves one step up,
* but this does not affect bl_count[max_length]
*/
overflow -= 2;
} while (overflow > 0);
/* Now recompute all bit lengths, scanning in increasing frequency.
* h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
* lengths instead of fixing only the wrong ones. This idea is taken
* from 'ar' written by Haruhiko Okumura.)
*/
for (bits = max_length; bits !== 0; bits--) {
n = s.bl_count[bits];
while (n !== 0) {
m = s.heap[--h];
if (m > max_code) { continue; }
if (tree[m * 2 + 1]/*.Len*/ !== bits) {
// Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
tree[m * 2 + 1]/*.Len*/ = bits;
}
n--;
}
}
}
/* ===========================================================================
* Generate the codes for a given tree and bit counts (which need not be
* optimal).
* IN assertion: the array bl_count contains the bit length statistics for
* the given tree and the field len is set for all tree elements.
* OUT assertion: the field code is set for all tree elements of non
* zero code length.
*/
function gen_codes(tree, max_code, bl_count)
// ct_data *tree; /* the tree to decorate */
// int max_code; /* largest code with non zero frequency */
// ushf *bl_count; /* number of codes at each bit length */
{
var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */
var code = 0; /* running code value */
var bits; /* bit index */
var n; /* code index */
/* The distribution counts are first used to generate the code values
* without bit reversal.
*/
for (bits = 1; bits <= MAX_BITS; bits++) {
next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
}
/* Check that the bit counts in bl_count are consistent. The last code
* must be all ones.
*/
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
// "inconsistent bit counts");
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
for (n = 0; n <= max_code; n++) {
var len = tree[n * 2 + 1]/*.Len*/;
if (len === 0) { continue; }
/* Now reverse the bits */
tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);
//Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
// n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
}
}
/* ===========================================================================
* Initialize the various 'constant' tables.
*/
function tr_static_init() {
var n; /* iterates over tree elements */
var bits; /* bit counter */
var length; /* length value */
var code; /* code value */
var dist; /* distance index */
var bl_count = new Array(MAX_BITS + 1);
/* number of codes at each bit length for an optimal tree */
// do check in _tr_init()
//if (static_init_done) return;
/* For some embedded targets, global variables are not initialized: */
/*#ifdef NO_INIT_GLOBAL_POINTERS
static_l_desc.static_tree = static_ltree;
static_l_desc.extra_bits = extra_lbits;
static_d_desc.static_tree = static_dtree;
static_d_desc.extra_bits = extra_dbits;
static_bl_desc.extra_bits = extra_blbits;
#endif*/
/* Initialize the mapping length (0..255) -> length code (0..28) */
length = 0;
for (code = 0; code < LENGTH_CODES - 1; code++) {
base_length[code] = length;
for (n = 0; n < (1 << extra_lbits[code]); n++) {
_length_code[length++] = code;
}
}
//Assert (length == 256, "tr_static_init: length != 256");
/* Note that the length 255 (match length 258) can be represented
* in two different ways: code 284 + 5 bits or code 285, so we
* overwrite length_code[255] to use the best encoding:
*/
_length_code[length - 1] = code;
/* Initialize the mapping dist (0..32K) -> dist code (0..29) */
dist = 0;
for (code = 0; code < 16; code++) {
base_dist[code] = dist;
for (n = 0; n < (1 << extra_dbits[code]); n++) {
_dist_code[dist++] = code;
}
}
//Assert (dist == 256, "tr_static_init: dist != 256");
dist >>= 7; /* from now on, all distances are divided by 128 */
for (; code < D_CODES; code++) {
base_dist[code] = dist << 7;
for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
_dist_code[256 + dist++] = code;
}
}
//Assert (dist == 256, "tr_static_init: 256+dist != 512");
/* Construct the codes of the static literal tree */
for (bits = 0; bits <= MAX_BITS; bits++) {
bl_count[bits] = 0;
}
n = 0;
while (n <= 143) {
static_ltree[n * 2 + 1]/*.Len*/ = 8;
n++;
bl_count[8]++;
}
while (n <= 255) {
static_ltree[n * 2 + 1]/*.Len*/ = 9;
n++;
bl_count[9]++;
}
while (n <= 279) {
static_ltree[n * 2 + 1]/*.Len*/ = 7;
n++;
bl_count[7]++;
}
while (n <= 287) {
static_ltree[n * 2 + 1]/*.Len*/ = 8;
n++;
bl_count[8]++;
}
/* Codes 286 and 287 do not exist, but we must include them in the
* tree construction to get a canonical Huffman tree (longest code
* all ones)
*/
gen_codes(static_ltree, L_CODES + 1, bl_count);
/* The static distance tree is trivial: */
for (n = 0; n < D_CODES; n++) {
static_dtree[n * 2 + 1]/*.Len*/ = 5;
static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
}
// Now data ready and we can init static trees
static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS);
static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS);
//static_init_done = true;
}
/* ===========================================================================
* Initialize a new block.
*/
function init_block(s) {
var n; /* iterates over tree elements */
/* Initialize the trees. */
for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }
for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }
for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }
s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
s.opt_len = s.static_len = 0;
s.last_lit = s.matches = 0;
}
/* ===========================================================================
* Flush the bit buffer and align the output on a byte boundary
*/
function bi_windup(s)
{
if (s.bi_valid > 8) {
put_short(s, s.bi_buf);
} else if (s.bi_valid > 0) {
//put_byte(s, (Byte)s->bi_buf);
s.pending_buf[s.pending++] = s.bi_buf;
}
s.bi_buf = 0;
s.bi_valid = 0;
}
/* ===========================================================================
* Copy a stored block, storing first the length and its
* one's complement if requested.
*/
function copy_block(s, buf, len, header)
//DeflateState *s;
//charf *buf; /* the input data */
//unsigned len; /* its length */
//int header; /* true if block header must be written */
{
bi_windup(s); /* align on byte boundary */
if (header) {
put_short(s, len);
put_short(s, ~len);
}
// while (len--) {
// put_byte(s, *buf++);
// }
utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
s.pending += len;
}
/* ===========================================================================
* Compares to subtrees, using the tree depth as tie breaker when
* the subtrees have equal frequency. This minimizes the worst case length.
*/
function smaller(tree, n, m, depth) {
var _n2 = n * 2;
var _m2 = m * 2;
return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
(tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
}
/* ===========================================================================
* Restore the heap property by moving down the tree starting at node k,
* exchanging a node with the smallest of its two sons if necessary, stopping
* when the heap property is re-established (each father smaller than its
* two sons).
*/
function pqdownheap(s, tree, k)
// deflate_state *s;
// ct_data *tree; /* the tree to restore */
// int k; /* node to move down */
{
var v = s.heap[k];
var j = k << 1; /* left son of k */
while (j <= s.heap_len) {
/* Set j to the smallest of the two sons: */
if (j < s.heap_len &&
smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
j++;
}
/* Exit if v is smaller than both sons */
if (smaller(tree, v, s.heap[j], s.depth)) { break; }
/* Exchange v with the smallest son */
s.heap[k] = s.heap[j];
k = j;
/* And continue down the tree, setting j to the left son of k */
j <<= 1;
}
s.heap[k] = v;
}
// inlined manually
// var SMALLEST = 1;
/* ===========================================================================
* Send the block data compressed using the given Huffman trees
*/
function compress_block(s, ltree, dtree)
// deflate_state *s;
// const ct_data *ltree; /* literal tree */
// const ct_data *dtree; /* distance tree */
{
var dist; /* distance of matched string */
var lc; /* match length or unmatched char (if dist == 0) */
var lx = 0; /* running index in l_buf */
var code; /* the code to send */
var extra; /* number of extra bits to send */
if (s.last_lit !== 0) {
do {
dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);
lc = s.pending_buf[s.l_buf + lx];
lx++;
if (dist === 0) {
send_code(s, lc, ltree); /* send a literal byte */
//Tracecv(isgraph(lc), (stderr," '%c' ", lc));
} else {
/* Here, lc is the match length - MIN_MATCH */
code = _length_code[lc];
send_code(s, code + LITERALS + 1, ltree); /* send the length code */
extra = extra_lbits[code];
if (extra !== 0) {
lc -= base_length[code];
send_bits(s, lc, extra); /* send the extra length bits */
}
dist--; /* dist is now the match distance - 1 */
code = d_code(dist);
//Assert (code < D_CODES, "bad d_code");
send_code(s, code, dtree); /* send the distance code */
extra = extra_dbits[code];
if (extra !== 0) {
dist -= base_dist[code];
send_bits(s, dist, extra); /* send the extra distance bits */
}
} /* literal or match pair ? */
/* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
//Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
// "pendingBuf overflow");
} while (lx < s.last_lit);
}
send_code(s, END_BLOCK, ltree);
}
/* ===========================================================================
* Construct one Huffman tree and assigns the code bit strings and lengths.
* Update the total bit length for the current block.
* IN assertion: the field freq is set for all tree elements.
* OUT assertions: the fields len and code are set to the optimal bit length
* and corresponding code. The length opt_len is updated; static_len is
* also updated if stree is not null. The field max_code is set.
*/
function build_tree(s, desc)
// deflate_state *s;
// tree_desc *desc; /* the tree descriptor */
{
var tree = desc.dyn_tree;
var stree = desc.stat_desc.static_tree;
var has_stree = desc.stat_desc.has_stree;
var elems = desc.stat_desc.elems;
var n, m; /* iterate over heap elements */
var max_code = -1; /* largest code with non zero frequency */
var node; /* new node being created */
/* Construct the initial heap, with least frequent element in
* heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
* heap[0] is not used.
*/
s.heap_len = 0;
s.heap_max = HEAP_SIZE;
for (n = 0; n < elems; n++) {
if (tree[n * 2]/*.Freq*/ !== 0) {
s.heap[++s.heap_len] = max_code = n;
s.depth[n] = 0;
} else {
tree[n * 2 + 1]/*.Len*/ = 0;
}
}
/* The pkzip format requires that at least one distance code exists,
* and that at least one bit should be sent even if there is only one
* possible code. So to avoid special checks later on we force at least
* two codes of non zero frequency.
*/
while (s.heap_len < 2) {
node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
tree[node * 2]/*.Freq*/ = 1;
s.depth[node] = 0;
s.opt_len--;
if (has_stree) {
s.static_len -= stree[node * 2 + 1]/*.Len*/;
}
/* node is 0 or 1 so it does not have extra bits */
}
desc.max_code = max_code;
/* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
* establish sub-heaps of increasing lengths:
*/
for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }
/* Construct the Huffman tree by repeatedly combining the least two
* frequent nodes.
*/
node = elems; /* next internal node of the tree */
do {
//pqremove(s, tree, n); /* n = node of least frequency */
/*** pqremove ***/
n = s.heap[1/*SMALLEST*/];
s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
pqdownheap(s, tree, 1/*SMALLEST*/);
/***/
m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */
s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
s.heap[--s.heap_max] = m;
/* Create a new node father of n and m */
tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;
/* and insert the new node in the heap */
s.heap[1/*SMALLEST*/] = node++;
pqdownheap(s, tree, 1/*SMALLEST*/);
} while (s.heap_len >= 2);
s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];
/* At this point, the fields freq and dad are set. We can now
* generate the bit lengths.
*/
gen_bitlen(s, desc);
/* The field len is now set, we can generate the bit codes */
gen_codes(tree, max_code, s.bl_count);
}
/* ===========================================================================
* Scan a literal or distance tree to determine the frequencies of the codes
* in the bit length tree.
*/
function scan_tree(s, tree, max_code)
// deflate_state *s;
// ct_data *tree; /* the tree to be scanned */
// int max_code; /* and its largest code of non zero frequency */
{
var n; /* iterates over all tree elements */
var prevlen = -1; /* last emitted length */
var curlen; /* length of current code */
var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */
var count = 0; /* repeat count of the current code */
var max_count = 7; /* max repeat count */
var min_count = 4; /* min repeat count */
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;
if (++count < max_count && curlen === nextlen) {
continue;
} else if (count < min_count) {
s.bl_tree[curlen * 2]/*.Freq*/ += count;
} else if (curlen !== 0) {
if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
s.bl_tree[REP_3_6 * 2]/*.Freq*/++;
} else if (count <= 10) {
s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;
} else {
s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen === nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
/* ===========================================================================
* Send a literal or distance tree in compressed form, using the codes in
* bl_tree.
*/
function send_tree(s, tree, max_code)
// deflate_state *s;
// ct_data *tree; /* the tree to be scanned */
// int max_code; /* and its largest code of non zero frequency */
{
var n; /* iterates over all tree elements */
var prevlen = -1; /* last emitted length */
var curlen; /* length of current code */
var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */
var count = 0; /* repeat count of the current code */
var max_count = 7; /* max repeat count */
var min_count = 4; /* min repeat count */
/* tree[max_code+1].Len = -1; */ /* guard already set */
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;
if (++count < max_count && curlen === nextlen) {
continue;
} else if (count < min_count) {
do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);
} else if (curlen !== 0) {
if (curlen !== prevlen) {
send_code(s, curlen, s.bl_tree);
count--;
}
//Assert(count >= 3 && count <= 6, " 3_6?");
send_code(s, REP_3_6, s.bl_tree);
send_bits(s, count - 3, 2);
} else if (count <= 10) {
send_code(s, REPZ_3_10, s.bl_tree);
send_bits(s, count - 3, 3);
} else {
send_code(s, REPZ_11_138, s.bl_tree);
send_bits(s, count - 11, 7);
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen === nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
/* ===========================================================================
* Construct the Huffman tree for the bit lengths and return the index in
* bl_order of the last bit length code to send.
*/
function build_bl_tree(s) {
var max_blindex; /* index of last bit length code of non zero freq */
/* Determine the bit length frequencies for literal and distance trees */
scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
/* Build the bit length tree: */
build_tree(s, s.bl_desc);
/* opt_len now includes the length of the tree representations, except
* the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
*/
/* Determine the number of bit length codes to send. The pkzip format
* requires that at least 4 bit length codes be sent. (appnote.txt says
* 3 but the actual value used is 4.)
*/
for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {
break;
}
}
/* Update opt_len to include the bit length tree and counts */
s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
//Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
// s->opt_len, s->static_len));
return max_blindex;
}
/* ===========================================================================
* Send the header for a block using dynamic Huffman trees: the counts, the
* lengths of the bit length codes, the literal tree and the distance tree.
* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
*/
function send_all_trees(s, lcodes, dcodes, blcodes)
// deflate_state *s;
// int lcodes, dcodes, blcodes; /* number of codes for each tree */
{
var rank; /* index in bl_order */
//Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
//Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
// "too many codes");
//Tracev((stderr, "\nbl counts: "));
send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
send_bits(s, dcodes - 1, 5);
send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */
for (rank = 0; rank < blcodes; rank++) {
//Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);
}
//Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
//Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
//Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}
/* ===========================================================================
* Check if the data type is TEXT or BINARY, using the following algorithm:
* - TEXT if the two conditions below are satisfied:
* a) There are no non-portable control characters belonging to the
* "black list" (0..6, 14..25, 28..31).
* b) There is at least one printable character belonging to the
* "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
* - BINARY otherwise.
* - The following partially-portable control characters form a
* "gray list" that is ignored in this detection algorithm:
* (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
* IN assertion: the fields Freq of dyn_ltree are set.
*/
function detect_data_type(s) {
/* black_mask is the bit mask of black-listed bytes
* set bits 0..6, 14..25, and 28..31
* 0xf3ffc07f = binary 11110011111111111100000001111111
*/
var black_mask = 0xf3ffc07f;
var n;
/* Check for non-textual ("black-listed") bytes. */
for (n = 0; n <= 31; n++, black_mask >>>= 1) {
if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {
return Z_BINARY;
}
}
/* Check for textual ("white-listed") bytes. */
if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
return Z_TEXT;
}
for (n = 32; n < LITERALS; n++) {
if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
return Z_TEXT;
}
}
/* There are no "black-listed" or "white-listed" bytes:
* this stream either is empty or has tolerated ("gray-listed") bytes only.
*/
return Z_BINARY;
}
var static_init_done = false;
/* ===========================================================================
* Initialize the tree data structures for a new zlib stream.
*/
function _tr_init(s)
{
if (!static_init_done) {
tr_static_init();
static_init_done = true;
}
s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
s.bi_buf = 0;
s.bi_valid = 0;
/* Initialize the first block of the first file: */
init_block(s);
}
/* ===========================================================================
* Send a stored block
*/
function _tr_stored_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf; /* input block */
//ulg stored_len; /* length of input block */
//int last; /* one if this is the last block for a file */
{
send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */
copy_block(s, buf, stored_len, true); /* with header */
}
/* ===========================================================================
* Send one empty static block to give enough lookahead for inflate.
* This takes 10 bits, of which 7 may remain in the bit buffer.
*/
function _tr_align(s) {
send_bits(s, STATIC_TREES << 1, 3);
send_code(s, END_BLOCK, static_ltree);
bi_flush(s);
}
/* ===========================================================================
* Determine the best encoding for the current block: dynamic trees, static
* trees or store, and output the encoded block to the zip file.
*/
function _tr_flush_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf; /* input block, or NULL if too old */
//ulg stored_len; /* length of input block */
//int last; /* one if this is the last block for a file */
{
var opt_lenb, static_lenb; /* opt_len and static_len in bytes */
var max_blindex = 0; /* index of last bit length code of non zero freq */
/* Build the Huffman trees unless a stored block is forced */
if (s.level > 0) {
/* Check if the file is binary or text */
if (s.strm.data_type === Z_UNKNOWN) {
s.strm.data_type = detect_data_type(s);
}
/* Construct the literal and distance trees */
build_tree(s, s.l_desc);
// Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
// s->static_len));
build_tree(s, s.d_desc);
// Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
// s->static_len));
/* At this point, opt_len and static_len are the total bit lengths of
* the compressed block data, excluding the tree representations.
*/
/* Build the bit length tree for the above two trees, and get the index
* in bl_order of the last bit length code to send.
*/
max_blindex = build_bl_tree(s);
/* Determine the best encoding. Compute the block lengths in bytes. */
opt_lenb = (s.opt_len + 3 + 7) >>> 3;
static_lenb = (s.static_len + 3 + 7) >>> 3;
// Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
// opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
// s->last_lit));
if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }
} else {
// Assert(buf != (char*)0, "lost buf");
opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
}
if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {
/* 4: two words for the lengths */
/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
* Otherwise we can't have processed more than WSIZE input bytes since
* the last block flush, because compression would have been
* successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
* transform a block into a stored block.
*/
_tr_stored_block(s, buf, stored_len, last);
} else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {
send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
compress_block(s, static_ltree, static_dtree);
} else {
send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
compress_block(s, s.dyn_ltree, s.dyn_dtree);
}
// Assert (s->compressed_len == s->bits_sent, "bad compressed size");
/* The above check is made mod 2^32, for files larger than 512 MB
* and uLong implemented on 32 bits.
*/
init_block(s);
if (last) {
bi_windup(s);
}
// Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
// s->compressed_len-7*last));
}
/* ===========================================================================
* Save the match info and tally the frequency counts. Return true if
* the current block must be flushed.
*/
function _tr_tally(s, dist, lc)
// deflate_state *s;
// unsigned dist; /* distance of matched string */
// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
//var out_length, in_length, dcode;
s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff;
s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;
s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
s.last_lit++;
if (dist === 0) {
/* lc is the unmatched char */
s.dyn_ltree[lc * 2]/*.Freq*/++;
} else {
s.matches++;
/* Here, lc is the match length - MIN_MATCH */
dist--; /* dist = match distance - 1 */
//Assert((ush)dist < (ush)MAX_DIST(s) &&
// (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
// (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;
s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef TRUNCATE_BLOCK
// /* Try to guess if it is profitable to stop the current block here */
// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
// /* Compute an upper bound for the compressed length */
// out_length = s.last_lit*8;
// in_length = s.strstart - s.block_start;
//
// for (dcode = 0; dcode < D_CODES; dcode++) {
// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
// }
// out_length >>>= 3;
// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
// // s->last_lit, in_length, out_length,
// // 100L - out_length*100L/in_length));
// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
// return true;
// }
// }
//#endif
return (s.last_lit === s.lit_bufsize - 1);
/* We avoid equality with lit_bufsize because of wraparound at 64K
* on 16 bit machines and because stored blocks are restricted to
* 64K-1 bytes.
*/
}
exports._tr_init = _tr_init;
exports._tr_stored_block = _tr_stored_block;
exports._tr_flush_block = _tr_flush_block;
exports._tr_tally = _tr_tally;
exports._tr_align = _tr_align;
},{"../utils/common":41}],53:[function(require,module,exports){
'use strict';
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
function ZStream() {
/* next input byte */
this.input = null; // JS specific, because we have no pointers
this.next_in = 0;
/* number of bytes available at input */
this.avail_in = 0;
/* total number of input bytes read so far */
this.total_in = 0;
/* next output byte should be put there */
this.output = null; // JS specific, because we have no pointers
this.next_out = 0;
/* remaining free space at output */
this.avail_out = 0;
/* total number of bytes output so far */
this.total_out = 0;
/* last error message, NULL if no error */
this.msg = ''/*Z_NULL*/;
/* not visible by applications */
this.state = null;
/* best guess about the data type: binary or text */
this.data_type = 2/*Z_UNKNOWN*/;
/* adler32 value of the uncompressed data */
this.adler = 0;
}
module.exports = ZStream;
},{}],54:[function(require,module,exports){
(function (global){
(function (global, undefined) {
"use strict";
if (global.setImmediate) {
return;
}
var nextHandle = 1; // Spec says greater than zero
var tasksByHandle = {};
var currentlyRunningATask = false;
var doc = global.document;
var registerImmediate;
function setImmediate(callback) {
// Callback can either be a function or a string
if (typeof callback !== "function") {
callback = new Function("" + callback);
}
// Copy function arguments
var args = new Array(arguments.length - 1);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i + 1];
}
// Store and register the task
var task = { callback: callback, args: args };
tasksByHandle[nextHandle] = task;
registerImmediate(nextHandle);
return nextHandle++;
}
function clearImmediate(handle) {
delete tasksByHandle[handle];
}
function run(task) {
var callback = task.callback;
var args = task.args;
switch (args.length) {
case 0:
callback();
break;
case 1:
callback(args[0]);
break;
case 2:
callback(args[0], args[1]);
break;
case 3:
callback(args[0], args[1], args[2]);
break;
default:
callback.apply(undefined, args);
break;
}
}
function runIfPresent(handle) {
// From the spec: "Wait until any invocations of this algorithm started before this one have completed."
// So if we're currently running a task, we'll need to delay this invocation.
if (currentlyRunningATask) {
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
// "too much recursion" error.
setTimeout(runIfPresent, 0, handle);
} else {
var task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
run(task);
} finally {
clearImmediate(handle);
currentlyRunningATask = false;
}
}
}
}
function installNextTickImplementation() {
registerImmediate = function(handle) {
process.nextTick(function () { runIfPresent(handle); });
};
}
function canUsePostMessage() {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can't be used for this purpose.
if (global.postMessage && !global.importScripts) {
var postMessageIsAsynchronous = true;
var oldOnMessage = global.onmessage;
global.onmessage = function() {
postMessageIsAsynchronous = false;
};
global.postMessage("", "*");
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
}
function installPostMessageImplementation() {
// Installs an event handler on `global` for the `message` event: see
// * https://developer.mozilla.org/en/DOM/window.postMessage
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
var messagePrefix = "setImmediate$" + Math.random() + "$";
var onGlobalMessage = function(event) {
if (event.source === global &&
typeof event.data === "string" &&
event.data.indexOf(messagePrefix) === 0) {
runIfPresent(+event.data.slice(messagePrefix.length));
}
};
if (global.addEventListener) {
global.addEventListener("message", onGlobalMessage, false);
} else {
global.attachEvent("onmessage", onGlobalMessage);
}
registerImmediate = function(handle) {
global.postMessage(messagePrefix + handle, "*");
};
}
function installMessageChannelImplementation() {
var channel = new MessageChannel();
channel.port1.onmessage = function(event) {
var handle = event.data;
runIfPresent(handle);
};
registerImmediate = function(handle) {
channel.port2.postMessage(handle);
};
}
function installReadyStateChangeImplementation() {
var html = doc.documentElement;
registerImmediate = function(handle) {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var script = doc.createElement("script");
script.onreadystatechange = function () {
runIfPresent(handle);
script.onreadystatechange = null;
html.removeChild(script);
script = null;
};
html.appendChild(script);
};
}
function installSetTimeoutImplementation() {
registerImmediate = function(handle) {
setTimeout(runIfPresent, 0, handle);
};
}
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
// Don't get fooled by e.g. browserify environments.
if ({}.toString.call(global.process) === "[object process]") {
// For Node.js before 0.9
installNextTickImplementation();
} else if (canUsePostMessage()) {
// For non-IE10 modern browsers
installPostMessageImplementation();
} else if (global.MessageChannel) {
// For web workers, where supported
installMessageChannelImplementation();
} else if (doc && "onreadystatechange" in doc.createElement("script")) {
// For IE 6–8
installReadyStateChangeImplementation();
} else {
// For older browsers
installSetTimeoutImplementation();
}
attachTo.setImmediate = setImmediate;
attachTo.clearImmediate = clearImmediate;
}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[10])(10)
});
//}}}
/***
|Name|KeyboardPlugin|
|License|[[TW Notes License]]|
|Requires|[[jquery.keyboard.js]] [[UpdateTaggedTiddlersOnSaveChangesPlugin]]|
<<keyboard layout desc:yes>>
<<keyboard altKeys desc:yes>>
<<option chkKeyboardLockInput desc:yes>>
<<option chkKeyboardSound desc:yes>>
<<option chkKeyboardVibrate desc:yes>>
<<option txtKeyboardAutoWidth desc:yes>>
<<option txtKeyboardMaxEditRows desc:yes>>
<<option txtKeyboardKeyHighlightDelay desc:yes>>
!!!!!Code
***/
//{{{
if (config.options.txtKeyboardLayout === undefined) {
config.options.txtKeyboardLayout = "tw-qwerty";
}
if (config.options.txtKeyboardAltKeys === undefined) {
config.options.txtKeyboardAltKeys = "default";
}
if (config.options.chkKeyboardLockInput === undefined) {
config.options.chkKeyboardLockInput = false;
}
if (config.options.chkKeyboardSound === undefined) {
config.options.chkKeyboardSound = false;
}
if (config.options.chkKeyboardVibrate === undefined) {
config.options.chkKeyboardVibrate = false;
}
if (config.options.txtKeyboardAutoWidth === undefined) {
config.options.txtKeyboardAutoWidth = "";
}
if (config.options.txtKeyboardMaxEditRows === undefined) {
config.options.txtKeyboardMaxEditRows = "";
}
if (config.options.txtKeyboardKeyHighlightDelay === undefined) {
config.options.txtKeyboardKeyHighlightDelay = "";
}
merge(config.optionsDesc, {
txtKeyboardLayout: "The keyboard layout used by the KeyboardPlugin (blank to disable)",
txtKeyboardAltKeys: "The alternative key set used by the KeyboardPlugin (blank to disable)",
chkKeyboardLockInput: "Lock the input when using the keyboard, stopping the native keyboard from opening",
chkKeyboardSound: "Play a key click sound when pressing keyboard keys",
chkKeyboardVibrate: "Vibrate the device when pressing keyboard keys",
txtKeyboardAutoWidth: "Adjusted keyboard's width with respect to the tiddler EditTemplate input's width (for example: 0.5 = 50%, blank to disable)",
txtKeyboardMaxEditRows: "Maximum number of tiddler EditTemplate rows displayed (blank to disable)",
txtKeyboardKeyHighlightDelay: "The time in milliseconds that a keyboard key remains highlighted after being clicked (blank to disable)"
});
//}}}
//{{{
(function () {
var keyboard = store.getTiddlerText("jquery.keyboard.js");
var index;
keyboard = keyboard.replace("span = document.createElement( 'span' );", "var positionSpan = document.createElement('span'); positionSpan.textContent = '\u2060'; div.appendChild(positionSpan); span = document.createElement( 'span' );");
keyboard = keyboard.replace("offset = $(span).position();", "offset = $(positionSpan).position();");
keyboard = keyboard.replace("if (!base.isContentEditable && base.last.virtual) {", "if (!base.isContentEditable && base.last.virtual && (!base.options.scrollAtEnd || ($.keyboard.caret(base.$preview).start >= base.getValue().length - 1))) {");
keyboard = keyboard.replace("base.options.repeatRate = 0;", "base.options.altKeysRepeatRate = base.options.repeatRate; base.options.repeatRate = 0;");
index = keyboard.indexOf("})", keyboard.indexOf("// add hold key functionality for popups"));
keyboard = keyboard.substring(0, index) + "else if ((base.options.altKeysRepeatRate !== 0) && !(!$key || ($key.hasClass(jQuery.keyboard.css.keyAction) && !$key.is('.' + jQuery.keyboard.css.keyPrefix + ('tab bksp space enter'.split(' ').join(',.' + jQuery.keyboard.css.keyPrefix)))))) { base.mouseRepeat = [true, $key]; setTimeout(function () { if (base && base.mouseRepeat[0] && base.mouseRepeat[1] === $key && !$key[0].disabled) { base.repeatKey($key); } }, base.options.repeatDelay); }\n" + keyboard.substring(index);
index = keyboard.indexOf("touchstart", keyboard.indexOf("base.altkeypopup_setup = function() {"));
keyboard = keyboard.substring(0, index) + "contextmenu " + keyboard.substring(index);
index = keyboard.indexOf("})", index);
keyboard = keyboard.substring(0, index) + "return false; " + keyboard.substring(index);
keyboard = keyboard.replace("handler.call(this, e);", "return handler.call(this, e);");
index = keyboard.indexOf("base.altKeyPopup_close();", keyboard.indexOf("// action/value now processed by core functions"));
keyboard = keyboard.substring(0, index) + keyboard.substring(index + "base.altKeyPopup_close();".length);
index = keyboard.indexOf("touchstart", keyboard.indexOf("// overlay keyboard"));
keyboard = keyboard.substring(0, index) + "touchend" + keyboard.substring(index + "touchstart".length);
index = keyboard.indexOf("key in $keyboard.altKeys", keyboard.indexOf("// add hold key functionality for popups"));
keyboard = keyboard.substring(0, index) + "!base.altkeypopup_popupVisible && " + keyboard.substring(index);
index = keyboard.indexOf("base.$el.trigger", keyboard.indexOf("// trigger popup visible event"));
keyboard = keyboard.substring(0, index) + "base.altkeypopup_popupVisible = true;\n" + keyboard.substring(index);
index = keyboard.indexOf("base.altkeypopup_blockingFlag", keyboard.indexOf("base.altKeyPopup_close = function() {"));
keyboard = keyboard.substring(0, index) + "base.altkeypopup_popupVisible = false;\n" + keyboard.substring(index);
keyboard = keyboard.replace("data.popupTop = positionVert < top ? top : positionVert;", "data.popupTop = positionVert;");
eval(keyboard);
})();
//}}}
//{{{
jQuery.keyboard.layouts["tw-qwerty"] = {
"normal": [
"` 1 2 3 4 5 6 7 8 9 0 - = {bksp}",
"{tab} q w e r t y u i o p [ ] \\",
"a s d f g h j k l ; ' {enter}",
"{shift} z x c v b n m , . / {shift}",
"{meta_ctrl} {space} {alt}"
],
"shift": [
"~ ! @ # $ % ^ & * ( ) _ + {bksp}",
"{tab} Q W E R T Y U I O P { } |",
"A S D F G H J K L : \" {enter}",
"{shift} Z X C V B N M < > ? {shift}",
"{meta_ctrl} {space} {alt}"
],
"meta_ctrl": [
"{empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty_wide}",
"{empty}",
"{empty}",
"{empty_wide} {empty} {empty} {empty} {paste} {empty} {empty} {empty} {empty} {empty} {empty} {empty_wide}",
"{meta_ctrl} {empty_spacebar} {empty_wide}"
],
"alt": [
"{empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty} {empty_wide}",
"{left} {sp:1.0em} {clip} {sp:1.0em} {hr} {sp:1.0em} {checkbox} {sp:1.0em} {right}",
"{accept} {sp:1.5em} {blink} {sp:1.0em} {marquee} {sp:1.5em} {cancel}",
"{empty}",
"{empty_wide} {empty_spacebar} {alt}"
]
};
jQuery.keyboard.layouts["tw-qwerty"]["alt-shift"] = jQuery.keyboard.layouts["tw-qwerty"]["alt"];
//}}}
//{{{
jQuery.extend(jQuery.keyboard.language.en.display, {
blink: "<blink>blink</blink>",
checkbox: "\u2610:Checkbox",
clip: "\u{1f4cb}:Clip",
empty_spacebar: "<empty_spacebar/>",
empty_wide: "<empty_wide/>",
hr: "\u2015:Horizontal rule",
marquee: "<marquee width='auto' height='1em'>marquee</marquee>",
meta_ctrl: "Ctrl:Ctrl",
paste: "v:Paste"
});
jQuery.extend(jQuery.keyboard.keyaction, {
blink: function (keyboard) {
keyboard.insertText('<blink></blink>');
var p = jQuery.keyboard.caret(keyboard.$preview);
keyboard.last.start = p.start - "</blink>".length;
keyboard.last.end = p.start - "</blink>".length;
jQuery.keyboard.caret(keyboard.$preview, keyboard.last);
keyboard.setScroll();
},
cancel: function (keyboard) {
if (confirm("Are you sure you want to abandon your changes?")) {
keyboard.close();
}
return false;
},
checkbox: function (keyboard) {
keyboard.insertText("[_] ");
},
clip: function (keyboard) {
keyboard.insertText('<clip title=""></clip>');
var p = jQuery.keyboard.caret(keyboard.$preview);
keyboard.last.start = p.start - "</clip>".length;
keyboard.last.end = p.start - "</clip>".length;
jQuery.keyboard.caret(keyboard.$preview, keyboard.last);
keyboard.setScroll();
},
empty_spacebar: function (keyboard) {},
empty_wide: function (keyboard) {},
hr: function (keyboard) {
var p = jQuery.keyboard.caret(keyboard.$preview);
keyboard.insertText((((p.start > 0) && (keyboard.getValue().charAt(p.start - 1) !== "\n")) ? "\n" : "") + "-------\n");
},
marquee: function (keyboard) {
keyboard.insertText('<marquee></marquee>');
var p = jQuery.keyboard.caret(keyboard.$preview);
keyboard.last.start = p.start - "</marquee>".length;
keyboard.last.end = p.start - "</marquee>".length;
jQuery.keyboard.caret(keyboard.$preview, keyboard.last);
keyboard.setScroll();
},
paste: function (keyboard) {
try {
navigator.clipboard.readText().then(function (clipText) {
keyboard.insertText(clipText);
keyboard.showKeySet("normal");
});
} catch (e) {
displayMessage("Pasting failed");
}
}
});
//}}}
//{{{
config.macros.keyboard = {
altKeys: {
"default": (function () {
var altKeys = jQuery.extend(true, {}, jQuery.keyboard.altKeys);
altKeys["'"] += " \u00ab"; // «
altKeys['"'] += " \u00bb"; // »
for (var k in altKeys) {
altKeys[k] = k + " " + altKeys[k];
}
return altKeys;
})(),
"french": {
a : "a \u00e0 \u00e2 \u00e6", // a à â æ
A : "A \u00c0 \u00c2 \u00c6", // A À Â Æ
c : "c \u00e7", // c ç
C : "C \u00c7", // C Ç
e : "e \u00e9 \u00e8 \u00ea \u00eb", // e é è ê ë
E : "E \u00c9 \u00c8 \u00ca \u00cb", // E É È Ê Ë
i : "i \u00ee \u00ef", // i î ï
I : "I \u00ce \u00cf", // I Î Ï
o : "o \u00f4 \u0153", // o ô œ
O : "O \u00d4 \u0152", // O Ô Œ
u : "u \u00f9 \u00fb \u00fc", // u ù û ü
U : "U \u00d9 \u00db \u00dc", // U Ù Û Ü
'"' : "\" \u00ab \u00bb", // " « »
"$" : "$ \u20ac \u00a3 \u00a4 \u00a5 \u00a2 \u20a1 \u20b1 \u20a9 \u20b9 \u20aa \u20ad \u20ae \u20a6 \u20a4", // $€£¤¥¢₡₱₩₹₪₭₮₦₤
}
},
sound: {
audio: (function () {
var data = "data:audio/mp3;base64,//uQZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAADAAAJYABra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2vKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr///////////////////////////////////////////8AAABQTEFNRTMuMTAwBLkAAAAAAAAAADUgJAKbTQAB4AAACWB9l8BnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//vQZAAAAa0A1X0AAAAAAA0goAABIPXTT/msAAAAADSDAAAAlmioeGmtp4FNhQEABOCcPqBMPlwffUUDGUcXBD9UEKwf9flDnB9//BCUBAMf4nPwfPzn4gDGc+b1Zfd0xrxN9TIaaiihRmpBkA5snZ0Lp4aY8uJipkAIXTmmgACkDxpAGU2MqUNyCNiMEkoUIpqYoNzGOmVdZThIaOOU0hZiqqx2fsGsNZbx/lNHcKhEIFYHIZTFZdLWYQ1OsvlrjvG88COwwy78poIBlVqnt7d+NyyER+rDkVma2EZiMJjVI68nprNjOA2Vv3qJQxKK0no9z12mrymk5Tzl2nxp7a6Keffi1VfuLxHK7juzUpcMtVq9+WX5RUuYfzOWY52878vqUkYpJvuHXag13Irh3vPrb7z/3vu6e3jG5fVldJOTd+/yxhdpaa3jW7h//////////////////bE69H+VeTsVl2qmyp2MyKd0+rAiEZiGQEihkyoXLiAIZIUIRhlxphgJgySM5ggyw5ikSeZjoIhCmOBiIcdYaqicqpQQ8a+jiBjAXFWM0mnQXWGQVafEU02TuJRAQVYFuVNDaiqDarlB4PUzWgyxnMkhisu2Www50Nw42aB24NNsy67jGpTKLsVwmHmf6GJfCoIWPDb3Om5FmM26drtCwWo5sqvskvSyMv/F38jF6SyqnjMnxhmSSKRULLXGyr3ZbZcRxIclfZR2XR+KUlnClq1N3sb92USi5Wq449tbymoBd2HIHmqkpkMpfuliEbtPjlS001ZvUt65QX6XdPjLvzs8pbXbmsbFWI2e4fhb7+///7//////////l3ffz7/4/3n/////////9J9WI1rWeEqlX2LcslpeKl7dIVP9WEBGmzWjsStPAco/taDiD1Os0swasDHEsNMj08+hAuizMDANqocrVxjw4mykuYRHRlAamMEgI0Zw1Qggm1CBwo4yU2Bk1IMISmSDg4MITBkjwkuCwUGgBgEVTBoyQFOLGcgMLIPJrGhIKWgZyAjjwPibAmBQgQiEJovQCr4wLQtM+pMm4BTVN4LAzBAB0ag0AhRZIwYEQhnABSFOmVAIaHDB4mOAk7VoBQIggP/7wGTwgAlWiFj+ayAQAAANIMAAADQKI0H5zQKAAAA0gwAAAMyAAwEKh1gCQgSDFrOku5irYzApCAwZ0iKll3CwtaIJAggKBgqD8BIDlzVFFkFnRDgsIEgDPE6SIJAEAhhpP4GE0JinaH6Jj7w5TITmUuDXYayl05ZEnuW6h1gBBOSB2/QFMSZqyRnDz5OwvFE2iCwF8VwylbTAQMBBgOAhoO2NPUvixxjCA5HJxl8y1CTLmfhUIW2gVxl9P+oeoA7ccYCyh0E+Gsrm9kyaCJaA14S0CPKqzJuSqNZzMtu6moeltLz/////////+Iz+pFVmaGrYlM9ll/////////+4MzLXBnuUUNzcKhduDzA1RkVoVmV2NteTAIzEYQzhZ6MNgsxyZQcMTDwJMMggeSAEAQKbS6zFgHMYBkweDmzmMQWYeEBiMJBwQMqEVDLWiwARCNPBUQtkgyjw00GgC2BEiHHrJWBTHkCULqjAYhAL0MARvU0Z6hHecpi4XRbMDkxGMoGjMm+ncplRSuWMteVpr0s9JhxZUxBwxIv005u6IrlvM2aBI+4rlOw16kVWcUaFRFh96JMzaLvw889QZzVZoD8vpDkdiUMUrD3sdySNNgF+F5NJeiXuzQQzDOMOU0KqzUrjccfaUzVhmVNHXJgaB4Nd1u9eQO7fjFFE4EmI3GL1Wvao62FqbgKmllFjb1btRGHpt6aaq9Loyugi7uwzIZrsZmY1jbl0Zu2rlvOxl3uGGeHL1S7jSf/////////5ffgKSZYSOlZ9RSqG/5YFiQcCQhttmtkDjacaA5gB0APhjJEhKgAngHYsJMhDlUpTpYBvA5ghB4BYRiRVAEhama5WihaihYWua72Zm5Vb9muRUVtV2aSRW1WttRW1WttVtVWvZlVVX2ZeVWoZl/WoY4Yelg4InwaUe/8qd5VMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVV//uQZOaACjx6y35zIIAAAA0gwAAADTD9G7z0AAAAADSDgAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"; // https://mixkit.co/free-sound-effects/keyboard
var binaryString = atob(data.substring(data.indexOf(",") + 1));
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
var audio = {
context: new AudioContext()
};
audio.context.decodeAudioData(bytes.buffer, function (buffer) {
audio.buffer = buffer;
});
return audio;
})(),
play: function () {
var audio = config.macros.keyboard.sound.audio;
var source = audio.context.createBufferSource();
source.connect(audio.context.destination);
source.buffer = audio.buffer;
source.start(0);
}
},
editTemplateKeyboards: {},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
if (params[0] == "layout") {
var layouts = [];
for (var layout in jQuery.keyboard.layouts) {
if (layout) {
layouts.push(layout);
}
}
if (config.options.txtKeyboardLayout && (layouts.indexOf(config.options.txtKeyboardLayout) === -1)) {
layouts.push(config.options.txtKeyboardLayout);
}
if (config.macros.collator && config.macros.collator.compare) {
layouts.sort(config.macros.collator.compare);
} else {
layouts.sort();
}
layouts.unshift("");
var select = jQuery("<select>");
for (var i = 0; i < layouts.length; i++) {
select.append(jQuery("<option>").attr("value", layouts[i]).text(
(jQuery.keyboard.layouts[layouts[i]] && jQuery.keyboard.layouts[layouts[i]].name)
? jQuery.keyboard.layouts[layouts[i]].name
: layouts[i]));
}
select.val(config.options.txtKeyboardLayout);
var desc = getParam(paramString.parseParams("anon", null, true, false, false), "desc", "no");
var selected = null;
if (desc != "no") {
selected = jQuery("<span></span>");
selected.text(config.options.txtKeyboardLayout ? " : " + config.options.txtKeyboardLayout : "");
}
select.on("change", function () {
config.options.txtKeyboardLayout = jQuery(this).val();
if (selected) {
selected.text(config.options.txtKeyboardLayout ? " : " + config.options.txtKeyboardLayout : "");
}
saveOption("txtKeyboardLayout");
});
if (desc != "no") {
jQuery(place).append(select, " ", config.optionsDesc.txtKeyboardLayout, selected);
} else {
jQuery(place).append(select);
}
} else if (params[0] == "altKeys") {
var altKeys = [];
for (var altKeySet in config.macros.keyboard.altKeys) {
if (altKeySet) {
altKeys.push(altKeySet);
}
}
var keyboardAltKeys = (config.options.txtKeyboardAltKeys || "").trim();
if (keyboardAltKeys && (altKeys.indexOf(keyboardAltKeys) === -1)) {
altKeys.push(config.options.txtKeyboardAltKeys);
}
altKeys.unshift("");
var select = jQuery("<select>");
for (var i = 0; i < altKeys.length; i++) {
select.append(jQuery("<option>").attr("value", altKeys[i]).text(altKeys[i]));
}
select.val(config.options.txtKeyboardAltKeys);
select.on("change", function () {
config.options.txtKeyboardAltKeys = jQuery(this).val();
saveOption("txtKeyboardAltKeys");
});
var desc = getParam(paramString.parseParams("anon", null, true, false, false), "desc", "no");
if (desc != "no") {
jQuery(place).append(select, " ", config.optionsDesc.txtKeyboardAltKeys);
} else {
jQuery(place).append(select);
}
} else {
var keyboardLayout = (params[0] && jQuery.keyboard.layouts[params[0]]) ? params[0] : null;
var selectorParam = keyboardLayout ? 1 : 0;
keyboardLayout = keyboardLayout || ((paramString && jQuery.keyboard.layouts[paramString]) ? paramString : null);
keyboardLayout = keyboardLayout || config.options.txtKeyboardLayout;
var keyboardAltKeys = (config.options.txtKeyboardAltKeys || "").trim();
keyboardAltKeys = keyboardAltKeys ? (config.macros.keyboard.altKeys[keyboardAltKeys] || config.macros.keyboard.altKeys["default"]) : null;
var lockInput = (params[selectorParam + 1] == "lock") || config.options.chkKeyboardLockInput;
var autoWidth = config.options.txtKeyboardAutoWidth;
autoWidth = jQuery.isNumeric(autoWidth) ? parseFloat(autoWidth) : -1;
var maxEditRows = config.options.txtKeyboardMaxEditRows;
maxEditRows = jQuery.isNumeric(maxEditRows) ? parseFloat(maxEditRows) : -1;
var editRows = -1;
var keyHighlightDelay = config.options.txtKeyboardKeyHighlightDelay;
keyHighlightDelay = jQuery.isNumeric(keyHighlightDelay) ? parseInt(keyHighlightDelay) : -1;
var sound = config.options.chkKeyboardSound;
var vibrate = config.options.chkKeyboardVibrate;
if (keyboardLayout) {
var isEditTemplate = false;
var editTextarea = null;
if (params[selectorParam]) {
try {
editTextarea = eval(params[selectorParam]);
} catch (e) {}
}
if (!editTextarea && paramString) {
try {
editTextarea = eval(paramString);
} catch (e) {}
}
if (!editTextarea) {
editTextarea = jQuery(place).closest("[template='EditTemplate']").find("textarea").eq(0);
isEditTemplate = true;
}
var scaleKeyboard = null;
var readonly = editTextarea.prop("readonly");
var layout = keyboardLayout || "tw-qwerty";
layout = jQuery.keyboard.layouts[layout] ? layout : "tw-qwerty";
var defaultCommand = jQuery(place).closest(".tiddler").children(".toolbar").children(".defaultCommand");
var keyboardOptions = {
layout: layout,
reposition: false,
usePreview: false,
stayOpen: true,
autoAccept: true,
appendLocally: true,
openOn: "",
useCombos: !keyboardAltKeys,
scrollAtEnd: isEditTemplate && lockInput,
beforeVisible: function (e, keyboard, el) {
defaultCommand.removeClass("defaultCommand");
if (lockInput && !readonly) {
editTextarea.prop("readonly", true);
}
if (isEditTemplate && (maxEditRows > 0)) {
editRows = (jQuery.isNumeric(editTextarea.attr("rows")) ? parseInt(editTextarea.attr("rows")) : -1) || -1;
if ((editRows > 0) && (editRows > maxEditRows)) {
editTextarea.attr("rows", maxEditRows);
} else {
editRows = -1;
}
}
var ratio = 1;
if (autoWidth > 0) {
scaleKeyboard = function () {
setStylesheet("span.ui-keyboard-text { font-size: 1.1em; } .ui-keyboard-button { height: 2em; width: 2em; margin: .1em; cursor: pointer; } .ui-keyboard-widekey { width: 5.5em; } .ui-keyboard-space { width: 15em; }", "KeyboardPluginAutoSizeStylesheet");
var keyboardWidth = keyboard.$keyboard.width();
var inputWidth = keyboard.$el.width();
var ratio = inputWidth * autoWidth / keyboardWidth;
setStylesheet("span.ui-keyboard-text { font-size: " + (1.1 * ratio) + "em; } .ui-keyboard-button { height: " + (2 * ratio) + "em; width: " + (2 * ratio) + "em; margin: " + (.1 * ratio) + "em; cursor: pointer; } .ui-keyboard-widekey { width: " + (5.5 * ratio) + "em; } .ui-keyboard-space { width: " + (15 * ratio) + "em; }", "KeyboardPluginAutoSizeStylesheet");
keyboard.reposition();
return ratio;
};
jQuery(window).on("resize", null, scaleKeyboard);
ratio = scaleKeyboard();
} else {
scaleKeyboard = null;
removeStyleSheet("KeyboardPluginAutoSizeStylesheet");
}
keyboard.$keyboard.find("blink").each(function (index) {
var element = jQuery(this);
var value = element.text();
var container = element.parent();
container.empty();
if (jQuery.isFunction(jQuery.fn.blink)) {
wikify('<blink>' + value + '</blink>', container[0]);
} else {
container.text(value);
}
});
keyboard.$keyboard.find("empty_spacebar").each(function (index) {
var element = jQuery(this);
var container = element.parent();
container.empty();
container.parent().addClass("ui-state-disabled ui-keyboard-empty ui-keyboard-space");
});
keyboard.$keyboard.find("empty_wide").each(function (index) {
var element = jQuery(this);
var container = element.parent();
container.empty();
container.parent().addClass("ui-state-disabled ui-keyboard-empty ui-keyboard-widekey");
});
keyboard.$keyboard.find("marquee").each(function (index) {
var element = jQuery(this);
var value = element.text();
var container = element.parent();
if (jQuery.isFunction(element.marquee)) {
element.attr("width", 5.5 * ratio + "em");
element.marquee();
container.children("div").css("vertical-align", "text-bottom");
container.find("div").css({"font-size": "1em", "line-height": "1em"});
} else {
container.empty();
container.text(value);
}
});
if (isEditTemplate && tiddler) {
config.macros.keyboard.editTemplateKeyboards[tiddler.title] = keyboard;
}
},
hidden: function (e, keyboard, el) {
defaultCommand.addClass("defaultCommand");
if (lockInput && !readonly) {
editTextarea.prop("readonly", false);
}
if (autoWidth > 0) {
removeStyleSheet("KeyboardPluginAutoSizeStylesheet");
}
if (isEditTemplate && (editRows > 0)) {
editTextarea.attr("rows", editRows);
editRows = -1;
}
if (scaleKeyboard) {
jQuery(window).off("resize", null, scaleKeyboard);
scaleKeyboard = null;
}
if (isEditTemplate && tiddler) {
delete config.macros.keyboard.editTemplateKeyboards[tiddler.title];
}
}
};
if (isEditTemplate) {
keyboardOptions.position = {
of: null,
my: "center top",
at: "center top",
at2: "center bottom",
collision: "center top"
};
} else {
autoWidth = -1;
}
var jQueryKeyboard = jQuery(editTextarea).keyboard(keyboardOptions).addTyping();
if (keyboardAltKeys) {
jQuery.keyboard.altKeys = keyboardAltKeys;
// https://github.com/Mottie/Keyboard/wiki/Extension-AltKeysPopup
jQueryKeyboard.addAltKeyPopup({
holdTime: 500,
popupVisible: "popup-visible",
popupHidden: "popup-hidden",
popupPosition: function (keyboard, data) {
}
}).on("popup-visible", function (keyboard) {
}).on("popup-hidden", function (keyboard) {
});
}
jQuery(editTextarea).off("keyboardChange");
jQuery(editTextarea).on("keyboardChange", function (event, keyboard, el) {
if (sound && keyboard.last.virtual && !keyboard.last.$key.hasClass(keyboard.options.css.buttonDisabled)) {
config.macros.keyboard.sound.play();
}
if (keyHighlightDelay > 0 && keyboard.last.virtual && !keyboard.last.$key.hasClass(keyboard.options.css.buttonDisabled) && (el.type !== "password")) {
var keys = keyboard.$keyboard.find("." + jQuery.keyboard.css.keyButton);
keys.each(function () {
var key = jQuery(this);
if (key.data("keyboardHighlighTimeout")) {
clearTimeout(key.data("keyboardHighlighTimeout"));
key.removeData("keyboardHighlighTimeout");
}
});
keys.removeClass("ui-keyboard-button-highlight");
var key = keyboard.last.$key;
key.addClass("ui-keyboard-button-highlight");
key.data("keyboardHighlighTimeout", setTimeout(function () {
key.removeData("keyboardHighlighTimeout");
key.removeClass("ui-keyboard-button-highlight");
}, keyHighlightDelay));
}
if (vibrate && keyboard.last.virtual && !keyboard.last.$key.hasClass(keyboard.options.css.buttonDisabled)) {
try {
window.navigator.vibrate([50]);
} catch (e) {}
}
});
if (lockInput) {
jQueryKeyboard.addCaret();
}
var keyboard = jQuery(editTextarea).data("keyboard");
var keyboardIcon = store.getTiddlerText("jquery.keyboard.js##keyboard.svg");
keyboardIcon = jQuery(keyboardIcon.substring(keyboardIcon.indexOf("<svg"), keyboardIcon.lastIndexOf("</svg>") + "</svg>".length));
keyboardIcon.on("click", function () {
if (!keyboard.isOpen) {
keyboard.reveal().$keyboard.draggable();
}
});
jQuery(place).append(keyboardIcon, "<span> </span>");
}
}
}
}
//}}}
//{{{
config.macros.keyboard.saveTiddlerHandler = config.commands.saveTiddler.handler;
config.commands.saveTiddler.handler = function (event, src, title) {
if (title && config.macros.keyboard.editTemplateKeyboards[title]) {
config.macros.keyboard.editTemplateKeyboards[title].accept();
}
return config.macros.keyboard.saveTiddlerHandler.call(config.commands.saveTiddler, event, src, title);
}
config.macros.keyboard.cancelTiddlerHandler = config.commands.cancelTiddler.handler;
config.commands.cancelTiddler.handler = function (event, src, title) {
if (title && config.macros.keyboard.editTemplateKeyboards[title]) {
config.macros.keyboard.editTemplateKeyboards[title].accept();
}
return config.macros.keyboard.cancelTiddlerHandler.call(config.commands.cancelTiddler, event, src, title);
}
//}}}
/***
|Name|LinkMapperPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.txtLinkMap === undefined) {
config.options.txtLinkMap = "";
}
merge(config.optionsDesc, {
txtLinkMap: 'The comma-separated, quoted external link to tiddler map, for example: "https://galasoft.com/tiddlywiki/notes.html#%5B%5BTW%20Notes%20License%5D%5D": "TW Notes License", "https://galasoft.com/tiddlywiki/notes.html#Notes": "Notes"'
});
//}}}
//{{{
config.macros.linkMapper = {
createMappedLink: function (place, link, isStatic, linkedFromTiddler) {
if (link && config.options.txtLinkMap) {
eval("var linkMap = {" + config.options.txtLinkMap + "}");
if ((link in linkMap) && store.tiddlerExists(linkMap[link])) {
return createTiddlyLink(place, linkMap[link], false, null, isStatic, linkedFromTiddler);
}
}
return createExternalLink(place, link);
}
};
//}}}
//{{{
(function () {
for (var i = 0; i < config.formatters.length; i++) {
if ((config.formatters[i].name === "prettyLink") || (config.formatters[i].name === "urlLink")) {
var code = eval("config.formatters[i].handler").toString();
code = code.replace(/(createExternalLink)(\(.*?)(\))/g, "config.macros.linkMapper.createMappedLink$2, w.isStatic, w.tiddler)");
eval("config.formatters[i].handler = function handler" + code.substring(code.indexOf("(")));
}
}
})();
//}}}
/***
|Name|LoadTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#LoadTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#LoadTiddlersPluginInfo|
|Version|3.9.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|macro for automated updates or one-click installations of tiddlers from remote sources|
!!!!!Documentation
>see [[LoadTiddlersPluginInfo]]
!!!!!Configuration
<<<
<<option chkLoadTiddlersShowReport>>after loading tiddlers, automatically display [[ImportedTiddlers]] (if created)
__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 [[ExternalTiddlersPlugin]] and [[ImportTiddlersPlugin]]''
<<<
!!!!!Revisions
<<<
2010.08.11 3.9.0 added 'autosave' optional param
|please see [[LoadTiddlersPluginInfo]] for additional revision details|
2005.07.20 1.0.0 Initial Release
<<<
!!!!!Code
***/
//{{{
version.extensions.LoadTiddlersPlugin= {major: 3, minor: 9, revision: 0, date: new Date(2010,8,11)};
if (config.options.chkLoadTiddlersShowReport===undefined)
config.options.chkLoadTiddlersShowReport=true;
config.macros.loadTiddlers = {
label: '',
tip: "add/update tiddlers from '%0'",
lockedTag: 'noReload', // if existing tiddler has this tag value, don't overwrite it, even if inbound tiddler is newer
askMsg: 'Please enter a local path/filename or a remote URL',
openMsg: 'Opening %0',
openErrMsg: 'Could not open %0 - error=%1',
readMsg: 'Read %0 bytes from %1',
foundMsg: 'Found %0 tiddlers in %1',
nochangeMsg: "'%0' is up-to-date... skipped.",
lockedMsg: "'%0' is tagged '%1'... skipped.",
skippedMsg: 'skipped (cancelled by user)',
loadedMsg: 'Loaded %0 of %1 tiddlers from %2',
reportTitle: 'ImportedTiddlers',
warning: "Warning!! Processing '%0' as a systemConfig (plugin) tiddler may produce unexpected results! Press OK to proceed.",
autosaveMsg: 'Save current document? Press OK to proceed.',
handler: function(place,macroName,params) {
var label=(params[0] && params[0].substr(0,6)=='label:')?params.shift().substr(6):this.label;
var tip=(params[0] && params[0].substr(0,7)=='prompt:')?params.shift().substr(7):this.tip;
var filter='updates';
if (params[0] && (params[0]=='all' || params[0]=='new' || params[0]=='changes' || params[0]=='updates'
|| params[0].substr(0,8)=='tiddler:' || params[0].substr(0,4)=='tag:'))
filter=params.shift();
var src=params.shift(); if (!src || !src.length) return; // filename is required
var quiet=(params[0]=='quiet'); if (quiet) params.shift();
var ask=(params[0]=='confirm'); if (ask) params.shift();
var force=(params[0]=='force'); if (force) params.shift();
var init=(params[0]=='init'); if (init) params.shift();
var nodirty=(params[0]=='nodirty'); if (nodirty) params.shift();
var norefresh=(params[0]=='norefresh'); if (norefresh) params.shift();
var noreport=(params[0]=='noreport'); if (noreport) params.shift();
var autosave=(params[0]=='autosave'); if (autosave) params.shift();
this.newTags=[]; if (params[0]) this.newTags=params; // any remaining params are used as 'autotags'
var flags={quiet:quiet, ask:ask, filter:filter, force:force, init:init,
nodirty:nodirty, norefresh:norefresh, noreport:noreport, autosave:autosave};
if (label.trim().length) { // CLICKABLE LINK
createTiddlyButton(place,
label.format([src.replace(/%20/g,' ')]),
tip.format([src.replace(/%20/g,' ')]),
function() {
var cml=config.macros.loadTiddlers;
cml.loadFile(src,cml.doImport,flags);
return false;
})
}
else // IMMEDIATE IMPORT
this.loadFile(src,this.doImport,flags);
},
loadFile: function(src,callback,params) {
var quiet=params.quiet;
if (src=='ask') src=prompt(this.askMsg);
if (src==undefined || !src.length) return null; // filename is required
if (!quiet) clearMessage();
if (!quiet) displayMessage(this.openMsg.format([src.replace(/%20/g,' ')]));
// if working locally and src is not a URL, read from local filesystem
if (document.location.protocol=='file:' && src.substr(0,5)!='http:' && src.substr(0,5)!='file:') {
var txt=loadFile(src);
if (!txt) { // file didn't load, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf('/'); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf('\\');
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!='http:') src=getLocalPath(src);
var txt=loadFile(src);
}
if (!txt) { // file still didn't load, report error
if (!quiet) displayMessage(this.openErrMsg.format([src.replace(/%20/g,' '),'(unknown)']));
} else {
if (!quiet) displayMessage(this.readMsg.format([txt.length,src.replace(/%20/g,' ')]));
if (version.major+version.minor*.1+version.revision*.01!=2.52)
txt=convertUTF8ToUnicode(txt);
if (callback) callback(true,params,txt,src,null);
}
} else { // use XMLHttpRequest
doHttp('GET',src,null,null,config.options.txtRemoteUsername,config.options.txtRemotePassword,callback,params,null);
}
},
readTiddlersFromHTML: function(html) {
// for TW2.2+
if (TiddlyWiki.prototype.importTiddlyWiki!=undefined) {
var remoteStore=new TiddlyWiki();
remoteStore.importTiddlyWiki(html);
return remoteStore.getTiddlers('title');
}
},
readTiddlersFromCSV: function(CSV) {
var remoteStore=new TiddlyWiki();
// GET NAMES
var lines=CSV.replace(/\r/g,'').split('\n');
var names=lines.shift().replace(/"/g,'').split(',');
CSV=lines.join('\n');
// ENCODE commas and newlines within quoted values
var comma='!~comma~!'; var commaRE=new RegExp(comma,'g');
var newline='!~newline~!'; var newlineRE=new RegExp(newline,'g');
CSV=CSV.replace(/"([^"]*?)"/g,
function(x){ return x.replace(/\,/g,comma).replace(/\n/g,newline); });
// PARSE lines
var lines=CSV.split('\n');
for (var i=0; i<lines.length; i++) { if (!lines[i].length) continue;
var values=lines[i].split(',');
// DECODE commas, newlines, and doubled-quotes, and remove enclosing quotes (if any)
for (var v=0; v<values.length; v++)
values[v]=values[v].replace(commaRE,',').replace(newlineRE,'\n')
.replace(/^"|"$/g,'').replace(/""/g,'"');
// EXTRACT tiddler values
var title=''; var text=''; var tags=[]; var fields={};
var created=null; var when=new Date(); var who=config.options.txtUserName;
for (var v=0; v<values.length; v++) { var val=values[v];
if (names[v]) switch(names[v].toLowerCase()) {
case 'title': title=val.replace(/\[\]\|/g,'_'); break;
case 'created': created=new Date(val); break;
case 'modified':when=new Date(val); break;
case 'modifier':who=val; break;
case 'text': text=val; break;
case 'tags': tags=val.readBracketedList(); break;
default: fields[names[v].toLowerCase()]=val; break;
}
}
// CREATE tiddler in temporary store
if (title.length)
remoteStore.saveTiddler(title,title,text,who,when,tags,fields,true,created||when);
}
return remoteStore.getTiddlers('title');
},
createTiddlerFromFile: function(src,txt) {
var t=new Tiddler();
var pos=src.lastIndexOf("/"); if (pos==-1) pos=src.lastIndexOf("\\");
t.title=pos==-1?src:src.substr(pos+1);
t.text=txt;
t.created=t.modified=new Date();
t.modifier=config.options.txtUserName;
if (src.substr(src.length-3,3)=='.js') t.tags=['systemConfig'];
return [t];
},
doImport: function(status,params,html,src,xhr) {
var cml=config.macros.loadTiddlers; // abbrev
src=src.split('?')[0]; // strip off "?nocache=..."
if (!status) {
displayMessage(cml.openErrMsg.format([src.replace(/%20/g,' '),xhr.status]));
return false;
}
var quiet=params.quiet;
var ask=params.ask;
var filter=params.filter;
var force=params.force;
var init=params.init;
var nodirty=params.nodirty;
var norefresh=params.norefresh;
var noreport=params.noreport;
var autosave=params.autosave;
var tiddlers = cml.readTiddlersFromHTML(html);
if (!tiddlers||!tiddlers.length) tiddlers=cml.readTiddlersFromCSV(html);
if (!tiddlers||!tiddlers.length) tiddlers=cml.createTiddlerFromFile(src,html);
var count=tiddlers?tiddlers.length:0;
if (!quiet) displayMessage(cml.foundMsg.format([count,src.replace(/%20/g,' ')]));
var wasDirty=store.isDirty();
store.suspendNotifications();
var count=0;
if (tiddlers) for (var t=0;t<tiddlers.length;t++) {
var inbound = tiddlers[t];
var theExisting = store.getTiddler(inbound.title);
if (inbound.title==cml.reportTitle)
continue; // skip 'ImportedTiddlers' history from the other document...
if (theExisting && theExisting.tags.contains(cml.lockedTag)) {
if (!quiet) displayMessage(cml.lockedMsg.format([theExisting.title,cml.lockedTag]));
continue; // skip existing tiddler if tagged with 'noReload'
}
// apply the all/new/changes/updates filter (if any)
if (filter && filter!='all') {
if ((filter=='new') && theExisting) // skip existing tiddlers
continue;
if ((filter=='changes') && !theExisting) // skip new tiddlers
continue;
if ((filter.substr(0,4)=='tag:') && inbound.tags.indexOf(filter.substr(4))==-1) // must match specific tag value
continue;
if ((filter.substr(0,8)=='tiddler:') && inbound.title!=filter.substr(8)) // must match specific tiddler name
continue;
if (!force && store.tiddlerExists(inbound.title) && ((theExisting.modified.getTime()-inbound.modified.getTime())>=0)) {
var msg=cml.nochangeMsg;
if (!quiet&&msg.length) displayMessage(msg.format([inbound.title]));
continue;
}
}
// get confirmation if required
var msg=(theExisting?'Update':'Add')+" tiddler '"+inbound.title+"'\n"
+'from '+src.replace(/%20/g,' ')+'\n\nOK to proceed?';
if (ask && !confirm(msg))
{ tiddlers[t].status=cml.skippedMsg; continue; }
// DO IT!
var tags=new Array().concat(inbound.tags,cml.newTags);
store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier,
inbound.modified, tags, inbound.fields, true, inbound.created);
// force creation date to imported value - needed for TW2.1.3 or earlier
store.fetchTiddler(inbound.title).created = inbound.created;
tiddlers[t].status=theExisting?'updated':'added'
if (init && tags.contains('systemConfig') && !tags.contains('systemConfigDisable')) {
var ok=true;
if (ask||!quiet) ok=confirm(cml.warning.format([inbound.title]))
if (ok) { // run the plugin
try { window.eval(inbound.text); tiddlers[t].status+=' (plugin initialized)'; }
catch(ex) { displayMessage(config.messages.pluginError.format([exceptionText(ex)])); }
}
}
count++;
}
store.resumeNotifications();
if (count) {
// set/clear 'unsaved changes' flag, refresh page display, and generate a report
store.setDirty(wasDirty||!nodirty);
if (!norefresh) {
story.forEachTiddler(function(t,e){
if(!story.isDirty(t))story.refreshTiddler(t,null,true)
});
store.notifyAll();
}
if (!noreport) cml.report(src,tiddlers,count,quiet);
}
if (!quiet||count) // force msg if tiddlers were loaded
displayMessage(cml.loadedMsg.format([count,tiddlers.length,src.replace(/%20/g,' ')]));
if (count && autosave && (!ask||confirm(cml.autosaveMsg))) saveChanges();
},
showReport: true,
report: function(src,tiddlers,count,quiet) {
var cml=config.macros.loadTiddlers; // abbrev
// format the new report content
var newText = 'On '+(new Date()).toLocaleString()+', ';
newText += config.options.txtUserName+' loaded '+count+' tiddlers ';
newText += 'from\n[['+src+'|'+src+']]:\n';
newText += '<<<\n';
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].status)
newText += '#[['+tiddlers[t].title+']] - '+tiddlers[t].status+'\n';
newText += '<<<\n';
var title=cml.reportTitle;
var currText='';
var t=store.getTiddler(title);
if (t) currText=(t.text.length?'\n----\n':'')+t.text;
store.saveTiddler(title, title, newText+currText,
config.options.txtUserName, new Date(), t?t.tags:null, t?t.fields:null);
if (!quiet) {
if (config.options.chkLoadTiddlersShowReport)
story.displayTiddler(null,title);
story.refreshTiddler(title,null,true);
}
}
}
//}}}
/***
|Name|LoadTiddlersPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[LoadTiddlersPlugin]]|
!!!!!Code
***/
//{{{
var code = eval("config.macros.loadTiddlers.doImport").toString();
var newCode = null;
var startPosition = code.indexOf("store.tiddlerExists");
if (startPosition > -1) {
var endPosition = code.indexOf(')', startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + "theExisting" + code.substring(endPosition + 1);
code = newCode;
}
}
var startPosition = code.indexOf("saveTiddler");
if (startPosition > -1) {
startPosition = code.indexOf('inbound.title', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "oldTitle" + code.substring(startPosition + "inbound.title".length);
code = newCode;
}
}
var startPosition = code.indexOf("theExisting");
if (startPosition > -1) {
startPosition = code.indexOf(';', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1)
+ ' if (inbound.fields && inbound.fields["guid"]) {\
var guid = inbound.fields["guid"];\
var existingTiddlers = store.getTiddlers();\
for (var ti = 0; ti < existingTiddlers.length; ti++) {\
if (existingTiddlers[ti].fields && (existingTiddlers[ti].fields["guid"] == guid)) {\
theExisting = existingTiddlers[ti];\
break;\
}\
}\
}\
var oldTitle = theExisting ? theExisting.title : inbound.title; ' + code.substring(startPosition + 1);
code = newCode;
}
}
var startPosition = code.indexOf("inbound");
if (startPosition > -1) {
startPosition = code.indexOf(';', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1) + " if (params.tiddlerValidator && params.tiddlerValidator.exclude && params.tiddlerValidator.exclude(inbound)) continue; " + code.substring(startPosition + 1);
code = newCode;
}
}
var startPosition = code.indexOf("store.fetchTiddler(inbound.title).created");
if (startPosition > -1) {
newCode = code.replace("store.fetchTiddler(inbound.title).created",
"if (inbound.fields && (inbound.fields.changecount != null) && store.fetchTiddler(inbound.title).fields) store.fetchTiddler(inbound.title).fields.changecount = inbound.fields.changecount; " +
"if (inbound.creator) store.fetchTiddler(inbound.title).creator = inbound.creator; " +
"if (!inbound.creator) delete store.fetchTiddler(inbound.title).creator; " +
"store.fetchTiddler(inbound.title).created");
code = newCode;
}
if (newCode != null) {
eval("config.macros.loadTiddlers.doImport = function doImport" + newCode.substring(newCode.indexOf("(")));
}
//}}}
//{{{
var code = eval("config.macros.loadTiddlers.loadFile").toString();
var newCode = null;
var startPosition = code.indexOf("convertUTF8ToUnicode");
if (startPosition > -1) {
var endPosition = code.indexOf(';', startPosition);
if (endPosition > -1) {
startPosition = code.lastIndexOf('txt', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition - 1)
+ " {\
try {\
var textTemp = '';\
while (txt.length > 0) {\
var index = txt.indexOf(' ', 10000);\
index = (index > -1) ? index : txt.length;\
textTemp = textTemp + convertUTF8ToUnicode(txt.substring(0, index));\
txt = txt.substring(index);\
}\
txt = textTemp;\
} catch (err) {\
if (!quiet) displayMessage(this.openErrMsg.format([src.replace(/%20/g,' '),'(' + err + ')']));\
return false;\
}\
} " + code.substring(endPosition + 1);
code = newCode;
}
}
}
if (newCode != null) {
eval("config.macros.loadTiddlers.loadFile = function loadFile" + newCode.substring(newCode.indexOf("(")));
}
//}}}
/***
|Name|LoadTiddlersPluginPatch|
|Source|http://www.TiddlyTools.com/#LoadTiddlersPluginPatch|
|Version|3.6.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|LoadTiddlersPlugin|
|Description|backward-compatible function patches for use with LoadTiddlersPlugin and TW2.1.x or earlier|
!!!!!Usage
<<<
The current version LoadTiddlersPlugin is compatible with the TW2.2.x core functions.
This "patch" plugin provides additional functions needed to enable the current version of LoadTiddlersPlugin to operate correctly under TW2.1.x or earlier.
{{medium{You do not need to install this plugin if you are using TW2.2.0 or above}}}
(though it won't hurt anything if you do... it will just take up more space).
<<<
!!!!!Revisions
<<<
2008.08.05 [3.6.2] rewrote loadFile to eliminate use of platform-specific fileExists() function
2008.01.03 [3.5.0] added support for passing txtRemoteUsername and txtRemotePassword for accessing password-protected remote servers
2007.06.27 [3.4.8] compatibility functions split from LoadTiddlersPlugin
|please see [[LoadTiddlersPlugin]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
//{{{
// these functions are only defined when installed in TW2.1.x and earlier...
if (version.major+version.minor/10 <= 2.1) {
// Version
version.extensions.LoadTiddlersPluginPatch= {major: 3, minor: 6, revision: 2, date: new Date(2008,8,5)};
config.macros.loadTiddlers.loadFile = function(src,callback,params) {
var quiet=params.quiet;
if (src==undefined || !src.length) return null; // filename is required
if (!quiet) clearMessage();
if (!quiet) displayMessage(this.openMsg.format([src]));
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
var txt=loadFile(src);
if (!txt) { // file didn't load, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\");
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
var txt=loadFile(src);
}
if (!txt) { // file still didn't load, report error
if (!quiet) displayMessage(this.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"]));
} else {
if (!quiet) displayMessage(this.readMsg.format([txt.length,src.replace(/%20/g," ")]));
if (callback) callback(true,params,convertUTF8ToUnicode(txt),src,null);
}
} else {
var x; // get an request object
try {x = new XMLHttpRequest()} // moz
catch(e) {
try {x = new ActiveXObject("Msxml2.XMLHTTP")} // IE 6
catch (e) {
try {x = new ActiveXObject("Microsoft.XMLHTTP")} // IE 5
catch (e) { return }
}
}
// setup callback function to handle server response(s)
x.onreadystatechange = function() {
if (x.readyState == 4) {
if (x.status==0 || x.status == 200) {
if (!quiet) displayMessage(config.macros.loadTiddlers.readMsg.format([x.responseText.length,src]));
if (callback) callback(true,params,x.responseText,src,x);
}
else {
if (!quiet) displayMessage(config.macros.loadTiddlers.openErrMsg.format([src,x.status]));
}
}
}
// get privileges to read another document's DOM via http:// or file:// (moz-only)
if (typeof(netscape)!="undefined") {
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); }
catch (e) { if (!quiet) displayMessage(e.description?e.description:e.toString()); }
}
// send the HTTP request
try {
var url=src+(src.indexOf('?')<0?'?':'&')+'nocache='+Math.random();
x.open("GET",src,true,config.options.txtRemoteUsername,config.options.txtRemotePassword);
if (x.overrideMimeType) x.overrideMimeType('text/html');
x.send(null);
}
catch (e) {
if (!quiet) {
displayMessage(this.openErrMsg.format([src,"(unknown)"]));
displayMessage(e.description?e.description:e.toString());
}
}
}
}
config.macros.loadTiddlers.readTiddlersFromHTML=function(html) {
// for TW2.1 and earlier
// extract store area from html
var start=html.indexOf('<div id="storeArea">');
var end=html.indexOf("<!--POST-BODY-START--"+">",start);
if (end==-1) var end=html.indexOf("</body"+">",start); // backward-compatibility for older documents
var sa="<html><body>"+html.substring(start,end)+"</body></html>";
// load html into iframe document
var f=document.getElementById("loaderFrame"); if (f) document.body.removeChild(f);
f=document.createElement("iframe"); f.id="loaderFrame";
f.style.width="0px"; f.style.height="0px"; f.style.border="0px";
document.body.appendChild(f);
var d=f.document;
if (f.contentDocument) d=f.contentDocument; // For NS6
else if (f.contentWindow) d=f.contentWindow.document; // For IE5.5 and IE6
d.open(); d.writeln(sa); d.close();
// read tiddler DIVs from storeArea DOM element
var sa = d.getElementById("storeArea");
if (!sa) return null;
sa.normalize();
var nodes = sa.childNodes;
if (!nodes || !nodes.length) return null;
var tiddlers = [];
for(var t = 0; t < nodes.length; t++) {
var title = null;
if(nodes[t].getAttribute)
title = nodes[t].getAttribute("tiddler");
if(!title && nodes[t].id && (nodes[t].id.substr(0,5) == "store"))
title = nodes[t].id.substr(5);
if(title && title != "")
tiddlers.push((new Tiddler()).loadFromDiv(nodes[t],title));
}
return tiddlers;
}
// // COPIED FROM TW2.2beta5
// // enables reading tiddler definitions using TW2.2 storeArea format, even when plugin is running under TW2.1.x
// // storeArea format changes include:
// // <pre> nodes
// // attribute(tiddler) renamed to attribute(title)
// // attribute(modified) is omitted if created==modified
TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
{
var e = node.firstChild;
var text = null;
if(node.getAttribute("tiddler")) {
text = getNodeText(e).unescapeLineBreaks();
} else {
while(e.nodeName!="PRE" && e.nodeName!="pre") {
e = e.nextSibling;
}
text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
}
var modifier = node.getAttribute("modifier");
var c = node.getAttribute("created");
var m = node.getAttribute("modified");
var created = c ? Date.convertFromYYYYMMDDHHMM(c) : version.date;
var modified = m ? Date.convertFromYYYYMMDDHHMM(m) : created;
var tags = node.getAttribute("tags");
var fields = {};
var attrs = node.attributes;
for(var i = attrs.length-1; i >= 0; i--) {
var name = attrs[i].name;
if (attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
fields[name] = attrs[i].value.unescapeLineBreaks();
}
}
tiddler.assign(title,text,modifier,modified,tags,created,fields);
return tiddler;
};
} // END OF TW2.1.x backward-compatibility functions
//}}}
/***
|Name|MarqueePlugin|
|Source|https://gist.github.com/floatplane/6226786|
|Documentation|https://remysharp.com/2008/09/10/the-silky-smooth-marquee|
|Version|2013-08-13 https://gist.githubusercontent.com/floatplane/6226786/raw/38901f839914adcded7491f36bd4dbec0128b1ae/gistfile1.js|
|License|[[MIT|https://opensource.org/licenses/MIT]]|
|Description|The Silky Smooth Marquee|
!!!!!gistfile1.js
//{{{
/**
* author Remy Sharp
* url http://remysharp.com/tag/marquee
* /
require(['jquery'], function ($) {
$.fn.marquee = function (klass) {
var newMarquee = [],
last = this.length;
// works out the left or right hand reset position, based on scroll
// behavior, current direction and new direction
function getReset(newDir, marqueeRedux, marqueeState) {
var behavior = marqueeState.behavior, width = marqueeState.width, dir = marqueeState.dir;
var r = 0;
if (behavior == 'alternate') {
r = newDir == 1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : width;
} else if (behavior == 'slide') {
if (newDir == -1) {
r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] : width;
} else {
r = dir == -1 ? marqueeRedux[marqueeState.widthAxis] - (width*2) : 0;
}
} else {
r = newDir == -1 ? marqueeRedux[marqueeState.widthAxis] : 0;
}
return r;
}
// single "thread" animation
function animateMarquee() {
var i = newMarquee.length,
marqueeRedux = null,
$marqueeRedux = null,
marqueeState = {},
newMarqueeList = [],
hitedge = false;
while (i--) {
marqueeRedux = newMarquee[i];
$marqueeRedux = $(marqueeRedux);
marqueeState = $marqueeRedux.data('marqueeState');
if ($marqueeRedux.data('paused') !== true) {
// TODO read scrollamount, dir, behavior, loops and last from data
marqueeRedux[marqueeState.axis] += (marqueeState.scrollamount * marqueeState.dir);
// only true if it's hit the end
hitedge = marqueeState.dir == -1 ? marqueeRedux[marqueeState.axis] <= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState) : marqueeRedux[marqueeState.axis] >= getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
if ((marqueeState.behavior == 'scroll' && marqueeState.last == marqueeRedux[marqueeState.axis]) || (marqueeState.behavior == 'alternate' && hitedge && marqueeState.last != -1) || (marqueeState.behavior == 'slide' && hitedge && marqueeState.last != -1)) {
if (marqueeState.behavior == 'alternate') {
marqueeState.dir *= -1; // flip
}
marqueeState.last = -1;
$marqueeRedux.trigger('stop');
marqueeState.loops--;
if (marqueeState.loops === 0) {
if (marqueeState.behavior != 'slide') {
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
} else {
// corrects the position
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir * -1, marqueeRedux, marqueeState);
}
$marqueeRedux.trigger('end');
} else {
// keep this marquee going
newMarqueeList.push(marqueeRedux);
$marqueeRedux.trigger('start');
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
}
} else {
newMarqueeList.push(marqueeRedux);
}
marqueeState.last = marqueeRedux[marqueeState.axis];
// store updated state only if we ran an animation
$marqueeRedux.data('marqueeState', marqueeState);
} else {
// even though it's paused, keep it in the list
newMarqueeList.push(marqueeRedux);
}
}
newMarquee = newMarqueeList;
if (newMarquee.length) {
setTimeout(animateMarquee, 25);
}
}
// TODO consider whether using .html() in the wrapping process could lead to loosing predefined events...
this.each(function (i) {
var $marquee = $(this),
width = $marquee.attr('width') || $marquee.width(),
height = $marquee.attr('height') || $marquee.height(),
widthWithUnits = (typeof(width) == 'string' ? width : width + 'px'),
heightWithUnits = (typeof(height) == 'string' ? height : height + 'px'),
$marqueeRedux = $marquee.after('<div ' + (klass ? 'class="' + klass + '" ' : '') + 'style="display: block-inline; width: ' + widthWithUnits + '; height: ' + heightWithUnits + '; overflow: hidden;"><div style="float: left; white-space: nowrap;">' + $marquee.html() + '</div></div>').next(),
marqueeRedux = $marqueeRedux.get(0),
hitedge = 0,
direction = ($marquee.attr('direction') || 'left').toLowerCase(),
marqueeState = {
dir : /down|right/.test(direction) ? -1 : 1,
axis : /left|right/.test(direction) ? 'scrollLeft' : 'scrollTop',
widthAxis : /left|right/.test(direction) ? 'scrollWidth' : 'scrollHeight',
last : -1,
loops : $marquee.attr('loop') || -1,
scrollamount : $marquee.attr('scrollamount') || this.scrollAmount || 2,
behavior : ($marquee.attr('behavior') || 'scroll').toLowerCase(),
width : /left|right/.test(direction) ? width : height
};
// corrects a bug in Firefox - the default loops for slide is -1
if ($marquee.attr('loop') == -1 && marqueeState.behavior == 'slide') {
marqueeState.loops = 1;
}
$marquee.remove();
// add padding
if (/left|right/.test(direction)) {
$marqueeRedux.find('> div').css('padding', '0 ' + widthWithUnits);
} else {
$marqueeRedux.find('> div').css('padding', heightWithUnits + ' 0');
}
// events
$marqueeRedux.bind('stop', function () {
$marqueeRedux.data('paused', true);
}).bind('pause', function () {
$marqueeRedux.data('paused', true);
}).bind('start', function () {
$marqueeRedux.data('paused', false);
}).bind('unpause', function () {
$marqueeRedux.data('paused', false);
}).data('marqueeState', marqueeState); // finally: store the state
// todo - rerender event allowing us to do an ajax hit and redraw the marquee
newMarquee.push(marqueeRedux);
marqueeRedux[marqueeState.axis] = getReset(marqueeState.dir, marqueeRedux, marqueeState);
$marqueeRedux.trigger('start');
// on the very last marquee, trigger the animation
if (i+1 == last) {
animateMarquee();
}
});
return $(newMarquee);
};
});
//}}}
!!!!!Code
***/
//{{{
eval(store.getTiddlerText("MarqueePlugin##gistfile1.js").trim().replace(/\* \//g, "*/").replace("require(['jquery'], function ($) {", "(function ($) {").replace("});\n//}}}", "}(jQuery));").replace("if ($marqueeRedux.data('paused') !== true) {", "if (marqueeState && $marqueeRedux.data('paused') !== true) {").replace("display: block-inline", "display: inline-block"));
//}}}
//{{{
config.formatters[config.formatters.length] = {
name: "marquee",
match: "<[Mm][Aa][Rr][Qq][Uu][Ee][Ee]>",
termRegExp: /(<\/[Mm][Aa][Rr][Qq][Uu][Ee][Ee]>)/mg,
handler: function(w) {
var marqueeElement = createTiddlyElement(w.output, "span");
w.subWikifyTerm(marqueeElement, this.termRegExp);
jQuery(marqueeElement).marquee();
}
};
//}}}
/***
|Name|MatchTagsPlugin|
|Source|http://www.TiddlyTools.com/#MatchTagsPlugin|
|Documentation|http://www.TiddlyTools.com/#MatchTagsPluginInfo|
|Version|2.0.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|'tag matching' with full boolean expressions (AND, OR, NOT, and nested parentheses)|
!!!!!Documentation
> see [[MatchTagsPluginInfo]]
!!!!!Revisions
<<<
2011.10.28 2.0.6 added .matchTags CSS class to popups to enable custom styling via StyleSheet
2011.01.23 2.0.5 fix core tweak for TW262+: adjust code in config.filters['tag'] instead of filterTiddlers()
2010.08.11 2.0.4 in getMatchingTiddlers(), fixed sorting for descending order (e.g, "-created")
| please see [[MatchTagsPluginInfo]] for additional revision details |
2008.02.28 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.MatchTagsPlugin= {major: 2, minor: 0, revision: 6, date: new Date(2011,10,28)};
// store.getMatchingTiddlers() processes boolean expressions for tag matching
// sortfield (optional) sets sort order for tiddlers - default=title
// tiddlers (optional) use alternative set of tiddlers (instead of current store)
TiddlyWiki.prototype.getMatchingTiddlers = function(tagexpr,sortfield,tiddlers) {
var debug=config.options.chkDebug; // abbreviation
var cmm=config.macros.matchTags; // abbreviation
var r=[]; // results are an array of tiddlers
var tids=tiddlers||store.getTiddlers();
if (tids && sortfield) tids=store.sortTiddlers(tids,sortfield);
if (debug) displayMessage(cmm.msg1.format([tids.length]));
// try simple lookup to quickly find single tags or tags that
// contain boolean operators as literals, e.g. "foo and bar"
for (var t=0; t<tids.length; t++)
if (tids[t].isTagged(tagexpr)) r.pushUnique(tids[t]);
if (r.length) {
if (debug) displayMessage(cmm.msg4.format([r.length,tagexpr]));
return r;
}
// convert expression into javascript code with regexp tests,
// so that "tag1 AND ( tag2 OR NOT tag3 )" becomes
// "/\~tag1\~/.test(...) && ( /\~tag2\~/.test(...) || ! /\~tag3\~/.test(...) )"
// normalize whitespace, tokenize operators, delimit with "~"
var c=tagexpr.trim(); // remove leading/trailing spaces
c = c.replace(/\s+/ig," "); // reduce multiple spaces to single spaces
c = c.replace(/\(\s?/ig,"~(~"); // open parens
c = c.replace(/\s?\)/ig,"~)~"); // close parens
c = c.replace(/(\s|~)?&&(\s|~)?/ig,"~&&~"); // &&
c = c.replace(/(\s|~)AND(\s|~)/ig,"~&&~"); // AND
c = c.replace(/(\s|~)?\|\|(\s|~)?/ig,"~||~"); // ||
c = c.replace(/(\s|~)OR(\s|~)/ig,"~||~"); // OR
c = c.replace(/(\s|~)?!(\s|~)?/ig,"~!~"); // !
c = c.replace(/(^|~|\s)NOT(\s|~)/ig,"~!~"); // NOT
c = c.replace(/(^|~|\s)NOT~\(/ig,"~!~("); // NOT(
// change tag terms to regexp tests
var terms=c.split("~"); for (var i=0; i<terms.length; i++) { var t=terms[i];
if (/(&&)|(\|\|)|[!\(\)]/.test(t) || t=="") continue; // skip operators/parens/spaces
if (t==config.macros.matchTags.untaggedKeyword)
terms[i]="tiddlertags=='~~'"; // 'untagged' tiddlers
else
terms[i]="/\\~"+t+"\\~/.test(tiddlertags)";
}
c=terms.join(" ");
if (debug) { displayMessage(cmm.msg2.format([tagexpr])); displayMessage(cmm.msg3.format([c])); }
// scan tiddlers for matches
for (var t=0; t<tids.length; t++) {
// assemble tags from tiddler into string "~tag1~tag2~tag3~"
var tiddlertags = "~"+tids[t].tags.join("~")+"~";
try { if(eval(c)) r.push(tids[t]); } // test tags
catch(e) { // error in test
displayMessage(cmm.msg2.format([tagexpr]));
displayMessage(cmm.msg3.format([c]));
displayMessage(e.toString());
break; // skip remaining tiddlers
}
}
if (debug) displayMessage(cmm.msg4.format([r.length,tagexpr]));
return r;
}
//}}}
//{{{
config.macros.matchTags = {
msg1: "scanning %0 input tiddlers",
msg2: "looking for '%0'",
msg3: "using expression: '%0'",
msg4: "found %0 tiddlers matching '%1'",
noMatch: "no matching tiddlers",
untaggedKeyword: "-",
untaggedLabel: "no tags",
untaggedPrompt: "show tiddlers with no tags",
defTiddler: "MatchingTiddlers",
defTags: "",
defFormat: "[[%0]]",
defSeparator: "\n",
reportHeading: "Found %0 tiddlers tagged with: '{{{%1}}}'\n----\n",
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var mode=params[0]?params[0].toLowerCase():'';
if (mode=="inline")
params.shift();
if (mode=="report" || mode=="panel") {
params.shift();
var target=params.shift()||this.defTiddler;
}
if (mode=="popup") {
params.shift();
if (params[0]&¶ms[0].substr(0,6)=="label:") var label=params.shift().substr(6);
if (params[0]&¶ms[0].substr(0,7)=="prompt:") var prompt=params.shift().substr(7);
} else {
var fmt=(params.shift()||this.defFormat).unescapeLineBreaks();
var sep=(params.shift()||this.defSeparator).unescapeLineBreaks();
}
var sortBy="+title";
if (params[0]&¶ms[0].substr(0,5)=="sort:") sortBy=params.shift().substr(5);
var expr = params.join(" ");
if (mode!="panel" && (!expr||!expr.trim().length)) return;
if (expr==this.untaggedKeyword)
{ var label=this.untaggedLabel; var prompt=this.untaggedPrompt };
switch (mode) {
case "popup": this.createPopup(place,label,expr,prompt,sortBy); break;
case "panel": this.createPanel(place,expr,fmt,sep,sortBy,target); break;
case "report": this.createReport(target,this.defTags,expr,fmt,sep,sortBy); break;
case "inline": default: this.createInline(place,expr,fmt,sep,sortBy); break;
}
},
formatList: function(tids,fmt,sep) {
var out=[];
for (var i=0; i<tids.length; i++) { var t=tids[i];
var title=t.title;
var who=t.modifier;
var when=t.modified.toLocaleString();
var text=t.text;
var first=t.text.split("\n")[0];
var desc=store.getTiddlerSlice(t.title,"description");
desc=desc||store.getTiddlerSlice(t.title,"Description");
desc=desc||store.getTiddlerText(t.title+"##description");
desc=desc||store.getTiddlerText(t.title+"##Description");
var tags=t.tags.length?'[['+t.tags.join(']] [[')+']]':'';
out.push(fmt.format([title,who,when,text,first,desc,tags]));
}
return out.join(sep);
},
createInline: function(place,expr,fmt,sep,sortBy) {
wikify(this.formatList(store.sortTiddlers(store.getMatchingTiddlers(expr),sortBy),fmt,sep),place);
},
createPopup: function(place,label,expr,prompt,sortBy) {
var btn=createTiddlyButton(place,
(label||expr).format([expr]),
(prompt||config.views.wikified.tag.tooltip).format([expr]),
function(ev){ return config.macros.matchTags.showPopup(this,ev||window.event); });
btn.setAttribute("sortBy",sortBy);
btn.setAttribute("expr",expr);
},
showPopup: function(here,ev) {
var p=Popup.create(here,null,"matchTags popup"); if (!p) return false;
var tids=store.getMatchingTiddlers(here.getAttribute("expr"));
store.sortTiddlers(tids,here.getAttribute("sortBy"));
var list=[]; for (var t=0; t<tids.length; t++) list.push(tids[t].title);
if (!list.length) createTiddlyText(p,this.noMatch);
else {
var b=createTiddlyButton(createTiddlyElement(p,"li"),
config.views.wikified.tag.openAllText,
config.views.wikified.tag.openAllTooltip,
function() {
var list=this.getAttribute("list").readBracketedList();
story.displayTiddlers(null,tids);
});
b.setAttribute("list","[["+list.join("]] [[")+"]]");
createTiddlyElement(p,"hr");
}
var out=this.formatList(tids," [[%0]] ","\n"); wikify(out,p);
Popup.show();
ev.cancelBubble=true;
if(ev.stopPropagation) ev.stopPropagation();
return false;
},
createReport: function(target,tags,expr,fmt,sep,sortBy) {
var tids=store.sortTiddlers(store.getMatchingTiddlers(expr),sortBy);
if (!tids.length) { displayMessage('no matches for: '+expr); return false; }
var msg=config.messages.overwriteWarning.format([target]);
if (store.tiddlerExists(target) && !confirm(msg)) return false;
var out=this.reportHeading.format([tids.length,expr])
out+=this.formatList(tids,fmt,sep);
store.saveTiddler(target,target,out,config.options.txtUserName,new Date(),tags,{});
story.closeTiddler(target); story.displayTiddler(null,target);
},
createPanel: function(place,expr,fmt,sep,sortBy,tid) {
var s=createTiddlyElement(place,"span"); s.innerHTML=store.getTiddlerText("MatchTagsPlugin##html");
var f=s.getElementsByTagName("form")[0];
f.expr.value=expr; f.fmt.value=fmt; f.sep.value=sep.escapeLineBreaks();
f.tid.value=tid; f.tags.value=this.defTags;
}
};
//}}}
/***
//{{{
!html
<form style='display:inline;white-space:nowrap'>
<input type='text' name='expr' style='width:50%' title='tag expression'><!--
--><input type='text' name='fmt' style='width:10%' title='list item format'><!--
--><input type='text' name='sep' style='width:5%' title='list item separator'><!--
--><input type='text' name='tid' style='width:12%' title='target tiddler title'><!--
--><input type='text' name='tags' style='width:10%' title='target tiddler tags'><!--
--><input type='button' name='go' style='width:8%' value='go' onclick="
var expr=this.form.expr.value;
if (!expr.length) { alert('Enter a boolean tag expression'); return false; }
var fmt=this.form.fmt.value;
if (!fmt.length) { alert('Enter the list item output format'); return false; }
var sep=this.form.sep.value.unescapeLineBreaks();
var tid=this.form.tid.value;
if (!tid.length) { alert('Enter a target tiddler title'); return false; }
var tags=this.form.tags.value;
config.macros.matchTags.createReport(tid,tags,expr,fmt,sep,'title');
return false;">
</form>
!end
//}}}
***/
//{{{
// SHADOW TIDDLER for displaying default panel input form
config.shadowTiddlers.MatchTags="<<matchTags panel>>";
//}}}
//{{{
// TWEAK core filterTiddlers() or config.filters['tag'] (in TW262+)
// to use getMatchingTiddlers instead getTaggedTiddlers
// for enhanced boolean matching in [tag[...]] syntax
var TW262=config.filters && config.filters['tag']; // detect TW262+
var fname=TW262?"config.filters['tag']":"TiddlyWiki.prototype.filterTiddlers";
var code=eval(fname).toString().replace(/getTaggedTiddlers/g,'getMatchingTiddlers');
eval(fname+'='+code);
//}}}
//{{{
// REDEFINE core handler for enhanced boolean matching in tag:"..." paramifier
// use filterTiddlers() instead of getTaggedTiddlers() to get list of tiddlers.
config.paramifiers.tag = {
onstart: function(v) {
var tagged = store.filterTiddlers("[tag["+v+"]]");
story.displayTiddlers(null,tagged,null,false,null);
}
};
//}}}
/***
|Name|MatchTagsPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[MatchTagsPlugin]]|
!!!!!Code
***/
//{{{
config.macros.matchTags.handlerOverride_base = config.macros.matchTags.handler;
config.macros.matchTags.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var newTiddlerParamString;
for (var i = 0; i < params.length; i++) {
if (params[i].indexOf("newTiddler:") == 0) {
newTiddlerParamString = params[i].substring("newTiddler:".length);
params.splice(i, 1);
break;
}
}
if (newTiddlerParamString) {
config.macros.newTiddler.handler(place,"newTiddler",[],wikifier,newTiddlerParamString,tiddler);
wikify("\n\n", place);
}
config.macros.matchTags.handlerOverride_base(place,macroName,params,wikifier,paramString,tiddler);
};
//}}}
//{{{
(function () {
var code = eval("TiddlyWiki.prototype.getMatchingTiddlers").toString();
var newCode = null;
var startPosition = code.indexOf("c.replace");
if (startPosition > -1) {
var startPosition = code.lastIndexOf('c', startPosition - 1);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' c = c.replace(/\\\\\\(/ig,"\("); c = c.replace(/\\\\\\)/ig,"\)"); ' + code.substring(startPosition);
code = newCode;
}
}
var startPosition = code.indexOf("continue");
if (startPosition > -1) {
var startPosition = code.indexOf('if', startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' t = t.replace(/\(/ig,"\\\\("); t = t.replace(/\)/ig,"\\\\)"); ' + code.substring(startPosition);
code = newCode;
}
}
var startPosition = code.indexOf("if (tids && sortfield) tids=store.sortTiddlers(tids,sortfield);");
if (startPosition > -1) {
var startPosition = startPosition + "if (tids && sortfield) tids=store.sortTiddlers(tids,sortfield);".length;
newCode = code.substring(0, startPosition) + ' if (tids && !sortfield && config.macros.collator) tids.sort(function(a, b) { return config.macros.collator.compare(!a ? "" : a.title, !b ? "" : b.title); }); ' + code.substring(startPosition);
code = newCode;
}
if (newCode != null) {
eval("TiddlyWiki.prototype.getMatchingTiddlers = function getMatchingTiddlers" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
(function () {
var code = eval("TiddlyWiki.prototype.getMatchingTiddlers").toString();
var position = code.indexOf('var c=tagexpr.trim();');
if (position > -1) {
code = code.substring(position);
var endText = 'c=terms.join(" ");'
position = code.indexOf(endText);
if (position > -1) {
code = code.substring(0, position + endText.length);
code = 'config.macros.matchTags.isTagged = function (tags, tagexpr) { var tagged = false; if (tags && tags.length) { ' + code + ' var tiddlertags = "~"+tags.join("~")+"~"; try { if(eval(c)) tagged = true; } catch(e) {} } return tagged; }';
eval(code);
}
}
})();
//}}}
//{{{
config.macros.matchTags.createLinks = function (listPlace) {
jQuery(listPlace).find("span.tiddlerlink").each(function () {
var tiddlerLink = jQuery(this);
var link = tiddlerLink.text();
tiddlerLink.empty();
createTiddlyText(createTiddlyLink(tiddlerLink[0], link, false, null, false, null), link);
});
};
(function () {
var code = eval("config.macros.matchTags.formatList").toString();
code = code.replace(",sep)", ",sep,escapeLinks)");
code = code.replace("var out", "if (escapeLinks) fmt = fmt.replace(/\\[\\[%0\\]\\]/g, '[[\\0]]');\nvar out");
code = code.replace("]));", ']));\nif (escapeLinks) out[out.length - 1] = out[out.length - 1].replace(/\\[\\[\\0\\]\\]/g, function () { return "<html><span class=\'tiddlerlink\'>" + title.replace(/</g, "&lt;").replace(/</g, "<") + "</span></html>"; });');
eval("config.macros.matchTags.formatList = function formatList" + code.substring(code.indexOf("(")));
})();
(function () {
var code = eval("config.macros.matchTags.createInline").toString();
code = code.replace("wikify", "var listPlace = jQuery('<span></span>').appendTo(place);\nwikify");
code = code.replace("sep),place);", "sep,true),listPlace[0]);\nconfig.macros.matchTags.createLinks(listPlace);");
eval("config.macros.matchTags.createInline = function createInline" + code.substring(code.indexOf("(")));
})();
(function () {
var code = eval("config.macros.matchTags.showPopup").toString();
code = code.replace("); wikify(out,p);", ",true); wikify(out,p);\config.macros.matchTags.createLinks(p);");
eval("config.macros.matchTags.showPopup = function showPopup" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|''Name:''|MediaWikiFormatterPlugin|
|''Description:''|Allows Tiddlers to use [[MediaWiki|http://meta.wikimedia.org/wiki/Help:Wikitext]] ([[WikiPedia|http://meta.wikipedia.org/]]) text formatting|
|''Author:''|Martin Budden (mjbudden (at) gmail (dot) com)|
|''Source:''|http://www.martinswiki.com/#MediaWikiFormatterPlugin |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/formatters/MediaWikiFormatterPlugin.js |
|''Version:''|0.4.6|
|''Date:''|Jul 27, 2007|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.1.0|
|''Display instrumentation''|<<option chkDisplayInstrumentation>>|
|''Display empty template links:''|<<option chkMediaWikiDisplayEmptyTemplateLinks>>|
|''Allow zooming of thumbnail images''|<<option chkMediaWikiDisplayEnableThumbZoom>>|
|''List references''|<<option chkMediaWikiListReferences>>|
|''Display unsupported magic words''|<<option chkDisplayMediaWikiMagicWords>>|
This is the MediaWikiFormatterPlugin, which allows you to insert MediaWiki formated text into a TiddlyWiki.
The aim is not to fully emulate MediaWiki, but to allow you to work with MediaWiki content off-line and then resync the content with your MediaWiki later on, with the expectation that only minor edits will be required.
To use MediaWiki format in a Tiddler, tag the Tiddler with MediaWikiFormat or set the tiddler's {{{wikiformat}}} extended field to {{{mediawiki}}}.
!!!Issues
There are (at least) the following known issues:
# Not all styles from http://meta.wikimedia.org/wiki/MediaWiki:Common.css incorporated
## Styles for tables don't yet match Wikipedia styles.
## Styles for image galleries don't yet match Wikipedia styles.
# Anchors not yet supported.
!!!Not supported
# Template parser functions (also called colon functions) http://meta.wikimedia.org/wiki/ParserFunctions eg {{ #functionname: argument 1 | argument 2 | argument 3... }}
# Magic words and variables http://meta.wikimedia.org/wiki/Help:Magic_words eg {{{__TOC__}}}, {{CURRENTDAY}}, {{PAGENAME}}
# {{{^''}}} (italic at start of line) indents, makes italic and quotes with guilmot quote
!!!No plans to support
# Template substitution on save http://meta.wikimedia.org/wiki/Help:Substitution eg {{ subst: templatename }}
***/
//{{{
// Ensure that the MediaWikiFormatter Plugin is only installed once.
if(!version.extensions.MediaWikiFormatterPlugin) {
version.extensions.MediaWikiFormatterPlugin = {installed:true};
if(version.major < 2 || (version.major == 2 && version.minor < 1))
{alertAndThrow('MediaWikiFormatterPlugin requires TiddlyWiki 2.1 or later.');}
if(config.options.chkDisplayInstrumentation == undefined)
{config.options.chkDisplayInstrumentation = false;}
if(config.options.chkMediaWikiDisplayEmptyTemplateLinks == undefined)
{config.options.chkMediaWikiDisplayEmptyTemplateLinks = false;}
if(config.options.chkMediaWikiDisplayEnableThumbZoom == undefined)
{config.options.chkMediaWikiDisplayEnableThumbZoom = false;}
if(config.options.chkMediaWikiListReferences == undefined)
{config.options.chkMediaWikiListReferences = false;}
if(config.options.chkDisplayMediaWikiMagicWords == undefined)
{config.options.chkDisplayMediaWikiMagicWords = false;}
//<div class='viewer' macro='view text wikified'></div>;
config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if((tiddler instanceof Tiddler) && params[0]) {
var host = store.getValue(tiddler,'server.host');
if(host && host.indexOf('wikipedia')!=-1) {
var t = store.fetchTiddler(params[0]);
var text = store.getValue(t,'text');
wikify(text,place,highlightHack,tiddler);
}
}
};
MediaWikiFormatter = {}; // 'namespace' for local functions
mwDebug = function(out,str)
{
createTiddlyText(out,str.replace(/\n/mg,'\\n').replace(/\r/mg,'RR'));
createTiddlyElement2(out,'br');
};
MediaWikiFormatter.Tiddler_changed = Tiddler.prototype.changed;
Tiddler.prototype.changed = function()
{
if((this.fields.wikiformat==config.parsers.mediawikiFormatter.format) || this.isTagged(config.parsers.mediawikiFormatter.formatTag)) {
this.links = [];
var tiddlerLinkRegExp = /\[\[(?::?([A-Za-z]{2,}:)?)(#?)([^\|\]]*?)(?:(\]\])|(\|(.*?)\]\]))/mg;
tiddlerLinkRegExp.lastIndex = 0;
var match = tiddlerLinkRegExp.exec(this.text);
while(match) {
if(!match[1] && !match[2])
this.links.pushUnique(match[3]);
match = tiddlerLinkRegExp.exec(this.text);
}
} else if(!this.isTagged('systemConfig')) {
MediaWikiFormatter.Tiddler_changed.apply(this,arguments);
return;
}
this.linksUpdated = true;
};
TiddlyWiki.prototype.getMediaWikiPagesInNamespace = function(namespace)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(tiddler.title.indexOf(namespace)==0)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
TiddlyWiki.prototype.getMediaWikiPages = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(!tiddler.isTagged('excludeLists') && tiddler.title.indexOf(':')==-1)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
TiddlyWiki.prototype.getMediaWikiOtherPages = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(!tiddler.isTagged('excludeLists') && tiddler.title.indexOf(':')!=-1)
results.push(tiddler);
});
results.sort(function(a,b) {return a.title < b.title ? -1 : +1;});
return results;
};
config.macros.list.otherpages = {};
config.macros.list.otherpages.handler = function(params)
{
return store.getMediaWikiOtherPages();
};
config.macros.list.templates = {};
config.macros.list.templates.handler = function(params)
{
return store.getMediaWikiPagesInNamespace('Template:');
};
config.macros.list.categories = {};
config.macros.list.categories.handler = function(params)
{
return store.getMediaWikiPagesInNamespace('Category:');
};
function createTiddlyElement2(parent,element)
{
return parent.appendChild(document.createElement(element));
}
config.formatterHelpers.createElementAndWikify = function(w)
{
w.subWikifyTerm(createTiddlyElement2(w.output,this.element),this.termRegExp);
};
MediaWikiFormatter.hijackListAll = function ()
{
MediaWikiFormatter.oldListAll = config.macros.list.all.handler;
config.macros.list.all.handler = function(params) {
return store.getMediaWikiPages();
};
};
MediaWikiFormatter.hijackListAll();
MediaWikiFormatter.normalizedTitle = function(title)
{
title = title.trim();
var n = title.charAt(0).toUpperCase() + title.substr(1);
return n.replace(/\s/g,'_');
};
MediaWikiFormatter.expandVariable = function(w,variable)
{
switch(variable) {
case 'PAGENAME':
createTiddlyText(w.output,w.tiddler.title);
break;
case 'PAGENAMEE':
createTiddlyText(w.output,MediaWikiFormatter.normalizedTitle(w.tiddler.title));
break;
case 'REVISIONID':
var text = w.tiddler.fields['server.revision'];
if(text)
createTiddlyText(w.output,text);
break;
default:
return false;
}
return true;
};
MediaWikiFormatter.getParserFunctionParams = function(text)
{
var params = [];
// #if: foo | do if true | do if false
text += '|';
//var fRegExp = / ? # ?([a-z]*) ?:/mg;
//var fRegExp = /#(if) : (foo) :/mg;
var fRegExp = /(#([a-z]*) *: ([^\|]*))\|/mg;
fRegExp.lastIndex = 0;
var match = fRegExp.exec(text);
var pRegExp = /([^\|]*)\|/mg;
if(match) {
pRegExp.lastIndex = fRegExp.lastIndex;
match = pRegExp.exec(text);
}
var i = 1;
while(match) {
params[i] = match[1].trim();
i++;
match = pRegExp.exec(text);
}
return params;
};
MediaWikiFormatter.getTemplateParams = function(text)
{
var params = {};
text += '|';
var pRegExp = /(?:([^\|]*)=)?([^\|]*)\|/mg;
var match = pRegExp.exec(text);
if(match) {
match = pRegExp.exec(text);
}
var i = 1;
while(match) {
if(match[1]) {
params[match[1]] = match[2];
} else {
params[i] = match[2];
i++;
}
match = pRegExp.exec(text);
}
return params;
};
MediaWikiFormatter.expandParserFunction = function(w,text,expr,params)
{
var fnRegExp = / *#(if) */mg;
var t = '';//params[0];
fnRegExp.lastIndex = 0;
var match = fnRegExp.exec(text);
if(match) {
switch(match[1]) {
case 'if':
t = expr.trim()=='' ? params[2] : params[1];
break;
default:
break;
}
}
return t;
};
MediaWikiFormatter.expandTemplate = function(w,templateText,params)
{
var text = templateText;
text = text.replace(/<noinclude>((?:.|\n)*?)<\/noinclude>/mg,'');// remove text between noinclude tags
var includeOnlyRegExp = /<includeonly>((?:.|\n)*?)<\/includeonly>/mg;
var t = '';
var match = includeOnlyRegExp.exec(text);
while(match) {
t += match[1];
match = includeOnlyRegExp.exec(text);
}
text = t == '' ? text : t;
var paramsRegExp = /\{\{\{(.*?)(?:\|(.*?))?\}\}\}/mg;
t = '';
var pi = 0;
match = paramsRegExp.exec(text);
while(match) {
var name = match[1];
var val = params[name];
if(!val) {
val = match[2];
}
if(!val) {
val = '';//val = match[0];
}
t += text.substring(pi,match.index) + val;
pi = paramsRegExp.lastIndex;
match = paramsRegExp.exec(text);
}
t += text.substring(pi);
return t;
//return t == '' ? text : t;
/* //displayMessage("ss:"+text.substring(pi));
t += text.substring(pi);
t = MediaWikiFormatter.evaluateTemplateParserFunctions(t);
//{{#if: {{{perihelion|}}} | <tr><th>[[Perihelion|Perihelion distance]]:</th><td>{{{perihelion}}}</td></tr>}}
//{{#if:{{{symbol|}}} | {{{symbol}}} | }}
text = t == '' ? text : t;
displayMessage("t2:"+text);
return text;
*/
};
MediaWikiFormatter.endOfParams = function(w,text)
{
var p = 0;
var i = text.indexOf('|');
if(i==-1) {return -1;}
var n = text.indexOf('\n');
if(n!=-1 && n<i) {return -1;}
var b = text.indexOf('[[');
if(b!=-1 && b<i) {return -1;}
b = text.indexOf('{{');
while(b!=-1 && b<i) {
p += b;
text = text.substr(b);
var c = text.indexOf('}}');
p += c;
text = text.substr(c);
i = text.indexOf('|');
if(i==-1) {return -1;}
n = text.indexOf('\n');
if(n!=-1 && n<i) {return -1;}
b = text.indexOf('{{');
i = -1;
}
return i;
};
MediaWikiFormatter.readToDelim = function(w)
//!!! this is a bit rubish, needs doing properly.
{
var dRegExp = /\|/mg;
var sRegExp = /\[\[/mg;
var tRegExp = /\]\]/mg;
dRegExp.lastIndex = w.startMatch;
var dMatch = dRegExp.exec(w.source);
sRegExp.lastIndex = w.startMatch;
var sMatch = sRegExp.exec(w.source);
tRegExp.lastIndex = w.startMatch;
var tMatch = tRegExp.exec(w.source);
if(!tMatch) {
return false;
}
while(sMatch && sMatch.index<tMatch.index) {
if(dMatch && dMatch.index<sMatch.index) {
w.nextMatch = dRegExp.lastIndex;
w.matchLength = dMatch.index - w.startMatch;
return true;
}
tRegExp.lastIndex = sRegExp.lastIndex;
tMatch = tRegExp.exec(w.source);
w.nextMatch = tRegExp.lastIndex;
dRegExp.lastIndex = w.nextMatch;
dMatch = dRegExp.exec(w.source);
sRegExp.lastIndex = w.nextMatch;
sMatch = sRegExp.exec(w.source);
tRegExp.lastIndex = w.nextMatch;
tMatch = tRegExp.exec(w.source);
}
if(dMatch && dMatch.index<tMatch.index) {
w.nextMatch = dRegExp.lastIndex;
w.matchLength = dMatch.index - w.startMatch;
return true;
}
if(tMatch) {
w.nextMatch = tRegExp.lastIndex;
w.matchLength = tMatch.index - w.startMatch;
return false;
}
w.nextMatch = tRegExp.lastIndex;
w.matchLength = -1;
return false;
};
MediaWikiFormatter.getParams = function(w)
{
var params = [];
var i = 1;
w.startMatch = w.nextMatch;
var read = MediaWikiFormatter.readToDelim(w);
if(w.matchLength!=-1) {
params[i] = w.source.substr(w.startMatch,w.matchLength);
}
while(read) {
i++;
w.startMatch = w.nextMatch;
read = MediaWikiFormatter.readToDelim(w);
if(w.matchLength!=-1) {
params[i] = w.source.substr(w.startMatch,w.matchLength);
}
}
return params;
};
MediaWikiFormatter.setFromParams = function(w,p)
{
var r = {};
var re = /\s*(.*?)=(?:(?:"(.*?)")|(?:'(.*?)')|((?:\w|%|#)*))/mg;
var match = re.exec(p);
while(match)
{
var s = match[1].unDash();
if(match[2]) {
r[s] = match[2];
} else if(match[3]) {
r[s] = match[3];
} else {
r[s] = match[4];
}
match = re.exec(p);
}
return r;
};
MediaWikiFormatter.setAttributesFromParams = function(e,p)
{
var re = /\s*(.*?)=(?:(?:"(.*?)")|(?:'(.*?)')|((?:\w|%|#)*))/mg;
var match = re.exec(p);
while(match) {
var s = match[1].unDash();
if(s == 'bgcolor') {
s = 'backgroundColor';
}
try {
if(match[2]) {
e.setAttribute(s,match[2]);
} else if(match[3]) {
e.setAttribute(s,match[3]);
} else {
e.setAttribute(s,match[4]);
}
}
catch(ex) {}
match = re.exec(p);
}
};
config.mediawiki = {};
config.mediawiki.formatters = [
{
name: 'mediaWikiHeading',
match: '^={1,6}(?!=)\\n?',
termRegExp: /(={1,6}\n?)/mg,
handler: function(w)
{
var output = w.output;
var e = createTiddlyElement2(output,'h' + w.matchLength);
var a = createTiddlyElement2(e,'a');
var t = w.tiddler ? MediaWikiFormatter.normalizedTitle(w.tiddler.title) + ':' : '';
var len = w.source.substr(w.nextMatch).indexOf('=');
a.setAttribute('name',t+MediaWikiFormatter.normalizedTitle(w.source.substr(w.nextMatch,len)));
w.subWikifyTerm(e,this.termRegExp);
}
},
{
name: 'mediaWikiTable',
// see http://www.mediawiki.org/wiki/Help:Tables, http://meta.wikimedia.org/wiki/Help:Table
match: '^\\{\\|', // ^{|
tableTerm: '\\n\\|\\}', // |}
rowStart: '\\n\\|\\-', // \n|-
cellStart: '\\n!|!!|\\|\\||\\n\\|', //\n! or !! or || or \n|
caption: '\\n\\|\\+',
rowTerm: null,
cellTerm: null,
inCellTerm: null,
tt: 0,
debug: null,
rowTermRegExp: null,
handler: function(w)
{
if(!this.rowTermRegExp) {
this.rowTerm = '(' + this.tableTerm +')|(' + this.rowStart + ')';
this.cellTerm = this.rowTerm + '|(' + this.cellStart + ')';
this.inCellTerm = '(' + this.match + ')|' + this.rowTerm + '|(' + this.cellStart + ')';
this.caption = '(' + this.caption + ')|' + this.cellTerm;
this.rowTermRegExp = new RegExp(this.rowTerm,'mg');
this.cellTermRegExp = new RegExp(this.cellTerm,'mg');
this.inCellTermRegExp = new RegExp(this.inCellTerm,'mg');
this.captionRegExp = new RegExp(this.caption,'mg');
}
this.captionRegExp.lastIndex = w.nextMatch;
var match = this.captionRegExp.exec(w.source);
if(!match) {return;}
var output = w.output;
var table = createTiddlyElement2(output,'table');
var rowContainer = table;
var i = w.source.indexOf('\n',w.nextMatch);
if(i>w.nextMatch) {
MediaWikiFormatter.setAttributesFromParams(table,w.source.substring(w.nextMatch,i));
w.nextMatch = i;
}
var rowCount = 0;
var eot = false;
if(match[1]) {
var caption = createTiddlyElement2(table,'caption');
w.nextMatch = this.captionRegExp.lastIndex;
var captionText = w.source.substring(w.nextMatch);
var n = captionText.indexOf('\n');
captionText = captionText.substr(0,n);
i = MediaWikiFormatter.endOfParams(w,captionText);
if(i!=-1) {
captionText = w.source.substr(w.nextMatch,i);
w.nextMatch += i+1;
}
if(caption != table.firstChild) {
table.insertBefore(caption,table.firstChild);
}
w.subWikify(caption,this.cellTerm);
w.nextMatch -= w.matchLength;
this.cellTermRegExp.lastIndex = w.nextMatch;
var match2 = this.cellTermRegExp.exec(w.source);
if(match2) {
if(match2[3]) {
eot = this.rowHandler(w,createTiddlyElement2(rowContainer,'tr'));
rowCount++;
}
}
} else if(match[3]) {
w.nextMatch = this.captionRegExp.lastIndex-match[3].length;
} else if(match[4]) {
w.nextMatch = this.captionRegExp.lastIndex-match[4].length;
eot = this.rowHandler(w,createTiddlyElement2(rowContainer,'tr'));
rowCount++;
}
this.rowTermRegExp.lastIndex = w.nextMatch;
match = this.rowTermRegExp.exec(w.source);
while(match && eot==false) {
if(match[1]) {
w.nextMatch = this.rowTermRegExp.lastIndex;
if(w.tableDepth==0) {
return;
}
} else if(match[2]) {
var rowElement = createTiddlyElement2(rowContainer,'tr');
w.nextMatch += match[2].length;
i = w.source.indexOf('\n',w.nextMatch);
if(i>w.nextMatch) {
MediaWikiFormatter.setAttributesFromParams(rowElement,w.source.substring(w.nextMatch,i));
w.nextMatch = i;
}
eot = this.rowHandler(w,rowElement);
}
rowCount++;
this.rowTermRegExp.lastIndex = w.nextMatch;
match = this.rowTermRegExp.exec(w.source);
}//# end while
if(w.tableDepth==0) {
w.nextMatch +=3;
}
},//# end handler
rowHandler: function(w,e)
{
var cell;
this.inCellTermRegExp.lastIndex = w.nextMatch;
var match = this.inCellTermRegExp.exec(w.source);
while(match) {
if(match[1]) {
w.tableDepth++;
w.subWikify(cell,this.tableTerm);
w.nextMatch = this.tt;
w.tableDepth--;
return false;
} else if(match[2]) {
this.tt = this.inCellTermRegExp.lastIndex;
return true;
} else if(match[3]) {
return false;
} else if(match[4]) {
var len = match[4].length;
cell = createTiddlyElement2(e,match[4].substr(len-1)=='!'?'th':'td');
w.nextMatch += len;
this.inCellTermRegExp.lastIndex = w.nextMatch;
var lookahead = this.inCellTermRegExp.exec(w.source);
if(!lookahead) {
return false;
}
var cellText = w.source.substr(w.nextMatch,lookahead.index-w.nextMatch);
var oldSource = w.source;
var i = MediaWikiFormatter.endOfParams(w,cellText);//cellText.indexOf('|');
if(i!=-1) {
cellText = cellText.replace(/^\+/mg,''); //!!hack until I fix this properly
MediaWikiFormatter.setAttributesFromParams(cell,cellText.substr(0,i-1));
cellText = cellText.substring(i+1);
}
cellText = cellText.replace(/^\s*/mg,'');
w.source = cellText;
w.nextMatch = 0;
w.subWikifyUnterm(cell);
w.source = oldSource;
w.nextMatch = lookahead.index;
}
this.inCellTermRegExp.lastIndex = w.nextMatch;
match = this.inCellTermRegExp.exec(w.source);
}//# end while
return false;
}//# end rowHandler
},
{
name: 'mediaWikiList',
match: '^[\\*#;:]+',
lookaheadRegExp: /(?:(?:(\*)|(#)|(;)|(:))+)(?: ?)/mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var stack = [w.output];
var currLevel = 0, currType = null;
var listType, itemType;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
if(lookaheadMatch[1]) {
listType = 'ul';
itemType = 'li';
} else if(lookaheadMatch[2]) {
listType = 'ol';
itemType = 'li';
} else if(lookaheadMatch[3]) {
listType = 'dl';
itemType = 'dt';
} else if(lookaheadMatch[4]) {
listType = 'dl';
itemType = 'dd';
}
var listLevel = lookaheadMatch[0].length;
w.nextMatch += listLevel;
if(listLevel > currLevel) {
for(var i=currLevel; i<listLevel; i++) {
stack.push(createTiddlyElement2(stack[stack.length-1],listType));
}
} else if(listLevel < currLevel) {
for(i=currLevel; i>listLevel; i--) {
stack.pop();
}
} else if(listLevel == currLevel && listType != currType) {
stack.pop();
stack.push(createTiddlyElement2(stack[stack.length-1],listType));
}
currLevel = listLevel;
currType = listType;
var e = createTiddlyElement2(stack[stack.length-1],itemType);
var ci = w.source.indexOf(':',w.nextMatch);
var ni = w.source.indexOf('\n',w.nextMatch);
if(itemType=='dt' && (ni==-1 || (ci!=-1 && ci<ni))) {
w.subWikifyTerm(e,/(:)/mg);
w.nextMatch--;
} else {
w.subWikifyTerm(e,this.termRegExp);
}
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
}
},
{
name: 'mediaWikiRule',
match: '^----+$\\n?',
handler: function(w)
{
createTiddlyElement2(w.output,'hr');
}
},
{
name: 'mediaWikiLeadingSpaces',
match: '^ ',
lookaheadRegExp: /^ /mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var e = createTiddlyElement2(w.output,'pre');
while(true) {
w.subWikifyTerm(e,this.termRegExp);
createTiddlyElement2(e,'br');
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
w.nextMatch += lookaheadMatch[0].length;
} else {
break;
}
}
}
},
{
name: 'mediaWikiImage',
match: '\\[\\[(?:[Ii]mage|Bild):',
lookaheadRegExp: /\[\[(?:[Ii]mage|Bild):/mg,
defaultPx: 180,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var params = MediaWikiFormatter.getParams(w);
var src = params[1];
src = src.trim().replace(/ /mg,'_');
src = src.substr(0,1).toUpperCase() + src.substring(1);
var palign = null;
var ptitle = null;
var psrc = false;
var px = null;
var pthumb = false;
var pframed = false;
for(var i=2;i<params.length;i++) {
var p = params[i];
if(p=='right'||p=='left'||p=='center'||p=='none') {
palign = p;
} else if(p=='thumbnail'||p=='thumb') {
pthumb = true;
} else if(p=='framed') {
pframed = true;
} else if(/\d{1,4} ?px/.exec(p)) {
px = p.substr(0,p.length-2).trim();
} else {
ptitle = p;
}
}//#end for
if(pthumb) {
var output = w.output;
if(!palign) {
palign = 'right';
}
if(!px) {
px = 180;
}
psrc = px + 'px-' + src;
var t = createTiddlyElement(output,'div',null,'thumb'+(palign?' t'+palign:''));
var s = createTiddlyElement2(t,'div');
s.style['width'] = Number(px) + 2 + 'px';
var a = createTiddlyElement(s,'a',null,'internal');
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
a.href = src;
}
a.title = ptitle;
var img = createTiddlyElement2(a,'img');
img.src = 'images/' + psrc;
img.width = px;
img.longdesc = 'Image:' + src;
img.alt = ptitle;
var tc = createTiddlyElement(s,'div',null,'thumbcaption');
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = ptitle; w.nextMatch = 0;
w.subWikifyUnterm(tc);
w.source = oldSource; w.nextMatch = oldMatch;
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
var tm = createTiddlyElement(tc,'div',null,'magnify');
tm.style['float'] = 'right';
var ta = createTiddlyElement(tm,'a',null,'internal');
ta.title = 'Enlarge';
timg = createTiddlyElement2(ta,'img'); timg.src = 'magnify-clip.png'; timg.alt = 'Enlarge'; timg.width = '15'; timg.height = '11';
ta.href = src;
}
} else {
a = createTiddlyElement(w.output,'a',null,'image');
a.title = ptitle;
img = createTiddlyElement2(a,'img');
if(palign) {img.align = palign;}
img.src = px ? 'images/' + px + 'px-' + src : 'images/' + src;
if(px) {img.width = px;}
img.longdesc = 'Image:' + src;
img.alt = ptitle;
}
}
}//#end image handler
},
{
name: 'mediaWikiExplicitLink',
match: '\\[\\[',
lookaheadRegExp: /\[\[(?:([a-z]{2,3}:)?)(#?)([^\|\]]*?)(?:(\]\](\w*))|(\|(.*?)\]\]))/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
if(!lookaheadMatch[1]) {
var e;
var link = lookaheadMatch[3];
var text = link;
link = link.substr(0,1).toUpperCase() + link.substring(1);
if(lookaheadMatch[4]) {
if(lookaheadMatch[2]) {
var a = createTiddlyElement(w.output,'a');
var t = w.tiddler ? MediaWikiFormatter.normalizedTitle(w.tiddler.title) + ':' : '';
t = '#' + t + MediaWikiFormatter.normalizedTitle(link);
a.setAttribute('href',t);
a.title = '#' + MediaWikiFormatter.normalizedTitle(link);
createTiddlyText(a,'#'+link);
} else {
e = createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
if(lookaheadMatch[5]) {
text += lookaheadMatch[5];
}
createTiddlyText(e,text);
}
} else if(lookaheadMatch[6]) {
if(link.charAt(0)==':')
link = link.substring(1);
e = createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = lookaheadMatch[7].trim(); w.nextMatch = 0;
w.subWikifyUnterm(e);
w.source = oldSource; w.nextMatch = oldMatch;
}
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiTemplate',
match: '\\{\\{[^\\{]',
lookaheadRegExp: /\{\{((?:.|\n)*?)\}\}/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var lastIndex = this.lookaheadRegExp.lastIndex;
var contents = lookaheadMatch[1];
if(MediaWikiFormatter.expandVariable(w,contents)) {
w.nextMatch = lastIndex;
return;
}
var i = contents.indexOf('|');
var title = i==-1 ? contents : contents.substr(0,i);
title = title.trim().replace(/_/mg,' ');
if(title.substr(0,1)=='#') {
var parserFn = true;
var j = contents.indexOf(':');
var expr = contents.substring(j+1,i);
i = title.indexOf(':');
title = title.substr(0,i);
} else {
title = 'Template:' + title.substr(0,1).toUpperCase() + title.substring(1);
var tiddler = store.fetchTiddler(title);
}
var oldSource = w.source;
if(tiddler) {
var params = {};
if(i!=-1) {
params = MediaWikiFormatter.getTemplateParams(lookaheadMatch[1]);
}
w.source = MediaWikiFormatter.expandTemplate(w,tiddler.text,params);
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
} else if(parserFn) {
if(i!=-1) {
params = MediaWikiFormatter.getParserFunctionParams(lookaheadMatch[1]);
}
w.source = MediaWikiFormatter.expandParserFunction(w,title,expr,params);
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
} else {
if(config.options.chkMediaWikiDisplayEmptyTemplateLinks) {
w.source = '[['+title+']]';
w.nextMatch = 0;
w.subWikifyUnterm(w.output);
}
}
w.source = oldSource;
w.nextMatch = lastIndex;
}
}
},
{
name: 'mediaWikiParagraph',
match: '\\n{2,}',
handler: function(w)
{
w.output = createTiddlyElement2(w.output,'p');
}
},
{
name: 'mediaWikiExplicitLineBreak',
match: '<br ?/?>',
handler: function(w)
{
createTiddlyElement2(w.output,'br');
}
},
{
name: 'mediaWikiExplicitLineBreakWithParams',
match: "<br(?:\\s*(?:(?:.*?)=[\"']?(?:.*?)[\"']?))*?\\s*/?>",
lookaheadRegExp: /<br((?:\s+(?:.*?)=["']?(?:.*?)["']?)*?)?\s*\/?>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e =createTiddlyElement2(w.output,'br');
if(lookaheadMatch[1]) {
MediaWikiFormatter.setAttributesFromParams(e,lookaheadMatch[1]);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;// empty tag
}
}
},
{
name: 'mediaWikiTitledUrlLink',
match: '\\[' + config.textPrimitives.urlPattern + '(?:\\s+[^\\]]+)?' + '\\]',
handler: function(w)
{
var lookaheadRegExp = new RegExp('\\[(' + config.textPrimitives.urlPattern + ')(?:\\s+([^\[]+))?' + '\\]','mg');
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index==w.matchStart) {
var link = lookaheadMatch[1];
if(lookaheadMatch[2]) {
var e = createExternalLink(w.output,link);
var oldSource = w.source; var oldMatch = w.nextMatch;
w.source = lookaheadMatch[2].trim(); w.nextMatch = 0;
w.subWikifyUnterm(e);
w.source = oldSource; w.nextMatch = oldMatch;
} else {
e = createExternalLink(createTiddlyElement2(w.output,'sup'),link);
w.linkCount++;
createTiddlyText(e,'['+w.linkCount+']');
}
w.nextMatch = lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiUrlLink',
match: config.textPrimitives.urlPattern,
handler: function(w)
{
w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
}
},
{
name: "mediaWikiCharacterFormat",
match: "'{2,5}|(?:<[usbi]>)",
handler: function(w)
{
switch(w.matchText) {
case "'''''":
var e = createTiddlyElement(w.output,'strong');
w.subWikifyTerm(createTiddlyElement(e,'em'),/('''''|(?=\n))/mg);
break;
case "'''":
w.subWikifyTerm(createTiddlyElement(w.output,'strong'),/('''|(?=\n))/mg);
break;
case "''":
w.subWikifyTerm(createTiddlyElement(w.output,'em'),/((?:''(?!'))|(?=\n))/mg);
break;
case '<u>':
w.subWikifyTerm(createTiddlyElement(w.output,'u'),/(<\/u>|(?=\n))/mg);
break;
case '<s>':
w.subWikifyTerm(createTiddlyElement(w.output,'del'),/(<\/s>|(?=\n))/mg);
break;
case '<b>':
w.subWikifyTerm(createTiddlyElement(w.output,'b'),/(<\/b>|(?=\n))/mg);
break;
case '<i>':
w.subWikifyTerm(createTiddlyElement(w.output,'i'),/(<\/i>|(?=\n))/mg);
break;
}
}
},
{
name: 'mediaWikiTemplateParam',
match: '\\{\\{\\{',
lookaheadRegExp: /(\{\{\{(?:.|\n)*?\}\}\})/mg,
element: 'span',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiInsertReference',
match: '<ref[^/]*>',
lookaheadRegExp: /<ref(\s+(?:.*?)=["']?(?:.*?)["']?)?>([^<]*?)<\/ref>/mg,
handler: function(w)
{
if(config.browser.isIE) {
refRegExp = /<ref[^\/]*>((?:.|\n)*?)<\/ref>/mg;
refRegExp.lastIndex = w.matchStart;
var refMatch = refRegExp.exec(w.source);
if(refMatch && refMatch.index == w.matchStart) {
w.nextMatch = refRegExp.lastIndex;
return;
}
}
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var x = {id:'',value:''};
w.nextMatch = this.lookaheadRegExp.lastIndex;
if(!w.referenceCount) {
w.referenceCount = 0;
w.references = {};
}
var s = createTiddlyElement(w.output,'sup',null,'reference');
var a = createTiddlyElement2(s,'a');
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
var name;
if(lookaheadMatch[1]) {
var r = MediaWikiFormatter.setFromParams(w,lookaheadMatch[1]);
name = r.name ? r.name.trim() : '';
name = name.replace(/ /g,'_');
s.id = prefix + '_ref-' + name;// + '_' + nameCount;(w.referenceCount+1);
if(!w.references[name]) {
w.references[name] = x;
w.references[name].id = w.referenceCount;
w.references[name].value = lookaheadMatch[2].trim();
}
} else {
w.references[w.referenceCount] = x;
w.references[w.referenceCount].id = w.referenceCount;
w.references[w.referenceCount].value = lookaheadMatch[2].trim();
name = w.referenceCount;
s.id = prefix + '_ref-' + w.referenceCount;
}
w.referenceCount++;
a.title = lookaheadMatch[2].trim();//mb, extra to wikipedia
a.href = '#' + prefix + '_note-' + name;
a.innerHTML = '['+w.referenceCount+']';
}
}
},
{
name: 'mediaWikiListReferences',
match: '<references ?/>',
lookaheadRegExp: /<references ?\/>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(config.options.chkMediaWikiListReferences && w.referenceCount) {
var ol = createTiddlyElement(w.output,'ol',null,'references');
var oldSource = w.source;
if(w.referenceCount>0) {
for(var i in w.references) {
var li = createTiddlyElement2(ol,'li');
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
var b = createTiddlyElement2(li,'b');
var a = createTiddlyElement2(b,'a');
li.id = prefix + '_note-' + i;
a.href = '#' + prefix + '_ref-' + i;
a.innerHTML = '^';
w.source = w.references[i].value;
w.nextMatch = 0;
w.subWikifyUnterm(li);
}
}
w.source = oldSource;
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
},
{
name: 'mediaWikiRepeatReference',
match: '<ref[^/]*/>',
lookaheadRegExp: /<ref(\s+(?:.*?)=["'](?:.*?)["'])?\s*\/>/mg,
handler: function(w)
{
if(config.browser.isIE) {
refRegExp = /<ref.*?\/>/mg;
refRegExp.lastIndex = w.matchStart;
var refMatch = refRegExp.exec(w.source);
if(refMatch && refMatch.index == w.matchStart) {
w.nextMatch = refRegExp.lastIndex;
return;
}
}
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var x = {id:'',value:''};
w.nextMatch = this.lookaheadRegExp.lastIndex;
var s = createTiddlyElement(w.output,"sup",null,"reference");
var a = createTiddlyElement2(s,"a");
var prefix = w.tiddler ? w.tiddler.title : '';
if(lookaheadMatch[1]) {
var r = {};
r = MediaWikiFormatter.setFromParams(w,lookaheadMatch[1]);
var name = r.name ? r.name.trim() : '';
name = name.replace(/ /g,'_');
s.id = prefix + '_ref-' + name +'_' + (w.referenceCount+1);
var count = w.references && w.references[name] ? (w.references[name].id+1) : '?';
}
a.href = '#' + prefix + '_note-' + name;
a.innerHTML = '['+count+']';
a.title = name;
}
}//# end handler
},
{
name: 'mediaWikiHtmlEntitiesEncoding',
match: '&#?[a-zA-Z0-9]{2,8};',
handler: function(w)
{
if(!config.browser.isIE)
createTiddlyElement(w.output,"span").innerHTML = w.matchText;
}
},
{
name: 'mediaWikiComment',
match: '<!\\-\\-',
lookaheadRegExp: /<!\-\-((?:.|\n)*?)\-\->/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiIncludeOnly',
match: '<includeonly>',
lookaheadRegExp: /<includeonly>((?:.|\n)*?)<\/includeonly>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiNoWiki',
match: '<nowiki>',
lookaheadRegExp: /<nowiki>((?:.|\n)*?)<\/nowiki>/mg,
element: 'span',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiPreNoWiki',
match: '<pre>\s*<nowiki>',
lookaheadRegExp: /<pre>\s*<nowiki>((?:.|\n)*?)<\/nowiki>\s*<\/pre>/mg,
element: 'pre',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiPre',
match: '<pre>',
lookaheadRegExp: /<pre>((?:.|\n)*?)<\/pre>/mg,
element: 'pre',
handler: config.formatterHelpers.enclosedTextHelper
},
{
name: 'mediaWikiMagicWords',
match: '__',
lookaheadRegExp: /__([A-Z]*?)__/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
if(lookaheadMatch[1]=='NOTOC') {
} else if(config.options.chkDisplayMediaWikiMagicWords) {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: 'mediaWikiGallery',
match: '<gallery>',
lookaheadRegExp: /[Ii]mage:(.*?)\n/mg,
handler: function(w)
{
var table = createTiddlyElement(w.output,'table',null,'gallery');
table.cellspacing = '0';
table.cellpadding = '0';
var rowElem = createTiddlyElement2(table,'tr');
var col = 0;
this.lookaheadRegExp.lastIndex = w.matchStart;
var nM = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
var oldSource = w.source;
while(lookaheadMatch) {
nM += lookaheadMatch[1].length;
w.source = lookaheadMatch[1] +']]';//!! ]] is hack until getParams is working
w.nextMatch = 0;
var params = MediaWikiFormatter.getParams(w);
var src = params[1];
src = src.trim().replace(/ /mg,'_');
src = src.substr(0,1).toUpperCase() + src.substring(1);
var palign = 'right';
var psrc = '120px-'+src;
var px = 120;
var pframed = false;
ptitle = null;
for(var i=2;i<params.length;i++) {
var p = params[i];
if(p=='right'||p=='left'||p=='center'||p=='none') {
palign = p;
} else if(p=='framed') {
pframed = true;
} else if(/\d{1,4}px/.exec(p)) {
px = p.substr(0,p.length-2).trim();
psrc = px + 'px-' + src;
} else {
ptitle = p;
}
}//#end for
var td = createTiddlyElement2(rowElem,'td');
var gb = createTiddlyElement(td,'div',null,'gallerybox');
var t = createTiddlyElement(gb,'div',null,'thumb');
t.style['padding'] = '26px 0';
var a = createTiddlyElement2(t,'a');
if(config.options.chkMediaWikiDisplayEnableThumbZoom) {
a.href = src;
}
a.title = ptitle;
var img = createTiddlyElement2(a,'img');
img.src = psrc;
img.width = px;
img.alt = '';
var gt = createTiddlyElement(gb,'div',null,'gallerytext');
p = createTiddlyElement2(gt,'p');
var oldSource2 = w.source; var oldMatch = w.nextMatch;
w.source = ptitle; w.nextMatch = 0;
w.subWikifyUnterm(p);
w.source = oldSource2; w.nextMatch = oldMatch;
col++;
if(col>3) {
rowElem = createTiddlyElement2(table,'tr');
col = 0;
}
w.source = oldSource;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
w.nextMatch = nM + '<gallery>'.length*2+1+'Image:'.length;//!! hack
}
},
{
name: 'mediaWikiHtmlTag',
match: "<[a-zA-Z]{2,}(?:\\s*(?:(?:.*?)=[\"']?(?:.*?)[\"']?))*?>",
lookaheadRegExp: /<([a-zA-Z]{2,})((?:\s+(?:.*?)=["']?(?:.*?)["']?)*?)?\s*(\/)?>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e =createTiddlyElement2(w.output,lookaheadMatch[1]);
if(lookaheadMatch[2]) {
MediaWikiFormatter.setAttributesFromParams(e,lookaheadMatch[2]);
}
if(lookaheadMatch[3]) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
} else {
w.subWikify(e,'</'+lookaheadMatch[1]+'>');
}
}
}
}
];
config.parsers.mediawikiFormatter = new Formatter(config.mediawiki.formatters);
config.parsers.mediawikiFormatter.format = 'mediawiki';
config.parsers.mediawikiFormatter.formatTag = 'MediaWikiFormat';
} //# end of 'install only once'
//}}}
/***
|Name|MediaWikiFormatterPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[MediaWikiFormatterPlugin]]|
!!!!!Code
***/
//{{{
config.macros.list.all.handler = MediaWikiFormatter.oldListAll;
//}}}
//{{{
MediaWikiFormatter.expandVariableOriginal = MediaWikiFormatter.expandVariable;
MediaWikiFormatter.expandVariable = function (w, variable) {
var subWikifyUnterm = function (source, output, wikifier) {
if (source != null) {
var wikifierSource = wikifier.source;
var wikifierNextMatch = wikifier.nextMatch;
wikifier.source = source;
wikifier.nextMatch = 0;
wikifier.subWikifyUnterm(output);
wikifier.source = wikifierSource;
wikifier.nextMatch = wikifierNextMatch;
}
};
var createReference = function (referenceGroupId, refPrefix, ref, value, numeric, w) {
var referenceGroup = w;
if (referenceGroupId) {
referenceGroupId = referenceGroupId.trim();
w.referenceGroups = w.referenceGroups || {};
w.referenceGroups[referenceGroupId] = w.referenceGroups[referenceGroupId] || {};
referenceGroup = w.referenceGroups[referenceGroupId];
referenceGroup.numeric = numeric;
}
referenceGroup.referenceCount = referenceGroup.referenceCount || 0;
referenceGroup.references = referenceGroup.references || {};
referenceGroupId = referenceGroupId || 'n';
var s = createTiddlyElement(w.output, 'sup', null, 'reference');
var a = createTiddlyElement2(s, "a");
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
var namedRef = false;
var name = null;
if (typeof ref === "string") {
namedRef = true;
name = referenceGroupId + '_' + ref.trim().replace(/ /g, '_');
ref = referenceGroup.references[name];
} else {
name = referenceGroupId + '_' + referenceGroup.referenceCount;
}
if (!ref) {
ref = {id: '', value: '', refs: []};
referenceGroup.references[name] = ref;
ref.id = referenceGroup.referenceCount++;
ref.value = value;
s.id = prefix + '_ref-' + name;
ref.refs.push(s.id);
} else {
if (!namedRef && ref.refs.length) {
s.id = ref.refs[ref.refs.length - 1];
} else {
s.id = prefix + '_ref-' + name;
ref.refs.push(s.id);
}
if (ref.value === '') {
ref.value = value;
}
}
a.title = "sup";
a.href = '#' + prefix + '_note-' + referenceGroupId + '_' + ref.id;
a.innerHTML = '[' + (refPrefix ? refPrefix + ' ' : '') + (numeric ? (ref.id + 1) : String.fromCharCode('a'.charCodeAt(0) + ref.id)) + ']';
return ref;
};
MediaWikiFormatter.createReference = createReference;
var processHarvnb = function (params) {
var citeRef = "";
var citeRefLabel = "";
for (var i = 1; params[i]; i++) {
citeRef += params[i];
if (params[i + 1]) {
citeRefLabel += (citeRefLabel.length ? '; ' : '') + params[i];
} else {
citeRefLabel += (citeRefLabel.length ? ' ' : '') + params[i];
}
}
var citeRefInfo = "";
if (params.p) {
citeRefInfo += (citeRefInfo.length ? ', ' : '') + 'p. ' + params.p;
}
if (params.pp) {
citeRefInfo += (citeRefInfo.length ? ', ' : '') + 'pp. ' + params.pp;
}
if (params.loc) {
citeRefInfo += (citeRefInfo.length ? ', ' : '') + params.loc;
}
citeRef = citeRef.replace(/ /g, '_');
citeRef = (citeRef.length ? '[[#CITEREF' + citeRef + '| ' + citeRefLabel + ']]' : '') + (citeRef.length ? ', ' : '') + citeRefInfo;
citeRef += citeRef.length ? '.' : '';
return citeRef;
};
var normalizedVariable = variable.toLowerCase();
if ((/(^'$)|(^[0-9]+[a-z]*$)/).test(normalizedVariable)) {
createTiddlyText(w.output, variable);
return true;
} else if (normalizedVariable === "!") {
createTiddlyText(w.output, "|");
return true;
} else if (normalizedVariable.indexOf("abbr") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm(params[1], w.output, w);
return true;
} else if ((/^as\s*of/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var monthName = jQuery.isNumeric(params[2]) ? monthNames[parseInt(params[2]) - 1] : null;
createTiddlyText(w.output, variable.substring(0, variable.indexOf('|')) + (monthName ? ' ' + monthName : '') + ' ' + params[1]);
return true;
} else if ((normalizedVariable.indexOf("blockquote") === 0) || (normalizedVariable.indexOf("commentaire") === 0)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var text = params["1"] ? params["1"] : params["text"];
if (text) {
text = text.replace(/<\/?poem>/g, "");
var blockquote = jQuery("<blockquote style='white-space: pre-wrap; border-left:none'></blockquote>").appendTo(w.output);
subWikifyUnterm(text, blockquote[0], w);
if (params["author"]) {
blockquote.append("\n— ");
subWikifyUnterm(params["author"], jQuery("<span></span>").appendTo(blockquote)[0], w);
}
}
return true;
} else if (normalizedVariable.indexOf("cast") === 0) {
subWikifyUnterm(variable.substring(variable.indexOf('|') + 1), w.output, w);
return true;
} else if (normalizedVariable.indexOf("categories") === 0) {
if (w.categories && w.categories.length) {
createTiddlyText(w.output, (w.categories.length > 1) ? "Categories: " : "Category: ");
for (var i = 0; i < w.categories.length; i++) {
if (i > 0) {
createTiddlyText(w.output, " | ");
}
var e = createTiddlyLink(w.output, w.categories[i][0] + ":" + w.categories[i][1], false, null, w.isStatic, w.tiddler);
createTiddlyText(e, w.categories[i][1]);
}
}
return true;
} else if ((normalizedVariable.indexOf("cite") === 0) || ((normalizedVariable.indexOf("citation") === 0) && !(/^citation\s*needed/).test(normalizedVariable)) || (normalizedVariable.indexOf("lien") === 0) || (normalizedVariable.indexOf("ouvrage") === 0)) {
var type = /^(?:cite|citation|lien|ouvrage)\s*([a-z]*)/.exec(normalizedVariable)[1] || "book";
var params = MediaWikiFormatter.getTemplateParams(variable);
var cite = {};
for (var k in params) {
var match = /^first(\d*)|^last(\d*)|^(editor\d*-link)|^editor(\d*)-first|^editor(\d*)-last|^editor(\d*)|^éditeur(\d*)|^(author\d*-link)|^author(\d*)|^auteur(\d*)/.exec(k);
if (match) {
var name;
if (match[1] != null) {
name = "a" + match[1];
cite.authors = cite.authors || {};
cite.authors[name] = cite.authors[name] || {};
cite.authors[name].first = params[k].trim();
} else if (match[2] != null) {
name = "a" + match[2];
cite.authors = cite.authors || {};
cite.authors[name] = cite.authors[name] || {};
cite.authors[name].last = params[k].trim();
} else if (match[3] != null) {
} else if (match[4] != null) {
name = "e" + match[4];
cite.editors = cite.editors || {};
cite.editors[name] = cite.editors[name] || {};
cite.editors[name].first = params[k].trim();
} else if (match[5] != null) {
name = "e" + match[5];
cite.editors = cite.editors || {};
cite.editors[name] = cite.editors[name] || {};
cite.editors[name].last = params[k].trim();
} else if (match[6] != null) {
name = "a" + match[6];
cite.editors = cite.editors || {};
cite.editors[name] = cite.editors[name] || {};
cite.editors[name].first = params[k].trim();
} else if (match[7] != null) {
name = "a" + match[7];
cite.editors = cite.editors || {};
cite.editors[name] = cite.editors[name] || {};
cite.editors[name].first = params[k].trim();
} else if (match[8] != null) {
} else if (match[9] != null) {
name = "a" + match[9];
cite.authors = cite.authors || {};
cite.authors[name] = cite.authors[name] || {};
cite.authors[name].first = params[k].trim();
} else if (match[10] != null) {
name = "a" + match[10];
cite.authors = cite.authors || {};
cite.authors[name] = cite.authors[name] || {};
cite.authors[name].first = params[k].trim();
}
} else {
cite[k] = params[k].trim();
}
}
var formatNames = function (names) {
var namesText = "";
var count = 0;
if (names) {
for (var k in names) {
namesText += (namesText.length ? "; " : "") + (names[k].last || "") + ((names[k].last && names[k].first) ? ", " : "") + (names[k].first || "");
count++;
}
}
return [namesText, count];
};
var citeText = formatNames(cite.authors)[0];
citeText = (/^\s*<!--.*?-->\s*$/).test(citeText) ? "" : citeText;
if (cite.author && !(/^\s*<!--.*?-->\s*$/).test(cite.author)) {
citeText += (citeText.length ? "; " : "") + cite.author;
}
var yearDisplayed = false;
if (citeText.length) {
citeText += (cite.year || cite.date || cite["année"]) ? ((citeText.length ? " " : "") + "(" + (cite.year || cite.date || cite["année"]) + ")") : "";
citeText += cite["orig-year"] ? ((citeText.length ? " " : "") + "[" + cite["orig-year"] + "]") : "";
yearDisplayed = true;
}
citeText += citeText.length ? "." : "";
var editors = formatNames(cite.editors);
if (editors[0].length) {
citeText += (citeText.length ? " " : "") + editors[0] + " (ed" + (editors[1] > 1 ? "s" : "") + ".).";
}
if (cite.chapter || cite.encyclopedia || cite.journal || cite.magazine || cite.title || cite.titre || cite.work) {
type = (normalizedVariable.indexOf("citation") === 0) && cite.work ? "news" : type;
var chapter = cite.chapter;
var chapterUrl = cite["chapter-url"];
var title = cite.title || cite.titre;
title = title ? title.replace(/\[\[/g, '\0').replace(/\[/g, '[').replace(/\0/g, '[[').replace(/\]\]/g, '\0').replace(/\]/g, ']').replace(/\0/g, ']]') : title;
var url = cite.url;
switch (type) {
case "encyclopedia":
chapter = title;
chapterUrl = cite.url;
title = cite.encyclopedia;
url = null;
break;
case "journal":
chapter = title;
chapterUrl = cite.url;
title = cite.journal;
url = null;
break;
case "magazine":
chapter = title;
chapterUrl = cite.url;
title = cite.magazine;
url = null;
break;
case "news":
case "press":
case "web":
chapter = title || ((cite["script-title"] && cite["script-title"].indexOf(":") > -1) ? cite["script-title"].substring(cite["script-title"].indexOf(":") + 1) : cite["script-title"]);
chapterUrl = cite.url;
title = cite.work || cite.newspaper || cite.site || cite.website || cite.magazine || cite.agency;
url = null;
break;
}
var languages = {fr: "French", hu: "Hungarian", it: "Italian", ja: "Japanese", ru: "Russian", sv: "Swedish"};
citeText += (citeText.length ? " " : "")
+ (chapter ? ((chapterUrl ? ("[" + chapterUrl + " ") : "") + '"' + chapter + '"' + (chapterUrl ? "]" : "") + ((type === "press") ? " (Press release)" : "") + (cite["trans-title"] ? " [" + cite["trans-title"] + "]" : "")) : "")
+ (title ? (chapter ? ". " : "") + ((url ? ("[" + url + " ") : "") + "''" + title + "''" + (url ? "]" : "")) : "")
+ (((type === "news") && cite.work && cite.agency) ? (". " + cite.agency) : "")
+ ((cite.language && languages[cite.language]) ? (" (in " + languages[cite.language] + ")") : "")
+ (cite.type ? (" (" + cite.type + ")") : "")
+ (cite.edition ? (" (" + cite.edition + " ed.)") : "")
+ ((cite.location && !cite.publisher) ? ". " + cite.location : "")
+ ".";
}
if (cite.volume) {
citeText += (citeText.length ? " " : "") + "Vol. " + cite.volume + (cite.issue ? (", no. " + cite.issue) : "") + ".";
} else if (cite.issue) {
citeText += (citeText.length ? " " : "") + "No. " + cite.issue + ".";
}
if (cite["pages totales"]) {
citeText += (citeText.length ? " " : "") + cite["pages totales"] + " pages.";
}
if (cite.others) {
citeText += (citeText.length ? " " : "") + cite.others + ".";
}
if (cite.publisher) {
citeText += (citeText.length ? " " : "") + (cite.location ? (cite.location + ": ") : "") + cite.publisher + ".";
}
if (!yearDisplayed) {
citeText += (cite.year || cite.date || cite["année"]) ? ((citeText.length ? " " : "") + (cite.year || cite.date || cite["année"]) + ".") : "";
yearDisplayed = true;
}
if (cite.page || cite.passage) {
citeText += (citeText.length ? " " : "") + "p. " + (cite.page || cite.passage) + ".";
}
if (cite.pages) {
citeText += (citeText.length ? " " : "") + "pp. " + cite.pages + ".";
}
if (cite.isbn) {
citeText += (citeText.length ? " " : "") + "ISBN [https://isbnsearch.org/isbn/" + cite.isbn.replace(/[\s-]/g, '') + " " + cite.isbn + "].";
}
if (cite.doi) {
citeText += (citeText.length ? " " : "") + "[https://doi.org/" + cite.doi + " doi:" + cite.doi + "].";
}
if (cite.issn) {
citeText += (citeText.length ? " " : "") + "ISSN [https://www.worldcat.org/issn/" + cite.issn + " " + cite.issn + "].";
}
if (cite.oclc) {
citeText += (citeText.length ? " " : "") + "OCLC [https://www.worldcat.org/oclc/" + cite.oclc + " " + cite.oclc + "].";
}
if (cite.jstor) {
citeText += (citeText.length ? " " : "") + "JSTOR [https://www.jstor.org/stable/" + cite.jstor + " " + cite.jstor + "].";
}
if (cite["archive-url"] || cite["archive-date"]) {
citeText += (citeText.length ? " " : "") + ((cite["archive-url"] && (cite["url-status"] !== "dead")) ? "[" + cite["archive-url"] + " " : "") + "Archived" + ((cite["archive-url"] && (cite["url-status"] !== "dead")) ? "]" : "") + " from the " + ((cite["archive-url"] && (cite["url-status"] === "dead")) ? "[" + cite["archive-url"] + " " : "") + "original" + ((cite["archive-url"] && (cite["url-status"] === "dead")) ? "]" : "") + (cite["archive-date"] ? " on " + cite["archive-date"] : "") + ".";
}
if (cite["access-date"] || cite["accessdate"]) {
citeText += (citeText.length ? " " : "") + "Retrieved " + (cite["access-date"] || cite["accessdate"]) + (cite.via ? " - via " + cite.via : "") + ".";
}
if (cite["consulté le"]) {
citeText += (citeText.length ? " " : "") + "Consulté le " + cite["consulté le"] + ".";
}
if (cite.quote) {
citeText += (citeText.length ? " " : "") + (cite.quote.indexOf('"') !== 0 ? '"' : '') + cite.quote + (cite.quote.lastIndexOf('"') !== cite.quote.length - 1 ? '"' : '');
}
var citeRef = "";
if (cite.authors) {
for (var k in cite.authors) {
citeRef += cite.authors[k].last;
}
}
citeRef += (cite.year || cite.date) ? (cite.year || cite.date) : "";
citeRef = citeRef.replace(/ /g, '_');
citeRef = citeRef ? (" id='CITEREF" + citeRef + "'") : "";
subWikifyUnterm(citeText, jQuery("<span" + citeRef + "></span>").appendTo(w.output)[0], w);
return true;
} else if ((normalizedVariable.indexOf("cn") === 0) || (/^citation\s*needed/).test(normalizedVariable)) {
var s = createTiddlyElement(w.output, 'sup');
createTiddlyText(s, '[');
var i = createTiddlyElement(s, 'i');
createTiddlyText(i, 'citation needed');
createTiddlyText(s, ']');
return true;
} else if (normalizedVariable.indexOf("colonnes") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var d = jQuery('<div style="margin-top: 1em; column-gap: 1em"></div>').appendTo(w.output);
d.css("column-count", params["nombre"] ? params["nombre"] : "1");
var content = "";
for (var i = 1; params[i] != null; i++) {
content += ((i > 1) ? "|" : "") + params[i];
}
subWikifyUnterm(content, d[0], w);
d.find("ul").css("margin-top", "0");
d.find("li").css("break-inside", "avoid-column");
return true;
} else if (normalizedVariable.indexOf("dagger") === 0) {
subWikifyUnterm('†', w.output, w);
return true;
} else if (normalizedVariable.indexOf("dash") === 0) {
subWikifyUnterm('–', w.output, w);
return true;
} else if (normalizedVariable.indexOf("date") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
createTiddlyText(w.output, params[1]);
return true;
} else if (normalizedVariable.indexOf("fileref") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
w.filerefs = w.filerefs || {};
w.filerefs[params["name"]] = params["url"];
return true;
} else if (normalizedVariable.indexOf("film") === 0) {
// to be implemented
subWikifyUnterm(variable, jQuery("<span></span>")[0], w);
return true;
} else if (normalizedVariable.indexOf("harvnb") === 0) {
subWikifyUnterm(processHarvnb(MediaWikiFormatter.getTemplateParams(variable)), w.output, w);
return true;
} else if (normalizedVariable.indexOf("heure") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
createTiddlyText(w.output, params[1] + " h" + (params[2] ? " " + params[2] : ""));
return true;
} else if (normalizedVariable.indexOf("iblist") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
if (params.id && params.type && params.name) {
subWikifyUnterm("[https://web.archive.org/web/http://www.iblist.com/" + params.type.trim() + params.id.trim() + ".htm" + " ''" + params.name.trim() + "''] at the Internet Book List", jQuery("<span></span>").appendTo(w.output)[0], w);
}
return true;
} else if (normalizedVariable.indexOf("imdb") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[https://www.imdb.com/" + ((/^imdb\s*name/).test(normalizedVariable) ? "name/nm" : "title/tt") + params[1] + " " + tiddler.title + "] at [https://en.wikipedia.org/wiki/IMDb IMDb]", w.output, w);
return true;
} else if (normalizedVariable.indexOf("infobox") === 0) {
// to be implemented
var params = MediaWikiFormatter.getTemplateParams(variable);
var index = 1;
var anon = "";
while (params[index]) {
anon += params[index];
delete params[index];
index++;
}
if (anon.length) {
subWikifyUnterm(anon, jQuery("<span></span>")[0], w);
}
for (var p in params) {
subWikifyUnterm(params[p], jQuery("<span></span>")[0], w);
}
return true;
} else if (normalizedVariable.indexOf("isbn") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("ISBN [https://isbnsearch.org/isbn/" + params[1].replace(/[\s-]/g, '') + " " + params[1] + "]", w.output, w);
return true;
} else if (normalizedVariable.indexOf("isfdb") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
if (params.id && params.title) {
subWikifyUnterm("[https://www.isfdb.org/cgi-bin/title.cgi?" + params.id.trim() + " ''" + params.title.trim() + "''] title listing at the [https://en.wikipedia.org/wiki/Internet_Speculative_Fiction_Database Internet Speculative Fiction Database]", jQuery("<span></span>").appendTo(w.output)[0], w);
}
return true;
} else if (normalizedVariable.indexOf("linktext") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[https://en.wiktionary.org/wiki/" + params[1] + " " + params[1] + "]", w.output, w);
return true;
} else if (normalizedVariable.indexOf("langue") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
if (params[2]) {
subWikifyUnterm("''" + params[2] + "''", w.output, w);
}
return true;
} else if ((normalizedVariable.indexOf("main") === 0) || (/^see\s*also/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var articles = [];
var i = 1;
while (params[i] != null) {
if (params[i]) {
articles.push(params[i]);
}
i++;
}
if (articles.length) {
var filter = function (article) {
return (article.indexOf("{{!}}") > -1) ? article.substring(0, article.indexOf("{{!}}")) : article;
};
var articlesText = "[[" + filter(articles[0]) + "]]";
for (var i = 1; i < articles.length; i++) {
articlesText += ((articles.length > 2) ? "," : "") + ((i === articles.length - 1) ? " and" : "") + " [[" + filter(articles[i]) + "]]";
}
subWikifyUnterm(((normalizedVariable.indexOf("main") === 0) ? ("Main article" + ((articles.length > 1) ? "s" : "") + ": ") : "See also: ") + articlesText, jQuery('<div style="padding-left: 1.6em; margin-bottom: 0.5em; font-style: italic"></div>').appendTo(w.output)[0], w);
}
return true;
} else if (normalizedVariable.indexOf("mobygames") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[https://www.mobygames.com/game" + (params[1] || "").trim() + " " + (params[2] || "").trim() + "] at MobyGames", w.output, w);
return true;
} else if ((/^(?:n°)|(?:p\.)\s*\|/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
createTiddlyText(w.output, variable.substring(0, variable.indexOf("|")).trim() + " " + params[1]);
return true;
} else if (normalizedVariable.indexOf("nihongo") === 0) {
var value = '';
var params = MediaWikiFormatter.getTemplateParams(variable);
if ((/^nihongo\s*foot/).test(normalizedVariable)) {
if (params[1]) {
subWikifyUnterm(params[1], w.output, w);
if (params[2] && params[3]) {
value = 'Japanese: ' + params[2] + ', Hepburn: ' + params[3];
} else if (params[2]) {
value = 'Japanese: ' + params[2];
} else {
value = params[1];
}
createReference("fn", null, null, value, false, w);
}
} else {
if (params[2] && params[4]) {
value = ' (' + params[2] + ', ' + params[4] + ')';
} else if (params[2]) {
value = ' (' + params[2] + ')';
}
subWikifyUnterm(params[1] + (value ? ' ' + value : ''), w.output, w);
}
return true;
} else if ((normalizedVariable === "nominated") || (normalizedVariable === "nom")) {
createTiddlyText(w.output, "Nominated");
return true;
} else if (normalizedVariable.indexOf("notelist") === 0) {
if (w.referenceGroups && w.referenceGroups.fn) {
var referenceCount = w.referenceCount;
var references = w.references;
w.referenceCount = w.referenceGroups.fn.referenceCount;
w.references = w.referenceGroups.fn.references;
MediaWikiFormatter.mediaWikiListReferencesHandler.call({lookaheadRegExp: MediaWikiFormatter.mediaWikiTemplateLookaheadRegExp, listType: 'lower-alpha'}, w);
w.referenceCount = referenceCount;
w.references = references;
}
return true;
} else if (normalizedVariable.indexOf("nowrap") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm(params[1], jQuery('<span style="white-space: nowrap"></span>').appendTo(w.output)[0], w);
return true;
} else if ((/^official\s*website/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var value = params[1];
if ((value == null) && w.wikidata && w.wikidata["official website"] && w.wikidata["official website"][""]) {
value = w.wikidata["official website"][""];
}
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[" + value + " Official website]", w.output, w);
return true;
} else if ((/^pending\s*films\s*key/).test(normalizedVariable)) {
subWikifyUnterm(
'{| class="wikitable"\n' +
'|+Key\n' +
'|{{dagger}}\n' +
'|Denotes films that have not yet been released\n' +
'|}\n', w.output, w);
return true;
} else if ((normalizedVariable.indexOf("reflist") === 0) || (normalizedVariable.indexOf("références") === 0)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var referenceCount = w.referenceCount;
var references = w.references;
var group = params.group;
if (group) {
group = group.trim().replace(/"/g, '');
if (w.referenceGroups && w.referenceGroups[group]) {
w.referenceCount = w.referenceGroups[group].referenceCount;
w.references = w.referenceGroups[group].references;
} else {
w.referenceCount = 0;
w.references = {};
}
}
MediaWikiFormatter.mediaWikiListReferencesHandler.call({lookaheadRegExp: MediaWikiFormatter.mediaWikiTemplateLookaheadRegExp, listType: 'decimal'}, w);
w.referenceCount = referenceCount;
w.references = references;
return true;
} else if (normalizedVariable.indexOf("rt") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
if (w.wikidata && w.wikidata["RT"] && w.wikidata["RT"][params[1]]) {
createTiddlyText(w.output, w.wikidata["RT"][params[1]]);
}
return true;
} else if ((/rotten\s*tomatoes/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[https://www.rottentomatoes.com/celebrity/" + params[1] + " " + tiddler.title + "] at [https://en.wikipedia.org/wiki/Rotten_Tomatoes Rotten Tomatoes]", w.output, w);
return true;
} else if (normalizedVariable.indexOf("sfn") === 0) {
w.sfn = w.sfn || {};
var ref = w.sfn[variable];
if (ref) {
var prefix = w.tiddler ? w.tiddler.title + ':' : '';
ref.refs.push(prefix + '_ref-' + 'n_' + ref.id + "_" + ref.refs.length);
createReference(null, null, w.sfn[variable], null, true, w);
} else {
w.sfn[variable] = createReference(null, null, null, processHarvnb(MediaWikiFormatter.getTemplateParams(variable)), true, w);
}
return true;
} else if (normalizedVariable.indexOf("snd") === 0) {
createTiddlyText(w.output, " - ");
return true;
} else if (normalizedVariable.indexOf("sortname") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm(params[1] + ' ' + params[2], w.output, w);
return true;
} else if (normalizedVariable.indexOf("sort") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm(params[2] || '', w.output, w);
return true;
} else if (normalizedVariable.indexOf("tcmdb") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
var value = params[1];
if ((value == null) && w.wikidata && w.wikidata["Tcmdb"] && w.wikidata["Tcmdb"][""]) {
value = w.wikidata["Tcmdb"][""];
}
subWikifyUnterm("[https://www.tcm.com/tcmdb/person/" + value + " " + tiddler.title + "] at the [https://en.wikipedia.org/wiki/Turner_Classic_Movies TCM Movie Database]", w.output, w);
return true;
} else if ((/timeline\s*of\s*release\s*years/).test(normalizedVariable)) {
var params = MediaWikiFormatter.getTemplateParams(variable.replace(/(\|\s*)([0-9]+[a-zA-Z]*\s*=)/mg, "$1t$2"));
var timeline = {};
for (var k in params) {
var match = (/(t[0-9]+)[a-zA-Z]*/).exec(k);
if (match) {
timeline[match[1]] = timeline[match[1]] || [];
timeline[match[1]].push(params[k].trim());
}
}
var colours = (/(#[a-z0-9]+)\s*(#[a-z0-9]+)/).exec((params["range1_color"] || "#000 #fff").toLowerCase().trim());
colours = [colours && colours[1] ? colours[1] : "#000", colours && colours[2] ? colours[2] : "#fff"];
var table = jQuery("<table class='release-timeline' style='border-collapse: separate'><caption style='font-weight: bold'>Release timeline</caption></table>").appendTo(w.output);
var previousYear = null;
for (var y in timeline) {
var addRow = function (year, colour, span) {
var row = jQuery("<tr><td" + ((span && span > 1) ? " rowspan='" + span + "'" : "") + " style='border: none; border-right: 1.4em solid " + colour + "'></td><td style='border: none'></td></tr>").appendTo(table);
row.children("td").eq(0).text(year);
return row;
};
var year = parseInt(y.substring(1));
if ((previousYear != null) && previousYear < year - 1) {
for (var i = previousYear + 1; i < year; i++) {
addRow(i, colours[1]);
}
}
subWikifyUnterm(timeline[y][0], addRow(year, colours[0], timeline[y].length).children("td").eq(1)[0], w);
if (timeline[y].length > 1) {
for (var i = 1; i < timeline[y].length; i++) {
subWikifyUnterm(timeline[y][i], jQuery("<tr><td style='border: none'></td></tr>").appendTo(table).children("td")[0], w);
}
}
previousYear = year;
}
return true;
} else if (normalizedVariable.indexOf("tooltip") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm(params[1], w.output, w);
return true;
} else if (normalizedVariable.indexOf("unité") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
createTiddlyText(w.output, params[1] + (params[2] ? " " + params[2] : ""));
return true;
} else if (normalizedVariable.indexOf("us$") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm('$' + params[1], jQuery('<span style="white-space: nowrap"></span>').appendTo(w.output)[0], w);
return true;
} else if (normalizedVariable.indexOf("wikidata") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
w.wikidata = w.wikidata || {};
w.wikidata[params["category"]] = w.wikidata[params["category"]] || {};
w.wikidata[params["category"]][params["name"]] = params["value"];
return true;
} else if (normalizedVariable.indexOf("webarchive") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[" + (params.url || "").trim() + " Archived] " + (params.date || "").trim() + ", at the [https://en.wikipedia.org/wiki/Wayback_Machine Wayback Machine]", w.output, w);
return true;
} else if (normalizedVariable === "won") {
createTiddlyText(w.output, "Won");
return true;
} else if (normalizedVariable.indexOf("youtube") === 0) {
var params = MediaWikiFormatter.getTemplateParams(variable);
subWikifyUnterm("[https://www.youtube.com/watch?v=" + params[1] + " " + params[2] + "] on YouTube", w.output, w);
return true;
}
return MediaWikiFormatter.expandVariableOriginal(w, variable);
};
//}}}
//{{{
(function () {
for (var i = 0; i < config.formatters.length; i++) {
if (config.formatters[i].name === "macro") {
config.mediawiki.formatters.push(config.formatters[i]);
break;
}
}
for (var i = 0; i < config.mediawiki.formatters.length; i++) {
if (config.mediawiki.formatters[i].name === "mediaWikiInsertReference") {
config.mediawiki.formatters[i].match = '<ref(?:"[^"]*"[\'"]*|\'[^\']*\'[\'"]*|[^\'">])*(?<!/\\s*)>'; // https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags#1736801
config.mediawiki.formatters[i].lookaheadRegExp = /<ref(\s+(?:.*?)=["']?(?:.*?)["']?)?>([\s\S]*?)<\/ref>/mg;
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("if(!w.referenceCount) {", "var r = lookaheadMatch[1] ? MediaWikiFormatter.setFromParams(w,lookaheadMatch[1]) : {}; MediaWikiFormatter.createReference(r.group, r.group ? r.group.trim() : null, r.name, (lookaheadMatch[2] || '').trim(), true, w); return; if(!w.referenceCount) {");
eval("config.mediawiki.formatters[i].handler = " + handler);
} else if (config.mediawiki.formatters[i].name === "mediaWikiRepeatReference") {
config.mediawiki.formatters[i].lookaheadRegExp = /<ref(\s+(?:.*?)=["']?(?:.*?)["']?)?\s*\/>/mg;
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("name = name", "name = 'n_' + name");
handler = handler.replace("var prefix = w.tiddler ? w.tiddler.title : '';", "var prefix = w.tiddler ? w.tiddler.title + ':' : '';");
handler = handler.replace("s.id =", "w.referenceCount = w.referenceCount || 0;\nw.references = w.references || {};\nw.references[name] = w.references[name] || {id: w.referenceCount++, value: '', refs: []};\ns.id =");
handler = handler.replace("var count ", "w.references[name].refs.push(s.id);\nvar count ");
eval("config.mediawiki.formatters[i].handler = " + handler);
} else if (config.mediawiki.formatters[i].name === "mediaWikiListReferences") {
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("var oldSource", "if (this.listType) ol.style['list-style-type'] = this.listType; var oldSource");
handler = handler.replace("createTiddlyElement2(b,'a');", "createTiddlyElement2(b, w.references[i].refs.length > 1 ? 'span' : 'a');");
handler = handler.replace("'^';", "'^';\n" +
"createTiddlyText(li, ' ');\n" +
"if (w.references[i].refs.length > 1) {\n" +
"for (var j = 0; j < w.references[i].refs.length; j++) {\n" +
"b = createTiddlyElement2(li, 'b');\n" +
"b = createTiddlyElement2(b, 'sup');\n" +
"b = createTiddlyElement2(b, 'i');\n" +
"a = createTiddlyElement2(b, 'a');\n" +
"createTiddlyText(b, ' ');\n" +
"a.href = '#' + w.references[i].refs[j];\n" +
"a.innerHTML = String.fromCharCode('a'.charCodeAt(0) + j);\n" +
"}\n" +
"}");
eval("config.mediawiki.formatters[i].handler = " + handler);
MediaWikiFormatter.mediaWikiListReferencesHandler = config.mediawiki.formatters[i].handler;
} else if (config.mediawiki.formatters[i].name === "mediaWikiExplicitLink") {
config.mediawiki.formatters[i].lookaheadRegExp = {
lastIndex: -1,
exec: function (source) {
var index = null;
var count = 0;
var i = this.lastIndex;
for (; i < source.length; i++) {
if (source.charAt(i) === "[" && source.charAt(i + 1) === "[" && source.charAt(i + 2) !== "[") {
index = (index == null) ? i : index;
count++;
i++;
} else if (source.charAt(i) === "]" && source.charAt(i + 1) === "]") {
count--;
if (count === 0) {
break;
}
}
}
var match = [];
if ((index != null) && (count === 0)) {
match.index = index;
this.lastIndex = i + "]]".length;
match[0] = source.substring(index + "[[".length, i);
var segments = /(?:([a-z]{2,3}:)?)(#?)([^\|]*)(?:(\|(.*))|(.*))/y.exec(match[0]);
match[0] = "[[" + match[0] + "]]";
match[1] = segments[1];
match[2] = segments[2];
match[3] = segments[3];
if (segments[6] != null) {
var pattern = /(\w*)/y;
pattern.lastIndex = this.lastIndex;
var match5 = pattern.exec(source);
match[4] = "]]" + match5[1];
match[5] = match5[1];
match[0] += match5[1];
this.lastIndex += match[5].length;
} else {
match[6] = (segments[4] == null) ? segments[4] : segments[4] + "]]";
match[7] = segments[5];
}
} else {
match.index = -1;
this.lastIndex = source.length;
}
return match;
}
};
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("link = link.substring(1);", "link = link.substring(1);\nif (lookaheadMatch[2]) {\ne = createTiddlyElement2(w.output, 'a');\ne.href = '#' + link;\n} else\n");
handler = handler.replace("var oldMatch = w.nextMatch;", "var oldMatch = w.nextMatch; var oldLastIndex = this.lookaheadRegExp.lastIndex;");
handler = handler.replace("w.nextMatch = oldMatch;", "w.nextMatch = oldMatch; this.lookaheadRegExp.lastIndex = oldLastIndex;");
eval("config.mediawiki.formatters[i].handler = " + handler);
} else if (config.mediawiki.formatters[i].name === "mediaWikiImage") {
config.mediawiki.formatters[i].match = '\\[\\[(?:[Ii]mage|Bild|Fichier|File):';
config.mediawiki.formatters[i].lookaheadRegExp = /\[\[(?:[Ii]mage|Bild|Fichier|File):/mg;
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace(".replace(/ /mg,'_')", ".replace(/[ \\?]/mg,'_')");
handler = handler.replace("px = p.substr(0,p.length-2).trim();", "p.replace(/(\\d{1,4})/, function (match, p1) {px = p1});");
handler = handler.replace("'images/' + psrc", "(w.filerefs && w.filerefs[psrc]) ? w.filerefs[psrc] : 'images/' + psrc");
handler = handler.replace("px ? 'images/' + px + 'px-' + src : 'images/' + src", "(w.filerefs && w.filerefs[src]) ? w.filerefs[src] : (px ? 'images/' + px + 'px-' + src : 'images/' + src)");
eval("config.mediawiki.formatters[i].handler = " + handler);
config.mediawiki.formatters.splice(i + 1, 0, {
name: 'mediaWikiCategory',
match: '\\[\\[(?:Category|Catégorie):',
lookaheadRegExp: /\[\[(Category|Catégorie):/mg,
handler: function (w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if (lookaheadMatch && (lookaheadMatch.index === w.matchStart)) {
var params = MediaWikiFormatter.getParams(w);
w.categories = w.categories || [];
w.categories.push([lookaheadMatch[1], params[1]]);
}
}
});
} else if (config.mediawiki.formatters[i].name === "mediaWikiLeadingSpaces") {
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("createTiddlyElement2(w.output,'pre')", "jQuery(\"<span style='white-space: pre-wrap'></span>\").appendTo(w.output)[0]");
eval("config.mediawiki.formatters[i].handler = " + handler);
} else if (config.mediawiki.formatters[i].name === "mediaWikiList") {
eval("config.mediawiki.formatters[i].handler = " + config.mediawiki.formatters[i].handler.toString().replace("lookaheadMatch[0].length", "lookaheadMatch[0].trim().length"));
} else if (config.mediawiki.formatters[i].name === "mediaWikiTable") {
eval("config.mediawiki.formatters[i].handler = " + config.mediawiki.formatters[i].handler.toString().replace("if", "w.tableDepth = w.tableDepth == null ? 0 : w.tableDepth; if"));
eval("config.mediawiki.formatters[i].rowHandler = " + config.mediawiki.formatters[i].rowHandler.toString().replace("cellText.substr(0,i-1)", "cellText.substr(0,i)"));
} else if (config.mediawiki.formatters[i].name === "mediaWikiTemplate") {
config.mediawiki.formatters[i].lookaheadRegExp = {
lastIndex: -1,
exec: function (source) {
var index = null;
var count = 0;
var i = this.lastIndex;
for (; i < source.length; i++) {
if (source.charAt(i) === "{" && source.charAt(i + 1) === "{" && source.charAt(i + 2) !== "{") {
index = (index == null) ? i : index;
count++;
i++;
} else if (source.charAt(i) === "}" && source.charAt(i + 1) === "}") {
count--;
if (count === 0) {
break;
}
}
}
var match = [];
if ((index != null) && (count === 0)) {
match.index = index;
match[1] = source.substring(index + "{{".length, i);
this.lastIndex = i + "}}".length;
} else {
match.index = -1;
this.lastIndex = source.length;
}
return match;
}
};
MediaWikiFormatter.mediaWikiTemplateLookaheadRegExp = config.mediawiki.formatters[i].lookaheadRegExp;
} else if (config.mediawiki.formatters[i].name === "mediaWikiTitledUrlLink") {
config.mediawiki.formatters[i].match = '\\[' + config.textPrimitives.urlPattern + '[\\)\\+]*(?:\\s+[^\\]]+)?' + '\\]';
var handler = config.mediawiki.formatters[i].handler.toString();
handler = handler.replace("')", "'[\\\\)\\\\+]*)");
eval("config.mediawiki.formatters[i].handler = " + handler);
}
}
var mediawikiFormatter = config.parsers.mediawikiFormatter;
config.parsers.mediawikiFormatter = new Formatter(config.mediawiki.formatters);
config.parsers.mediawikiFormatter.format = mediawikiFormatter.format;
config.parsers.mediawikiFormatter.formatTag = mediawikiFormatter.formatTag;
})();
//}}}
//{{{
(function () {
var code = eval("MediaWikiFormatter.getTemplateParams").toString();
code = code.replace("/(?:([^\\|]*)=)?([^\\|]*)\\|/mg", "/(?:([^\\|]*?)=)?((?:(?:[^\\|\\[\\{])|(?:\\[[^\\[])|(?:\\[\\[.*?\\]\\])|(?:\\{[^\\{])|(?:\\{\\{.*?\\}\\}))*)\\|/mg");
code = code.replace("params[match[1]]", "params[match[1].trim()]");
eval("MediaWikiFormatter.getTemplateParams = function getTemplateParams" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name|MindMapPlugin|
|License|[[TW Notes License]]|
|Requires|[[jsmind.js]]|
!!!!!Code
***/
//{{{
config.macros.mindmap = {
icons: {
// https://fontawesome.com/icons/redo?f=classic&s=solid
redo: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M386.3 160H336c-17.7 0-32 14.3-32 32s14.3 32 32 32H464c17.7 0 32-14.3 32-32V64c0-17.7-14.3-32-32-32s-32 14.3-32 32v51.2L414.4 97.6c-87.5-87.5-229.3-87.5-316.8 0s-87.5 229.3 0 316.8s229.3 87.5 316.8 0c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0c-62.5 62.5-163.8 62.5-226.3 0s-62.5-163.8 0-226.3s163.8-62.5 226.3 0L386.3 160z"/></svg>',
// https://fontawesome.com/icons/undo?f=classic&s=solid
undo: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M125.7 160H176c17.7 0 32 14.3 32 32s-14.3 32-32 32H48c-17.7 0-32-14.3-32-32V64c0-17.7 14.3-32 32-32s32 14.3 32 32v51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"/></svg>',
// Open Iconic Icon Set: MIT License
// https://iconduck.com/icons/53940/zoom-out
zoomout: '<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m3.5 0c-1.93 0-3.5 1.57-3.5 3.5s1.57 3.5 3.5 3.5c.61 0 1.19-.16 1.69-.44a1 1 0 0 0 .09.13l1 1.03a1.02 1.02 0 1 0 1.44-1.44l-1.03-1a1 1 0 0 0 -.13-.09c.27-.5.44-1.08.44-1.69 0-1.93-1.57-3.5-3.5-3.5zm0 1c1.39 0 2.5 1.11 2.5 2.5 0 .59-.2 1.14-.53 1.56-.01.01-.02.02-.03.03a1 1 0 0 0 -.06.03 1 1 0 0 0 -.25.28c-.44.37-1.01.59-1.63.59-1.39 0-2.5-1.11-2.5-2.5s1.11-2.5 2.5-2.5zm-1.5 2v1h3v-1z"/></svg>',
// https://iconduck.com/icons/53939/zoom-in
zoomin: '<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m3.5 0c-1.93 0-3.5 1.57-3.5 3.5s1.57 3.5 3.5 3.5c.61 0 1.19-.16 1.69-.44a1 1 0 0 0 .09.13l1 1.03a1.02 1.02 0 1 0 1.44-1.44l-1.03-1a1 1 0 0 0 -.13-.09c.27-.5.44-1.08.44-1.69 0-1.93-1.57-3.5-3.5-3.5zm0 1c1.39 0 2.5 1.11 2.5 2.5 0 .59-.2 1.14-.53 1.56-.01.01-.02.02-.03.03a1 1 0 0 0 -.06.03 1 1 0 0 0 -.25.28c-.44.37-1.01.59-1.63.59-1.39 0-2.5-1.11-2.5-2.5s1.11-2.5 2.5-2.5zm-.5 1v1h-1v1h1v1h1v-1h1v-1h-1v-1z"/></svg>',
// Neuicons (https://github.com/neuicons/neu): MIT License
// https://www.svgrepo.com/svg/487265/delete-node
noderemove: '<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6,5.414,4.707,6.707A1,1,0,0,1,3.293,5.293L4.586,4,3.293,2.707A1,1,0,0,1,4.707,1.293L6,2.586,7.293,1.293A1,1,0,0,1,8.707,2.707L7.414,4,8.707,5.293A1,1,0,1,1,7.293,6.707ZM21,10v4a1,1,0,0,1-1,1H16a1,1,0,0,1-1-1V13H7v6h8V18a1,1,0,0,1,1-1h4a1,1,0,0,1,1,1v4a1,1,0,0,1-1,1H16a1,1,0,0,1-1-1V21H6a1,1,0,0,1-1-1V12a1,1,0,0,1,1-1h9V10a1,1,0,0,1,1-1h4A1,1,0,0,1,21,10ZM17,21h2V19H17Zm2-10H17v2h2Z"/></svg>',
// https://www.svgrepo.com/svg/487001/add-node
nodeadd: '<svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6,6H4A1,1,0,0,1,4,4H6V2A1,1,0,0,1,8,2V4h2a1,1,0,0,1,0,2H8V8A1,1,0,0,1,6,8Zm15,4v4a1,1,0,0,1-1,1H16a1,1,0,0,1-1-1V13H8v6h7V18a1,1,0,0,1,1-1h4a1,1,0,0,1,1,1v4a1,1,0,0,1-1,1H16a1,1,0,0,1-1-1V21H7a1,1,0,0,1-1-1V12a1,1,0,0,1,1-1h8V10a1,1,0,0,1,1-1h4A1,1,0,0,1,21,10ZM17,21h2V19H17Zm2-10H17v2h2Z"/></svg>',
// Remix Icon Set: Apache License 2.0
// https://iconduck.com/icons/77692/screenshot-2-fill
screenshot: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h24v24h-24z" fill="none"/><path d="m3 3h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm0 4h2v2h-2zm-16 12h2v2h-2zm0-4h2v2h-2zm0-4h2v2h-2zm0-4h2v2h-2zm7.667 4 1.036-1.555a1 1 0 0 1 .832-.445h2.93a1 1 0 0 1 .832.445l1.036 1.555h2.667a1 1 0 0 1 1 1v8a1 1 0 0 1 -1 1h-12a1 1 0 0 1 -1-1v-8a1 1 0 0 1 1-1zm3.333 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></svg>',
// Zwicon Icon Set: Creative Commons Attribution 4.0 International
// https://iconduck.com/icons/116907/duplicate
duplicate: '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m8 7v-1.49899762c0-1.38071188 1.11928813-2.5 2.5-2.5h5.9720225c.1327463-.00841947.2709238.03583949.3815309.14644661l3 3c.1106071.11060712.1548661.24878464.1464466.38153087v7.97202254c0 1.3807119-1.1192881 2.5-2.5 2.5h-1.5v1.4989976c0 1.3807119-1.1192881 2.5-2.5 2.5h-7c-1.38071187 0-2.5-1.1192881-2.5-2.5v-9c0-1.38071187 1.11928813-2.5 2.5-2.5zm0 1h-1.5c-.82842712 0-1.5.67157288-1.5 1.5v9c0 .8284271.67157288 1.5 1.5 1.5h7c.8284271 0 1.5-.6715729 1.5-1.5v-1.4989976h-2.5c-.2761424 0-.5-.2238576-.5-.5s.2238576-.5.5-.5h5c.8284271 0 1.5-.6715729 1.5-1.5v-7.50000002h-2.5c-.2761424 0-.5-.22385763-.5-.5v-2.5h-5.5c-.82842712 0-1.5.67157287-1.5 1.5v4.99902132c0 .2761358-.22385233.4999881-.49998817.4999881h-.00001183c-.27614429-.0000065-.5-.2238675-.5-.5000118zm9-1.99899762h1.2928932l-1.2928932-1.29289322zm-3.7071068 3.99899762h-1.7928932c-.2761424 0-.5-.22385763-.5-.5s.2238576-.5.5-.5h3c.2761424 0 .5.22385763.5.5v3c0 .2761424-.2238576.5-.5.5s-.5-.2238576-.5-.5v-1.7928932l-5.14644661 5.1464466c-.19526215.1952621-.51184463.1952621-.70710678 0-.19526215-.1952622-.19526215-.5118446 0-.7071068z"/></svg>'
},
history: {},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var cmm = config.macros.mindmap;
var dataTiddler = null;
var section = null;
var source = params[0] || (config.textPrimitives.sectionSeparator + "mindmap");
if (source.indexOf(config.textPrimitives.sectionSeparator) === 0) {
dataTiddler = tiddler;
section = source.substring(config.textPrimitives.sectionSeparator.length);
source = tiddler.title + source;
} else if (source.indexOf(config.textPrimitives.sectionSeparator) > 0) {
dataTiddler = source.substring(0, source.indexOf(config.textPrimitives.sectionSeparator));
section = source.substring(source.indexOf(config.textPrimitives.sectionSeparator) + config.textPrimitives.sectionSeparator.length);
} else {
dataTiddler = source;
}
var mindmapPlace = jQuery("<span></span>").appendTo(place);
var icons = cmm.icons;
var controlButtons = jQuery(
'<div style="float: right">' +
'<span class="redo_place" style="display: none;"><button type="button" class="redo" style="padding: 5px;" title="Redo">' + icons.redo.replace('viewBox=', 'width="16" height="16" viewBox=') + '</button>' +
'<span> </span></span>' +
'<span class="undo_place" style="display: none;"><button type="button" class="undo" style="padding: 5px; visibility: hidden;" title="Undo">' + icons.undo.replace('viewBox=', 'width="16" height="16" viewBox=') + '</button>' +
'<span> ' +
' </span></span>' +
'<button type="button" class="zoomout" style="padding: 5px;" title="Zoom out">' + icons.zoomout.replace('height="8"', 'width="16" height="16"') + '</button>' +
'<span> </span>' +
'<button type="button" class="zoomin" style="padding: 5px;" title="Zoom in">' + icons.zoomin.replace('height="8"', 'width="16" height="16"') + '</button>' +
'<span> ' +
'<span> </span>' +
'<button type="button" class="remove" style="padding: 5px;" title="Remove node">' + icons.noderemove.replace('width="800px" ', '').replace('height="800px"', 'width="16" height="16"') + '</button>' +
'<span> </span>' +
'<button type="button" class="add" style="padding: 5px;" title="Add node">' + icons.nodeadd.replace('width="800px" ', '').replace('height="800px"', 'width="16" height="16"') + '</button>' +
'<span> ' +
' </span>' +
'<button type="button" class="duplicate" style="padding: 5px;" title="Duplicate nodes">' + icons.duplicate.replace('height="24"', 'width="16" height="16"') + '</button>' +
'<span> ' +
' </span>' +
'<button type="button" class="screenshot" style="padding: 5px;" title="Download screenshot">' + icons.screenshot.replace('viewBox=', 'width="16" height="16" viewBox=') + '</button>' +
'</div>').appendTo(mindmapPlace);
controlButtons.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
jQuery('<br>').appendTo(mindmapPlace);
var disableButton = function (button, disable) {
button.css("visibility", "visible");
if (disable) {
button.prop("disabled", true);
button.css("opacity", "0.5");
button.css("filter", "invert(0)");
} else {
button.prop("disabled", false);
button.css("opacity", "1");
}
};
disableButton(controlButtons.find("button.remove"), true);
disableButton(controlButtons.find("button.add"), true);
disableButton(controlButtons.find("button.duplicate"), true);
var determineHeight = function () {
var height = jQuery(window).height();
try {
height = jQuery(window).height() - mindmapPlace.parent().offset().top + mindmapPlace.closest(".tiddler").offset().top - mindmapPlace.closest("#displayArea").offset().top - mindmapPlace.closest("#displayArea").children("#topMenu").first().height();
} catch (e) {}
return height;
};
var container = jQuery("<div style='width: 100%; height: " + determineHeight() + "px'></div>").appendTo(mindmapPlace);
container.on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
jQuery(window).on("resize", function () {
container.css("height", determineHeight() + "px")
});
var dataHistory = {
index: -1,
history: [],
push: function (data) {
if ((this.index > -1) && (this.index < this.history.length - 1)) {
this.history.splice(this.index + 1, this.history.length - (this.index + 1));
}
this.history.push(jQuery.extend(true, {}, data));
this.index = this.history.length - 1;
if (this.index >= this.history.length - 1) {
controlButtons.find("span.redo_place").hide();
}
if (this.index > 0) {
controlButtons.find("span.undo_place").show();
disableButton(controlButtons.find("button.undo"), false);
}
},
redo: function () {
if ((this.index > -1) && (this.index < this.history.length - 1)) {
disableButton(controlButtons.find("button.undo"), false);
this.index++;
if (this.index >= this.history.length - 1) {
controlButtons.find("span.redo_place").hide();
}
return jQuery.extend(true, {}, this.history[this.index]);
}
return null;
},
undo: function () {
if (this.index > 0) {
controlButtons.find("span.redo_place").show();
this.index--;
if (this.index <= 0) {
disableButton(controlButtons.find("button.undo"), true);
}
return jQuery.extend(true, {}, this.history[this.index]);
}
return null;
}
};
if (cmm.history[source]) {
dataHistory.index = cmm.history[source].index;
dataHistory.history = cmm.history[source].history;
}
cmm.history[source] = dataHistory;
var load = function () {
var data = null;
try {
eval("data = " + store.getTiddlerText(source));
} catch (e) {
console.log(e);
}
return data || (function () {
var data = {
options: {
editable: true,
theme: "primary",
shortcut: {
enable: false,
handles: {},
mapping: {
editnode: 13
}
},
wikify: false,
zoom: 1
},
handlers: {
init: function (cfg) {},
onEdit: function (cfg, node, state) {},
onMove: function (cfg, node, beforeId, parentId) {},
onSelect: function (cfg, node) {},
onUpdate: function (cfg) {},
onJsMindEvent: function (cfg, type, data) {}
},
map: {
meta: {},
format: "node_array",
data: [{id: "root", isroot: true, topic: "Mind Map"}]
}
};
eval("data.handlers.onSelect = function (cfg, node) {\n\t\t\treturn cfg.c.CAN_ADD | cfg.c.CAN_REMOVE | cfg.c.CAN_DUPLICATE_COPY | cfg.c.CAN_DUPLICATE_PASTE;\n\t\t}");
return data;
})();
};
var jmData = load();
if (!dataHistory.history.length && store.getTiddlerText(source)) {
dataHistory.push(jmData);
}
var jmOptions = jmData.options ? jQuery.extend(true, {}, jmData.options) : {};
if (jmOptions.shortcut && jmOptions.shortcut.mapping) {
var shortcutMapping = jmOptions.shortcut.mapping;
jmOptions.shortcut.mapping = {
addchild: 0,
addbrother: 0,
editnode: 0,
delnode: 0,
toggle: 0,
left: 0,
up: 0,
right: 0,
down: 0
};
for (var k in shortcutMapping) {
jmOptions.shortcut.mapping[k] = shortcutMapping[k];
}
}
jmOptions.container = container[0];
var jm = new jsMind(jmOptions);
var handlerConfig = {
jm: jm,
data: jmData,
buttons: controlButtons,
container: container,
duplicated: null,
place: mindmapPlace,
tiddler: tiddler,
c: {
CAN_ADD: parseInt("0001", 2),
CAN_REMOVE: parseInt("0010", 2),
CAN_DUPLICATE_COPY: parseInt("0100", 2),
CAN_DUPLICATE_PASTE: parseInt("1000", 2),
EDIT_START: "EDIT_START",
EDIT_DISPLAYED: "EDIT_DISPLAYED",
EDIT_DONE: "EDIT_DONE"
},
helpers: {
disableButton: disableButton
}
};
jm.handlerConfig = handlerConfig;
var update = function (updateHistory) {
if (typeof dataTiddler === "string") {
if (store.tiddlerExists(dataTiddler)) {
dataTiddler = store.getTiddler(dataTiddler);
} else {
var created = new Date();
dataTiddler = store.saveTiddler(dataTiddler, dataTiddler, "", config.options.txtUserName, created, null, null, false, created, config.options.txtUserName);
}
}
jmData = jmData || load();
handlerConfig.data = jmData;
var functions = {shortcutHandles: {}, handlers: {}};
var storeFunctions = function (functions, store) {
if (functions) {
for (var k in functions) {
if (typeof functions[k] === "function") {
store[k] = functions[k];
functions[k] = k;
}
}
}
};
storeFunctions(((jmData.options || {}).shortcut || {}).handles, functions.shortcutHandles);
storeFunctions(jmData.handlers, functions.handlers);
var data = JSON.stringify(jmData, null, "\t");
var restoreFunctions = function (functions, store, placeholder) {
if (functions) {
var dataFunctions = data.substring(data.indexOf(placeholder) + placeholder.length);
for (var k in store) {
dataFunctions = dataFunctions.replace('\t"' + k + '": "' + k + '"', '\t"' + k + '": ' + store[k].toString());
functions[k] = store[k];
}
data = data.substring(0, data.indexOf(placeholder) + placeholder.length) + dataFunctions;
}
};
restoreFunctions(((jmData.options || {}).shortcut || {}).handles, functions.shortcutHandles, '\n\t\t\t"handles"');
restoreFunctions(jmData.handlers, functions.handlers, '\n\t"handlers"');
data = "//{{{\n" + data + "\n//}}}";
if (data !== store.getTiddlerText(source)) {
if (updateHistory !== false) {
dataHistory.push(jmData);
}
if (section == null) {
dataTiddler.text = data;
} else {
var headerRE = new RegExp("(^!{1,6}[ \t]*" + section.escapeRegExp() + "[ \t]*\n)", "mg");
headerRE.lastIndex = 0;
var match = headerRE.exec(dataTiddler.text);
if (!match) {
dataTiddler.text = dataTiddler.text + (dataTiddler.text.length ? "\n" : "") + "!" + section + "\n" + data;
} else {
var pos = match.index + match[1].length;
endPos = dataTiddler.text.indexOf("\n!", pos);
endPos = (endPos === -1) ? dataTiddler.text.length : endPos;
dataTiddler.text = dataTiddler.text.substring(0, pos) + data + dataTiddler.text.substring(endPos);
}
}
dataTiddler.modifier = config.options.txtUserName;
dataTiddler.modified = new Date();
store.setDirty(true);
if (jmData.handlers && (typeof jmData.handlers.onUpdate === "function")) {
try {
jmData.handlers.onUpdate(handlerConfig);
} catch (e) {
console.log(e);
}
}
}
};
var duplicatedNode = null;
var disableButtons = function () {
disableButton(controlButtons.find("button.remove"), true);
disableButton(controlButtons.find("button.add"), true);
disableButton(controlButtons.find("button.duplicate"), true);
duplicatedNode = null;
handlerConfig.duplicated = duplicatedNode;
};
var wikifyTopic = function (node) {
if (jmData.options.wikify && (node != null)) {
var view_data = node._data.view;
var element = view_data.element;
jQuery(element).empty();
wikify(node.topic, element, null, tiddler);
}
};
jm.wikifyTopic = wikifyTopic;
var refreshMap = function (map) {
jm._reset();
if (map) {
jm.mind = jm.data.load(map);
}
jm.view.load();
jm.layout.layout();
for (var nodeid in jm.mind.nodes) {
wikifyTopic(jm.mind.nodes[nodeid]);
jm.updateTiddlyLink(jm.mind.nodes[nodeid], false);
}
jm.layout.layout();
jm.view.show(false);
disableButtons();
};
controlButtons.find("button.redo").on("click", function () {
var data = dataHistory.redo();
if (data) {
data.options.zoom = jm.view.actualZoom;
jmData = data;
refreshMap(jmData.map);
update(false);
}
});
controlButtons.find("button.undo").on("click", function () {
var data = dataHistory.undo();
if (data) {
data.options.zoom = jm.view.actualZoom;
jmData = data;
refreshMap(jmData.map);
update(false);
}
});
if ((dataHistory.index > -1) && (dataHistory.index < dataHistory.history.length - 1)) {
controlButtons.find("span.redo_place").show();
controlButtons.find("span.undo_place").show();
disableButton(controlButtons.find("button.undo"), true);
}
if (dataHistory.index > 0) {
controlButtons.find("span.undo_place").show();
disableButton(controlButtons.find("button.undo"), false);
}
controlButtons.find("button.zoomout").on("click", function () {
var initialZoom = jm.view.actualZoom;
jm.view.zoomOut();
if (initialZoom !== jm.view.actualZoom) {
jmData.options.zoom = jm.view.actualZoom;
update(false);
}
refreshZoomButtons();
});
controlButtons.find("button.zoomin").on("click", function () {
var initialZoom = jm.view.actualZoom;
jm.view.zoomIn();
if (initialZoom !== jm.view.actualZoom) {
jmData.options.zoom = jm.view.actualZoom;
update(false);
}
refreshZoomButtons();
});
var refreshZoomButtons = function () {
disableButton(controlButtons.find("button.zoomout"), jm.view.actualZoom - jm.view.zoomStep < jm.view.minZoom);
disableButton(controlButtons.find("button.zoomin"), jm.view.actualZoom + jm.view.zoomStep > jm.view.maxZoom);
};
controlButtons.find("button.remove").on("click", function () {
var selectedNode = jm.get_selected_node();
if (selectedNode) {
jm.remove_node(selectedNode.id);
}
});
controlButtons.find("button.add").on("click", function () {
var selectedNode = jm.get_selected_node();
if (selectedNode) {
jm.add_node(selectedNode, jsMind.util.uuid.newid(), "New Node");
}
});
controlButtons.find("button.duplicate").on("click", function () {
if (duplicatedNode) {
duplicatedNode = null;
jQuery(this).css("filter", "invert(0)");
} else {
var selectedNode = jm.get_selected_node();
if (selectedNode) {
duplicatedNode = selectedNode;
jQuery(this).css("filter", "invert(1)");
}
}
handlerConfig.duplicated = duplicatedNode;
});
controlButtons.find("button.screenshot").on("click", function () {
jm.screenshot.shootDownload();
});
jm.add_event_listener(function (type, data) {
if ((type === jsMind.event_type.show) || (type === jsMind.event_type.edit)) {
jmData.options.zoom = jm.view.actualZoom;
jmData.map = jm.get_data();
update(jmData);
}
if (jmData.handlers && (typeof jmData.handlers.onJsMindEvent === "function")) {
try {
jmData.handlers.onJsMindEvent(handlerConfig, type, data);
} catch (e) {
console.log(e);
}
}
});
var create_node_element = jm.view.create_node_element;
jm.view.create_node_element = function (node, parent_node) {
create_node_element.apply(this, arguments);
wikifyTopic(node);
};
var remove_node = jm.view.remove_node;
jm.view.remove_node = function (node) {
remove_node.apply(this, arguments);
disableButtons();
if (jmData.handlers && (typeof jmData.handlers.onSelect === "function")) {
try {
jmData.handlers.onSelect(handlerConfig, null);
} catch (e) {
console.log(e);
}
}
};
var update_node = jm.view.update_node;
jm.view.update_node = function (node) {
update_node.apply(this, arguments);
if (jmData.options.wikify) {
wikifyTopic(node);
var view_data = node._data.view;
var element = view_data.element;
if (this.layout.is_visible(node)) {
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
} else {
var style = element.getAttribute('style');
element.style = 'visibility: visible; left:0; top:0;';
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
element.style = style;
}
}
};
var select_node = jm.view.select_node;
jm.view.select_node = function (node) {
select_node.apply(this, arguments);
handlerConfig.duplicated = duplicatedNode;
var handlerResult = null;
if (jmData.handlers && (typeof jmData.handlers.onSelect === "function")) {
try {
handlerResult = jmData.handlers.onSelect(handlerConfig, node);
} catch (e) {
console.log(e);
}
}
disableButton(controlButtons.find("button.remove"), (node == null) || node.isroot || ((typeof handlerResult === "number") && ((handlerResult & handlerConfig.c.CAN_REMOVE) !== handlerConfig.c.CAN_REMOVE)));
disableButton(controlButtons.find("button.add"), (node == null) || ((typeof handlerResult === "number") && ((handlerResult & handlerConfig.c.CAN_ADD) !== handlerConfig.c.CAN_ADD)));
disableButton(controlButtons.find("button.duplicate"), (node == null) || ((typeof handlerResult === "number") && ((handlerResult & handlerConfig.c.CAN_DUPLICATE_COPY) !== handlerConfig.c.CAN_DUPLICATE_COPY)));
if ((node != null) && duplicatedNode) {
if ((typeof handlerResult !== "number") || ((handlerResult & handlerConfig.c.CAN_DUPLICATE_PASTE) === handlerConfig.c.CAN_DUPLICATE_PASTE)) {
var collect = function (source, collected) {
collected.node = source;
collected.children = [];
for (var i = 0; i < source.children.length; i++) {
collected.children[i] = {};
collect(source.children[i], collected.children[i]);
}
};
var duplicate = function (collected, destination) {
destination = jm.add_node(destination, jsMind.util.uuid.newid(), collected.node.topic, jQuery.extend(true, {}, collected.node.data));
for (var i = 0; i < collected.children.length; i++) {
duplicate(collected.children[i], destination);
}
destination.expanded = collected.node.expanded;
};
var collected = {};
collect(duplicatedNode, collected);
duplicate(collected, node);
refreshMap();
if (jmData.handlers && (typeof jmData.handlers.onSelect === "function")) {
try {
jmData.handlers.onSelect(handlerConfig, null);
} catch (e) {
console.log(e);
}
}
}
}
controlButtons.find("button.duplicate").css("filter", "invert(0)");
duplicatedNode = null;
handlerConfig.duplicated = duplicatedNode;
};
jm.show(jmData.map);
jm.view.setZoom(jmData.options.zoom || jm.view.actualZoom);
refreshZoomButtons();
if (jmData.handlers && (typeof jmData.handlers.init === "function")) {
setTimeout(function () {
try {
jmData.handlers.init(handlerConfig);
} catch (e) {
console.log(e);
}
}, 0);
}
}
};
//}}}
//{{{
jsMind.register_plugin(new jsMind.plugin("tiddlerAndColor", function (jm) {
var cmm = config.macros.mindmap;
var wikifyTopic = jm.wikifyTopic;
var rgb2hex = function (rgb) {
// https://stackoverflow.com/questions/1740700/how-to-get-hex-color-value-rather-than-rgb-value
var hex = "";
if (rgb) {
if (/^#[0-9A-F]{6}$/i.test(rgb)) {
hex = rgb;
} else {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (rgb) {
var toHex = function (x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
};
hex = "#" + toHex(rgb[1]) + toHex(rgb[2]) + toHex(rgb[3]);
}
}
}
return hex;
};
var jmnode = jQuery("<jmnode></jmnode>").appendTo(jm.view.e_nodes);
var defaultBackgroundColor = rgb2hex(jmnode.css("background-color") || "#000000");
var defaultTextColor = rgb2hex(jmnode.css("color") || "#ffffff");
jmnode.addClass("selected");
var defaultTextSelectedColor = rgb2hex(jmnode.css("color") || "#ffffff");
jmnode.remove();
var processEditApplyCancel = function (e) {
var keycode = e.keyCode || e.which;
if ((keycode === 13) || (keycode === 27)) {
e.preventDefault();
e.stopPropagation();
if ((keycode === 27) && (jm.view.editing_node != null)) {
jm.view.e_editor.value = "";
}
var input = jQuery(this);
if (input.colpickHide) {
input.colpickHide();
}
jm.view.edit_node_end();
}
};
jm.view.e_editor = jQuery("<input type='text' class='jsmind-editor' />").on("keydown", function (e) {
processEditApplyCancel.call(this, e);
})[0];
jm.view.backgroundColorEditor = jQuery("<div><style>jmnode.selected span svg {stroke: " + defaultTextSelectedColor + "}</style><input type='text' class='jsmind-editor' title='background color' placeholder='background color' /> <span style='cursor: pointer' title='apply background color to subnodes'> " + cmm.icons.duplicate + " </span></div>");
jm.view.backgroundColorEditor.children("input").on("keydown", function (e) {
processEditApplyCancel.call(this, e);
});
if (jm.view.backgroundColorEditor.children("input").colpick) {
jm.view.backgroundColorEditor.children("input").colpick({
styles: {
"z-index": "6"
},
onBeforeShow: function (el) {
var input = jQuery(this);
input.colpickSetColor(input.val() ? input.val() : defaultBackgroundColor, true);
},
onSubmit: function (hsb, hex, rgb, el, bySetColor) {
var input = jQuery(el);
input.val("#" + hex);
input.colpickHide();
}
});
}
jm.view.backgroundColorEditor.children("span").on("click", function () {
jm.view.backgroundColorSubnodes = true;
jQuery(this).hide();
});
jm.view.textColorEditor = jQuery("<div><input type='text' class='jsmind-editor' title='text color' placeholder='text color' /> <span style='cursor: pointer' title='apply text color to subnodes'> " + cmm.icons.duplicate + " </span></div>");
jm.view.textColorEditor.children("input").on("keydown", function (e) {
processEditApplyCancel.call(this, e);
});
if (jm.view.textColorEditor.children("input").colpick) {
jm.view.textColorEditor.children("input").colpick({
styles: {
"z-index": "6"
},
onBeforeShow: function (el) {
var input = jQuery(this);
input.colpickSetColor(input.val() ? input.val() : defaultTextColor, true);
},
onSubmit: function (hsb, hex, rgb, el, bySetColor) {
var input = jQuery(el);
input.val("#" + hex);
input.colpickHide();
}
});
}
jm.view.textColorEditor.children("span").on("click", function () {
jm.view.textColorSubnodes = true;
jQuery(this).hide();
});
jm.view.tiddlerEditor = jQuery("<div><input type='text' class='jsmind-editor' title='tiddler title or GUID' placeholder='tiddler title or GUID' /> <span class='cancel' style='cursor: pointer; font-size: 1.1em' title='cancel'> ✗ </span><span class='ok' style='cursor: pointer; font-size: 1.1em' title='apply changes'> ✓ </span></div>");
jm.view.tiddlerEditor.children("input").on("keydown", function (e) {
processEditApplyCancel.call(this, e);
});
jm.view.tiddlerEditor.children("span.cancel").on("click", function () {
if (jm.view.editing_node != null) {
jm.view.e_editor.value = "";
}
jm.view.edit_node_end();
});
jm.view.tiddlerEditor.children("span.ok").on("click", function () {
jm.view.edit_node_end();
});
var resolveTiddlerRef = function (text, forGUID) {
var tiddlerRef = ["", false];
if (text) {
tiddlerRef = text;
var section = null;
var pos = tiddlerRef.indexOf(config.textPrimitives.sectionSeparator);
if (pos > -1) {
section = tiddlerRef.substring(pos);
tiddlerRef = tiddlerRef.substring(0, pos);
}
var tiddler = null;
store.forEachTiddler(function (title, currentTiddler) {
if (currentTiddler.fields.guid === tiddlerRef) {
tiddler = currentTiddler;
}
});
tiddler = tiddler ? tiddler : store.fetchTiddler(tiddlerRef);
if (tiddler && forGUID && tiddler.fields.guid) {
tiddlerRef = [tiddler.fields.guid + (section ? section : ""), true];
} else if (tiddler) {
tiddlerRef = [tiddler.title + (section ? section : ""), true];
} else {
tiddlerRef = [text, false];
}
}
return tiddlerRef;
};
jm.resolveTiddlerRef = resolveTiddlerRef;
var updateTiddlyLink = function (node, updateLayout) {
if (node) {
var view_data = node._data.view;
var element = view_data.element;
var tiddlyLink = jQuery(element).children("div.tiddlyLink");
if (node.data.tiddler) {
if (tiddlyLink.length) {
tiddlyLink = tiddlyLink.find("a.tiddlyLink");
} else {
var tiddlerRef = resolveTiddlerRef(node.data.tiddler, false);
if (!tiddlerRef[1] && config.formatterHelpers.isExternalLink(tiddlerRef[0])) {
tiddlyLink = jQuery(createExternalLink(jQuery("<div class='tiddlyLink' style='width: 100%; text-align: right'><style>jmnode.selected span a.tiddlyLink {color: " + defaultTextSelectedColor + " !important}</style><span></span></div>").appendTo(jQuery(element)).children("span")[0], tiddlerRef[0]));
tiddlyLink.removeClass("externalLink");
tiddlyLink.css("text-decoration", "none");
} else {
tiddlyLink = jQuery(createTiddlyLink(jQuery("<div class='tiddlyLink' style='width: 100%; text-align: right'><style>jmnode.selected span a.tiddlyLink {color: " + defaultTextSelectedColor + " !important}</style><span></span></div>").appendTo(jQuery(element)).children("span")[0], tiddlerRef[0]));
}
tiddlyLink.html("→");
}
tiddlyLink.css("color", node.data["foreground-color"] ? node.data["foreground-color"] : defaultTextColor);
} else {
tiddlyLink.remove();
}
var layoutChanged = false;
if (jm.view.layout.is_visible(node)) {
layoutChanged = (view_data.width !== element.clientWidth) || (view_data.height !== element.clientHeight);
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
} else {
var origin_style = element.getAttribute("style");
element.style = "visibility: visible; left:0; top:0;";
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
element.style = origin_style;
}
if (updateLayout && layoutChanged) {
jm.layout.layout();
jm.view.show(false);
}
}
};
jm.updateTiddlyLink = updateTiddlyLink;
var remove_node = jm.view.remove_node;
jm.view.remove_node = function (node) {
if ((this.editing_node != null) && (this.editing_node.id === node.id)) {
this.backgroundColorEditor.detach();
this.textColorEditor.detach();
this.tiddlerEditor.detach();
}
remove_node.apply(this, arguments);
};
var edit_node_begin = jm.view.edit_node_begin;
jm.view.edit_node_begin = function (node) {
var handlerConfig = jm.handlerConfig;
var jmData = handlerConfig.data;
var allowEdit = true;
if (jmData.handlers && (typeof jmData.handlers.onEdit === "function")) {
try {
allowEdit = jmData.handlers.onEdit(handlerConfig, node, handlerConfig.c.EDIT_START) !== false;
} catch (e) {
console.log(e);
}
}
if (allowEdit) {
edit_node_begin.apply(this, arguments);
this.backgroundColorEditor.children("input").val((node.data["background-color"] == null) ? "" : node.data["background-color"]);
this.backgroundColorEditor.children("input").css("width", jQuery(this.e_editor).css("width"));
this.backgroundColorEditor.children("span").css("stroke", node.data["foreground-color"] || defaultTextColor);
this.backgroundColorEditor.children("span").show();
this.backgroundColorSubnodes = false;
this.textColorEditor.children("input").val((node.data["foreground-color"] == null) ? "" : node.data["foreground-color"]);
this.textColorEditor.children("input").css("width", jQuery(this.e_editor).css("width"));
this.textColorEditor.children("span").css("stroke", node.data["foreground-color"] || defaultTextColor);
this.textColorEditor.children("span").show();
this.textColorSubnodes = false;
this.tiddlerEditor.children("input").val(resolveTiddlerRef(node.data.tiddler, false)[0]);
this.tiddlerEditor.children("input").css("width", jQuery(this.e_editor).css("width"));
jQuery(node._data.view.element).append(this.backgroundColorEditor);
jQuery(node._data.view.element).append(this.textColorEditor);
jQuery(node._data.view.element).append(this.tiddlerEditor);
var lineHeight = this.backgroundColorEditor.css("line-height");
lineHeight = (lineHeight && jQuery.isNumeric(lineHeight.replace("px", "").replace("em", ""))) ? lineHeight : "24";
this.backgroundColorEditor.find("svg").attr("width", lineHeight);
this.backgroundColorEditor.find("svg").attr("height", lineHeight);
this.textColorEditor.find("svg").attr("width", lineHeight);
this.textColorEditor.find("svg").attr("height", lineHeight);
if (jmData.handlers && (typeof jmData.handlers.onEdit === "function")) {
try {
jmData.handlers.onEdit(handlerConfig, node, handlerConfig.c.EDIT_DISPLAYED);
} catch (e) {
console.log(e);
}
}
}
};
var edit_node_end = jm.view.edit_node_end;
jm.view.edit_node_end = function () {
var handlerConfig = jm.handlerConfig;
var jmData = handlerConfig.data;
var sendEvent = false;
var nodeid = null;
var node = null;
var topic = null;
if (this.editing_node != null) {
nodeid = this.editing_node.id;
node = jm.get_node(nodeid);
if (jm.get_editable()) {
topic = this.e_editor.value;
if (node && !jsMind.util.text.is_empty(this.e_editor.value)) {
var updateSubnodeColor = function (style, color, nodes) {
dataChanged = false;
if (nodes) {
for (var i = 0; i < nodes.length; i++) {
if ((nodes[i].data[style] || "") !== (color || "")) {
nodes[i].data[style] = (color || null);
jm.view.reset_node_custom_style(nodes[i]);
wikifyTopic(nodes[i]);
updateTiddlyLink(nodes[i], true);
if (color == null) {
delete nodes[i].data[style];
}
dataChanged = true;
}
dataChanged |= updateSubnodeColor(style, color, nodes[i].children);
}
}
return dataChanged;
};
var dataChanged = false;
dataChanged |= (node.data["background-color"] || "") !== this.backgroundColorEditor.children("input").val();
node.data["background-color"] = this.backgroundColorEditor.children("input").val();
if (jsMind.util.text.is_empty(node.data["background-color"])) {
delete node.data["background-color"];
}
if (this.backgroundColorSubnodes) {
dataChanged |= updateSubnodeColor("background-color", node.data["background-color"], node.children);
}
dataChanged |= (node.data["foreground-color"] || "") !== this.textColorEditor.children("input").val();
node.data["foreground-color"] = this.textColorEditor.children("input").val();
if (jsMind.util.text.is_empty(node.data["foreground-color"])) {
delete node.data["foreground-color"];
}
if (this.textColorSubnodes) {
dataChanged |= updateSubnodeColor("foreground-color", node.data["foreground-color"], node.children);
}
if (jsMind.util.text.is_empty(this.tiddlerEditor.children("input").val())) {
dataChanged |= "tiddler" in node.data;
delete node.data.tiddler;
} else {
var tiddlerRef = resolveTiddlerRef(this.tiddlerEditor.children("input").val(), true)[0];
dataChanged |= (node.data.tiddler || "") !== tiddlerRef;
node.data.tiddler = tiddlerRef;
}
if (jmData.handlers && (typeof jmData.handlers.onEdit === "function")) {
try {
dataChanged |= jmData.handlers.onEdit(handlerConfig, node, handlerConfig.c.EDIT_DONE) === true;
} catch (e) {
console.log(e);
}
}
sendEvent = (this.editing_node.topic === topic) && dataChanged;
}
}
this.backgroundColorEditor.detach();
this.textColorEditor.detach();
this.tiddlerEditor.detach();
}
edit_node_end.apply(this, arguments);
wikifyTopic(node);
updateTiddlyLink(node, true);
if (sendEvent) {
jm.invoke_event_handle(jsMind.event_type.edit, { evt: "update_node", data: [nodeid, topic], node: nodeid });
}
};
var move_node = jm.move_node;
jm.move_node = function (nodeid, beforeid, parentid, direction) {
var jmData = jm.handlerConfig.data;
var allowMove = true;
if (jmData.handlers && (typeof jmData.handlers.onMove === "function")) {
try {
nodeid = (nodeid != null) ? jm.get_node(nodeid) : null;
allowMove = jmData.handlers.onMove(jm.handlerConfig, nodeid, beforeid, parentid) !== false;
} catch (e) {
console.log(e);
}
}
if (allowMove) {
move_node.apply(this, arguments);
wikifyTopic(this.get_node(nodeid));
updateTiddlyLink(this.get_node(nodeid), true);
}
};
for (var nodeid in jm.mind.nodes) {
wikifyTopic(jm.mind.nodes[nodeid]);
updateTiddlyLink(jm.mind.nodes[nodeid], false);
}
jm.layout.layout();
jm.view.show(false);
}));
//}}}
//{{{
(function () {
var code;
jsMind.layout_provider.prototype._collect_expanded_nodes = function (node, nodes) {
node = node || this.jm.mind.root;
nodes = nodes || {};
nodes[node.id] = node;
if (node.expanded) {
for (var i = 0; i < node.children.length; i++) {
this._collect_expanded_nodes(node.children[i], nodes);
}
}
return nodes;
};
code = eval("jsMind.layout_provider.prototype.get_min_size").toString();
code = code.replace("this.jm.mind.nodes", "this._collect_expanded_nodes()");
eval("jsMind.layout_provider.prototype.get_min_size = function get_min_size" + code.substring(code.indexOf("(")));
code = eval("jsMind.view_provider.prototype.setZoom").toString();
code = code.replace(".style.zoom = zoom;", ".style.transform = 'scale(' + zoom + ')'; this.e_panel.children[i].style['transform-origin'] = (this.size.w * this.actualZoom > this.e_panel.clientWidth ? '0' : ((this.e_panel.clientWidth - (this.size.w * this.actualZoom)) / 2 / (1 - this.actualZoom)) + 'px') + ' ' + (this.size.h * this.actualZoom > this.e_panel.clientHeight ? '0' : ((this.e_panel.clientHeight - (this.size.h * this.actualZoom)) / 2 / (1 - this.actualZoom)) + 'px');");
eval("jsMind.view_provider.prototype.setZoom = function setZoom" + code.substring(code.indexOf("(")));
code = eval("jsMind.view_provider.prototype._center_root").toString();
code = code.replace(/if \(/g, "if (true || ");
eval("jsMind.view_provider.prototype._center_root = function _center_root" + code.substring(code.indexOf("(")));
code = eval("jsMind.draggable.prototype.init").toString();
code = code.replace("this._event_bind();", "this._event_bind(); this.jm.view.setZoom(this.jm.view.actualZoom);");
eval("jsMind.draggable.prototype.init = function init" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name:|NewMeansNewPlugin|
|Description:|If 'New Tiddler' already exists then create 'New Tiddler (1)' and so on|
|Version:|1.1.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/empty.html#NewMeansNewPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Note: I think this should be in the core
***/
//{{{
// change this or set config.newMeansNewForJournalsToo it in MptwUuserConfigPlugin
if (config.newMeansNewForJournalsToo == undefined) config.newMeansNewForJournalsToo = true;
String.prototype.getNextFreeName = function() {
numberRegExp = / \(([0-9]+)\)$/;
var match = numberRegExp.exec(this);
if (match) {
var num = parseInt(match[1]) + 1;
return this.replace(numberRegExp," ("+num+")");
}
else {
return this + " (1)";
}
}
config.macros.newTiddler.checkForUnsaved = function(newName) {
var r = false;
story.forEachTiddler(function(title,element) {
if (title == newName)
r = true;
});
return r;
}
config.macros.newTiddler.getName = function(newName) {
while (store.getTiddler(newName) || config.macros.newTiddler.checkForUnsaved(newName))
newName = newName.getNextFreeName();
return newName;
}
config.macros.newTiddler.onClickNewTiddler = function()
{
var title = this.getAttribute("newTitle");
if(this.getAttribute("isJournal") == "true") {
title = new Date().formatString(title.trim());
}
// ---- these three lines should be the only difference between this and the core onClickNewTiddler
if (config.newMeansNewForJournalsToo || this.getAttribute("isJournal") != "true")
title = config.macros.newTiddler.getName(title);
var params = this.getAttribute("params");
var tags = params ? params.split("|") : [];
var focus = this.getAttribute("newFocus");
var template = this.getAttribute("newTemplate");
var customFields = this.getAttribute("customFields");
if(!customFields && !store.isShadowTiddler(title))
customFields = String.encodeHashMap(config.defaultCustomFields);
story.displayTiddler(null,title,template,false,null,null);
var tiddlerElem = story.getTiddler(title);
if(customFields)
story.addCustomFields(tiddlerElem,customFields);
var text = this.getAttribute("newText");
if(typeof text == "string")
story.getTiddlerField(title,"text").value = text.format([title]);
for(var t=0;t<tags.length;t++)
story.setTiddlerTag(title,tags[t],+1);
story.focusTiddler(title,focus);
return false;
};
//}}}
/***
|Name|NewMeansNewPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[NewMeansNewPlugin]]|
!!!!!Code
***/
//{{{
(function () {
var code = eval("config.macros.newTiddler.checkForUnsaved").toString();
code = code.replace('title == newName', 'title == newName && element.getAttribute("dirty") == "true"');
eval("config.macros.newTiddler.checkForUnsaved = function checkForUnsaved" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
var newOnClickNewTiddler = store.getTiddlerText("NewMeansNewPlugin");
var start = newOnClickNewTiddler.indexOf("// ----");
newOnClickNewTiddler = newOnClickNewTiddler.substring(start, newOnClickNewTiddler.indexOf(";", start) + 1);
var onClickNewTiddler = jQuery("#jsArea").text();
start = onClickNewTiddler.indexOf("config.macros.newTiddler.onClickNewTiddler");
onClickNewTiddler = onClickNewTiddler.substring(start, onClickNewTiddler.indexOf("};", start) + "};".length);
onClickNewTiddler = onClickNewTiddler.replace("var params", newOnClickNewTiddler + "\nvar params");
eval(onClickNewTiddler);
})();
//}}}
/***
|Name|PDFPlugin|
|License|[[TW Notes License]]|
<<pdf viewer>>
!!!!!Code
***/
//{{{
config.macros.pdf = {
dataURItoObjectURL: function (dataURI, mimeType) {
// http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = unescape(dataURI.split(',')[1]);
}
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// write the bytes of the string to an ArrayBuffer
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
// http://stackoverflow.com/questions/10412299/whats-the-difference-between-blobbuilder-and-the-new-blob-constructor
var dataView = new DataView(ab);
var blob = new Blob([dataView], { type: (mimeType ? mimeType : (mimeString ? mimeString : "application/octet-stream")) });
var DOMURL = self.URL || self.webkitURL || self;
return "" + DOMURL.createObjectURL(blob);
},
createViewer: function (url, title, place, params, tiddler) {
var viewer = jQuery("<iframe title='' src='' width='" + jQuery(place).parent().width() + "px' height='" + jQuery(window).height() + "px'></iframe>", place).appendTo(place);
viewer.attr("title", title);
viewer.attr("src", url);
jQuery(window).resize(function () {
viewer.width(jQuery(place).parent().width() + "px");
viewer.height(jQuery(window).height() + "px");
});
},
createLink: function (url, label, title, place) {
var link = jQuery("<a href='' download='' target='_blank'></a>").appendTo(place);
link.attr("href", url);
link.attr("download", title);
link.text(label);
},
createLoader: function (place, params, tiddler) {
var loader = jQuery('<span><div><input type="file" /></div><div class="pdfViewer" style="display: none"></div></span>').appendTo(place);
loader.find("input").on("change", function () {
var fileName = null;
var fileReader = new FileReader();
fileReader.onload = function () {
var dataView = new DataView(fileReader.result);
var blob = new Blob([dataView], { type: "application/pdf" });
var DOMURL = self.URL || self.webkitURL || self;
var viewer = loader.find("div.pdfViewer");
viewer.empty();
viewer.show();
config.macros.pdf.createViewer("" + DOMURL.createObjectURL(blob), fileName, viewer, params, tiddler);
};
fileReader.onerror = function () {
console.log(fileReader.error);
};
fileName = this.files[0].name;
fileReader.readAsArrayBuffer(this.files[0]);
});
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var parsedParams = paramString.parseParams("anon", null, true, false, false);
if (parsedParams[0].anon && (parsedParams[0].anon.indexOf("viewer") > -1)) {
config.macros.pdf.createLoader(place, parsedParams, tiddler);
} else {
var title = null;
var filter = (parsedParams.length && parsedParams[0].filter) ? parsedParams[0].filter[0] : null;
if (filter) {
var related = null;
if (filter.indexOf("guid:") == 0) {
filter = filter.substring("guid:".length);
try {
filter = eval(filter);
} catch (e) {}
var storeTiddlers = store.getTiddlers();
for (var i = 0; i < storeTiddlers.length; i++) {
if (storeTiddlers[i].fields && storeTiddlers[i].fields["guid"]) {
if (filter.test) {
if (filter.test(storeTiddlers[i].fields["guid"])) {
title = storeTiddlers[i].title;
break;
}
} else if (storeTiddlers[i].fields["guid"].indexOf(filter) > -1) {
title = storeTiddlers[i].title;
break;
}
}
}
} else {
if (filter.indexOf("tagged:") == 0) {
related = tiddler ? store.getTaggedTiddlers(tiddler.title) : related;
filter = filter.substring("tagged:".length);
} else if (filter.indexOf("tag:") == 0) {
related = tiddler ? tiddler.tags : related;
filter = filter.substring("tag:".length);
}
related = related ? related : store.getTiddlers();
try {
filter = eval(filter);
} catch (e) {}
for (var i = 0; i < (related ? related.length : 0); i++) {
var currentTitle = related[i].title ? related[i].title : related[i];
if (filter.test) {
if (filter.test(currentTitle)) {
title = currentTitle;
break;
}
} else if (currentTitle.indexOf(filter) > -1) {
title = currentTitle;
break;
}
}
}
}
var processors = parsedParams.length ? parsedParams[0].processor : null;
var sourceReference = (parsedParams.length && parsedParams[0].source) ? parsedParams[0].source[0] : null;
var source = null;
if (title !== null) {
store.getTiddlerText(title);
source = (sourceReference !== null) ? store.getTiddlerText(title + sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(title + (processors ? "" : "##pdf"));
}
if (source === null) {
var tiddlerTitle = tiddler ? tiddler.title : "";
source = (sourceReference !== null) ? store.getTiddlerText(sourceReference) : null;
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + ((sourceReference !== null) ? sourceReference : (processors ? "" : "##pdf")));
source = (source !== null) ? source : store.getTiddlerText(tiddlerTitle + (processors ? "" : "##pdf"));
}
if (processors) {
for (var i = 0; i < processors.length; i++) {
try {
eval(processors[i]);
} catch (e) {
throw "processing error: " + e;
}
}
}
if (source) {
source = source.trim();
source = ((source.indexOf("%/") > -1) && (source.lastIndexOf("%/") === source.length - "%/".length))
? source.substring(0, source.lastIndexOf("%/")).trim()
: source;
}
if (source) {
var objectURL = config.macros.pdf.dataURItoObjectURL(source);
if (parsedParams.length && parsedParams[0].link) {
config.urlFileNameMap = config.urlFileNameMap || {};
config.urlFileNameMap[objectURL] = parsedParams[0].link[0];
config.macros.pdf.createLink(objectURL, parsedParams[0].link[0], parsedParams[0].link[0], place);
} else {
config.macros.pdf.createViewer(objectURL, "", place, parsedParams, tiddler);
}
}
}
}
}
//}}}
/***
|Name|PDFjsPlugin|
|License|[[TW Notes License]]|
|Requires|[[PDFPlugin]]|
!!!!!Code
***/
//{{{
if (config.options.txtPDFjsPluginMaxCanvasPixels === undefined) {
config.options.txtPDFjsPluginMaxCanvasPixels = "-1";
}
if (config.options.txtPDFjsPluginMaxImageSize === undefined) {
config.options.txtPDFjsPluginMaxImageSize = "-1";
}
if (config.options.txtPDFjsPluginViewer === undefined) {
config.options.txtPDFjsPluginViewer = "";
}
merge(config.optionsDesc, {
txtPDFjsPluginMaxCanvasPixels: "The maximum PDF canvas size in total width * height pixels (-1 for no limit, 0 for CSS-only zooming, blank for the default viewer value)",
txtPDFjsPluginMaxImageSize: "The maximum size, in total width * height pixels, that an image can have before it is removed during PDF rendering (-1 to disable image removal, blank for the default viewer value)",
txtPDFjsPluginViewer: "The URL of the PDF.js viewer (blank for the default device path): on unsupported devices, the PDF.js viewer is used only if the pagefield macro parameter is specified"
});
//}}}
//{{{
config.macros.pdf.pdfjsViewer = "file:///android_asset/pdfjs/viewer.html";
config.macros.pdf.pdfjsMessageHandlerCounter = 0;
config.macros.pdf.pdfjsMessageHandlers = {};
config.macros.pdf.createStandardViewer = config.macros.pdf.createViewer;
config.macros.pdf.createViewer = function (url, title, place, params, tiddler, viewerPath) {
var pagefieldSpecified = params && params.length && params[0].pagefield && tiddler;
if (viewerPath || (config.macros.device && config.macros.device.supported) || (config.options.txtPDFjsPluginViewer && pagefieldSpecified)) {
var viewerId = "v_" + config.macros.pdf.pdfjsMessageHandlerCounter++;
config.macros.pdf.pdfjsMessageHandlers[viewerId] = {
viewerUnavailableTimeout: null,
process: function (message) {
switch (message.type) {
case "viewer available":
if (this.viewerUnavailableTimeout != null) {
clearTimeout(this.viewerUnavailableTimeout);
this.viewerUnavailableTimeout = null;
}
break;
case "page change":
if (pagefieldSpecified && params[0].pagefield[0] && (tiddler.fields[params[0].pagefield[0]] !== "" + message.page)) {
tiddler.fields[params[0].pagefield[0]] = "" + message.page;
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
}
break;
}
}
};
var viewer = jQuery("<iframe title='' src='' width='" + jQuery(place).parent().width() + "px' height='" + jQuery(window).height() + "px'></iframe>", place).appendTo(place);
viewer.attr("title", title);
viewerPath = viewerPath || config.options.txtPDFjsPluginViewer || config.macros.pdf.pdfjsViewer;
var src = viewerPath + "?id=" + encodeURIComponent(viewerId) + "&url=" + encodeURIComponent(url) + "&title=" + encodeURIComponent(title || "PDF.js") + "&maxCanvasPixels=" + encodeURIComponent(config.options.txtPDFjsPluginMaxCanvasPixels) + "&maxImageSize=" + encodeURIComponent(config.options.txtPDFjsPluginMaxImageSize);
if (pagefieldSpecified && params[0].pagefield[0] && tiddler.fields[params[0].pagefield[0]]) {
src += "&page=" + encodeURIComponent(tiddler.fields[params[0].pagefield[0]]);
}
config.macros.pdf.pdfjsMessageHandlers[viewerId].viewerUnavailableTimeout = setTimeout(function () {
viewer.remove();
if (config.macros.device && config.macros.device.supported && (viewerPath !== config.macros.pdf.pdfjsViewer)) {
config.macros.pdf.createViewer(url, title, place, params, tiddler, config.macros.pdf.pdfjsViewer);
} else {
config.macros.pdf.createStandardViewer(url, title, place, params, tiddler);
}
}, 1000);
viewer.attr("src", src);
jQuery(window).resize(function () {
viewer.width(jQuery(place).parent().width() + "px");
viewer.height(jQuery(window).height() + "px");
});
} else {
config.macros.pdf.createStandardViewer(url, title, place, params, tiddler);
}
};
window.onmessage = function (e) {
try {
var message = JSON.parse(e.data);
if (message && (message.source === "pdfjsviewer") && config.macros.pdf.pdfjsMessageHandlers[message.id]) {
config.macros.pdf.pdfjsMessageHandlers[message.id].process.call(config.macros.pdf.pdfjsMessageHandlers[message.id], message);
}
} catch (e) {}
};
//}}}
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.10 (2011-05-23)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
''Syntax:''
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any tiddler content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations".
Wrap every citation with a part and a proper name.
''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.</part>
<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.</part>
<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.</part>
}}}
You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|
<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}
Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}
Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Revision history<html><a name="Revisions"/></html>
* v1.0.10 (2011-05-23)
** Adapt to TW 2.6.2 default behaviour when existing tiddlers are opened (don't select text) and fixed Firefox 4 issue. Thanks to dave for reporting the issue.
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside {{{<<tabs ...>>}}} macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Code<html><a name="Code"/></html>
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
//{{{
//============================================================================
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 10,
date: new Date(2011, 4, 23),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};
if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
//============================================================================
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
//
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result && text.charAt(result.index) == '\n')
? result.index+1
: index;
}
//============================================================================
// Constants
var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";
//============================================================================
// Plugin Specific Helpers
// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex < params.length) {
hidden = params[paramsIndex] == "hidden";
paramsIndex++;
}
return {
partName: name,
isHidden: hidden
};
}
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
//
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
}
//============================================================================
// Formatter
// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch && endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.subWikify(w.output,partEndTagREString);
}
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
}
return false;
}
config.formatters.push( {
name: "part",
match: "<part\\s+[^>]+>",
handler: function(w) {
if (!handlePartSection(w)) {
w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
}
}
} )
//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers
// as tiddlers.
var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments && arguments.partName == partName) {
return tagMatch;
}
}
i = tagMatch.index+tagMatch[0].length;
}
}
// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler
// object, using fullName as the Tiddler's title.
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
//
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag >= 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.set(
fullName,
partTiddlerText,
parentTiddler.modifier,
parentTiddler.modified,
parentTiddler.tags,
parentTiddler.created);
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
}
return null;
}
// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result && title) {
var i = title.lastIndexOf('/');
if (i > 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == ".")
? store.resolveTiddler(currentParent)
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+"/"+partName);
}
}
}
return result;
};
};
// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag.
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
var oldRestartFunc = restart;
window.restart = function() {
hijackFetchTiddler();
oldRestartFunc.apply(this,arguments);
};
} else
hijackFetchTiddler();
// The user must not edit a readOnly/partTiddler
//
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
}
return this.abegoIsPartTiddler;
}
config.commands.editTiddler.handler_PartTiddlerPlugin = config.commands.editTiddler.handler;
config.commands.editTiddler.handler = function(event,src,title)
{
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
{
return config.commands.editTiddler.handler_PartTiddlerPlugin(event,src,title);
}
return false;
}
// To allow the "./partName" syntax in macros we need to hijack
// the invokeMacro to define the "currentParent" while it is running.
//
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
}
}
window.invokeMacro = myInvokeMacro;
// To correctly support the "./partName" syntax while refreshing we need to hijack
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
//
(function() {
var oldTiddlerRefresher= config.refreshers.tiddler;
config.refreshers.tiddler = function(e,changeList) {
var oldCurrentParent = currentParent;
try {
currentParent = e.getAttribute("tiddler");
return oldTiddlerRefresher.apply(this,arguments);
} finally {
currentParent = oldCurrentParent;
}
};
})();
// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
var extendRelativeNames = function(e, title) {
var nodes = e.getElementsByTagName("a");
for(var i=0; i<nodes.length; i++) {
var node = nodes[i];
var s = node.getAttribute("content");
if (s && s.indexOf("./") == 0)
node.setAttribute("content",title+s.substr(1));
}
};
var oldHandler = config.macros.tabs.handler;
config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var result = oldHandler.apply(this,arguments);
if (tiddler)
extendRelativeNames(place, tiddler.title);
return result;
};
})();
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
}
if (!tiddlerElem && evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
}
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName("a");
for (var i = 0; i < children.length; i++) {
var child = children[i];
var name = child.getAttribute("name");
if (name == anchorName) {
var y = findPosY(child);
window.scrollTo(0,y);
return;
}
}
}
} // of "install only once"
//}}}
/***
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2011 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
/***
|Name|ReciprocalTagsPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
config.macros.reciprocalTags = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var btnName = params[0] || "Tags";
var panel = config.macros.slider.createSlider(place, "config.macros.reciprocalTags:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), btnName, "Open the list of tiddlers tagging and tagged by the current tiddler");
var updateTiddlerTag = function (event) {
var tiddlerTitle = this.getAttribute("tiddlertitle");
var tiddlerTag = this.getAttribute("tiddlertag");
var tiddler = (tiddlerTitle && tiddlerTag) ? store.getTiddler(tiddlerTitle) : null;
if (tiddler && tiddler.tags) {
if (this.checked && (tiddler.tags.indexOf(tiddlerTag) == -1)) {
tiddler.tags[tiddler.tags.length] = tiddlerTag;
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddlerTitle, null, true);
story.refreshTiddler(tiddlerTag, null, true);
}
if (!this.checked && (tiddler.tags.indexOf(tiddlerTag) > -1)) {
tiddler.tags.splice(tiddler.tags.indexOf(tiddlerTag), 1);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddlerTitle, null, true);
story.refreshTiddler(tiddlerTag, null, true);
}
}
};
var tiddlerTitle = tiddler ? tiddler.title : null;
wikify("\n''Tiddlers whose title is a tag of this tiddler''\n//If checked, that tiddler is also tagged with the title of the current tiddler.//\n\n", panel);
var tiddlerTags = tiddler ? tiddler.tags : null;
if (tiddlerTags) {
for (var i = 0; i < tiddlerTags.length; i++) {
var tagTiddler = tiddlerTags[i] ? store.getTiddler(tiddlerTags[i]) : null;
if (tagTiddler) {
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = updateTiddlerTag;
checkbox.checked = tagTiddler.tags && (tagTiddler.tags.indexOf(tiddlerTitle) > -1);
checkbox.setAttribute("tiddlertitle", tiddlerTags[i]);
checkbox.setAttribute("tiddlertag", tiddlerTitle);
panel.appendChild(checkbox);
wikify(" [[" + tiddlerTags[i] + "]]\n", panel);
}
}
}
wikify("\n", panel);
wikify("\n''Tiddlers that are tagged by the title of this tiddler''\n//If checked, the current tiddler is also tagged with the title of that tiddler.//\n\n", panel);
var taggedTiddlers = tiddlerTitle ? store.getTaggedTiddlers(tiddlerTitle) : null;
if (tiddler && tiddler.tags && taggedTiddlers) {
for (var i = 0; i < taggedTiddlers.length; i++) {
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = updateTiddlerTag;
checkbox.checked = tiddler.tags.indexOf(taggedTiddlers[i].title) > -1;
checkbox.setAttribute("tiddlertitle", tiddlerTitle);
checkbox.setAttribute("tiddlertag", taggedTiddlers[i].title);
panel.appendChild(checkbox);
wikify(" [[" + taggedTiddlers[i].title + "]]\n", panel);
}
}
}
}
//}}}
/***
|Name|RegexLinkPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
config.formatters[config.formatters.length] = {
name: "regexLink",
match: "\\[(.*?\:)?\\/.*?\\/.*?\\[",
lookaheadRegExp: /\[(?:(.*?)\:)?(\/.*?\/.*?)\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
handler: function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if (lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var collection = lookaheadMatch[1];
var filter = lookaheadMatch[2];
try {
filter = eval(filter);
} catch (e) {}
var title = null;
var tiddler = w.tiddler;
var related = null;
if (collection == "guid") {
if (filter) {
var storeTiddlers = store.getTiddlers();
for (var i = 0; i < storeTiddlers.length; i++) {
if (storeTiddlers[i].fields && storeTiddlers[i].fields["guid"]) {
if (filter.test) {
if (filter.test(storeTiddlers[i].fields["guid"])) {
title = storeTiddlers[i].title;
break;
}
} else if (storeTiddlers[i].fields["guid"].indexOf(filter) > -1) {
title = storeTiddlers[i].title;
break;
}
}
}
}
} else {
if (!tiddler) {
related = store.getTiddlers();
} else {
switch (collection) {
case "tag":
related = tiddler.tags;
break;
case "tagged":
related = store.getTaggedTiddlers(tiddler.title);
break;
default:
related = store.getTiddlers();
break;
}
}
for (var i = 0; i < (related ? related.length : 0); i++) {
var currentTitle = related[i].title ? related[i].title : related[i];
if (filter && jQuery.isFunction(filter.test)) {
if (filter.test(currentTitle)) {
title = currentTitle;
break;
}
} else if ((filter != null) && currentTitle.indexOf(filter) > -1) {
title = currentTitle;
break;
}
}
}
if (title !== null) {
var linkSuffix = ((lookaheadMatch[5] !== undefined) && (lookaheadMatch[5] !== null)) ? lookaheadMatch[5] : lookaheadMatch[3];
var text = ((lookaheadMatch[5] !== undefined) && (lookaheadMatch[5] !== null)) ? lookaheadMatch[3] : null;
wikify('[[' + (text ? text + '|' : '') + title + (linkSuffix ? linkSuffix : '') + ']]', w.output);
} else {
w.outputText(w.output, w.matchStart, this.lookaheadRegExp.lastIndex);
}
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
};
//}}}
/***
|Name|RelatedTiddlersPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
config.macros.relatedTiddlers = {
defaultbg: "#000000",
meetingbg: "#0000ff",
taskbg: "#00aa00",
disabledbg: "#cccccc",
determineRelatedTiddlers: function (tiddlerTitle, excludedTiddlers, visitedTiddlers) {
var isFirstCall = !visitedTiddlers;
visitedTiddlers = visitedTiddlers ? visitedTiddlers : [];
var relatedTiddlers = [];
if (tiddlerTitle && (visitedTiddlers.indexOf(tiddlerTitle) == -1)) {
visitedTiddlers[visitedTiddlers.length] = tiddlerTitle;
if (!excludedTiddlers || (excludedTiddlers.indexOf(tiddlerTitle) == -1)) {
var tiddler = store.getTiddler(tiddlerTitle);
if (tiddler) {
relatedTiddlers[relatedTiddlers.length] = tiddler;
var tiddlerTags = tiddler.tags;
for (var i = 0; tiddlerTags && (i < tiddlerTags.length); i++) {
relatedTiddlers = relatedTiddlers.concat(this.determineRelatedTiddlers(tiddlerTags[i], excludedTiddlers, visitedTiddlers));
}
} else if (isFirstCall) {
relatedTiddlers[relatedTiddlers.length] = tiddlerTitle;
}
if (tiddler || isFirstCall) {
var taggedTiddlers = store.getTaggedTiddlers(tiddlerTitle);
for (var i = 0; taggedTiddlers && (i < taggedTiddlers.length); i++) {
relatedTiddlers = relatedTiddlers.concat(this.determineRelatedTiddlers(taggedTiddlers[i].title, excludedTiddlers, visitedTiddlers));
}
}
}
}
return relatedTiddlers;
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var format = "graph";
var instance = {
btnName: "Related",
baseTiddlerTitle: tiddler ? tiddler.title : "",
expression: "",
excludedTiddlers: []
};
var parsedParams = paramString.parseParams("anon", null, true, false, false);
for (var i = 0; i < parsedParams.length; i++) {
if (parsedParams[i]) {
if (parsedParams[i].name == "anon") {
instance.btnName = parsedParams[i].value;
} else {
if (parsedParams[i].name == "tag") {
instance.expression = parsedParams[i].value;
} else if (parsedParams[i].name == "tiddler") {
instance.baseTiddlerTitle = (parsedParams[i].value == "all") ? "" : parsedParams[i].value;
} else if (parsedParams[i].name == "exclude") {
instance.excludedTiddlers[instance.excludedTiddlers.length] = parsedParams[i].value;
} else if (parsedParams[i].name == "format") {
format = parsedParams[i].value;
}
}
}
}
if (format == "list") {
config.macros.relatedTiddlers.handlerList(place, macroName, params, wikifier, paramString, tiddler, instance)
} else {
config.macros.relatedTiddlers.handlerGraph(place, macroName, params, wikifier, paramString, tiddler, instance)
}
},
handlerList: function (place, macroName, params, wikifier, paramString, tiddler, instance) {
var updateTiddlerTag = function (event) {
var tiddlerTitle = this.getAttribute("tiddlertitle");
var tiddlerTag = this.getAttribute("tiddlertag");
var tiddler = (tiddlerTitle && tiddlerTag) ? store.getTiddler(tiddlerTitle) : null;
if (tiddler && tiddler.tags) {
if (this.checked && (tiddler.tags.indexOf(tiddlerTag) == -1)) {
tiddler.tags[tiddler.tags.length] = tiddlerTag;
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddlerTitle, null, true);
story.refreshTiddler(tiddlerTag, null, true);
}
if (!this.checked && (tiddler.tags.indexOf(tiddlerTag) > -1)) {
tiddler.tags.splice(tiddler.tags.indexOf(tiddlerTag), 1);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
story.refreshTiddler(tiddlerTitle, null, true);
story.refreshTiddler(tiddlerTag, null, true);
}
}
};
var tiddlers = instance.baseTiddlerTitle ? config.macros.relatedTiddlers.determineRelatedTiddlers(instance.baseTiddlerTitle, instance.excludedTiddlers) : store.getTiddlers();
tiddlers = instance.expression ? store.getMatchingTiddlers(instance.expression, "title", tiddlers) : tiddlers;
if (tiddlers) {
tiddlers.sort(function (a, b) {
var aTest = null;
var bTest = null;
if (config.options.chkSearchByDate) {
aTest = a.modified ? a.modified : a.created;
bTest = b.modified ? b.modified : b.created;
} else {
aTest = a.title;
bTest = b.title;
}
if (!aTest && !bTest) {
return 0;
} else if (!aTest) {
return -1;
} else if (!bTest) {
return -1;
} else {
return aTest < bTest ? -1 : 1;
}
});
var containsTasks = false;
for (var i = 0; i < tiddlers.length; i++) {
if (!tiddler || (tiddler.title != tiddlers[i].title)) {
if (tiddlers[i].tags && (tiddlers[i].tags.indexOf("task") > -1)) {
containsTasks = true;
}
}
}
var createElement = function (parent, element) {
var e = createTiddlyElement(parent, element);
e.style.border = "none";
if (element == "table") {
e.style.margin = "0";
e.style.borderSpacing = "0";
e.style.borderCollapse = "collapse";
} else {
e.style.padding = "0";
if (element == "td") {
e.style.verticalAlign = "top";
}
}
return e;
};
var mainPlace = containsTasks ? createElement(place, "table") : place;
for (var i = 0; i < tiddlers.length; i++) {
if (!tiddler || (tiddler.title != tiddlers[i].title)) {
var tiddlerPlace = containsTasks ? createElement(mainPlace, "tr") : place;
if (tiddlers[i].tags && (tiddlers[i].tags.indexOf("task") > -1)) {
var checkboxPlace = createElement(tiddlerPlace, "td");
checkboxPlace.style.paddingRight = "0.25em";
var checkbox = createTiddlyElement(checkboxPlace, "input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = updateTiddlerTag;
checkbox.checked = tiddlers[i].tags.indexOf("done") > -1;
checkbox.setAttribute("tiddlertitle", tiddlers[i].title);
checkbox.setAttribute("tiddlertag", "done");
wikify("[[" + tiddlers[i].title + "]]", createElement(tiddlerPlace, "td"));
} else {
if (containsTasks) {
createElement(tiddlerPlace, "td").innerHTML = " ";
}
tiddlerPlace = containsTasks ? createElement(tiddlerPlace, "td") : place;
wikify("[[" + tiddlers[i].title + "]]" + (containsTasks ? "" : "\n"), tiddlerPlace);
}
}
}
}
},
handlerGraph: function (place, macroName, params, wikifier, paramString, tiddler, instance) {
var panel = config.macros.slider.createSlider(place, "config.macros.relatedTiddlers:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : ""), instance.btnName);
wikify("\n", panel);
var filterPanel = config.macros.slider.createSlider(panel, "config.macros.relatedTiddlers:" + (tiddler ? escape(tiddler.title) : "") + (paramString ? ":" + escape(paramString) : "") + ":" + "filterPanel", "filter");
var idSuffix = (Math.random() * 10000) | 0;
instance.drawGraph = function (graph, container) {
try {
setStylesheet("\
.relatedtiddlernode {\
stroke: #fff;\
stroke-width: 2px;\
}\
line.relatedtiddlerlink {\
stroke-width: 1px;\
stroke: #000;\
stroke-opacity: 0.6;\
}\
marker#relatedtiddlerarrow" + idSuffix + ", marker#relatedtiddlerarrowcenter" + idSuffix + " {\
stroke: #000;\
fill: #000;\
}", "RelatedTiddlersPlugin");
var graphSize = {};
graphSize.width = jQuery(jQuery(place).parent()).width();
graphSize.height = graphSize.width;
var radius = 5;
// http://stackoverflow.com/questions/9901565/charge-based-on-size-d3-force-layout
var k = Math.sqrt(graph.nodes.length / (graphSize.width * graphSize.height));
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-10 / k))
.force("x", d3.forceX(graphSize.width / 2).strength(100 * k)).force("y", d3.forceY(graphSize.height / 2).strength(100 * k))
.force("link", d3.forceLink().id(function (d) { return d.index; }).distance(radius * 10).strength(1))
.force("center", d3.forceCenter(graphSize.width / 2, graphSize.height / 2))
.force("collide",d3.forceCollide(function (d) { return d.r + (d.r / 2) }).iterations(16));
simulation.nodes(graph.nodes).force("link").links(graph.links);
instance.canvas = d3.select(container)
.append("svg")
.attr("width", graphSize.width)
.attr("height", graphSize.height);
jQuery(window).resize(function () {
graphSize.width = jQuery(jQuery(place).parent()).width();
graphSize.height = graphSize.width;
var k = Math.sqrt(graph.nodes.length / (graphSize.width * graphSize.height));
simulation.force("charge", d3.forceManyBody().strength(-10 / k))
.force("x", d3.forceX(graphSize.width / 2).strength(100 * k)).force("y", d3.forceY(graphSize.height / 2).strength(100 * k))
.force("center", d3.forceCenter(graphSize.width / 2, graphSize.height / 2));
instance.canvas.attr("width", graphSize.width).attr("height", graphSize.height);
simulation.alphaTarget(0.3).restart();
});
// http://www.jansipke.nl/creating-network-diagrams-with-d3-js
instance.canvas.append("svg:defs").selectAll("marker").data(["relatedtiddlerarrow" + idSuffix]).enter()
.append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 0 20 20")
.attr("refX", 22 + (radius * 3))
.attr("refY", 10)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 11)
.attr("markerHeight", 7)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M 0 0 L 20 10 L 0 20 z");
instance.canvas.append("svg:defs").selectAll("marker").data(["relatedtiddlerarrowcenter" + idSuffix]).enter()
.append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 0 20 20")
.attr("refX", 34 + (radius * 3))
.attr("refY", 10)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 11)
.attr("markerHeight", 7)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M 0 0 L 20 10 L 0 20 z");
var links = instance.canvas.append("g").selectAll("line.relatedtiddlerlink").data(graph.links).enter()
.append("line")
.attr("class", "relatedtiddlerlink")
.attr("marker-end", function (d) { return d.bidirectional ? "" : (d.target.centerNode ? "url(#relatedtiddlerarrowcenter" + idSuffix + ")" : "url(#relatedtiddlerarrow" + idSuffix + ")"); });
var icons = {
// http://stackoverflow.com/questions/5737975/circle-drawing-with-svgs-arc-path
circle: "M 0, 0 m -" + radius + ", 0 a " + radius + "," + radius + " 0 1,0 " + (radius * 2) + ",0 a " + radius + "," + radius + " 0 1,0 -" + (radius * 2) + ",0", // <circle cx="0" cy="0" r="radius" />
centercircle: "M 0, 0 m -" + (radius + 4) + ", 0 a " + (radius + 4) + "," + (radius + 4) + " 0 1,0 " + ((radius + 4) * 2) + ",0 a " + (radius + 4) + "," + (radius + 4) + " 0 1,0 -" + ((radius + 4) * 2) + ",0 M 0, 0 m -" + radius + ", 0 a " + radius + "," + radius + " 0 1,0 " + (radius * 2) + ",0 a " + radius + "," + radius + " 0 1,0 -" + (radius * 2) + ",0" // <circle cx="0" cy="0" r="radius + 4" /> <circle cx="0" cy="0" r="radius" />
};
var nodes = instance.canvas.selectAll(".relatedtiddlernode").data(graph.nodes).enter()
.append("g")
.attr("class", "relatedtiddlernode")
.on('mouseover', function (d) {
d.popupTimerId = config.macros.tiddlerPreviewPopup.showDelayed(
jQuery.event.fix(d3.event), d.title, d.title, 1000,
{
label: "X",
prompt: "",
onClick: function () {
instance.excludedTiddlers[instance.excludedTiddlers.length] = d.title;
instance.canvas.remove();
instance.oldTiddlerTags = instance.tiddlerTags;
instance.tiddlerTags = null;
instance.drawTiddlerGraph();
}
});
})
.on('mouseout', function (d) {
if (d.popupTimerId) {
window.clearTimeout(d.popupTimerId);
d.popupTimerId = null;
}
})
.call(d3.drag()
.on("start", function (d) {
if (!d3.event.active) {
simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
})
.on("drag", function (d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
})
.on("end", function (d) {
if (!d3.event.active) {
simulation.alphaTarget(0);
}
d.fx = null;
d.fy = null;
}));
nodes.append("path")
.attr("d", function (d) { return d.centerNode ? icons.centercircle : icons.circle; }) // http://bl.ocks.org/syntagmatic/4963194
.style("fill", function (d) {
var tiddlerTags = (d.tiddler && d.tiddler.tags) ? d.tiddler.tags : [];
var meeting = tiddlerTags.indexOf("meeting") > -1;
var task = tiddlerTags.indexOf("task") > -1;
//(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT attachment AND NOT Trash
var active = (!meeting || (tiddlerTags.indexOf("cancelled") == -1)) && (!task || (tiddlerTags.indexOf("done") == -1)) && (tiddlerTags.indexOf("attachment") == -1) && (tiddlerTags.indexOf("Trash") == -1);
return active ? (meeting ? config.macros.relatedTiddlers.meetingbg
: (task ? config.macros.relatedTiddlers.taskbg
: config.macros.relatedTiddlers.defaultbg))
: config.macros.relatedTiddlers.disabledbg;
});
simulation.on("tick", function () {
// http://mbostock.github.com/d3/talk/20110921/bounding.html
links.attr("x1", function (d) { return d.source.x = Math.max(radius, Math.min(graphSize.width - radius, d.source.x)); })
.attr("y1", function (d) { return d.source.y = Math.max(radius, Math.min(graphSize.height - radius, d.source.y)); })
.attr("x2", function (d) { return d.target.x = Math.max(radius, Math.min(graphSize.width - radius, d.target.x)); })
.attr("y2", function (d) { return d.target.y = Math.max(radius, Math.min(graphSize.height - radius, d.target.y)); });
nodes.attr("transform", function (d) { return "translate(" + (d.x = Math.max(radius, Math.min(graphSize.width - radius, d.x))) + "," + (d.y = Math.max(radius, Math.min(graphSize.height - radius, d.y))) + ")"; });
});
} catch (e) {}
};
var includeTiddler = function (tiddler) {
var tiddlerIncluded = false;
if (tiddler) {
var title = (tiddler.title !== undefined) ? tiddler.title : tiddler;
if (instance.excludedTiddlers.indexOf(title) == -1) {
tiddlerIncluded = true;
}
var tiddlerTags = instance.tiddlerTags ? instance.tiddlerTags : instance.oldTiddlerTags;
if (tiddlerIncluded && tiddler && tiddlerTags && instance.excludedTiddlerTags) {
tiddler = (tiddler.title !== undefined) ? tiddler : store.getTiddler(tiddler);
if (tiddler && tiddler.tags) {
var tags = tiddler.tags;
for (var i = 0; i < instance.excludedTiddlerTags.length; i++) {
if (tags.indexOf(instance.excludedTiddlerTags[i]) > -1) {
tiddlerIncluded = false;
break;
}
}
}
}
if (tiddlerIncluded && tiddler && tiddlerTags && instance.includedTiddlerTags) {
tiddler = (tiddler.title !== undefined) ? tiddler : store.getTiddler(tiddler);
if (tiddler && tiddler.tags) {
var isTagged = false;
var tagSelected = false;
var tags = tiddler.tags;
for (var i = 0; i < tags.length; i++) {
if (tiddlerTags.indexOf(tags[i]) > -1) {
isTagged = true;
if (instance.includedTiddlerTags.indexOf(tags[i]) > -1) {
tagSelected = true;
}
}
}
tiddlerIncluded = isTagged ? tagSelected : tiddlerIncluded;
}
}
}
return tiddlerIncluded;
};
instance.drawTiddlerGraph = function () {
var tiddlers = instance.baseTiddlerTitle ? config.macros.relatedTiddlers.determineRelatedTiddlers(instance.baseTiddlerTitle, instance.excludedTiddlers) : store.getTiddlers();
tiddlers = instance.expression ? store.getMatchingTiddlers(instance.expression, "title", tiddlers) : tiddlers;
if (tiddlers) {
if (!instance.tiddlerTags) {
var tiddlerTags = [];
for (var i = 0; i < tiddlers.length; i++) {
if (tiddlers[i].tags && tiddlers[i].tags.length) {
var tags = tiddlers[i].tags;
for (var j = 0; j < tags.length; j++) {
if (tiddlerTags.indexOf(tags[j]) == -1) {
tiddlerTags[tiddlerTags.length] = tags[j];
}
}
}
}
instance.tiddlerTags = [];
for (var i = 0; i < tiddlerTags.length; i++) {
if (config.macros.tagChooser.includeTag(tiddlerTags[i]) || (tiddlerTags[i] && (tiddlerTags[i].indexOf("Decrypt(") == 0))) {
instance.tiddlerTags[instance.tiddlerTags.length] = tiddlerTags[i];
}
}
instance.tiddlerTags.sort(function (a, b) { return a < b ? -1 : (a == b ? 0 : +1); });
if (instance.oldTiddlerTags && instance.includedTiddlerTags) {
var oldIncludedTiddlerTags = instance.includedTiddlerTags;
instance.includedTiddlerTags = [];
for (var i = 0; i < oldIncludedTiddlerTags.length; i++) {
if (instance.tiddlerTags.indexOf(oldIncludedTiddlerTags[i]) > -1) {
instance.includedTiddlerTags[instance.includedTiddlerTags.length] = oldIncludedTiddlerTags[i];
}
}
} else {
instance.includedTiddlerTags = instance.tiddlerTags.slice(0);
}
if (instance.oldTiddlerTags && instance.excludedTiddlerTags) {
var oldExcludedTiddlerTags = instance.excludedTiddlerTags;
instance.excludedTiddlerTags = [];
for (var i = 0; i < oldExcludedTiddlerTags.length; i++) {
if (instance.tiddlerTags.indexOf(oldExcludedTiddlerTags[i]) > -1) {
instance.excludedTiddlerTags[instance.excludedTiddlerTags.length] = oldExcludedTiddlerTags[i];
}
}
} else {
instance.excludedTiddlerTags = [];
}
instance.oldTiddlerTags = null;
jQuery(filterPanel).empty();
wikify("\n''Include tiddlers tagged with:''\n", filterPanel);
for (var i = 0; i < instance.tiddlerTags.length; i++) {
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = function (event) {
var tiddlerTag = this.getAttribute("tiddlertag");
if (tiddlerTag) {
if (this.checked && (instance.includedTiddlerTags.indexOf(tiddlerTag) == -1)) {
instance.includedTiddlerTags[instance.includedTiddlerTags.length] = tiddlerTag;
}
if (!this.checked && (instance.includedTiddlerTags.indexOf(tiddlerTag) > -1)) {
instance.includedTiddlerTags.splice(instance.includedTiddlerTags.indexOf(tiddlerTag), 1);
}
instance.canvas.remove();
instance.drawTiddlerGraph();
}
};
checkbox.checked = instance.includedTiddlerTags.indexOf(instance.tiddlerTags[i]) > -1;
checkbox.setAttribute("tiddlertag", instance.tiddlerTags[i]);
filterPanel.appendChild(checkbox);
wikify(instance.tiddlerTags[i] + "\n", filterPanel);
}
wikify("\n''Exclude tiddlers tagged with:''\n", filterPanel);
for (var i = 0; i < instance.tiddlerTags.length; i++) {
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.onclick = function (event) {
var tiddlerTag = this.getAttribute("tiddlertag");
if (tiddlerTag) {
if (this.checked && (instance.excludedTiddlerTags.indexOf(tiddlerTag) == -1)) {
instance.excludedTiddlerTags[instance.excludedTiddlerTags.length] = tiddlerTag;
}
if (!this.checked && (instance.excludedTiddlerTags.indexOf(tiddlerTag) > -1)) {
instance.excludedTiddlerTags.splice(instance.excludedTiddlerTags.indexOf(tiddlerTag), 1);
}
instance.canvas.remove();
instance.drawTiddlerGraph();
}
};
checkbox.checked = instance.excludedTiddlerTags.indexOf(instance.tiddlerTags[i]) > -1;
checkbox.setAttribute("tiddlertag", instance.tiddlerTags[i]);
filterPanel.appendChild(checkbox);
wikify(instance.tiddlerTags[i] + "\n", filterPanel);
}
}
var filteredTiddlers = [];
for (var i = 0; i < tiddlers.length; i++) {
if (includeTiddler(tiddlers[i])) {
filteredTiddlers[filteredTiddlers.length] = tiddlers[i];
}
}
tiddlers = filteredTiddlers;
var tiddlerNodes = {};
for (var i = 0; i < tiddlers.length; i++) {
var title = (tiddlers[i].title !== undefined) ? tiddlers[i].title : tiddlers[i];
tiddlerNodes[title] = {title: title, tiddler: (tiddlers[i].title !== undefined) ? tiddlers[i] : null, centerNode: title == instance.baseTiddlerTitle};
}
var tiddlerNodeArray = [];
for (var tiddlerNode in tiddlerNodes) {
tiddlerNodes[tiddlerNode].index = tiddlerNodeArray.length;
tiddlerNodeArray[tiddlerNodeArray.length] = tiddlerNodes[tiddlerNode];
}
var tiddlerLinks = {};
for (var i = 0; i < tiddlers.length; i++) {
var tiddlerTags = tiddlers[i].tags;
for (var j = 0; tiddlerTags && (j < tiddlerTags.length); j++) {
if (tiddlerTags[j]) {
if ((tiddlers[i].title != tiddlerTags[j]) && tiddlerNodes[tiddlerTags[j]] && (!tiddlerLinks[tiddlers[i].title] || !tiddlerLinks[tiddlers[i].title][tiddlerTags[j]])) {
var reverseLink = tiddlerLinks[tiddlerTags[j]] ? tiddlerLinks[tiddlerTags[j]][tiddlers[i].title] : null;
if (reverseLink) {
reverseLink.bidirectional = true;
} else {
tiddlerLinks[tiddlers[i].title] = tiddlerLinks[tiddlers[i].title] ? tiddlerLinks[tiddlers[i].title] : {}
tiddlerLinks[tiddlers[i].title][tiddlerTags[j]] = {source: tiddlerNodes[tiddlers[i].title], target: tiddlerNodes[tiddlerTags[j]]};
}
}
}
}
}
var tiddlerLinkArray = [];
for (var tiddlerLinkSource in tiddlerLinks) {
for (var tiddlerLinkTarget in tiddlerLinks[tiddlerLinkSource]) {
tiddlerLinkArray[tiddlerLinkArray.length] = tiddlerLinks[tiddlerLinkSource][tiddlerLinkTarget];
}
}
instance.drawGraph({nodes: tiddlerNodeArray, links: tiddlerLinkArray}, panel);
}
}
instance.drawTiddlerGraph();
}
}
//}}}
/***
|''Name:''|ReminderPlugin|
|''Version:''|2.3.10 (Jun 28, 2007)|
|''Source:''|http://remindermacros.tiddlyspot.com|
|''Author:''|Jeremy Sheeley(pop1280 [at] excite [dot] com)<<br>>Maintainer: simon.baird@gmail.com|
|''Licence:''|[[BSD open source license]]|
|''Macros:''|reminder, showreminders, displayTiddlersWithReminders, newReminder|
|''TiddlyWiki:''|2.0+|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
This plugin provides macros for tagging a date with a reminder. Use the {{{reminder}}} macro to do this. The {{{showReminders}}} and {{{displayTiddlersWithReminder}}} macros automatically search through all available tiddlers looking for upcoming reminders.
!Installation
* Create a new tiddler in your tiddlywiki titled ReminderPlugin and give it the {{{systemConfig}}} tag. The tag is important because it tells TW that this is executable code.
* Double click this tiddler, and copy all the text from the tiddler's body.
* Paste the text into the body of the new tiddler in your TW.
* Save and reload your TW.
* You can copy some examples into your TW as well. See [[Simple examples]], [[Holidays]], [[showReminders]] and [[Personal Reminders]]
!Syntax:
|>|See [[ReminderSyntax]] and [[showRemindersSyntax]]|
!Revision history
* v2.3.10 (Jun 28, 2007)
** Removed window.story = window backwards compatibility hacks since they were breaking TW 2.2
* v2.3.9 (Apr 26, 2007)
** allow bracketed list format in tags param lets you use tags with spaces
* v2.3.8 (Mar 9, 2006)
**Bug fix: A global variable had snuck in, which was killing FF 1.5.0.1
**Feature: You can now use TIDDLER and TIDDLERNAME in a regular reminder format
* v2.3.6 (Mar 1, 2006)
**Bug fix: Reminders for today weren't being matched sometimes.
**Feature: Solidified integration with DatePlugin and CalendarPlugin
**Feature: Recurring reminders will now return multiple hits in showReminders and the calendar.
**Feature: Added TIDDLERNAME to the replacements for showReminders format, for plugins that need the title without brackets.
* v2.3.5 (Feb 8, 2006)
**Bug fix: Sped up reminders lots. Added a caching mechanism for reminders that have already been matched.
* v2.3.4 (Feb 7, 2006)
**Bug fix: Cleaned up code to hopefully prevent the Firefox 1.5.0.1 crash that was causing lots of plugins
to crash Firefox. Thanks to http://www.jslint.com
* v2.3.3 (Feb 2, 2006)
**Feature: newReminder now has drop down lists instead of text boxes.
**Bug fix: A trailing space in a title would trigger an infinite loop.
**Bug fix: using tag:"birthday !reminder" would filter differently than tag:"!reminder birthday"
* v2.3.2 (Jan 21, 2006)
**Feature: newReminder macro, which will let you easily add a reminder to a tiddler. Thanks to Eric Shulman (http://www.elsdesign.com) for the code to do this.
** Bug fix: offsetday was not working sometimes
** Bug fix: when upgrading to 2.0, I included a bit to exclude tiddlers tagged with excludeSearch. I've reverted back to searching through all tiddlers
* v2.3.1 (Jan 7, 2006)
**Feature: 2.0 compatibility
**Feature AlanH sent some code to make sure that showReminders prints a message if no reminders are found.
* v2.3.0 (Jan 3, 2006)
** Bug Fix: Using "Last Sunday (-0)" as a offsetdayofweek wasn't working.
** Bug Fix: Daylight Savings time broke offset based reminders (for example year:2005 month:8 day:23 recurdays:7 would match Monday instead of Tuesday during DST.
!Code
***/
//{{{
//============================================================================
//============================================================================
// ReminderPlugin
//============================================================================
//============================================================================
version.extensions.ReminderPlugin = {major: 2, minor: 3, revision: 8, date: new Date(2006,3,9), source: "http://remindermacros.tiddlyspot.com/"};
//============================================================================
// Configuration
// Modify this section to change the defaults for
// leadtime and display strings
//============================================================================
config.macros.reminders = {};
config.macros["reminder"] = {};
config.macros["newReminder"] = {};
config.macros["showReminders"] = {};
config.macros["displayTiddlersWithReminders"] = {};
config.macros.reminders["defaultLeadTime"] = [0,6000];
config.macros.reminders["defaultReminderMessage"] = "DIFF: TITLE on DATE ANNIVERSARY";
config.macros.reminders["defaultShowReminderMessage"] = "DIFF: TITLE on DATE ANNIVERSARY -- TIDDLER";
config.macros.reminders["defaultAnniversaryMessage"] = "(DIFF)";
config.macros.reminders["untitledReminder"] = "Untitled Reminder";
config.macros.reminders["noReminderFound"] = "Couldn't find a match for TITLE in the next LEADTIMEUPPER days."
config.macros.reminders["todayString"] = "Today";
config.macros.reminders["tomorrowString"] = "Tomorrow";
config.macros.reminders["ndaysString"] = "DIFF days";
config.macros.reminders["emtpyShowRemindersString"] = "There are no upcoming events";
//============================================================================
// Code
// You should not need to edit anything
// below this. Make sure to edit this tiddler and copy
// the code from the text box, to make sure that
// tiddler rendering doesn't interfere with the copy
// and paste.
//============================================================================
//this object will hold the cache of reminders, so that we don't
//recompute the same reminder over again.
var reminderCache = {};
config.macros.showReminders.handler = function showReminders(place,macroName,params)
{
var now = new Date().getMidnight();
var paramHash = {};
var leadtime = [0,14];
paramHash = getParamsForReminder(params);
var bProvidedDate = (paramHash["year"] != null) ||
(paramHash["month"] != null) ||
(paramHash["day"] != null) ||
(paramHash["dayofweek"] != null);
if (paramHash["leadtime"] != null)
{
leadtime = paramHash["leadtime"];
if (bProvidedDate)
{
//If they've entered a day, we need to make
//sure to find it. We'll reset the
//leadtime a few lines down.
paramHash["leadtime"] = [-10000, 10000];
}
}
var matchedDate = now;
if (bProvidedDate)
{
var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash["leadtime"][0]);
var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash["leadtime"][1]);
matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);
}
var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash["tag"], paramHash["limit"]);
var elem = createTiddlyElement(place,"span",null,null, null);
var mess = "";
if (arr.length == 0)
{
mess += config.macros.reminders.emtpyShowRemindersString;
}
for (var j = 0; j < arr.length; j++)
{
if (paramHash["format"] != null)
{
arr[j]["params"]["format"] = paramHash["format"];
}
else
{
arr[j]["params"]["format"] = config.macros.reminders["defaultShowReminderMessage"];
}
mess += getReminderMessageForDisplay(arr[j]["diff"], arr[j]["params"], arr[j]["matchedDate"], arr[j]["tiddler"]);
mess += "\n";
}
wikify(mess, elem, null, null);
};
config.macros.displayTiddlersWithReminders.handler = function displayTiddlersWithReminders(place,macroName,params)
{
var now = new Date().getMidnight();
var paramHash = {};
var leadtime = [0,14];
paramHash = getParamsForReminder(params);
var bProvidedDate = (paramHash["year"] != null) ||
(paramHash["month"] != null) ||
(paramHash["day"] != null) ||
(paramHash["dayofweek"] != null);
if (paramHash["leadtime"] != null)
{
leadtime = paramHash["leadtime"];
if (bProvidedDate)
{
//If they've entered a day, we need to make
//sure to find it. We'll reset the leadtime
//a few lines down.
paramHash["leadtime"] = [-10000,10000];
}
}
var matchedDate = now;
if (bProvidedDate)
{
var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash["leadtime"][0]);
var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash["leadtime"][1]);
matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);
}
var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash["tag"], paramHash["limit"]);
for (var j = 0; j < arr.length; j++)
{
displayTiddler(null, arr[j]["tiddler"], 0, null, false, false, false);
}
};
config.macros.reminder.handler = function reminder(place,macroName,params)
{
var dateHash = getParamsForReminder(params);
if (dateHash["hidden"] != null)
{
return;
}
var leadTime = dateHash["leadtime"];
if (leadTime == null)
{
leadTime = config.macros.reminders["defaultLeadTime"];
}
var leadTimeLowerBound = new Date().getMidnight().addDays(leadTime[0]);
var leadTimeUpperBound = new Date().getMidnight().addDays(leadTime[1]);
var matchedDate = findDateForReminder(dateHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);
if (!store.getTiddler)
{
store.getTiddler=function(title) {return this.tiddlers[title];};
}
var title = window.story.findContainingTiddler(place).id.substr(7);
if (matchedDate != null)
{
var diff = matchedDate.getDifferenceInDays(new Date().getMidnight());
var elem = createTiddlyElement(place,"span",null,null, null);
var mess = getReminderMessageForDisplay(diff, dateHash, matchedDate, title);
wikify(mess, elem, null, null);
}
else
{
createTiddlyElement(place,"span",null,null, config.macros.reminders["noReminderFound"].replace("TITLE", dateHash["title"]).replace("LEADTIMEUPPER", leadTime[1]).replace("LEADTIMELOWER", leadTime[0]).replace("TIDDLERNAME", title).replace("TIDDLER", "[[" + title + "]]") );
}
};
config.macros.newReminder.handler = function newReminder(place,macroName,params)
{
var today=new Date().getMidnight();
var formstring = '<html><form>Year: <select name="year"><option value="">Every year</option>';
for (var i = 0; i < 5; i++)
{
formstring += '<option' + ((i == 0) ? ' selected' : '') + ' value="' + (today.getFullYear() +i) + '">' + (today.getFullYear() + i) + '</option>';
}
formstring += '</select> Month:<select name="month"><option value="">Every month</option>';
for (i = 0; i < 12; i++)
{
formstring += '<option' + ((i == today.getMonth()) ? ' selected' : '') + ' value="' + (i+1) + '">' + config.messages.dates.months[i] + '</option>';
}
formstring += '</select> Day:<select name="day"><option value="">Every day</option>';
for (i = 1; i < 32; i++)
{
formstring += '<option' + ((i == (today.getDate() )) ? ' selected' : '') + ' value="' + i + '">' + i + '</option>';
}
formstring += '</select> Reminder Title:<input type="text" size="40" name="title" value="please enter a title" onfocus="this.select();"><input type="button" value="ok" onclick="addReminderToTiddler(this.form)"></form></html>';
var panel = config.macros.slider.createSlider(place,null,"New Reminder","Open a form to add a new reminder to this tiddler");
wikify(formstring ,panel,null,store.getTiddler(params[1]));
};
// onclick: process input and insert reminder at 'marker'
window.addReminderToTiddler = function(form) {
if (!store.getTiddler)
{
store.getTiddler=function(title) {return this.tiddlers[title];};
}
var title = window.story.findContainingTiddler(form).id.substr(7);
var tiddler=store.getTiddler(title);
var txt='\n<<reminder ';
if (form.year.value != "")
txt += 'year:'+form.year.value + ' ';
if (form.month.value != "")
txt += 'month:'+form.month.value + ' ';
if (form.day.value != "")
txt += 'day:'+form.day.value + ' ';
txt += 'title:"'+form.title.value+'" ';
txt +='>>';
tiddler.set(null,tiddler.text + txt);
window.story.refreshTiddler(title,1,true);
store.setDirty(true);
};
function hasTag(tiddlerTags, tagFilters)
{
//Make sure we respond well to empty tiddlerTaglists or tagFilterlists
if (tagFilters.length==0 || tiddlerTags.length==0)
{
return true;
}
var bHasTag = false;
/*bNoPos says: "'till now there has been no check using a positive filter"
Imagine a filterlist consisting of 1 negative filter:
If the filter isn't matched, we want hasTag to be true.
Yet bHasTag is still false ('cause only positive filters cause bHasTag to change)
If no positive filters are present bNoPos is true, and no negative filters are matched so we have not returned false
Thus: hasTag returns true.
If at any time a positive filter is encountered, we want at least one of the tags to match it, so we turn bNoPos to false, which
means bHasTag must be true for hasTag to return true*/
var bNoPos=true;
for (var t3 = 0; t3 < tagFilters.length; t3++)
{
for(var t2=0; t2<tiddlerTags.length; t2++)
{
if (tagFilters[t3].length > 1 && tagFilters[t3].charAt(0) == '!')
{
if (tiddlerTags[t2] == tagFilters[t3].substring(1))
{
//If at any time a negative filter is matched, we return false
return false;
}
}
else
{
if (bNoPos)
{
//We encountered the first positive filter
bNoPos=false;
}
if (tiddlerTags[t2] == tagFilters[t3])
{
//A positive filter is matched. As long as no negative filter is matched, hasTag will return true
bHasTag=true;
}
}
}
}
return (bNoPos || bHasTag);
};
//This function searches all tiddlers for the reminder //macro. It is intended that other plugins (like //calendar) will use this function to query for
//upcoming reminders.
//The arguments to this function filter out reminders //based on when they will fire.
//
//ARGUMENTS:
//baseDate is the date that is used as "now".
//leadtime is a two element int array, with leadtime[0]
// as the lower bound and leadtime[1] as the
// upper bound. A reasonable default is [0,14]
//tags is a space-separated list of tags to use to filter
// tiddlers. If a tag name begins with an !, then
// only tiddlers which do not have that tag will
// be considered. For example "examples holidays"
// will search for reminders in any tiddlers that
// are tagged with examples or holidays and
// "!examples !holidays" will search for reminders
// in any tiddlers that are not tagged with
// examples or holidays. Pass in null to search
// all tiddlers.
//limit. If limit is null, individual reminders can
// override the leadtime specified earlier.
// Pass in 1 in order to override that behavior.
window.findTiddlersWithReminders = function findTiddlersWithReminders(baseDate, leadtime, tags, limit)
{
//function(searchRegExp,sortField,excludeTag)
// var macroPattern = "<<([^>\\]+)(?:\\*)([^>]*)>>";
var macroPattern = "<<(reminder)(.*)>>";
var macroRegExp = new RegExp(macroPattern,"mg");
var matches = store.search(macroRegExp,"title","");
var arr = [];
var tagsArray = null;
if (tags != null)
{
// tagsArray = tags.split(" ");
tagsArray = tags.readBracketedList(); // allows tags with spaces. thanks Robin Summerhill, 4-Oct-06.
}
for(var t=matches.length-1; t>=0; t--)
{
if (tagsArray != null)
{
//If they specified tags to filter on, and this tiddler doesn't
//match, skip it entirely.
if ( ! hasTag(matches[t].tags, tagsArray))
{
continue;
}
}
var targetText = matches[t].text;
do {
// Get the next formatting match
var formatMatch = macroRegExp.exec(targetText);
if(formatMatch && formatMatch[1] != null && formatMatch[1].toLowerCase() == "reminder")
{
//Find the matching date.
var params = formatMatch[2] != null ? formatMatch[2].readMacroParams() : {};
var dateHash = getParamsForReminder(params);
if (limit != null || dateHash["leadtime"] == null)
{
if (leadtime == null)
dateHash["leadtime"] = leadtime;
else
{
dateHash["leadtime"] = [];
dateHash["leadtime"][0] = leadtime[0];
dateHash["leadtime"][1] = leadtime[1];
}
}
if (dateHash["leadtime"] == null)
dateHash["leadtime"] = config.macros.reminders["defaultLeadTime"];
var leadTimeLowerBound = baseDate.addDays(dateHash["leadtime"][0]);
var leadTimeUpperBound = baseDate.addDays(dateHash["leadtime"][1]);
var matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);
while (matchedDate != null)
{
var hash = {};
hash["diff"] = matchedDate.getDifferenceInDays(baseDate);
hash["matchedDate"] = new Date(matchedDate.getFullYear(), matchedDate.getMonth(), matchedDate.getDate(), 0, 0);
hash["params"] = cloneParams(dateHash);
hash["tiddler"] = matches[t].title;
hash["tags"] = matches[t].tags;
arr.pushUnique(hash);
if (dateHash["recurdays"] != null || (dateHash["year"] == null))
{
leadTimeLowerBound = leadTimeLowerBound.addDays(matchedDate.getDifferenceInDays(leadTimeLowerBound)+ 1);
matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);
}
else matchedDate = null;
}
}
}while(formatMatch);
}
if(arr.length > 1) //Sort the array by number of days remaining.
{
arr.sort(function (a,b) {if(a["diff"] == b["diff"]) {return(0);} else {return (a["diff"] < b["diff"]) ? -1 : +1; } });
}
return arr;
};
//This function takes the reminder macro parameters and
//generates the string that is used for display.
//This function is not intended to be called by
//other plugins.
window.getReminderMessageForDisplay= function getReminderMessageForDisplay(diff, params, matchedDate, tiddlerTitle)
{
var anniversaryString = "";
var reminderTitle = params["title"];
if (reminderTitle == null)
{
reminderTitle = config.macros.reminders["untitledReminder"];
}
if (params["firstyear"] != null)
{
anniversaryString = config.macros.reminders["defaultAnniversaryMessage"].replace("DIFF", (matchedDate.getFullYear() - params["firstyear"]));
}
var mess = "";
var diffString = "";
if (diff == 0)
{
diffString = config.macros.reminders["todayString"];
}
else if (diff == 1)
{
diffString = config.macros.reminders["tomorrowString"];
}
else
{
diffString = config.macros.reminders["ndaysString"].replace("DIFF", diff);
}
var format = config.macros.reminders["defaultReminderMessage"];
if (params["format"] != null)
{
format = params["format"];
}
mess = format;
//HACK! -- Avoid replacing DD in TIDDLER with the date
mess = mess.replace(/TIDDLER/g, "TIDELER");
mess = matchedDate.formatStringDateOnly(mess);
mess = mess.replace(/TIDELER/g, "TIDDLER");
if (tiddlerTitle != null)
{
mess = mess.replace(/TIDDLERNAME/g, tiddlerTitle);
mess = mess.replace(/TIDDLER/g, "[[" + tiddlerTitle + "]]");
}
mess = mess.replace("DIFF", diffString).replace("TITLE", reminderTitle).replace("DATE", matchedDate.formatString("DDD MMM DD, YYYY")).replace("ANNIVERSARY", anniversaryString);
return mess;
};
// Parse out the macro parameters into a hashtable. This
// handles the arguments for reminder, showReminders and
// displayTiddlersWithReminders.
window.getParamsForReminder = function getParamsForReminder(params)
{
var dateHash = {};
var type = "";
var num = 0;
var title = "";
for(var t=0; t<params.length; t++)
{
var split = params[t].split(":");
type = split[0].toLowerCase();
var value = split[1];
for (var i=2; i < split.length; i++)
{
value += ":" + split[i];
}
if (type == "nolinks" || type == "limit" || type == "hidden")
{
num = 1;
}
else if (type == "leadtime")
{
var leads = value.split("...");
if (leads.length == 1)
{
leads[1]= leads[0];
leads[0] = 0;
}
leads[0] = parseInt(leads[0], 10);
leads[1] = parseInt(leads[1], 10);
num = leads;
}
else if (type == "offsetdayofweek")
{
if (value.substr(0,1) == "-")
{
dateHash["negativeOffsetDayOfWeek"] = 1;
value = value.substr(1);
}
num = parseInt(value, 10);
}
else if (type != "title" && type != "tag" && type != "format")
{
num = parseInt(value, 10);
}
else
{
title = value;
t++;
while (title.substr(0,1) == '"' && title.substr(title.length - 1,1) != '"' && params[t] != undefined)
{
title += " " + params[t++];
}
//Trim off the leading and trailing quotes
if (title.substr(0,1) == "\"" && title.substr(title.length - 1,1)== "\"")
{
title = title.substr(1, title.length - 2);
t--;
}
num = title;
}
dateHash[type] = num;
}
//date is synonymous with day
if (dateHash["day"] == null)
{
dateHash["day"] = dateHash["date"];
}
return dateHash;
};
//This function finds the date specified in the reminder
//parameters. It will return null if no match can be
//found. This function is not intended to be used by
//other plugins.
window.findDateForReminder= function findDateForReminder( dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound)
{
if (baseDate == null)
{
baseDate = new Date().getMidnight();
}
var hashKey = baseDate.convertToYYYYMMDDHHMM();
for (var k in dateHash)
{
hashKey += "," + k + "|" + dateHash[k];
}
hashKey += "," + leadTimeLowerBound.convertToYYYYMMDDHHMM();
hashKey += "," + leadTimeUpperBound.convertToYYYYMMDDHHMM();
if (reminderCache[hashKey] == null)
{
//If we don't find a match in this run, then we will
//cache that the reminder can't be matched.
reminderCache[hashKey] = false;
}
else if (reminderCache[hashKey] == false)
{
//We've already tried this date and failed
return null;
}
else
{
return reminderCache[hashKey];
}
var bOffsetSpecified = dateHash["offsetyear"] != null ||
dateHash["offsetmonth"] != null ||
dateHash["offsetday"] != null ||
dateHash["offsetdayofweek"] != null ||
dateHash["recurdays"] != null;
// If we are matching the base date for a dayofweek offset, look for the base date a
//little further back.
var tmp1leadTimeLowerBound = leadTimeLowerBound;
if ( dateHash["offsetdayofweek"] != null)
{
tmp1leadTimeLowerBound = leadTimeLowerBound.addDays(-6);
}
var matchedDate = baseDate.findMatch(dateHash, tmp1leadTimeLowerBound, leadTimeUpperBound);
if (matchedDate != null)
{
var newMatchedDate = matchedDate;
if (dateHash["recurdays"] != null)
{
while (newMatchedDate.getTime() < leadTimeLowerBound.getTime())
{
newMatchedDate = newMatchedDate.addDays(dateHash["recurdays"]);
}
}
else if (dateHash["offsetyear"] != null ||
dateHash["offsetmonth"] != null ||
dateHash["offsetday"] != null ||
dateHash["offsetdayofweek"] != null)
{
var tmpdateHash = cloneParams(dateHash);
tmpdateHash["year"] = dateHash["offsetyear"];
tmpdateHash["month"] = dateHash["offsetmonth"];
tmpdateHash["day"] = dateHash["offsetday"];
tmpdateHash["dayofweek"] = dateHash["offsetdayofweek"];
var tmpleadTimeLowerBound = leadTimeLowerBound;
var tmpleadTimeUpperBound = leadTimeUpperBound;
if (tmpdateHash["offsetdayofweek"] != null)
{
if (tmpdateHash["negativeOffsetDayOfWeek"] == 1)
{
tmpleadTimeLowerBound = matchedDate.addDays(-6);
tmpleadTimeUpperBound = matchedDate;
}
else
{
tmpleadTimeLowerBound = matchedDate;
tmpleadTimeUpperBound = matchedDate.addDays(6);
}
}
newMatchedDate = matchedDate.findMatch(tmpdateHash, tmpleadTimeLowerBound, tmpleadTimeUpperBound);
//The offset couldn't be matched. return null.
if (newMatchedDate == null)
{
return null;
}
}
if (newMatchedDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))
{
reminderCache[hashKey] = newMatchedDate;
return newMatchedDate;
}
}
return null;
};
//This does much the same job as findDateForReminder, but
//this one doesn't deal with offsets or recurring
//reminders.
Date.prototype.findMatch = function findMatch(dateHash, leadTimeLowerBound, leadTimeUpperBound)
{
var bSpecifiedYear = (dateHash["year"] != null);
var bSpecifiedMonth = (dateHash["month"] != null);
var bSpecifiedDay = (dateHash["day"] != null);
var bSpecifiedDayOfWeek = (dateHash["dayofweek"] != null);
if (bSpecifiedYear && bSpecifiedMonth && bSpecifiedDay)
{
return new Date(dateHash["year"], dateHash["month"]-1, dateHash["day"], 0, 0);
}
var bMatchedYear = !bSpecifiedYear;
var bMatchedMonth = !bSpecifiedMonth;
var bMatchedDay = !bSpecifiedDay;
var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;
if (bSpecifiedDay && bSpecifiedMonth && !bSpecifiedYear && !bSpecifiedDayOfWeek)
{
//Shortcut -- First try this year. If it's too small, try next year.
var tmpMidnight = this.getMidnight();
var tmpDate = new Date(this.getFullYear(), dateHash["month"]-1, dateHash["day"], 0,0);
if (tmpDate.getTime() < leadTimeLowerBound.getTime())
{
tmpDate = new Date((this.getFullYear() + 1), dateHash["month"]-1, dateHash["day"], 0,0);
}
if ( tmpDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))
{
return tmpDate;
}
else
{
return null;
}
}
var newDate = leadTimeLowerBound;
while (newDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))
{
var tmp = testDate(newDate, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek);
if (tmp != null)
return tmp;
newDate = newDate.addDays(1);
}
};
function testDate(testMe, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek)
{
var bMatchedYear = !bSpecifiedYear;
var bMatchedMonth = !bSpecifiedMonth;
var bMatchedDay = !bSpecifiedDay;
var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;
if (bSpecifiedYear)
{
bMatchedYear = (dateHash["year"] == testMe.getFullYear());
}
if (bSpecifiedMonth)
{
bMatchedMonth = ((dateHash["month"] - 1) == testMe.getMonth() );
}
if (bSpecifiedDay)
{
bMatchedDay = (dateHash["day"] == testMe.getDate());
}
if (bSpecifiedDayOfWeek)
{
bMatchedDayOfWeek = (dateHash["dayofweek"] == testMe.getDay());
}
if (bMatchedYear && bMatchedMonth && bMatchedDay && bMatchedDayOfWeek)
{
return testMe;
}
};
//Returns true if the date is in between two given dates
Date.prototype.isBetween = function isBetween(lowerBound, upperBound)
{
return (this.getTime() >= lowerBound.getTime() && this.getTime() <= upperBound.getTime());
}
//Return a new date, with the time set to midnight (0000)
Date.prototype.getMidnight = function getMidnight()
{
return new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0);
};
// Add the specified number of days to a date.
Date.prototype.addDays = function addDays(numberOfDays)
{
return new Date(this.getFullYear(), this.getMonth(), this.getDate() + numberOfDays, 0, 0);
};
//Return the number of days between two dates.
Date.prototype.getDifferenceInDays = function getDifferenceInDays(otherDate)
{
//I have to do it this way, because this way ignores daylight savings
var tmpDate = this.addDays(0);
if (this.getTime() > otherDate.getTime())
{
var i = 0;
for (i = 0; tmpDate.getTime() > otherDate.getTime(); i++)
{
tmpDate = tmpDate.addDays(-1);
}
return i;
}
else
{
var i = 0;
for (i = 0; tmpDate.getTime() < otherDate.getTime(); i++)
{
tmpDate = tmpDate.addDays(1);
}
return i * -1;
}
return 0;
};
function cloneParams(what) {
var tmp = {};
for (var i in what) {
tmp[i] = what[i];
}
return tmp;
}
// Substitute date components into a string
Date.prototype.formatStringDateOnly = function formatStringDateOnly(template)
{
template = template.replace("YYYY",this.getFullYear());
template = template.replace("YY",String.zeroPad(this.getFullYear()-2000,2));
template = template.replace("MMM",config.messages.dates.months[this.getMonth()]);
template = template.replace("0MM",String.zeroPad(this.getMonth()+1,2));
template = template.replace("MM",this.getMonth()+1);
template = template.replace("DDD",config.messages.dates.days[this.getDay()]);
template = template.replace("0DD",String.zeroPad(this.getDate(),2));
template = template.replace("DD",this.getDate());
return template;
};
//}}}
/***
|Name|ReminderMacrosOverride|
|License|[[TW Notes License]]|
|Requires|[[ReminderMacros]]|
!!!!!Code
***/
//{{{
if (config.options.chkEnhanceReminderPerformance === undefined) {
config.options.chkEnhanceReminderPerformance = false;
}
if (config.options.txtUnfilteredReminderTiddlers === undefined) {
config.options.txtUnfilteredReminderTiddlers = "[[iCalendarPlugin Reminders]]";
}
if (config.options.txtReminderPublishExcludeTags === undefined) {
config.options.txtReminderPublishExcludeTags = "doNotPublish";
}
if (config.options.txtReminderTiddlerTagDefinitions === undefined) {
config.options.txtReminderTiddlerTagDefinitions = "reminderTiddlerTags";
}
merge(config.optionsDesc, {
chkEnhanceReminderPerformance: "Use ReminderMacrosOverride performance enhancements which pre-filter reminder tiddlers according to their tags",
txtUnfilteredReminderTiddlers: "The bracketed list of tiddlers that are not pre-filtered when applying ReminderMacrosOverride performance enhancements",
txtReminderPublishExcludeTags: "The bracketed list of tags of reminders that are not to be included when publishing events",
txtReminderTiddlerTagDefinitions: 'The tiddler containing additional reminder tiddler tags defined using the following JSON format: {"tiddler-name": ["tag1", "tag2", ...]}'
});
//}}}
//{{{
config.macros.reminders["emtpyShowRemindersString"] = "";
//}}}
//{{{
config.macros.reminder.handler = function reminder(place, macroName, params, wikifier, paramString, tiddler)
{
params = config.macros.reminder.parseParams(paramString);
if (!store.getTiddler)
{
store.getTiddler=function(title) {return this.tiddlers[title];};
}
var title = (tiddler && tiddler.title) ? tiddler.title : TW21Loader&&TW21Loader.prototype.getTitle?TW21Loader.prototype.getTitle(store, window.story.findContainingTiddler(place)):window.story.findContainingTiddler(place).id.substr(7);
params["tiddlerreference"] = tiddler ? tiddler : store.getTiddler(title);
var dateHash = getParamsForReminder(params);
if (dateHash["hidden"] != null)
{
return;
}
var leadTime = dateHash["leadtime"];
if (leadTime == null)
{
leadTime = config.macros.reminders["defaultLeadTime"];
}
var leadTimeLowerBound = new Date().getMidnight().addDays(leadTime[0]);
var leadTimeUpperBound = new Date().getMidnight().addDays(leadTime[1]);
var matchedDate = findDateForReminder(dateHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);
if (matchedDate != null)
{
var diff = matchedDate.getDifferenceInDays(new Date().getMidnight());
var elem = createTiddlyElement(place,"span",null,null, null);
var mess = getReminderMessageForDisplay(diff, dateHash, matchedDate, title);
wikify(mess, elem, null, null);
}
else
{
var currentFormat = params["format"];
var customDisplayOptions = config.macros.reminders.evalMessageFunctions(diff, dateHash, null, title);
params["format"] = currentFormat;
var reminderTitle = dateHash["title"] ? wikifyPlainText(dateHash["title"], 0, null) : "";
var mess = ((customDisplayOptions && customDisplayOptions["noReminderFound"]) ? customDisplayOptions["noReminderFound"] : config.macros.reminders["noReminderFound"]).replace("TITLE", reminderTitle).replace("LEADTIMEUPPER", leadTime[1]).replace("LEADTIMELOWER", leadTime[0]).replace("TIDDLERNAME", title).replace("TIDDLER", "[[" + title + "]]");
createTiddlyElement(place,"span",null,null, wikifyPlainText(mess, 0, null));
}
};
window.addReminderToTiddler = function(form) {
if (!store.getTiddler)
{
store.getTiddler=function(title) {return this.tiddlers[title];};
}
var title = TW21Loader&&TW21Loader.prototype.getTitle?TW21Loader.prototype.getTitle(store, window.story.findContainingTiddler(form)):window.story.findContainingTiddler(form).id.substr(7);
var tiddler=store.getTiddler(title);
var txt='\n<<reminder ';
if (form.year.value != "")
txt += 'year:'+form.year.value + ' ';
if (form.month.value != "")
txt += 'month:'+form.month.value + ' ';
if (form.day.value != "")
txt += 'day:'+form.day.value + ' ';
txt += 'title:"'+form.title.value+'" ';
txt +='>>';
tiddler.set(null,tiddler.text + txt);
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
window.story.refreshTiddler(title,1,true);
store.setDirty(true);
};
config.macros.reminder.reminderFunctions = [];
config.macros.reminder.findTiddlersWithReminders_base = window.findTiddlersWithReminders;
window.findTiddlersWithReminders = function findTiddlersWithReminders(baseDate, leadtime, tags, limit, reminderTiddlerList) {
var searchOptions = {
chkSearchTitles: false,
chkSearchText: true,
chkSearchTags: false,
chkSearchFields: false,
chkSearchTitlesFirst: false,
chkSearchList: false,
chkSearchHighlight: false,
chkSearchListTiddler: false,
chkSearchByDate: false,
chkSearchShadows: true,
chkSearchOpenTiddlers: false,
chkSearchExcludeTags: false
};
var currentSearchOptions = {};
for (var id in searchOptions) {
currentSearchOptions[id] = config.options[id];
config.options[id] = searchOptions[id];
}
var arr = null;
try {
if (config.options.chkEnhanceReminderPerformance && tags && store.getMatchingTiddlers) {
if (!reminderTiddlerList) {
reminderTiddlerList = store.getTiddlers();
for (var t in config.shadowTiddlers) {
if (!store.tiddlerExists(t)) {
reminderTiddlerList.push((new Tiddler()).assign(t, config.shadowTiddlers[t]));
}
}
}
reminderTiddlerList = store.getMatchingTiddlers(tags, null, reminderTiddlerList);
var reminderTiddlers = config.options.txtUnfilteredReminderTiddlers ? config.options.txtUnfilteredReminderTiddlers.readBracketedList() : null;
if (reminderTiddlers) {
for (var i = 0; i < reminderTiddlerList.length; i++) {
var titleIndex = reminderTiddlerList[i].title ? reminderTiddlers.indexOf(reminderTiddlerList[i].title) : -1;
if (titleIndex > -1) {
reminderTiddlers.splice(titleIndex, 1);
if (!reminderTiddlers.length) {
break;
}
}
}
if (reminderTiddlers.length) {
for (var i = 0; i < reminderTiddlers.length; i++) {
var reminderTiddler = store.getTiddler(reminderTiddlers[i]);
if (reminderTiddler) {
reminderTiddlerList[reminderTiddlerList.length] = reminderTiddler;
}
}
}
}
}
arr = config.macros.reminder.findTiddlersWithReminders_base(baseDate, leadtime, tags, limit, reminderTiddlerList);
} finally {
for (var id in searchOptions) {
config.options[id] = currentSearchOptions[id];
}
}
if (config.macros.reminder.reminderFunctions.length > 0) {
for (var i = 0; i < config.macros.reminder.reminderFunctions.length; i++) {
var reminderArray = config.macros.reminder.reminderFunctions[i](baseDate, leadtime, tags, limit);
if (reminderArray && reminderArray.length && (reminderArray.length > 0)) {
arr = arr.concat(reminderArray);
}
}
}
if (tags && store.getMatchingTiddlers) {
for (var r = 0; r < arr.length; r++) {
var hash = arr[r];
var reminderTags = [];
var titles = [];
if (hash["params"]["title"]) {
window.getReminderMessageForDisplay(hash["diff"], cloneParams(hash["params"]), hash["matchedDate"], hash["tiddler"]).replace(/.*<<(goToTiddler|newTiddler).*title:\{\{(['|"](?:(?!['|"]\}\}).)*['|"])\}\}.*>>.*/, function () {
titles.push(Array.prototype.slice.call(arguments, 2, 3));
});
if (titles.length > 0) {
var tagTiddlerTitle = eval("" + titles[0]);
if (config.options.txtReminderTiddlerTagDefinitions && store.tiddlerExists(config.options.txtReminderTiddlerTagDefinitions)
&& (config.macros.reminder.reminderTiddlerTagDefinitionsText !== store.getTiddlerText(config.options.txtReminderTiddlerTagDefinitions))) {
try {
config.macros.reminder.reminderTiddlerTagDefinitionsText = store.getTiddlerText(config.options.txtReminderTiddlerTagDefinitions);
eval("config.macros.reminder.reminderTiddlerTagDefinitions = " + config.macros.reminder.reminderTiddlerTagDefinitionsText);
} catch (e) {
console.log(e);
}
}
if (config.macros.reminder.reminderTiddlerTagDefinitions && config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle] && config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle].length) {
var reminderTiddlerTagDefinitions = config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle].slice();
for (var i = 0; i < reminderTags.length; i++) {
reminderTiddlerTagDefinitions.pushUnique(reminderTags[i]);
}
reminderTags = reminderTiddlerTagDefinitions;
}
var tagTiddler = store.getTiddler(tagTiddlerTitle);
if (tagTiddler) {
for (var i = 0; i < tagTiddler.tags.length; i++) {
reminderTags.pushUnique(tagTiddler.tags[i]);
}
}
}
}
if (hash["params"]["tag"]) {
var paramTags = hash["params"]["tag"].readBracketedList();
for (var i = 0; i < paramTags.length; i++) {
reminderTags.pushUnique(paramTags[i]);
}
}
if (hash["tags"]) {
for (var i = 0; i < hash["tags"].length; i++) {
reminderTags.pushUnique(hash["tags"][i]);
}
}
hash["tags"] = reminderTags;
hash["isTagged"] = function (tag) {
return this.tags.indexOf(tag) != -1 || (config.macros.matchTags.isTagged && config.macros.matchTags.isTagged(this.tags, tag));
};
}
arr = store.getMatchingTiddlers(tags, null, arr);
}
arr.sort(function(a, b) {
if (a["diff"] == b["diff"]) {
if (!a["params"]) {
return -1;
} else if (!b["params"]) {
return 1;
}
if (a["params"]["title"] == b["params"]["title"]) {
return 0;
} else if (!a["params"]["title"]) {
return -1;
} else if (!b["params"]["title"]) {
return 1
}
if (!a["params"]["wikifiedtitletext"]) {
var wikifier = new Wikifier(a["params"]["title"], formatter, null, null);
var e = createTiddlyElement(document.body, "div");
var jqe = jQuery(e);
jqe.css("display", "none");
wikifier.subWikify(e);
a["params"]["wikifiedtitletext"] = jqe.text();
removeNode(e);
}
if (!b["params"]["wikifiedtitletext"]) {
var wikifier = new Wikifier(b["params"]["title"], formatter, null, null);
var e = createTiddlyElement(document.body, "div");
var jqe = jQuery(e);
jqe.css("display", "none");
wikifier.subWikify(e);
b["params"]["wikifiedtitletext"] = jqe.text();
removeNode(e);
}
return a["params"]["wikifiedtitletext"].toLocaleLowerCase().localeCompare(b["params"]["wikifiedtitletext"].toLocaleLowerCase());
} else {
return (a["diff"] < b["diff"]) ? -1 : 1;
}
});
return arr;
};
config.macros.reminders.evalMessageFunctions = function (diff, params, matchedDate, tiddlerTitle) {
if (params["messagefunctions"] != null) {
for (var i = 0; i < params["messagefunctions"].length; i++) {
var functionDefinition = params["messagefunctions"][i];
var parametersStart = functionDefinition.lastIndexOf("(");
var parametersEnd = functionDefinition.lastIndexOf(")");
if ((parametersStart > -1) && (parametersEnd > -1) && (parametersEnd > parametersStart)) {
eval(functionDefinition.substring(0, parametersEnd) + ((functionDefinition.substring(parametersStart + 1, parametersEnd).trim().length > 0) ? "," : "") + "diff, params, matchedDate, tiddlerTitle)");
}
}
}
var customDisplayOptions = null;
var currentFormat = params["format"];
if (currentFormat) {
var startIndex = currentFormat.indexOf("{");
var endIndex = currentFormat.indexOf("}");
if ((startIndex > -1) && (endIndex > -1) && (startIndex < endIndex)) {
try {
eval("customDisplayOptions = " + currentFormat.substring(startIndex, endIndex + 1));
customDisplayOptions = (typeof customDisplayOptions !== 'object') ? null : value;
} catch (e) {}
if (customDisplayOptions) {
params["format"] = currentFormat.substring(0, startIndex) + currentFormat.substring(endIndex + 1);
}
}
}
return customDisplayOptions;
}
config.macros.reminder.getReminderMessageForDisplayOverride_base = window.getReminderMessageForDisplay;
window.getReminderMessageForDisplay = function getReminderMessageForDisplay(diff, params, matchedDate, tiddlerTitle) {
var currentFormat = params["format"];
var customDisplayOptions = config.macros.reminders.evalMessageFunctions(diff, params, matchedDate, tiddlerTitle);
var displayOptions = null;
var currentDisplayOptions = {};
if (customDisplayOptions) {
displayOptions = {
defaultReminderMessage: "",
defaultShowReminderMessage: "",
defaultAnniversaryMessage: "",
untitledReminder: "",
noReminderFound: "",
todayString: "",
tomorrowString: "",
ndaysString: "",
emtpyShowRemindersString: ""
};
for (var id in displayOptions) {
currentDisplayOptions[id] = config.macros.reminders[id];
displayOptions[id] = config.macros.reminders[id];
}
for (var id in customDisplayOptions) {
config.macros.reminders[id] = customDisplayOptions[id];
}
}
var currentTitle = params["title"];
if (currentTitle) {
params["title"] = currentTitle.replace(/\$/g, "$$$$");
}
var mess = config.macros.reminder.getReminderMessageForDisplayOverride_base(diff, params, matchedDate, tiddlerTitle);
if (currentTitle) {
params["title"] = currentTitle;
}
if (customDisplayOptions) {
params["format"] = currentFormat;
for (var id in displayOptions) {
config.macros.reminders[id] = currentDisplayOptions[id];
}
}
return mess;
}
config.macros.reminder.findDateForReminderOverride_base = window.findDateForReminder;
window.findDateForReminder = function findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
if (baseDate == null) {
baseDate = new Date().getMidnight();
}
if (dateHash["functions"] != null) {
for (var i = 0; i < dateHash["functions"].length; i++) {
var functionDefinition = dateHash["functions"][i];
var parametersStart = functionDefinition.lastIndexOf("(");
var parametersEnd = functionDefinition.lastIndexOf(")");
if ((parametersStart > -1) && (parametersEnd > -1) && (parametersEnd > parametersStart)) {
eval(functionDefinition.substring(0, parametersEnd) + ((functionDefinition.substring(parametersStart + 1, parametersEnd).trim().length > 0) ? "," : "") + "dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound)");
}
}
}
var rrule = null;
var hasEvents = true;
if (dateHash["rrule"]) {
try {
var calendar = dateHash["rrule"];
if (calendar.indexOf("END:VTIMEZONE") > -1) {
calendar = calendar.substring(0, calendar.lastIndexOf("END:VTIMEZONE") + "END:VTIMEZONE".length) + "\\nBEGIN:VEVENT" + calendar.substring(calendar.lastIndexOf("END:VTIMEZONE") + "END:VTIMEZONE".length);
calendar = "BEGIN:VCALENDAR\n" + calendar.replace(/\\n/g, "\n") + "\nEND:VEVENT\nEND:VCALENDAR";
} else {
calendar = "BEGIN:VCALENDAR\nBEGIN:VEVENT\n" + calendar.replace(/\\n/g, "\n") + "\nEND:VEVENT\nEND:VCALENDAR";
}
var vcalendar = new ICAL.Component(ICAL.parse(calendar));
var vtimezones = vcalendar.getAllSubcomponents("vtimezone");
for (var i = 0; i < vtimezones.length; i++) {
var tzid = vtimezones[i].getFirstProperty("tzid");
if (!tzid || !ICAL.TimezoneService.has(tzid.getFirstValue())) {
ICAL.TimezoneService.register(vtimezones[i]);
}
}
rrule = new ICAL.Event(vcalendar.getFirstSubcomponent("vevent"));
if (!rrule.startDate) {
rrule.startDate = ICAL.Time.fromJSDate(baseDate);
}
hasEvents = !rrule.isRecurring() || rrule.iterator().next();
} catch (e) {}
}
var after = function (rrule, date) {
var rruleDate = null;
if (rrule && date) {
var icalDate = rrule.startDate.clone();
icalDate.year = date.getFullYear();
icalDate.month = date.getMonth() + 1;
icalDate.day = date.getDate();
var rruleIterator = rrule.iterator();
while (true) {
var next = rruleIterator.next();
if ((next !== undefined) && (next !== null)) {
if (next.compare(icalDate) >= 0) {
var occurrenceDetails = rrule.getOccurrenceDetails(next);
rruleDate = [occurrenceDetails.startDate.toJSDate(), occurrenceDetails.endDate ? occurrenceDetails.endDate.toJSDate() : occurrenceDetails.startDate.toJSDate()];
break;
}
} else {
break;
}
}
}
return rruleDate;
};
var startYear = dateHash["startyear"];
var startMonth = dateHash["startmonth"];
var startDay = dateHash["startday"];
var untilYear = dateHash["untilyear"];
var untilMonth = dateHash["untilmonth"];
var untilDay = dateHash["untilday"];
var date = null;
var dateHashCloned = false;
var checkUntil = function (date) {
var continueChecking = date != null;
if (continueChecking && (untilYear != null)) {
if (untilYear < date.getFullYear()) {
date = null;
}
continueChecking = (date != null) && (untilYear == date.getFullYear());
}
if (continueChecking && (untilMonth != null)) {
if (untilMonth - 1 < date.getMonth()) {
date = null;
}
continueChecking = (date != null) && (untilMonth - 1 == date.getMonth());
}
if (continueChecking && (untilDay != null)) {
if (untilDay < date.getDate()) {
date = null;
}
}
return date;
}
var rruleDate = null;
do {
var continueChecking = true;
baseDate = hasEvents ? checkUntil(baseDate) : null;
if (baseDate != null) {
date = config.macros.reminder.findDateForReminderOverride_base(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);
rruleDate = after(rrule, date);
if (date && rruleDate && (date.getTime() !== rruleDate[0].getTime())) {
date = null;
}
rruleDate = after(rrule, leadTimeLowerBound);
rruleDate = (rruleDate && (rruleDate[0].getMidnight().getTime() > leadTimeUpperBound.getTime())) ? null : rruleDate;
if (!date) {
date = rruleDate ? rruleDate[0] : null;
} else if (date && rruleDate) {
date = (date.getTime() < rruleDate[0].getTime()) ? date : rruleDate[0];
}
if (date != null) {
continueChecking = true;
if (continueChecking && (startYear != null)) {
if (startYear > date.getFullYear()) {
date = null;
}
continueChecking = (date != null) && (startYear == date.getFullYear());
}
if (continueChecking && (startMonth != null)) {
if (startMonth - 1 > date.getMonth()) {
date = null;
}
continueChecking = (date != null) && (startMonth - 1 == date.getMonth());
}
if (continueChecking && (startDay != null)) {
if (startDay > date.getDate()) {
date = null;
}
}
date = checkUntil(date);
}
continueChecking = true;
if ((date == null) && (dateHash["recurdays"] == null)) {
var isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
if (isNumber(dateHash["recurmonths"]) && (dateHash["recurmonths"] > 0) && isNumber(dateHash["month"])) {
if (!dateHashCloned) {
dateHash = cloneParams(dateHash);
dateHash["year"] = isNumber(dateHash["year"]) ? dateHash["year"] : leadTimeLowerBound.getFullYear();
dateHash["day"] = isNumber(dateHash["day"]) ? dateHash["day"] : baseDate.getDate();
dateHashCloned = true;
}
dateHash["month"] = dateHash["month"] + dateHash["recurmonths"];
var hashDate = new Date(dateHash["year"], dateHash["month"] - 1, dateHash["day"]);
if (hashDate.getTime() > leadTimeUpperBound.getTime()) {
continueChecking = false;
} else {
dateHash["year"] = hashDate.getFullYear();
dateHash["month"] = hashDate.getMonth() + 1;
dateHash["day"] = hashDate.getDate();
}
} else if (isNumber(dateHash["recuryears"]) && (dateHash["recuryears"] > 0) && isNumber(dateHash["year"])) {
if (!dateHashCloned) {
dateHash = cloneParams(dateHash);
dateHash["month"] = isNumber(dateHash["month"]) ? dateHash["month"] : baseDate.getMonth() + 1;
dateHash["day"] = isNumber(dateHash["day"]) ? dateHash["day"] : baseDate.getDate();
dateHashCloned = true;
}
dateHash["year"] = dateHash["year"] + dateHash["recuryears"];
var hashDate = new Date(dateHash["year"], dateHash["month"] - 1, dateHash["day"]);
if (hashDate.getTime() > leadTimeUpperBound.getTime()) {
continueChecking = false;
}
} else {
continueChecking = false;
}
} else if (date == null) {
continueChecking = false;
}
if ((date == null) && (baseDate != null) && !continueChecking) {
if (rrule && ((startYear != null) || (startMonth != null) || (startDay != null))) {
continueChecking = true;
if (continueChecking && (startYear != null)) {
if (startYear < baseDate.getFullYear()) {
baseDate = null;
}
continueChecking = (baseDate != null) && (startYear == baseDate.getFullYear());
}
if (continueChecking && (startMonth != null)) {
if (startMonth - 1 < baseDate.getMonth()) {
baseDate = null;
}
continueChecking = (baseDate != null) && (startMonth - 1 == baseDate.getMonth());
}
if (continueChecking && (startDay != null)) {
if (startDay < baseDate.getDate()) {
baseDate = null;
}
}
if (baseDate != null) {
leadTimeLowerBound = new Date(leadTimeLowerBound);
if (startYear != null) {
leadTimeLowerBound.setFullYear(startYear);
}
if (startMonth != null) {
leadTimeLowerBound.setMonth(startMonth - 1);
}
if (startDay != null) {
leadTimeLowerBound.setDate(startDay);
}
date = checkUntil(after(rrule, leadTimeLowerBound));
date = (date && (date.getMidnight().getTime() > leadTimeUpperBound.getTime())) ? null : date;
if (date == null) {
baseDate = null;
}
}
} else {
baseDate = null;
}
}
}
} while ((date == null) && (baseDate != null));
if (date) {
date.rruleDate = rruleDate;
}
return date;
};
config.macros.reminder.getParamsForReminder_base = window.getParamsForReminder;
window.getParamsForReminder = function getParamsForReminder(params) {
var rruleDefinition = null;
var functionDefinitions = [];
var messageFunctionDefinitions = [];
var paramsFunctionDefinitions = [];
for (var i = 0; i < params.length; i++) {
if (params[i].toLowerCase().indexOf("rrule:") == 0) {
rruleDefinition = params[i].substring(params[i].indexOf(":") + 1);
params.splice(i, 1);
while ((rruleDefinition.substr(0, 1) == '"') && (rruleDefinition.substr(rruleDefinition.length - 1, 1) != '"') && (params[i] != undefined)) {
rruleDefinition += " " + params[i];
params.splice(i, 1);
}
if ((rruleDefinition.substr(0, 1) == '"') && (rruleDefinition.substr(rruleDefinition.length - 1, 1) == '"')) {
rruleDefinition = rruleDefinition.substr(1, rruleDefinition.length - 2);
}
i--;
} else if (params[i].toLowerCase().indexOf("function:") == 0) {
functionDefinitions[functionDefinitions.length] = params[i].substring(params[i].indexOf(":") + 1);
params.splice(i, 1);
i--;
} else if (params[i].toLowerCase().indexOf("messagefunction:") == 0) {
messageFunctionDefinitions[messageFunctionDefinitions.length] = params[i].substring(params[i].indexOf(":") + 1);
params.splice(i, 1);
i--;
} else if (params[i].toLowerCase().indexOf("paramsfunction:") == 0) {
paramsFunctionDefinitions[paramsFunctionDefinitions.length] = params[i].substring(params[i].indexOf(":") + 1);
params.splice(i, 1);
i--;
}
}
var dateHash = config.macros.reminder.getParamsForReminder_base(params);
if (rruleDefinition != null) {
dateHash["rrule"] = rruleDefinition;
}
if (functionDefinitions.length > 0) {
dateHash["functions"] = functionDefinitions;
}
if (messageFunctionDefinitions.length > 0) {
dateHash["messagefunctions"] = messageFunctionDefinitions;
}
if (paramsFunctionDefinitions.length > 0) {
dateHash["paramsfunctions"] = paramsFunctionDefinitions;
for (var i = 0; i < paramsFunctionDefinitions.length; i++) {
var functionDefinition = paramsFunctionDefinitions[i];
var parametersStart = functionDefinition.lastIndexOf("(");
var parametersEnd = functionDefinition.lastIndexOf(")");
if ((parametersStart > -1) && (parametersEnd > -1) && (parametersEnd > parametersStart)) {
eval(functionDefinition.substring(0, parametersEnd) + ((functionDefinition.substring(parametersStart + 1, parametersEnd).trim().length > 0) ? "," : "") + "dateHash, params)");
}
}
}
return dateHash;
}
//}}}
//{{{
config.macros.reminder.findDateForReminderOverride_base_baseDateFindMatch_patch = function(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var tmp1leadTimeLowerBound = leadTimeLowerBound;
var tmp1leadTimeUpperBound = leadTimeUpperBound;
if (dateHash["offsetdayofweek"] != null) {
if (dateHash["negativeOffsetDayOfWeek"] == 1) {
tmp1leadTimeUpperBound = leadTimeUpperBound.addDays(6);
} else {
tmp1leadTimeLowerBound = leadTimeLowerBound.addDays(-6);
}
}
var matchedDate = baseDate.findMatch(dateHash, tmp1leadTimeLowerBound, tmp1leadTimeUpperBound);
return matchedDate;
};
(function () {
var code = eval("config.macros.reminder.findDateForReminderOverride_base").toString();
var startPosition = code.indexOf("var tmp1leadTimeLowerBound");
if (startPosition > -1) {
var endPosition = code.indexOf("findMatch", startPosition);
if (endPosition > -1) {
endPosition = code.indexOf(");", endPosition);
if (endPosition > -1) {
endPosition += ");".length;
code = code.substring(0, startPosition) + "var matchedDate = config.macros.reminder.findDateForReminderOverride_base_baseDateFindMatch_patch(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);" + code.substring(endPosition);
eval("config.macros.reminder.findDateForReminderOverride_base = function findDateForReminderOverride_base" + code.substring(code.indexOf("(")));
}
}
}
})();
(function () {
var code = eval("config.macros.reminder.findTiddlersWithReminders_base").toString();
var newCode = null;
var startPosition = code.indexOf("{");
if (startPosition > -1) {
newCode = " function findTiddlersWithReminders_base(baseDate, leadtime, tags, limit, reminderTiddlerList) { " + code.substring(startPosition + 1);
code = newCode;
startPosition = code.indexOf("store.search");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' reminderTiddlerList ? reminderTiddlerList : ' + code.substring(startPosition);
code = newCode;
}
startPosition = code.indexOf("getParamsForReminder");
if (startPosition > -1) {
startPosition = code.lastIndexOf(";", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition + 1) + ' params["tiddlerreference"] = matches[t]; ' + code.substring(startPosition + 1);
code = newCode;
}
}
startPosition = code.indexOf("tagsArray = tags.readBracketedList()");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' if (!store.getMatchingTiddlers) ' + code.substring(startPosition);
code = newCode;
}
startPosition = code.indexOf("arr.pushUnique(hash)");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' hash["matchedDate"].rruleDate = matchedDate.rruleDate; hash["macroIndex"] = formatMatch.index; hash["macroText"] = formatMatch[0]; ' + code.substring(startPosition);
code = newCode;
}
startPosition = code.indexOf("recurdays");
if (startPosition > -1) {
startPosition = code.indexOf("||", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition - 1) + '|| dateHash["recurmonths"] != null || dateHash["recuryears"] != null || dateHash["rrule"] != null ' + code.substring(startPosition - 1);
code = newCode;
}
}
}
if (newCode != null) {
newCode = newCode.replace("formatMatch[2].readMacroParams()", "config.macros.reminder.parseParams(formatMatch[2])");
newCode = newCode.replace("matches[t].text", "store.getTiddlerText(matches[t].title) || ''");
}
if (newCode != null) {
eval("config.macros.reminder.findTiddlersWithReminders_base = " + newCode);
}
})();
//}}}
//{{{
config.macros.reminder.parseParams = function (paramString) {
var params = [];
if (paramString) {
var parsedParams = [];
try {
parsedParams = paramString.parseParams("NO_NAME_PARAM", null, true, false, false, false);
} catch (e) {
parsedParams = paramString.parseParams("NO_NAME_PARAM", null, false, false, false, false);
}
for (var i = 1; i < parsedParams.length; i++) {
var value = parsedParams[i].value;
if (["title", "tag", "format"].indexOf(parsedParams[i].name.toLowerCase()) > -1) {
value = '"' + value + '"';
}
params.push((parsedParams[i].name === "NO_NAME_PARAM") ? value : parsedParams[i].name + ":" + value);
}
}
return params;
};
//}}}
//{{{
config.macros.reminder.replaceReminderDateTime = function(diff, params, matchedDate, tiddlerTitle) {
if (params["title"]) {
matchedDate = matchedDate || new Date();
if (params["title"].indexOf("REMINDERDATE") > -1) {
var formatDateString = function(date) {
return date.getFullYear() + "-"
+ (date.getMonth() < 9 ? "0" : "") + (date.getMonth() + 1) + "-"
+ (date.getDate() < 10 ? "0" : "") + date.getDate();
}
params["title"] = params["title"].replace(/REMINDERDATE/g, formatDateString(matchedDate));
}
if ((params["title"].indexOf("REMINDERSTART") > -1) || (params["title"].indexOf("REMINDEREND") > -1)) {
if (matchedDate.rruleDate) {
var start = ("0" + matchedDate.rruleDate[0].getHours()).slice(-2) + ":" + ("0" + matchedDate.rruleDate[0].getMinutes()).slice(-2);
var end = ("0" + matchedDate.rruleDate[1].getHours()).slice(-2) + ":" + ("0" + matchedDate.rruleDate[1].getMinutes()).slice(-2);
params["title"] = params["title"].replace(/REMINDERSTART/g, start);
params["title"] = params["title"].replace(/REMINDEREND/g, end);
} else {
var m = params["title"].match(/(2[0-3]|[01][0-9]):[0-5][0-9]/g);
if (m) {
var start = m[0];
var end = m[1] ? m[1] : m[0];
params["title"] = params["title"].replace(/REMINDERSTART/g, start);
params["title"] = params["title"].replace(/REMINDEREND/g, end);
}
}
}
}
}
//}}}
//{{{
config.macros.reminder.replaceTiddlerName = function(diff, params, matchedDate, tiddlerTitle) {
if (tiddlerTitle) {
var reminderTiddlerTitle = (tiddlerTitle.indexOf(" : ") > -1) ? tiddlerTitle.substring(tiddlerTitle.indexOf(" : ") + " : ".length) : tiddlerTitle;
if (!params["title"]) {
params["title"] = reminderTiddlerTitle;
} else {
params["title"] = params["title"].replace(/TIDDLERNAMEESC/g, reminderTiddlerTitle.replace(/'/g, "\\'").replace(/"/g, '\\"'));
params["title"] = params["title"].replace(/TIDDLERNAME/g, reminderTiddlerTitle);
params["title"] = params["title"].replace(/TIDDLERFULLNAMEESC/g, tiddlerTitle.replace(/'/g, "\\'").replace(/"/g, '\\"'));
params["title"] = params["title"].replace(/TIDDLERFULLNAME/g, tiddlerTitle);
}
}
}
//}}}
//{{{
config.macros.reminder.replaceTiddlerFormatting = function(diff, params, matchedDate, tiddlerTitle) {
var tiddler = null;
if (matchedDate === undefined) {
// window.replaceTiddlerFormatting = function(dateHash, params) {...}
tiddler = params["tiddlerreference"];
params = diff;
} else {
tiddler = tiddlerTitle ? store.getTiddler(tiddlerTitle) : null;
}
var paramsTitle = params["title"];
var formattedTitle = null
var formattedTitleStart = -1;
var formattedTitleEnd = -1;
if (paramsTitle) {
formattedTitleStart = paramsTitle.indexOf("@@");
if (formattedTitleStart > -1) {
formattedTitleStart += 2;
formattedTitleEnd = paramsTitle.indexOf("@@", formattedTitleStart);
if (formattedTitleEnd > -1) {
formattedTitle = paramsTitle.substring(formattedTitleStart, formattedTitleEnd);
}
}
}
if (formattedTitle) {
var tagStyles = null;
var tagFunctions = null;
var tiddlers = store.getTaggedTiddlers("reminderstyle");
for (var i = 0; i < tiddlers.length; i++) {
var matches = tiddlers[i].text.match(/^\s*reminderstyle\s*:\s*.+$/igm);
if (matches) {
for (var j = 0; j < matches.length; j++) {
var styleDefinitionMatches = matches[j].match(/(\s*reminderstyle\s*:\s*)(.+)(\s*)/i);
if (styleDefinitionMatches && styleDefinitionMatches[2]) {
var styleDefinitionMatch = styleDefinitionMatches[2];
var styleDefinitionParams = styleDefinitionMatch.parseParams(null, null, false, false, false);
if (styleDefinitionParams && styleDefinitionParams[1] && styleDefinitionParams[1].name) {
var tagName = styleDefinitionParams[1].name;
styleDefinitionMatch = styleDefinitionMatch.replace(/\(\s*function\s*\(\s*\)\s*\{[\s\S]*?}\s*\)\s*\(\s*\)\s*;?/g, function (match) {
tagFunctions = tagFunctions || {};
tagFunctions[tagName] = tagFunctions[tagName] || [];
tagFunctions[tagName].push(match);
return "";
});
var styleMatches = styleDefinitionMatch.substring(styleDefinitionMatch.indexOf(tagName) + tagName.length).match(/(\s*:\s*)(.*)(\s*)/i);
if (styleMatches && styleMatches[2]) {
var style = styleMatches[2];
style = ((style.length > 0) && (style.lastIndexOf(";") != style.length - 1)) ? style + ";" : style;
tagStyles = tagStyles || {};
tagStyles[tagName] = style;
}
}
}
}
}
}
var reminderTags = null;
if (tagStyles || tagFunctions) {
var titles = [];
paramsTitle.replace(/.*<<(goToTiddler|newTiddler).*title:\{\{(['|"](?:(?!['|"]\}\}).)*['|"])\}\}.*>>.*/, function () {
titles.push(Array.prototype.slice.call(arguments, 2, 3));
});
if (titles.length > 0) {
var tagTiddlerTitle = eval("" + titles[0]);
if (config.options.txtReminderTiddlerTagDefinitions && store.tiddlerExists(config.options.txtReminderTiddlerTagDefinitions)
&& (config.macros.reminder.reminderTiddlerTagDefinitionsText !== store.getTiddlerText(config.options.txtReminderTiddlerTagDefinitions))) {
try {
config.macros.reminder.reminderTiddlerTagDefinitionsText = store.getTiddlerText(config.options.txtReminderTiddlerTagDefinitions);
eval("config.macros.reminder.reminderTiddlerTagDefinitions = " + config.macros.reminder.reminderTiddlerTagDefinitionsText);
} catch (e) {
console.log(e);
}
}
if (config.macros.reminder.reminderTiddlerTagDefinitions && config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle] && config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle].length) {
reminderTags = config.macros.reminder.reminderTiddlerTagDefinitions[tagTiddlerTitle].slice();
}
var tagTiddler = store.getTiddler(tagTiddlerTitle);
if (tagTiddler) {
if (reminderTags == null) {
reminderTags = [];
}
for (var i = 0; i < tagTiddler.tags.length; i++) {
reminderTags[reminderTags.length] = tagTiddler.tags[i];
}
}
}
if (params["tag"]) {
reminderTags = (reminderTags || []).concat(params["tag"].readBracketedList());
}
if (tiddler && tiddler.tags) {
reminderTags = (reminderTags || []).concat(tiddler.tags);
}
}
if (reminderTags) {
if (tagStyles) {
for (var i = 0; i < reminderTags.length; i++) {
if (tagStyles[reminderTags[i]] != null) {
var cssRegExp = new RegExp(config.textPrimitives.cssLookahead, "mg");
var nextMatch = 0;
var lastIndex = 0;
var lookaheadMatch = cssRegExp.exec(formattedTitle);
if (lookaheadMatch) {
do {
lastIndex = cssRegExp.lastIndex;
nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
cssRegExp.lastIndex = nextMatch;
lookaheadMatch = cssRegExp.exec(formattedTitle);
} while (lookaheadMatch && (lookaheadMatch.index == nextMatch))
}
paramsTitle = paramsTitle.substring(0, formattedTitleStart) + tagStyles[reminderTags[i]] + paramsTitle.substring(lastIndex + formattedTitleStart);
params["title"] = paramsTitle;
break;
}
}
}
if (tagFunctions) {
for (var i = 0; i < reminderTags.length; i++) {
if (tagFunctions[reminderTags[i]]) {
for (var j = 0; j < tagFunctions[reminderTags[i]].length; j++) {
try {
eval(tagFunctions[reminderTags[i]][j]);
} catch (e) {
console.log(e);
}
}
}
}
}
}
}
}
//}}}
/***
|Name|SavingFixPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.txtSavingFixFileName === undefined) {
config.options.txtSavingFixFileName = "notes.html";
}
merge(config.optionsDesc, {
txtSavingFixFileName: "The TiddlyWiki file name to use for saving when it cannot be obtained from the document location"
});
//}}}
//{{{
function determineDocumentLocation() {
var documentLocation = document.location.toString();
if (documentLocation && config.options.txtSavingFixFileName) {
var firefoxMobile = /org\.mozilla\.firefox\/cache\/contentUri\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/;
var chromiumMobile = /content:\/\/media\/external\/file\/\d+/;
if (documentLocation.match(firefoxMobile)) {
documentLocation = documentLocation.replace(firefoxMobile, config.options.txtSavingFixFileName);
} else if (documentLocation.match(chromiumMobile)) {
documentLocation = "file:///" + config.options.txtSavingFixFileName;
}
}
return documentLocation;
}
//}}}
//{{{
(function () {
var code = eval("window.saveChanges").toString();
code = code.replace(/document\.location\.toString\(\)/g, "determineDocumentLocation()");
eval("window.saveChanges = function saveChanges" + code.substring(code.indexOf("(")));
if (window.saveChanges_TiddlerEncryptionPlugin != null) {
code = eval("window.saveChanges_TiddlerEncryptionPlugin").toString();
code = code.replace(/document\.location\.toString\(\)/g, "determineDocumentLocation()");
eval("window.saveChanges_TiddlerEncryptionPlugin = function saveChanges" + code.substring(code.indexOf("(")));
}
if (config.macros.guid != null) {
code = eval("config.macros.guid.saveChanges").toString();
code = code.replace(/document\.location\.toString\(\)/g, "determineDocumentLocation()");
eval("config.macros.guid.saveChanges = function saveChanges" + code.substring(code.indexOf("(")));
}
if ((typeof tw !== "undefined") && tw.io && tw.io.getOriginalLocalPath) {
code = eval("tw.io.getOriginalLocalPath").toString();
code = code.replace(/document\.location\.toString\(\)/g, "determineDocumentLocation()");
eval("tw.io.getOriginalLocalPath = function getOriginalLocalPath" + code.substring(code.indexOf("(")));
}
})();
//}}}
/***
|''Name''|Schedule|
|''Description''| Creates a rolling schedule displayed either as a timetable or an agenda.|
|''Author''|Michael Borck|
|''Version''|0.5.3|
|''Date''|7 Dec 2008|
|''Status''|@@beta@@|
|''Source''|http://schedule.tiddlyspot.com/|
|''Copyright''|2008|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''Requires''|ReminderMacros see http://remindermacros.tiddlyspot.com |
|''Optional''|DatePickerLibrary see http://svn.tiddlywiki.org/Trunk/contributors/SaqImtiaz/libraries/DatePicker.js <br> Sparklines
http://www.tiddlywiki.com/coreplugins.html|
|''Feedback''|borck.m@gmail.com|
|''CoreVersion''|2.4|
|''Documentation''|See [[ScheduleInfo]] for usage <br> http://schedule.tiddlyspot.com for API|
|''Type''|Macro|
|''Keywords''|timetable, schedule, appointments, hard landscape, macro|
!!!Code
***/
//{{{
if (!version.extensions.schedule) {
version.extensions.schedule = {
major: 0,
minor: 5,
revision: 3,
date: new Date(2008, 7, 12),
source: "http://schedule.tiddlyspot.com/"
};
config.macros.schedule = {
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
var args = paramString.parseParams(null, null, true);
switch (getParam(args, "view", "agenda")) {
case "timetable":
var timetable = new config.macros.schedule.Timetable(place, args);
delete timetable;
break;
case "agenda":
var agenda = new config.macros.schedule.Agenda(place, args);
delete agenda;
break;
default:
displayMessage("SchedulPlugin: error unkown schedule view");
}
}
};
config.macros.schedule.Schedule = function(place, args) {
this.place = place;
this.isShadow = (getParam(args, "shadow", "on") === "on");
this.step = parseInt(getParam(args, "step", 1), 10);
this.isSparks = getParam(args, "sparklines", undefined);
this.duration = parseInt(getParam(args, "duration", 7), 10);
this.span = parseInt(getParam(args, "span", 12), 10);
this.span = this.span > 24 ? 24 : this.span; // silently change!
this.today = new Date();
this.epoch = new Date(getParam(args, "date", new Date()));
this.hourFrom = parseInt(getParam(args, "hourFrom", this.today.getHours()), 10);
if (this.duration < 0) { // from the past?
this.epoch.addN(this.duration);
this.duration = -this.duration;
}
this.date = new Date(this.epoch); // epoch changes, save for reset
this.shading = new config.macros.schedule.Shadings(args);
this.events = new
config.macros.schedule.Events(getParam(args, "defTime", "09:00"));
this.eveningStart = getParam(args, "eveningStart", "18");
this.dayStart = getParam(args, "dayStart", "06");
this.control = createTiddlyElement(this.place, "div");
this.timeNow = this.today.getHHMM();
this.isPast = this.epoch.isBefore(this.today);
this.isToday = this.epoch.isEqual(this.today);
this.isWeekend = this.epoch.isWeekend();
this.nextPeriod = function(n) {
this.epoch.addN(n);
this.isPast = this.epoch.isBefore(this.today);
this.isToday = this.epoch.isEqual(this.today);
this.isWeekend = this.epoch.isWeekend();
};
this.isOutOfHours = function(hour) {
return String.zeroPad(hour, 2) >= this.eveningStart || String.zeroPad(hour, 2) <= this.dayStart;
};
this.showSparks = function(place, sparks, colorAsToday) {
var sparkDiv = createTiddlyElement(place, "div", "spark");
if (config.macros.sparkline) {
config.macros.sparkline.handler(sparkDiv, null, sparks);
var sparkGraph = sparkDiv.getElementsByTagName("span");
this.shading.styleBackground(sparkGraph[0], this.isWeekend, colorAsToday, false, this.isShadow);
var ticks = sparkGraph[0].getElementsByTagName("img");
for (var i = 0; i < ticks.length; i++) {
this.shading.styleAsBlock(ticks[i], this.isToday, false, false);
}
}
return sparkDiv;
};
this.addControls = function(that) {
var adjust = function(n) {
removeChildren(that.body);
that.date.addN(n);
that.epoch = new Date(that.date);
that.isPast = that.epoch.isBefore(that.today);
that.isToday = that.epoch.isEqual(that.today);
that.isWeekend = that.epoch.isWeekend();
that.make();
return false;
};
var fwdDay = function() {
return adjust(1);
};
var fwdDur = function() {
return adjust(that.duration);
};
var backDay = function() {
return adjust( - 1);
};
var backDur = function() {
return adjust( - that.duration);
};
var controls = createTiddlyElement(this.control, "div");
createTiddlyButton(controls, "<<", "-" + this.duration + " Days", backDur);
createTiddlyButton(controls, "<", "-1 Day", backDay);
createTiddlyButton(controls, ">", "+1 Day", fwdDay);
createTiddlyButton(controls, ">>", "+" + this.duration + " Days", fwdDur);
};
this.make = function() {
var endWant = this.hourFrom + this.span;
var start = endWant > 24 ? 0 : this.hourFrom;
var end = endWant > 24 ? endWant - 24 : endWant; //mod op error in TW
this.makeSchedule(start, end);
if (endWant > 24) { // We need a second table
this.nextPeriod( - this.duration);
this.makeSchedule(this.hourFrom, 24);
}
};
};
String.HHMM = function(hour, mins) {
return String.zeroPad(hour, 2) + ":" + String.zeroPad(mins, 2);
};
config.macros.schedule.Agenda = function(place, args) {
this.base = config.macros.schedule.Schedule;
this.base(place, args);
this.isSparks = this.isSparks ? this.isSparks === "on": true;
this.addControls(this);
this.body = createTiddlyElement(place, "div");
// from CalendarPlugin (thanks)
this.journalDateFmt = "YYYY/MMM/DD";
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];
}
}
for (var i = 0; i < this.duration; i += this.step) {
this.make();
this.nextPeriod(this.step);
}
};
config.macros.schedule.Agenda.prototype.makeSchedule = function(start, end) {
this.events.cache(this.epoch);
this.makeHeader(start, end);
var isActive = (this.timeNow <= start);
for (var i = 0; i < this.events.list.length; i++) {
var event = this.events.list[i];
if ((event.start >= String.HHMM(start, "00")) && (event.end <= String.HHMM(end, "00"))) {
var cell = createTiddlyElement(this.body, "div");
var link = createTiddlyLink(cell, event.tiddler, false);
var text = event.title ? event.title: event.tiddler;
link.appendChild(document.createTextNode(text));
if ((event.end < this.timeNow && this.isToday) || this.isPast) {
link.style.color = this.eventPast;
}
var hour = event.start.split(":")[0];
this.shading.styleBackground(cell, this.isWeekend || this.isOutOfHours(hour), this.isToday, isActive, this.isPast, this.isShadow);
}
}
};
config.macros.schedule.Agenda.prototype.makeHeader = function(start, end) {
var day = createTiddlyElement(this.body, "h3");
var text = this.epoch.formatString(this.journalDateFmt);
if (this.isSparks) {
var sparks = this.events.count(start, end, this.detail);
var graph = this.showSparks(day, sparks, false);
}
else {
graph = createTiddlyElement(day, "div", "spark");
}
createTiddlyLink(graph, text, true);
};
config.macros.schedule.Timetable = function(place, args) {
this.base = config.macros.schedule.Schedule;
this.base(place, args);
this.isSparks = this.isSparks ? (this.isSparks === "on") : false;
this.addControls(this);
this.table = createTiddlyElement(this.place, "table", null, "bordersOff", null);
this.wrap = createTiddlyElement(this.table, "tbody");
this.body = createTiddlyElement(this.wrap, "tr");
switch (getParam(args, "detail", "medium")) {
case "low":
this.detail = 60;
break;
case "high":
this.detail = 15;
break;
default:
this.detail = 30;
}
this.make();
};
config.macros.schedule.Timetable.prototype.makeSchedule = function(start, end) {
var cell = createTiddlyElement(this.body, "td"); // Holds 1st/only table
var table = createTiddlyElement(cell, "table", null, "bordersOff", null);
var body = createTiddlyElement(table, "tbody");
var row = createTiddlyElement(body, "tr");
this.makeHeader(row, start, end);
for (var i = 0; i < this.duration; i += this.step) {
row = createTiddlyElement(body, "tr");
this.makeDay(row, start, end);
this.nextPeriod(this.step);
}
};
config.macros.schedule.Timetable.prototype.makeHeader = function(head, start, end) {
createTiddlyElement(head, "td"); //cell above day names
for (var h = start; h < end; h++) {
for (var m = 0; m < 60; m += this.detail) {
var hour = String.zeroPad(h, 2);
var time = createTiddlyElement(head, "td", null, "bordersOff", m === 0 ? hour: "");
if ((hour < String.zeroPad(this.dayStart, 2)) || (hour > String.zeroPad(this.eveningStart, 2))) {
time.style.background = this.shading.outOfHours;
}
}
}
};
config.macros.schedule.Timetable.prototype.makeDay = function(day, start, end) {
var title = createTiddlyElement(day, "td", null, null, null);
var text = this.epoch.getDate() + " " + this.epoch.dayName();
createTiddlyElement(title, "p", null, null, text);
//var isActive = (this.timeNow <= start);
this.shading.styleBackground(title, this.isWeekend, this.isToday, true, this.isPast, this.isShadow);
this.events.cache(this.epoch);
this.events.sizeIndex(this.detail);
var sparks = []
for (var h = start; h < end; h++) {
sparks[h - start] = 0;
for (var m = 0; m < 60; m += this.detail) {
var startBdry = String.HHMM(h, m);
var mins = m + this.detail;
var next = mins === 60 ? h + 1 : h;
var endBdry = String.HHMM(next, (mins === 60 ? "00": mins));
this.events.addStarting(startBdry, endBdry);
sparks[h - start] += this.showActive(day, startBdry);
this.events.removeEnding(startBdry, endBdry);
}
}
if (this.isSparks) {
this.showSparks(title, sparks, this.isToday);
}
};
config.macros.schedule.Timetable.prototype.showActive = function(day, start) {
var count = 0;
var isActive = (this.timeNow <= start);
var slot = createTiddlyElement(day, "td", null, "bordersOff", null);
this.shading.styleBackground(slot, this.isOutOfHours(start) || this.isWeekend, this.isToday, isActive, this.isPast, this.isShadow);
for (var i = 0; i < this.events.index.length; i++) {
var p = createTiddlyElement(slot, "p", null, null, ".");
if (this.events.index[i]) {
count++;
p.title = this.events.list[this.events.index[i] - 1].title;
this.shading.styleAsBlock(p, this.isToday, isActive, this.isPast);
}
else { // fake empty entry so events 'line up'
p.style.visibility = "hidden";
}
}
return count;
};
config.macros.schedule.Events = function(time) {
this.defaultTime = time;
this.list = [];
this.index = [];
};
config.macros.schedule.Events.prototype.cache = function(epoch) {
this.list = [];
var items = findTiddlersWithReminders(epoch, [0, 1], null, 1);
var validTime = /(2[0-3]|[01][0-9]):[0-5][0-9]/g;
for (var i = 0; i < items.length; i++) {
if (items[i].diff < 1) { // today?
var event = {};
event.tiddler = items[i].tiddler;
event.title = items[i].params.title ? items[i].params.title: items[i].tiddler;
event.start = this.defaultTime;
event.end = this.defaultTime;
if (items[i].params.title) {
var m = items[i].params.title.match(validTime);
if (m) // found at least one valid time in title
{
event.start = m[0]; // first match is start time
event.end = m[1] ? m[1] : m[0];
}
}
this.list.push(event);
}
}
this.list = this.list.sort(function byStart(a, b) {
return a.start > b.start;
});
};
config.macros.schedule.Events.prototype.sizeIndex = function(detail) {
this.index = [];
var max = 0;
for (var h = 0; h < 24; h++) {
var clashes = 0;
for (var m = 0; m < 60; m += detail) {
var start = String.HHMM(h, m);
var next = ((m + detail) === 60 ? (h + 1) : h);
var end = String.HHMM(next, ((m + detail) === 60 ? "00": m + detail));
for (var i = 0; i < this.list.length; i++) {
var event = this.list[i];
if (event.start >= start && event.start < end) {
clashes++;
max = max < clashes ? clashes: max;
}
else if (event.end > start && event.end <= end) {
clashes--;
}
}
}
}
this.index.length = max;
};
config.macros.schedule.Events.prototype.addStarting = function(start, end) {
for (var i = 0; i < this.list.length; i++) {
if (this.list[i].start >= start && this.list[i].start < end) {
var j = 0;
while (j < this.index.length && !this.index.contains(i + 1)) {
if (!this.index[j]) // non zero true
{
this.index[j] = i + 1; // store non-zero!
}
j++;
}
}
}
};
config.macros.schedule.Events.prototype.removeEnding = function(start, end) {
for (var i = 0; i < this.list.length; i++) {
var eventEnd = this.list[i].end;
var eventStart = this.list[i].start;
if (isEnding(this.index)) {
this.index[this.index.indexOf(i + 1)] = 0;
}
}
function isEnding(index) {
return (((eventEnd > start && eventEnd <= end) || ((eventEnd === eventStart) && (eventEnd === start)) || ((eventEnd.split(":")[0] === 23) && (eventEnd === start))) && (index.contains(i + 1)));
};
};
config.macros.schedule.Events.prototype.count = function(start, end, detail) {
var sparks = [];
for (var h = start; h < end; h++) {
sparks[h - start] = 0;
for (var m = 0; m < 60; m += detail) {
var startBdry = String.HHMM(h, m);
var next = ((m + detail) === 60 ? (h + 1) : h);
var endBdry = String.HHMM(next, ((m + detail) === 60 ? "00": m + detail));
for (var i = 0; i < this.list.length; i++) {
var event = this.list[i];
if ((event.start >= startBdry && event.start < endBdry) || (event.start < startBdry && event.end > endBdry)) {
sparks[h - start]++;
}
else if (event.endBdry > start && event.end <= endBdry) {
sparks[h - start]--;
}
}
}
}
return sparks;
};
config.macros.schedule.Shadings = function(args) {
this.activeEvent = getParam(args, "activeEvent", "#ccf");
this.eventPast = getParam(args, "eventPast", "#edf");
this.outOfHours = getParam(args, "outOfHours", "#efe");
this.todayOOH = getParam(args, "todayOOH", "#eee");
this.todayFocus = getParam(args, "todayFocus", "#eef");
this.inPast = getParam(args, "inPast", "#eff");
this.inFocus = getParam(args, "inFocus", "#eef");
};
config.macros.schedule.Shadings.prototype.styleBackground = function(cell, isOutOfHours, isToday, isActive, isPast, isShadow) {
cell.style.background = "white";
if (isOutOfHours) {
cell.style.background = this.outOfHours;
}
if (isToday) {
cell.style.background = this.todayFocus;
}
if (isToday && isOutOfHours) {
cell.style.background = this.todayOOH;
}
if (isPast || (isToday && !isActive && isShadow)) {
cell.style.background = this.inPast;
}
};
config.macros.schedule.Shadings.prototype.styleAsBlock = function(block, isToday, isActive, isPast) {
if ((isToday && !isActive) || isPast) {
block.style.background = this.eventPast;
block.style.color = this.eventPast;
}
else // Must be an active event!
{
block.style.background = this.activeEvent;
block.style.color = this.activeEvent;
}
};
Date.prototype.isWeekend = function() {
return this.getDay() === 0 || this.getDay() === 6;
};
Date.prototype.getHHMM = function() {
return String.zeroPad(this.getHours(), 2) + ":" + String.zeroPad(this.getDate(), 2);
};
Date.prototype.dayName = function() {
var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
return days[this.getDay()];
};
Date.prototype.isBefore = function(date) {
return this.formatString("YYYY/0MM/0DD") < date.formatString("YYYY/0MM/0DD");
};
Date.prototype.isEqual = function(date) {
return this.formatString("YYYY/MM/DD") === date.formatString("YYYY/MM/DD");
};
Date.prototype.addN = function(n) {
var oneDay = (1000 * 60 * 60 * 24); // Milliseconds in a day
this.setTime(this.getTime() + (n * oneDay));
};
setStylesheet(".viewer table.bordersOff, .viewer table.bordersOff * " + "{margin:1; cell-padding:0; border:0; padding:0;} .viewer " + "td.bordersOff {width: 24px; height: 24px;} .sparkline { " + "background: Yellow; } .sparktick { background: White; }", "schedule");
}
//}}}
/***
|Name|ScheduleDeviceOverride|
|License|[[TW Notes License]]|
|Requires|[[DevicePlugin]] [[ScheduleOverride]]|
<<device cancelScheduleDeviceWakeup>>
!!!!!Code
***/
//{{{
(function () {
var cmd = config.macros.device;
var cmsa = config.macros.schedule.alarm;
cmd.handlers["cancelScheduleDeviceWakeup"] = function (place, macroName, params, wikifier, paramString, tiddler) {
var action = !cmd.supported ? null : function (e) {
cmd.cancelWakeup(cmsa.deviceSupport.wakeupTag);
displayMessage("alarm canceled");
};
var button = jQuery(createTiddlyButton(place, "cancel alarm", "Cancel the device wakeup alarm set for ScheduleDeviceOverride", action));
if (!cmd.supported) {
button.css("cursor", "not-allowed");
button.attr("title", "unsupported device");
}
};
if (cmd.supported) {
cmsa.deviceSupport = {};
cmsa.deviceSupport.notificationConfig = cmsa.notificationConfig;
cmsa.notificationConfig = function (notification) {
var notificationConfig = cmsa.deviceSupport.notificationConfig.call(this, notification);
if (Device.isPaused() && notification && notification.notify && notification.vibrate && cmsa.morse.sequence) {
notificationConfig.options.vibrate = cmsa.morse.sequence.slice();
notificationConfig.options.vibrate.splice(0, 0, 0);
if (notification.vibrateOnly) {
notificationConfig.options.body = null;
}
}
return notificationConfig;
};
cmsa.deviceSupport.display = cmsa.display;
cmsa.display = function (notification) {
if (Device.isPaused() && notification && notification.notify && notification.vibrate && cmsa.morse.sequence && !notification.display) {
notification.display = true;
notification.vibrateOnly = true;
}
cmsa.deviceSupport.display.call(this, notification);
};
cmsa.deviceSupport.vibrate = cmsa.vibrate;
cmsa.vibrate = function (notification) {
if (!Device.isPaused()) {
cmsa.deviceSupport.vibrate.call(this, notification);
}
};
cmsa.deviceSupport.wakeupTag = "ScheduleDeviceOverrideWakeup";
cmd.setScheduledWakeupTag(cmsa.deviceSupport.wakeupTag, 1000);
cmd.scheduledWakeupListeners.push({
name: cmsa.deviceSupport.wakeupTag,
scheduledDeviceWakeup: function (tag) {
if (tag === cmsa.deviceSupport.wakeupTag) {
var currentDate = new Date();
var nextAlarmStart = (new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), currentDate.getHours(), currentDate.getMinutes() + 1, 0, 0)).getTime();
if ((cmsa.deviceSupport.nextAlarmStart == null) || cmsa.deviceSupport.nextAlarmStart < nextAlarmStart) {
cmsa.deviceSupport.nextAlarmStart = nextAlarmStart;
cmd.scheduleWakeup(nextAlarmStart - currentDate.getTime(), 25000, cmsa.deviceSupport.wakeupTag, true);
}
cmsa.monitor();
}
}
});
cmsa.custom[cmsa.deviceSupport.wakeupTag] = {
name: cmsa.deviceSupport.wakeupTag,
init: function (index, customEvents, eventList, alarmEventList) {
var lastCustomEvent = customEvents[customEvents.length - 1];
if (!(lastCustomEvent instanceof Object) || (lastCustomEvent.name !== cmsa.deviceSupport.wakeupTag)) {
customEvents.push(customEvents[index]);
customEvents[index] = null;
}
},
events: function (eventList, alarmEventList) {
var nextAlarmStart;
var currentDate = new Date();
if (alarmEventList.length) {
nextAlarmStart = (new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), currentDate.getHours(), currentDate.getMinutes() + 1, 0, 0)).getTime();
} else {
nextAlarmStart = (new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() + 1, 0, 0, 0, 0)).getTime();
for (var i = 0; i < eventList.length; i++) {
var event = eventList[i];
if (event.alarm && (event.alarm.start > currentDate.getTime()) && (nextAlarmStart > event.alarm.start)) {
nextAlarmStart = event.alarm.start;
}
}
}
if (cmsa.deviceSupport.nextAlarmStart !== nextAlarmStart) {
cmsa.deviceSupport.nextAlarmStart = nextAlarmStart;
cmd.scheduleWakeup(nextAlarmStart - currentDate.getTime(), 25000, cmsa.deviceSupport.wakeupTag, true);
}
}
};
}
})();
//}}}
/***
|Name|ScheduleOverride|
|License|[[TW Notes License]]|
|Requires|[[MatchTagsPluginOverride]] [[Schedule]]|
!!!!!Code
***/
//{{{
(function () {
var code = config.macros.schedule.Events.prototype.cache.toString();
var newCode = null;
var startPosition = code.indexOf("{");
if (startPosition > -1) {
newCode = " function cache(epoch, tags, leadtime, validEvent) { " + code.substring(startPosition + 1);
code = newCode;
startPosition = code.indexOf("findTiddlersWithReminders(epoch, [0, 1], null, 1);");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "findTiddlersWithReminders(epoch, leadtime ? leadtime : [0, 1], tags, 1);" + code.substring(startPosition + "findTiddlersWithReminders(epoch, [0, 1], null, 1);".length);
code = newCode;
}
startPosition = code.indexOf("<");
if (startPosition > -1) {
startPosition = code.indexOf("1", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + "(leadtime ? leadtime[1] : 1)" + code.substring(startPosition + 1);
code = newCode;
}
}
startPosition = code.indexOf("event.tiddler");
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + " event.reminder = items[i]; " + code.substring(startPosition);
code = newCode;
}
startPosition = code.indexOf("var m");
var endPosition = code.indexOf("if (m)");
if ((startPosition > -1) && (endPosition > -1)) {
newCode = code.substring(0, startPosition) + 'var title = jQuery.isFunction(window.getReminderMessageForDisplay) ? window.getReminderMessageForDisplay(items[i]["diff"], items[i]["params"], items[i]["matchedDate"], items[i]["tiddler"]) : null; title = title || items[i].params.title; m = title.match(validTime); if (m && (!validEvent || validEvent.test(title)))' + code.substring(endPosition + "if (m)".length);
code = newCode;
}
}
if (newCode != null) {
eval("config.macros.schedule.Events.prototype.cache = " + newCode);
}
})();
//}}}
//{{{
function ScheduleReminderEvents() {};
ScheduleReminderEvents.prototype.determineEvents = config.macros.schedule.Events.prototype.cache;
//}}}
//{{{
if (config.options.txtScheduleAlarmEventFilter === undefined) {
config.options.txtScheduleAlarmEventFilter = "alarm AND NOT alarm:off AND (NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT Trash";
}
merge(config.optionsDesc, {
txtScheduleAlarmEventFilter: "The schedule event tag filter used to select reminders for which to display alarms"
});
config.macros.schedule.init = function () {
var monitor = function () {
setTimeout(function () {
config.macros.schedule.alarm.monitor(monitor);
}, 15000);
};
config.macros.schedule.alarm.monitor(monitor);
};
config.macros.schedule.alarm = {
icons: (function () {
var icons = {
transparent: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12NgAAIAAAUAAeImBZsAAAAASUVORK5CYII=",
alarm: {
/*
* Alarm Clock Twitter Twemoji
* Graphics Title: 23f0.svg
* Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
* Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/23f0.svg
* Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
*/
"apple-touch-icon": "data:image/png;base64,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",
"shortcut icon": "data:image/vnd.microsoft.icon;base64,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5f/m5OX/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/o5+b/6Ofm/+jn5v/n5uX/393l/83J5P+OguD/Szbd/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7c/0YyyPVPSZFkSMj3RkzK//BNzP//Tcz//03M//9NzP//S8z+xkzL/u5NzP//Tcz//03M//9NzP//Tcz//0zK/OlUhpvHWFdh+00/o/5EL9j+RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/Szbd/3Ji3/+aj+H/t7Di/83I5P/d2+X/4+Ll/+Tj5f/g3uX/1NDk/8C64/+km+H/gXPf/1hE3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9DLtz+SDe9/lVQc/5UgJLgTMb36k3M//9NzP//Tcz//03M//9NzP//TMv+70vL//1NzP//Tcz//03M//9NzP//Tcz//03L/v5OwO7+VneE/1hWXdVGMcuVQy3c9kQu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9GMN3/TDfd/1E93f9VQt3/WUbe/1pI3v9XQ93/Uz/d/0453f9IMt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Mt3P5CLdu0VU5xm1dyfP9OwO3/Tcv+/03M//9NzP//Tcz//03M//9NzP//S8v+90zM/uNNzP//Tcz//03M//9NzP//Tcz//03M//9NzP//TcTz/1Cpz1wkbdoHQincaEIs3OFDLdz8RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9DLdz+Qy3c70Er3KM/Nd8YSsn6PkzF9flNzP//Tcz//03M//9NzP//Tcz//03M//9NzP//S8v+20zM/6FNzP//Tcz//03M//9NzP//Tcz//03M//9NzP//Tcz//0zL/uNIy/9KAKr/A0Eo3DNCLNyhQy3d8UQu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qu3f9ELt3/RC7d/0Qt3ftCLdzAQCzaWx8/3whJyv9JTMv+203M//9NzP//Tcz//03M//9NzP//Tcz//03M//9NzP//S8v+n0rO/zpMzP75Tcz//03M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",
"icon32x32": "data:image/png;base64,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",
"icon16x16": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACz0lEQVQ4T32Tf0hTURTHv/e+uS0KmTn7QyHN6UjNokRCqGxtltuM/MciCSmKggq0H4T9UUIliUUFJgj1RzlDg7CgNrRcaSqZmUSiRTrTPyQryox0P3zv3e6eTlHEC4937rn3fs4533MvAR/svT0JEkwgrJ+ku14EfYq/K8cJBkLSn9lmfe9sWWAkEQJekTTnJ8I6rftAhRqAqPgmCbJo/nLA20vVQnaEVVURBIw1BE7LAdJorNMaIAsv+T6B40X+5RMeZYgfjpUZMDUmFY2c8cUy0OOEQBOKOvMXwWRHTKmmTx0tlBOeGBj7GgR4ua1+3ip06BzeJP2UFAH9Sqis20GN8QClkAeHITW0gI2MYoKS39/zl49YdkgpHBDgAPtDHqHZc8ifw5k2asqAuugIiFYDd5M7qATMFgtPWMTUnVpITxr5Oda1pkpbTTXYwgFpYYMHdbtAyVO6IQnqq+dBBKpk7XK6FIDNbp8WlTEEyiohN3eASfJRw/3xeyS44Ek1P+Zl5Goqr4AmxM2WvhAQXJB//oK/4BQ3pE7DR/fmacB6yw+s0kdpq29yjuJaNIOQ31dYAvbZI8qTQysIF58MplpEss5ItTcuzhO+va0ddbW1im9bZiby9uYptr+0AvLrtwjze2NmSrCMk9XR4dq75Qs6t/jUX1wGubuHyd5/OgUwkGppJpRkahy3QKMil4Qwrw++/SeBSd+AoacpcQZgPsZrrxJsJqgLDy8JCNTUQ3LU8zvLLhl63SUKoD8hQUOXxX0AwVrViQKodmfNEzNEFHndU7yNEKVRFfzJsT1tY7OS9yebUnj/W3gmkTQjDarcnXM30TMM0emG7G7nMTABkVnj+9ytQfBcz/hk/GxG/t8e9YPAt7BFywjTiwjf5D+nu/7mWmjDPAB7tLGYydg6eDniNn+ye0Bh5NePEkY8EpNdiRf+ZEMgfpLXzVWcHv8Bc88hGqK66d8AAAAASUVORK5CYII="
},
"apple-touch-icon": {sizes: "180x180", icon: null, href: null},
"shortcut icon": {sizes: "", icon: null, href: null},
"icon32x32": {sizes: "32x32", icon: null, href: null},
"icon16x16": {sizes: "16x16", icon: null, href: null}
};
for (var k in icons.alarm) {
icons[k].href = icons.alarm[k];
}
return icons;
})(),
sound: (function () {
var audio = document.createElement("AUDIO");
audio.src = "data:audio/mp3;base64,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"; // https://www.myinstants.com/en/instant/tardis-cloister-bell
return audio;
})(),
flashing: false,
flash: function (notification) {
var cmsa = config.macros.schedule.alarm;
if ((notification === false) && (cmsa.flashing === true)) {
cmsa.flashing = "stop";
} else if ((notification !== false) && (cmsa.flashing === false)) {
cmsa.flashing = true;
for (var k in cmsa.icons) {
if (typeof cmsa.icons[k].sizes === "string") {
cmsa.icons[k].icon = null;
}
}
var icons = [];
var genericIcons = {
"apple-touch-icon": null,
"icon": null
};
jQuery(document.documentElement).find("link").each(function () {
var link = jQuery(this);
var rel = (link.attr("rel") || "").toLowerCase();
switch (rel) {
case "apple-touch-icon":
if (!cmsa.icons[rel].icon && ((link.attr("sizes") || "").toLowerCase() === "180x180")) {
cmsa.icons[rel].icon = link;
link = null;
} else if (!genericIcons[rel]) {
genericIcons[rel] = link;
link = null;
}
break;
case "shortcut icon":
if (!cmsa.icons[rel].icon) {
cmsa.icons[rel].icon = link;
link = null;
}
break;
case "icon":
var label = "icon" + (link.attr("sizes") || "").toLowerCase();
if (cmsa.icons[label] && !cmsa.icons[label].icon) {
cmsa.icons[label].icon = link;
link = null;
} else if (!genericIcons[rel]) {
genericIcons[rel] = link;
link = null;
}
break;
}
if (link && (["apple-touch-icon", "shortcut icon", "icon"].indexOf(rel) > -1)) {
icons.push({icon: link});
}
});
if (!cmsa.icons["apple-touch-icon"].icon) {
cmsa.icons["apple-touch-icon"].icon = genericIcons["apple-touch-icon"];
genericIcons["apple-touch-icon"] = null;
}
if (!cmsa.icons["icon32x32"].icon) {
cmsa.icons["icon32x32"].icon = genericIcons["icon"];
genericIcons["icon"] = null;
}
if (!cmsa.icons["icon16x16"].icon) {
cmsa.icons["icon16x16"].icon = genericIcons["icon"];
genericIcons["icon"] = null;
}
if (genericIcons["apple-touch-icon"]) {
icons.push({icon: genericIcons["apple-touch-icon"]});
}
if (genericIcons["icon"]) {
icons.push({icon: genericIcons["icon"]});
}
for (var i = 0; i < icons.length; i++) {
icons[i].href = cmsa.icons["icon32x32"].href;
}
var head = jQuery(document.documentElement).find("head").first();
cmsa.icons["apple-touch-icon"].icon = cmsa.icons["apple-touch-icon"].icon || jQuery("<link rel='apple-touch-icon' sizes='180x180'>").appendTo(head);
cmsa.icons["shortcut icon"].icon = cmsa.icons["shortcut icon"].icon || jQuery("<link rel='shortcut icon' type='image/vnd.microsoft.icon'>").appendTo(head);
cmsa.icons["icon32x32"].icon = cmsa.icons["icon32x32"].icon || jQuery("<link rel='icon' type='image/png' sizes='32x32'>").appendTo(head);
cmsa.icons["icon16x16"].icon = cmsa.icons["icon16x16"].icon || jQuery("<link rel='icon' type='image/png' sizes='16x16'>").appendTo(head);
var alarmIcon = false;
var toggle = function () {
if ((cmsa.flashing !== "stop") || alarmIcon) {
for (var k in cmsa.icons) {
if (typeof cmsa.icons[k].sizes === "string") {
var sizes = cmsa.icons[k].icon.attr("sizes") || "";
var href = cmsa.icons[k].icon.attr("href") || cmsa.icons.transparent;
cmsa.icons[k].icon.attr("sizes", cmsa.icons[k].sizes);
cmsa.icons[k].icon.attr("href", cmsa.icons[k].href);
cmsa.icons[k].sizes = sizes;
cmsa.icons[k].href = href;
}
}
for (var i = 0; i < icons.length; i++) {
var href = icons[i].icon.attr("href") || cmsa.icons.transparent;
icons[i].icon.attr("href", icons[i].href);
icons[i].href = href;
}
alarmIcon = !alarmIcon;
}
if (cmsa.flashing !== "stop") {
setTimeout(toggle, 1000);
} else {
cmsa.flashing = false;
}
};
toggle();
}
},
notificationConfig: function (notification) {
var cmsa = config.macros.schedule.alarm;
return {
title: "TW Event",
options: {
body: (notification || {}).detail,
icon: cmsa.icons.alarm["icon32x32"],
tag: "config.macros.schedule.alarm.notify"
}
};
},
display: function (notification) {
var cmsa = config.macros.schedule.alarm;
if ((!notification || !notification.display) && cmsa.Notification) {
try {
cmsa.Notification.close();
cmsa.Notification = null;
} catch (e) {}
} else if (notification && notification.notify && notification.display) {
var notificationDetail = notification.detail || "";
if ("Notification" in window) {
Notification.requestPermission(function (status) {
if (status === "granted") {
if (cmsa.Notification) {
try {
cmsa.Notification.close();
cmsa.Notification = null;
} catch (e) {}
}
try {
var notificationConfig = cmsa.notificationConfig(notification);
var n = new Notification(notificationConfig.title || "", notificationConfig.options);
n.onshow = function () {
cmsa.Notification = n;
};
n.onclose = function () {
cmsa.Notification = null;
};
} catch (e) {
// Chromium for Android error
// Failed to construct 'Notification': Illegal constructor. Use ServiceWorkerRegistration.showNotification() instead.
// no straightforward workaround: https://stackoverflow.com/questions/75848607/how-can-i-create-a-javascript-serviceworker-instance-from-a-string
displayMessage(notificationDetail);
}
} else {
displayMessage(notificationDetail);
}
});
} else {
displayMessage(notificationDetail);
}
}
},
playing: false,
play: function (notification) {
var cmsa = config.macros.schedule.alarm;
if ((notification === false) && (cmsa.playing === true)) {
cmsa.playing = "stop";
} else if ((notification !== false) && (!notification || (notification.notify && notification.play)) && (cmsa.playing === false) && cmsa.sound.paused) {
cmsa.playing = true;
var count = 0;
cmsa.sound.onended = function () {
count++;
if ((cmsa.playing !== "stop") && (count < 3)) {
cmsa.sound.play();
} else {
cmsa.playing = false;
}
};
cmsa.sound.play();
}
},
morse: (function () {
var morse = {
text: "TW Event",
codes: {
A: ".-", B: "-...", C: "-.-.", D: "-..", E: ".",
F: "..-.", G: "--.", H: "....", I: "..", J: ".---",
K: "-.-", L: ".-..", M: "--", N: "-.", O: "---",
P: ".--.", Q: "--.-", R: ".-.", S: "...", T: "-",
U: "..-", V: "...-", W: ".--", X: "-..-", Y: "-.--",
Z: "--..",
"0": ".----", "1": "..---", "2": "...--", "3": "....-", "4": ".....",
"5": "-....", "6": "--...", "7": "---..", "8": "----.", "9": "-----"
},
interval: 100,
sequence: [],
encode: function (text, codes, interval) {
text = text.toUpperCase().trim();
var sequence = [];
var previousCharacter = " ";
for (var i = 0; i < text.length; i++) {
var character = text.charAt(i);
if (character === " ") {
sequence.push(7 * interval);
previousCharacter = character;
} else if (codes[character]) {
if (previousCharacter !== " ") {
sequence.push(3 * interval);
}
character = codes[character];
for (var j = 0; j < character.length; j++) {
if (j > 0) {
sequence.push(interval);
}
sequence.push(((character.charAt(j) === ".") ? 1 : 3) * interval);
}
previousCharacter = character;
}
}
return sequence;
}
};
morse.sequence = morse.encode(morse.text, morse.codes, morse.interval);
return morse;
})(),
vibrate: function (notification) {
var cmsa = config.macros.schedule.alarm;
if ((notification !== false) && (!notification || (notification.notify && notification.vibrate)) && cmsa.morse.sequence) {
try {
window.navigator.vibrate(cmsa.morse.sequence);
} catch (e) {}
}
},
notification: null,
notifications: {},
notify: function (notification, tag) {
var cmsa = config.macros.schedule.alarm;
var shouldNotify = false;
tag = (tag == null) ? "" : tag;
cmsa.notifications[tag] = cmsa.notifications[tag] || [];
if (cmsa.notifications[tag].indexOf(notification.detail) === -1) {
cmsa.notifications[tag].push(notification.detail);
if (cmsa.notifications[tag].indexOf("") === -1) {
cmsa.notifications[tag].push("");
}
shouldNotify = true;
}
notification.notify = shouldNotify;
cmsa.flash(notification);
cmsa.display(notification);
cmsa.play(notification);
cmsa.vibrate(notification);
},
custom: {},
monitor: function (monitor) {
var cmsa = config.macros.schedule.alarm;
try {
var currentDate = new Date();
var date = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), currentDate.getHours(), currentDate.getMinutes(), 0, 0);
var reminderEvents = new ScheduleReminderEvents();
reminderEvents.determineEvents(date, config.options.txtScheduleAlarmEventFilter, [0, 2]);
var playSound = false;
var vibrate = false;
var eventList = [];
var alarmEventList = [];
for (var i = 0; i < reminderEvents.list.length; i++) {
var reminderEvent = reminderEvents.list[i];
if (reminderEvent.start && reminderEvent.reminder.isTagged(config.options.txtScheduleAlarmEventFilter)) {
var eventStartDateTime = reminderEvent.reminder && reminderEvent.reminder.matchedDate
? new Date(reminderEvent.reminder.matchedDate.getTime())
: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventStartDateTime.setHours(Number(reminderEvent.start.substring(0, reminderEvent.start.indexOf(":"))));
eventStartDateTime.setMinutes(Number(reminderEvent.start.substring(reminderEvent.start.indexOf(":") + 1)));
var eventEndDateTime = eventStartDateTime;
if (reminderEvent.end) {
eventEndDateTime = reminderEvent.reminder && reminderEvent.reminder.matchedDate
? new Date(reminderEvent.reminder.matchedDate.getTime())
: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
eventEndDateTime.setHours(Number(reminderEvent.end.substring(0, reminderEvent.end.indexOf(":"))));
eventEndDateTime.setMinutes(Number(reminderEvent.end.substring(reminderEvent.end.indexOf(":") + 1)));
}
var event = (eventEndDateTime.getTime() > eventStartDateTime.getTime()) ? {start: eventStartDateTime, end: eventEndDateTime} : {start: eventEndDateTime, end: eventStartDateTime};
var alarmInterval = [0, 0];
var tags = reminderEvent.reminder.tags;
if (tags && tags.length) {
for (var j = 0; j < tags.length; j++) {
if (tags[j] && (tags[j].indexOf("alarm:") === 0)) {
var eventAlarmInterval = tags[j].substring(tags[j].indexOf(":") + 1);
eventAlarmInterval = eventAlarmInterval.split("...");
if (jQuery.isNumeric(eventAlarmInterval[0]) && ((eventAlarmInterval.length === 1) || jQuery.isNumeric(eventAlarmInterval[1]))) {
if (eventAlarmInterval.length === 1) {
eventAlarmInterval[1] = eventAlarmInterval[0];
eventAlarmInterval[0] = 0;
}
eventAlarmInterval[0] = parseInt(eventAlarmInterval[0], 10);
eventAlarmInterval[1] = parseInt(eventAlarmInterval[1], 10);
if (eventAlarmInterval[0] <= eventAlarmInterval[1]) {
alarmInterval[0] = eventAlarmInterval[0];
alarmInterval[1] = eventAlarmInterval[1];
} else {
alarmInterval[0] = eventAlarmInterval[1];
alarmInterval[1] = eventAlarmInterval[0];
}
break;
}
}
}
}
event.alarm = {};
event.alarm.start = event.start.getTime() + (alarmInterval[0] * 60 * 1000);
event.alarm.end = event.start.getTime() + (alarmInterval[1] * 60 * 1000);
eventList.push(event);
if ((date.getTime() >= event.alarm.start) && (date.getTime() <= event.alarm.end)) {
alarmEventList.push(event);
var reminder = reminderEvent.reminder;
reminder["params"]["format"] = "TITLE";
var eventTitle = wikifyPlainText(getReminderMessageForDisplay(reminder["diff"], reminder["params"], reminder["matchedDate"], reminder["tiddler"]), 0);
eventTitle = (eventTitle.lastIndexOf(">") === (eventTitle.length - 1)) ? eventTitle.substring(0, eventTitle.length - 1).trim() : eventTitle.trim();
event.detail = eventTitle;
event.display = !reminderEvent.reminder.isTagged("alarm:display:off");
playSound |= reminderEvent.reminder.isTagged("alarm:sound");
vibrate |= reminderEvent.reminder.isTagged("alarm:vibrate");
}
}
}
var customEvents = [];
cmsa.custom = cmsa.custom || {};
for (var k in cmsa.custom) {
customEvents.push(cmsa.custom[k]);
}
for (var i = 0; i < customEvents.length; i++) {
if ((customEvents[i] instanceof Object) && jQuery.isFunction(customEvents[i].init)) {
customEvents[i].init(i, customEvents, eventList, alarmEventList);
}
}
for (var i = 0; i < customEvents.length; i++) {
var events = null;
try {
if (jQuery.isFunction(customEvents[i])) {
events = customEvents[i].call(this, eventList, alarmEventList);
} else if ((customEvents[i] instanceof Object) && jQuery.isFunction(customEvents[i].events)) {
events = customEvents[i].events.call(this, eventList, alarmEventList);
} else if ((customEvents[i] instanceof Object) && (customEvents[i].events instanceof Object)) {
events = customEvents[i].events;
} else if (customEvents[i] instanceof Object) {
events = customEvents[i];
}
} catch (e) {
console.log(e);
}
if (events) {
events = Array.isArray(events) ? events : [events];
for (var j = 0; j < events.length; j++) {
var event = events[j];
eventList.push(event);
if (!event.alarm || (typeof event.alarm.start !== "number") || ((date.getTime() >= event.alarm.start) && (date.getTime() <= (event.alarm.end || event.alarm.start)))) {
alarmEventList.push(event);
playSound |= ("sound" in event) ? event.sound : playSound;
vibrate |= ("vibrate" in event) ? event.vibrate : vibrate;
}
}
}
}
if (!alarmEventList.length) {
cmsa.flash(false);
cmsa.display(false);
cmsa.play(false);
cmsa.vibrate(false);
} else {
alarmEventList.sort(function (a, b) {
if (a.start && b.start && (a.start.getTime() < b.start.getTime())) {
return -1;
} else if (a.start && b.start && (a.start.getTime() > b.start.getTime())) {
return 1;
} else if (a.end && b.end && (a.end.getTime() < b.end.getTime())) {
return -1;
} else if (a.end && b.end && (a.end.getTime() > b.end.getTime())) {
return 1;
} else if (config.macros.collator) {
return config.macros.collator.compare(a.detail || "", b.detail || "");
} else {
return (a.detail || "").localeCompare(b.detail || "", "en", {sensitivity: "base"});
}
});
var display = false;
var detail = "";
for (var i = 0; i < alarmEventList.length; i++) {
if (!("display" in alarmEventList[i]) || alarmEventList[i].display) {
detail += (detail ? "\n" : "") + alarmEventList[i].detail;
display = true;
}
}
var notification = {
detail: detail,
display: display,
play: playSound,
vibrate: vibrate
};
cmsa.notify(notification, currentDate.getFullYear() + "-" + currentDate.getMonth() + "-" + currentDate.getDate() + " " + currentDate.getHours() + ":" + currentDate.getMinutes());
}
} catch (e) {
console.log(e);
} finally {
if (jQuery.isFunction(monitor)) {
monitor();
}
}
}
};
//}}}
/***
|Name|SearchOptionsPlugin|
|Source|http://www.TiddlyTools.com/#SearchOptionsPlugin|
|Documentation|http://www.TiddlyTools.com/#SearchOptionsPluginInfo|
|Version|3.0.10|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|extend core search function with additional user-configurable options|
Adds extra options to core search function including selecting which data items to search, enabling/disabling incremental key-by-key searches, and generating a ''list of matching tiddlers'' instead of immediately displaying all matches. This plugin also adds syntax for rendering 'search links' within tiddler content to embed one-click searches using pre-defined 'hard-coded' search terms.
!!!!!Documentation
>see [[SearchOptionsPluginInfo]]
!!!!!Configuration
<<<
<<tiddler SearchOptions>>
<<option chkSearchResultsOptions>> Include {{{options...}}} slider in "search again" form
<<<
!!!!!Revisions
<<<
2011.04.08 3.0.10 fixed typo in CSS in formatSearchResults_buttons(). Restore missing options in Configuration section.
|please see [[SearchOptionsPluginInfo]] for additional revision details|
2005.10.18 1.0.0 Initial Release
<<<
!!!!!Code
***/
//{{{
version.extensions.SearchOptionsPlugin= {major: 3, minor: 0, revision: 10, date: new Date(2011,3,18)};
//}}}
//{{{
var defaults={
chkSearchTitles: true,
chkSearchText: true,
chkSearchTags: true,
chkSearchFields: true,
chkSearchTitlesFirst: true,
chkSearchList: true,
chkSearchHighlight: true,
chkSearchListTiddler: false,
chkSearchByDate: false,
chkIncrementalSearch: true,
chkSearchShadows: true,
chkSearchOpenTiddlers: false,
chkSearchResultsOptions:true,
chkSearchExcludeTags: true,
txtSearchExcludeTags: 'excludeSearch',
txtIncrementalSearchDelay: 500,
txtIncrementalSearchMin: 3
}; for (var id in defaults) if (config.options[id]===undefined)
config.options[id]=defaults[id];
if (config.macros.search.reportTitle==undefined)
config.macros.search.reportTitle="SearchResults"; // note: not a cookie!
config.macros.search.label+="\xa0"; // a little bit of space just because it looks better
//}}}
// // searchLink: {{{[search[text to find]] OR [search[text to display|text to find]]}}}
//{{{
config.formatters.push( {
name: "searchLink",
match: "\\[search\\[",
lookaheadRegExp: /\[search\[(.*?)(?:\|(.*?))?\]\]/mg,
prompt: "search for: '%0'",
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var label=lookaheadMatch[1];
var text=lookaheadMatch[2]||label;
var prompt=this.prompt.format([text]);
var btn=createTiddlyButton(w.output,label,prompt,
function(){story.search(this.getAttribute("searchText"))},"searchLink");
btn.setAttribute("searchText",text);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
});
//}}}
// // incremental search uses option settings instead of hard-coded delay and minimum input values
//{{{
var fn=config.macros.search.onKeyPress;
fn=fn.toString().replace(/500/g, "config.options.txtIncrementalSearchDelay||500");
fn=fn.toString().replace(/> 2/g, ">=(config.options.txtIncrementalSearchMin||3)");
eval("config.macros.search.onKeyPress="+fn);
//}}}
// // REPLACE story.search() for option to "show search results in a list"
//{{{
Story.prototype.search = function(text,useCaseSensitive,useRegExp)
{
var co=config.options; // abbrev
var re=new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
if (config.options.chkSearchHighlight) highlightHack=re;
var matches = store.search(re,co.chkSearchByDate?"modified":"title","");
if (co.chkSearchByDate) matches=matches.reverse(); // most recent first
var q = useRegExp ? "/" : "'";
clearMessage();
if (!matches.length) {
if (co.chkSearchListTiddler) discardSearchResults();
displayMessage(config.macros.search.failureMsg.format([q+text+q]));
} else {
if (co.chkSearchList||co.chkSearchListTiddler)
reportSearchResults(text,matches);
else {
var titles = []; for(var t=0; t<matches.length; t++) titles.push(matches[t].title);
this.closeAllTiddlers(); story.displayTiddlers(null,titles);
displayMessage(config.macros.search.successMsg.format([matches.length, q+text+q]));
}
}
highlightHack = null;
}
//}}}
// // REPLACE store.search() for enhanced searching/sorting options
//{{{
TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
{
var co=config.options; // abbrev
var tids = this.reverseLookup("tags",excludeTag,!!match,sortField);
var opened=[]; story.forEachTiddler(function(tid,elem){opened.push(tid);});
// eliminate tiddlers tagged with excluded tags
if (co.chkSearchExcludeTags&&co.txtSearchExcludeTags.length) {
var ex=co.txtSearchExcludeTags.readBracketedList();
var temp=[]; for(var t=tids.length-1; t>=0; t--)
if (!tids[t].tags.containsAny(ex)) temp.push(tids[t]);
tids=temp;
}
// scan for matching titles first...
var results = [];
if (co.chkSearchTitles) {
for(var t=0; t<tids.length; t++) {
if (co.chkSearchOpenTiddlers && !opened.contains(tids[t].title)) continue;
if(tids[t].title.search(searchRegExp)!=-1) results.push(tids[t]);
}
if (co.chkSearchShadows)
for (var t in config.shadowTiddlers) {
if (co.chkSearchOpenTiddlers && !opened.contains(t)) continue;
if ((t.search(searchRegExp)!=-1) && !store.tiddlerExists(t))
results.push((new Tiddler()).assign(t,config.shadowTiddlers[t]));
}
}
// then scan for matching text, tags, or field data
for(var t=0; t<tids.length; t++) {
if (co.chkSearchOpenTiddlers && !opened.contains(tids[t].title)) continue;
if (co.chkSearchText && tids[t].text.search(searchRegExp)!=-1)
results.pushUnique(tids[t]);
if (co.chkSearchTags && tids[t].tags.join(" ").search(searchRegExp)!=-1)
results.pushUnique(tids[t]);
if (co.chkSearchFields && store.forEachField!=undefined)
store.forEachField(tids[t],
function(tid,field,val) {
if (val.search(searchRegExp)!=-1) results.pushUnique(tids[t]);
},
true); // extended fields only
}
// then check for matching text in shadows
if (co.chkSearchShadows)
for (var t in config.shadowTiddlers) {
if (co.chkSearchOpenTiddlers && !opened.contains(t)) continue;
if ((config.shadowTiddlers[t].search(searchRegExp)!=-1) && !store.tiddlerExists(t))
results.pushUnique((new Tiddler()).assign(t,config.shadowTiddlers[t]));
}
// if not 'titles first', or sorting by modification date,
// re-sort results to so titles, text, tag and field matches are mixed together
if(!sortField) sortField = "title";
var bySortField=function(a,b){
if(a[sortField]==b[sortField])return(0);else return(a[sortField]<b[sortField])?-1:+1;
}
if (!co.chkSearchTitlesFirst || co.chkSearchByDate) results.sort(bySortField);
return results;
}
//}}}
// // HIJACK core {{{<<search>>}}} macro to add "report" and "simple inline" output
//{{{
config.macros.search.SOP_handler=config.macros.search.handler;
config.macros.search.handler = function(place,macroName,params)
{
// if "report", use SearchOptionsPlugin report generator for inline output
if (params[1]&¶ms[1].substr(0,6)=="report") {
var keyword=params[0];
var options=params[1].split("=")[1]; // split "report=option+option+..."
var heading=params[2]?params[2].unescapeLineBreaks():"";
var matches=store.search(new RegExp(keyword.escapeRegExp(),"img"),"title","excludeSearch");
if (matches.length) wikify(heading+window.formatSearchResults(keyword,matches,options),place);
} else if (params[1]) {
var keyword=params[0];
var heading=params[1]?params[1].unescapeLineBreaks():"";
var seperator=params[2]?params[2].unescapeLineBreaks():", ";
var matches=store.search(new RegExp(keyword.escapeRegExp(),"img"),"title","excludeSearch");
if (matches.length) {
var out=[];
for (var m=0; m<matches.length; m++) out.push("[["+matches[m].title+"]]");
wikify(heading+out.join(seperator),place);
}
} else
config.macros.search.SOP_handler.apply(this,arguments);
};
//}}}
// // SearchResults panel handling
//{{{
setStylesheet(".searchResults { padding:1em 1em 0 1em; }","searchResults"); // matches std tiddler padding
config.macros.search.createPanel=function(text,matches,body) {
function getByClass(e,c) { var d=e.getElementsByTagName("div");
for (var i=0;i<d.length;i++) if (hasClass(d[i],c)) return d[i]; }
var panel=createTiddlyElement(null,"div","searchPanel","searchPanel");
this.renderPanel(panel,text,matches,body);
var oldpanel=document.getElementById("searchPanel");
if (!oldpanel) { // insert new panel just above tiddlers
var da=document.getElementById("displayArea");
da.insertBefore(panel,da.firstChild);
} else { // if panel exists
var oldwrap=getByClass(oldpanel,"searchResults");
var newwrap=getByClass(panel,"searchResults");
// if no prior content, just insert new content
if (!oldwrap) oldpanel.insertBefore(newwrap,null);
else { // swap search results content but leave containing panel intact
oldwrap.style.display='block'; // unfold wrapper if needed
var i=oldwrap.getElementsByTagName("input")[0]; // get input field
if (i) { var pos=this.getCursorPos(i); i.onblur=null; } // get cursor pos, ignore blur
oldpanel.replaceChild(newwrap,oldwrap);
panel=oldpanel; // use existing panel
}
}
this.showPanel(true,pos);
return panel;
}
config.macros.search.renderPanel=function(panel,text,matches,body) {
var wrap=createTiddlyElement(panel,"div",null,"searchResults");
wrap.onmouseover = function(e){ addClass(this,"selected"); }
wrap.onmouseout = function(e){ removeClass(this,"selected"); }
// create toolbar: "open all", "fold/unfold", "close"
var tb=createTiddlyElement(wrap,"div",null,"toolbar");
var b=createTiddlyButton(tb, "open all", "open all matching tiddlers", function() {
story.displayTiddlers(null,this.getAttribute("list").readBracketedList()); return false; },"button");
var list=""; for(var t=0;t<matches.length;t++) list+='[['+matches[t].title+']] ';
b.setAttribute("list",list);
var b=createTiddlyButton(tb, "fold", "toggle display of search results", function() {
config.macros.search.foldPanel(this); return false; },"button");
var b=createTiddlyButton(tb, "close", "dismiss search results", function() {
config.macros.search.showPanel(false); return false; },"button");
createTiddlyText(createTiddlyElement(wrap,"div",null,"title"),"Search for: "+text); // title
wikify(body,createTiddlyElement(wrap,"div",null,"viewer")); // report
return panel;
}
config.macros.search.showPanel=function(show,pos) {
var panel=document.getElementById("searchPanel");
var i=panel.getElementsByTagName("input")[0];
i.onfocus=show?function(){config.macros.search.stayFocused(true);}:null;
i.onblur=show?function(){config.macros.search.stayFocused(false);}:null;
if (show && panel.style.display=="block") { // if shown, grab focus, restore cursor
if (i&&this.stayFocused()) { i.focus(); this.setCursorPos(i,pos); }
return;
}
if(!config.options.chkAnimate) {
panel.style.display=show?"block":"none";
if (!show) { removeChildren(panel); config.macros.search.stayFocused(false); }
} else {
var s=new Slider(panel,show,false,show?"none":"children");
s.callback=function(e,p){e.style.overflow="visible";}
anim.startAnimating(s);
}
return panel;
}
config.macros.search.foldPanel=function(button) {
var d=document.getElementById("searchPanel").getElementsByTagName("div");
for (var i=0;i<d.length;i++) if (hasClass(d[i],"viewer")) var v=d[i]; if (!v) return;
var show=v.style.display=="none";
if(!config.options.chkAnimate)
v.style.display=show?"block":"none";
else {
var s=new Slider(v,show,false,"none");
s.callback=function(e,p){e.style.overflow="visible";}
anim.startAnimating(s);
}
button.innerHTML=show?"fold":"unfold";
return false;
}
config.macros.search.stayFocused=function(keep) { // TRUE/FALSE=set value, no args=get value
if (keep===undefined) return this.keepReportInFocus;
this.keepReportInFocus=keep;
return keep
}
config.macros.search.getCursorPos=function(i) {
var s=0; var e=0; if (!i) return { start:s, end:e };
try {
if (i.setSelectionRange) // FF
{ s=i.selectionStart; e=i.selectionEnd; }
if (document.selection && document.selection.createRange) { // IE
var r=document.selection.createRange().duplicate();
var len=r.text.length; s=0-r.moveStart('character',-100000); e=s+len;
}
}catch(e){};
return { start:s, end:e };
}
config.macros.search.setCursorPos=function(i,pos) {
if (!i||!pos) return; var s=pos.start; var e=pos.end;
if (i.setSelectionRange) //FF
i.setSelectionRange(s,e);
if (i.createTextRange) // IE
{ var r=i.createTextRange(); r.collapse(true); r.moveStart("character",s); r.select(); }
}
//}}}
// // SearchResults report generation
// note: these functions are defined globally, so they can be more easily redefined to customize report formats//
//{{{
if (!window.reportSearchResults) window.reportSearchResults=function(text,matches)
{
var cms=config.macros.search; // abbrev
var body=window.formatSearchResults(text,matches);
if (!config.options.chkSearchListTiddler) // show #searchResults panel
window.scrollTo(0,ensureVisible(cms.createPanel(text,matches,body)));
else { // write [[SearchResults]] tiddler
var title=cms.reportTitle;
var who=config.options.txtUserName;
var when=new Date();
var tags="excludeLists excludeSearch temporary";
var tid=store.getTiddler(title); if (!tid) tid=new Tiddler();
tid.set(title,body,who,when,tags);
store.addTiddler(tid);
story.closeTiddler(title);
story.displayTiddler(null,title);
}
}
if (!window.formatSearchResults) window.formatSearchResults=function(text,matches,opt)
{
var body='';
var title=config.macros.search.reportTitle
var q = config.options.chkRegExpSearch ? "/" : "'";
if (!opt) var opt="all";
var parts=opt.split("+");
for (var i=0; i<parts.length; i++) { var p=parts[i].toLowerCase();
if (p=="again"||p=="all") body+=window.formatSearchResults_again(text,matches);
if (p=="summary"||p=="all") body+=window.formatSearchResults_summary(text,matches);
if (p=="list"||p=="all") body+=window.formatSearchResults_list(text,matches);
if (p=="buttons"||p=="all") body+=window.formatSearchResults_buttons(text,matches);
}
return body;
}
if (!window.formatSearchResults_again) window.formatSearchResults_again=function(text,matches)
{
var title=config.macros.search.reportTitle
var body='';
// search again
body+='{{span{<<search "'+text.replace(/"/g,'"')+'">> /%\n';
body+='%/<html><input type="button" value="search again"';
body+=' onclick="var t=this.parentNode.parentNode.getElementsByTagName(\'input\')[0];';
body+=' config.macros.search.doSearch(t); return false;">';
if (!config.options.chkSearchResultsOptions) // omit "options..."
body+='</html>';
else {
body+=' <a href="javascript:;" onclick="';
body+=' var e=this.parentNode.nextSibling;';
body+=' var show=e.style.display!=\'block\';';
body+=' if(!config.options.chkAnimate) e.style.display=show?\'block\':\'none\';';
body+=' else anim.startAnimating(new Slider(e,show,false,\'none\'));';
body+=' return false;">options...</a>';
body+='</html>@@display:none;border-left:1px dotted;margin-left:1em;padding:0;padding-left:.5em;font-size:90%;/%\n';
body+=' %/<<tiddler SearchOptions>>@@';
};
body+='}}}\n\n';
return body;
}
if (!window.formatSearchResults_summary) window.formatSearchResults_summary=function(text,matches)
{
// summary: nn tiddlers found matching '...', options used
var body='';
var co=config.options; // abbrev
var title=config.macros.search.reportTitle
var q = co.chkRegExpSearch ? "/" : "'";
body+="''"+config.macros.search.successMsg.format([matches.length,q+"{{{"+text+"}}}"+q])+"''\n";
var opts=[];
if (co.chkSearchTitles) opts.push("titles");
if (co.chkSearchText) opts.push("text");
if (co.chkSearchTags) opts.push("tags");
if (co.chkSearchFields) opts.push("fields");
if (co.chkSearchShadows) opts.push("shadows");
if (co.chkSearchOpenTiddlers) body+="^^//search limited to displayed tiddlers only//^^\n";
body+="~~ searched in "+opts.join(" + ")+"~~\n";
body+=(co.chkCaseSensitiveSearch||co.chkRegExpSearch?"^^ using ":"")
+(co.chkCaseSensitiveSearch?"case-sensitive ":"")
+(co.chkRegExpSearch?"pattern ":"")
+(co.chkCaseSensitiveSearch||co.chkRegExpSearch?"matching^^\n":"");
return body;
}
if (!window.formatSearchResults_list) window.formatSearchResults_list=function(text,matches)
{
// bullet list of links to matching tiddlers
var body='';
var co=config.options; // abbrev
var pattern=co.chkRegExpSearch?text:text.escapeRegExp();
var sensitive=co.chkCaseSensitiveSearch?"mg":"img";
var link='{{tiddlyLinkExisting{<html><nowiki><a href="javascript:;" onclick="'
+'if(config.options.chkSearchHighlight)'
+' highlightHack=new RegExp(\x27'+pattern+'\x27.escapeRegExp(),\x27'+sensitive+'\x27);'
+'story.displayTiddler(null,\x27%0\x27);'
+'highlightHack = null; return false;'
+'" title="%2">%1</a></html>}}}';
for(var t=0;t<matches.length;t++) {
body+="* ";
if (co.chkSearchByDate)
body+=matches[t].modified.formatString('YYYY.0MM.0DD 0hh:0mm')+" ";
var title=matches[t].title;
var fixup=title.replace(/'/g,"\\x27").replace(/"/g,"\\x22");
var tid=store.getTiddler(title);
var tip=tid?tid.getSubtitle():''; tip=tip.replace(/"/g,""");
body+=link.format([fixup,title,tip])+'\n';
}
return body;
}
if (!window.formatSearchResults_buttons) window.formatSearchResults_buttons=function(text,matches)
{
// embed buttons only if writing SearchResults to tiddler
if (!config.options.chkSearchListTiddler) return "";
// "open all" button
var title=config.macros.search.reportTitle;
var body="";
body+="@@display:block;<html><input type=\"button\" href=\"javascript:;\" "
+"onclick=\"story.displayTiddlers(null,[";
for(var t=0;t<matches.length;t++)
body+="'"+matches[t].title.replace(/\'/mg,"\\'")+"'"+((t<matches.length-1)?", ":"");
body+="],1);\" accesskey=\"O\" value=\"open all matching tiddlers\"></html> ";
// "discard SearchResults" button
body+="<html><input type=\"button\" href=\"javascript:;\" "
+"onclick=\"discardSearchResults()\" value=\"discard "+title+"\"></html>";
body+="@@\n";
return body;
}
if (!window.discardSearchResults) window.discardSearchResults=function()
{
// remove the tiddler
story.closeTiddler(config.macros.search.reportTitle);
store.deleteTiddler(config.macros.search.reportTitle);
store.notify(config.macros.search.reportTitle,true);
}
//}}}
// // DELIVER [[SearchOptions]] shadow payload
//{{{
config.shadowTiddlers.SearchOptions = store.getTiddlerText('SearchOptionsPlugin##panel','');
config.annotations.SearchOptions = 'created by SearchOptionsPlugin';
//}}}
/***
//{{{
!panel
search in:
{{nowrap{<<option chkSearchTitles>>titles <<option chkSearchText>>text <<option chkSearchTags>>tags}}} /%
%/{{nowrap{<<option chkSearchFields>>fields <<option chkSearchShadows>>shadows}}}
----
{{nowrap{<<option chkCaseSensitiveSearch>>case-sensitive}}} /%
%/{{nowrap{<<option chkRegExpSearch>>match text patterns}}}
{{nowrap{<<option chkIncrementalSearch>>key-by-key search:}}} /%
%/{{threechar smallform nowrap{<<option txtIncrementalSearchMin>> or more characters}}} /%
%/{{threechar smallform nowrap{<<option txtIncrementalSearchDelay>> msec delay}}}<hr>
{{nowrap{<<option chkSearchList>>show results in a list }}} /%
%/{{nowrap{<<option chkSearchListTiddler>>save list in ''[[SearchResults]]''}}}
{{nowrap{<<option chkSearchTitlesFirst>>show title matches first}}} /%
%/{{nowrap{<<option chkSearchByDate>>sort results by date}}} /%
%/{{nowrap{<<option chkSearchHighlight>>highlight matching text}}}
----
{{nowrap{<<option chkSearchOpenTiddlers>>search open tiddlers only}}}
{{nowrap{<<option chkSearchExcludeTags>>exclude tiddlers tagged with:}}}
{{editor{<<option txtSearchExcludeTags>>}}}
!end
//}}}
***/
/***
|Name|SearchOptionsPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[CollatorPlugin]] [[SearchOptionsPlugin]]|
!!!!!Code
***/
//{{{
(function () {
if (config.macros.collator) {
var text = store.getTiddlerText("SearchOptionsPlugin");
if (text) {
var startPosition = text.indexOf("TiddlyWiki.prototype.search");
if (startPosition > -1) {
var endPosition = text.indexOf("//}}}", startPosition);
if (endPosition > -1) {
text = text.substring(startPosition, endPosition)
.replace("if(a[sortField]==b[sortField])return(0);else return(a[sortField]<b[sortField])?-1:+1;", "if(a[sortField]==b[sortField])return(0);else return config.macros.collator.compare(a[sortField], b[sortField]);")
.replace("tids[t].text", "(store.getTiddlerText(tids[t].title) || '')");
eval(text);
}
}
}
}
})();
//}}}
//{{{
(function () {
var code = eval("window.formatSearchResults_list").toString();
code = code.replace("\\x27'+pattern+'\\x27", "\\x27'+pattern.replace(/'/g, \"\\\\'\").replace(/\"/g, \"\\\\x22\")+'\\x27");
eval("window.formatSearchResults_list = function formatSearchResults_list" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
var code = eval("config.macros.search.SOP_handler").toString();
code = code.replace(/(parseParams.*?)false/, '$1true');
eval("config.macros.search.SOP_handler = function SOP_handler" + code.substring(code.indexOf("(")));
})();
(function () {
var code = eval("window.formatSearchResults_again").toString();
code = code.replace(/<<search .*?>>/, '<<search {{"\'+text.replace(/\\{/g, " { ").replace(/\\}/g, " } ").replace(/\\\\/g, "\\\\\\\\").replace(/"/g, "\\\\\\\"")+\'".replace(/ \\\\{ /g, "{").replace(/ \\\\} /g, "}")}}>>');
eval("window.formatSearchResults_again = function formatSearchResults_again" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
removeStyleSheet("searchResults");
setStylesheet(".searchResults { padding:1em 1em 0 1em; } .searchResults .button { padding:0.2em 0.4em; }", "searchResults");
//}}}
/***
|Name|SectionLinksPlugin|
|Source|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Documentation|http://www.TiddlyTools.com/#SectionLinksPlugin|
|Version|1.4.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|allow tiddler sections in TiddlyLinks to be used as anchor points|
This plugin enhances tiddler links so that they can include section references that ''auto-scroll to the indicated section heading'' within a tiddler (i.e., similar to the 'anchor' behavior provided in HTML by {{{<a name="foo">}}} and {{{<a href="#foo">...</a>}}}). The {{{<<tiddler>>}}} macro syntax has also be extended to allow section references without a tiddler name, so that transclusion of //hidden sections from the same tiddler// can be easily accomplished. The plugin also adds a new macro, <<sectionTOC>> which can auto-generate and embed a 'Table of Contents' outline view into a tiddler to enable quick navigation to sections within that tiddler.
!!!Usage
<<<
!!!!~TiddlyLink syntax
You can link to a section of a tiddler by adding the "##sectionname" syntax to the tiddlername:
{{{
[[SomeTiddler##SomeSection]]
}}}
When clicked, the tiddler is displayed and the specified section heading is automatically scrolled into view. If the tiddler title is omitted or the 'here' keyword is used, e.g.,
{{{
[[##SomeSection]] or [[here##SomeSection]]>>
}}}
then the current containing tiddler is implied by default.
!!!!HTML anchor syntax
You can use HTML syntax to create a scrollable 'anchor' location within a tiddler without use of the standard TW section heading syntax:
{{{
<html><a name="sectionname" /></html>
}}}
You can then link to that section using the enhanced TiddlyLink syntax as above.
!!!!{{{<<tiddler>>}}} macro
The {{{<<tiddler>>}}} syntax has been extended so that when the tiddler title is omitted or the 'here' keyword is used, e.g.,
{{{
<<tiddler ##SomeSection>> or <<tiddler here##SomeSection>>
}}}
then the current containing tiddler is implied by default.
!!!!"""<<sectionTOC>>""" macro
This macro generates a 'Table of Contents' outline-style bullet list with links to all sections within the current tiddler. Simply place the following macro at the //end of the tiddler content// (i.e., following all section headings). Important note: //''The {{{<<sectionTOC>>}}} macro must occur at the end of the tiddler in order to locate the rendered section headings that precede it.''//
{{{
<<sectionTOC>> or <<sectionTOC className>>
}}}
To position the macro's //output// within the tiddler, you must create a special 'target element' that uses a specific classname (default='sectionTOC'), like this:
{{{
{{sectionTOC{}}}
}}}
When the {{{<<sectionTOC>>}}} macro is rendered, it will find the matching 'sectionTOC'-classed element and writes it's output there. You can also add the macro and/or target elements directly to the [[ViewTemplate]] definition, so that every tiddler can automatically display the table of contents:
{{{
<span class='sectionTOC'></span> <!-- target element -->
...
<span macro='sectionTOC'></span> <!-- must be at end of tiddler -->
}}}
<<<
!!!Configuration
<<<
You can change the {{{<<SectionTOC>>}}} output link format by adding the following statement to a tiddler tagged with <<tag systemConfig>>
{{{
config.macros.sectionTOC.linkFormat='[[%0|##%0]]';
}}}
The default value (shown above) produces a link to each section within the tiddler, using "%0" to insert the section name into the link. You can add extra formatting to generate additional output to suit your purposes. For example, if you have EditSectionPlugin installed, you could include a link that invokes that plugin's popup editor directly from each item in the TOC display, like this:
{{{
config.macros.sectionTOC.linkFormat='[[%0|##%0]] <<editSection [[##%0]] [[(edit)]]>>';
}}}
<<<
!!!Examples
<<<
links to sections defined by ''TW heading syntax'' (e.g, {{{!!!sectionname}}}):{{indent{
[[SectionLinksPlugin##onClickTiddlerLink]]
[[##onClickTiddlerLink]] //(current tiddler implied)//}}}
links to anchors defined by ''HTML syntax'' (e.g., {{{<html><a href="anchorname"></html>}}}):{{indent{
[[SectionLinksPlugin##sampleanchorlink]]
[[##sampleanchorlink]] //(current tiddler implied)//}}}
<<<
!!!Revisions
<<<
2011.12.21 1.4.2 refactor sectionTOCformat to permit customization
2011.02.08 1.4.1 in isExternalLink() hijack, strip section references before testing for external link
2010.08.09 1.4.0 in scrollToSection(), added support for using HTML <a name="..."> anchor elements
2009.08.21 1.3.4 added handling to ignore leading/trailing whitespace in section references
2009.08.21 1.3.3 in createTiddlyLink(), add tiddlyLinkNonExistingSection class if matching section is not found
2009.08.14 1.3.2 in createTiddlyLink(), don't override core value for ~TiddlyLink attribute
2009.08.02 1.3.1 in sectionTOC.handler(), trim leading/trailing whitespace from generated section links
2009.08.01 1.3.0 in scrollToSection(), apply 3-tier section matching (exact, startsWith, contains)
2009.07.06 1.2.2 fixed displayTiddler() hijack
2009.07.03 1.2.1 in {{{<<sectionTOC>>}}}, suppress output if target is not found
2009.06.02 1.2.0 added support for 'here' keyword in {{{[[here##section]]}}} links and {{{<<tiddler here##section>>}}} macro
2009.04.09 1.1.1 in sectionTOC macro, make target visible when TOC is rendered.
2009.01.18 1.1.0 added {{{<<sectionTOC>>}}} macro to generate numbered-bullet links to sections of current tiddler
2009.01.06 1.0.0 converted to stand-alone plugin
2008.10.14 0.0.0 initial release (as [[CoreTweaks]] #784 - http://trac.tiddlywiki.org/ticket/784)
<<<
!!!Code
***/
//{{{
version.extensions.SectionLinksPlugin= {major: 1, minor: 4, revision: 2, date: new Date(2011,12,21)};
Story.prototype.scrollToSection = function(title,section) {
if (!title||!section) return; var t=this.getTiddler(title); if (!t) return null;
var elems=t.getElementsByTagName('*');
var heads=[]; var anchors=[];
for (var i=0; i<elems.length; i++)
if (['H1','H2','H3','H4','H5'].contains(elems[i].nodeName)) heads.push(elems[i]);
for (var i=0; i<elems.length; i++)
if (elems[i].nodeName=='A' && (elems[i].getAttribute('name')||'').length) anchors.push(elems[i]);
var found=null;
for (var i=0; i<heads.length; i++)
if (getPlainText(heads[i]).trim()==section) { found=heads[i]; break; }
if (!found) for (var i=0; i<heads.length; i++)
if (getPlainText(heads[i]).trim().startsWith(section)) { found=heads[i]; break; }
if (!found) for (var i=0; i<heads.length; i++)
if (getPlainText(heads[i]).trim().indexOf(section)!=-1) { found=heads[i]; break; }
if (!found) for (var i=0; i<anchors.length; i++)
if (anchors[i].getAttribute('name')==section) { found=anchors[i]; break; }
if (!found) for (var i=0; i<anchors.length; i++)
if (anchors[i].getAttribute('name').startsWith(section)) { found=anchors[i]; break; }
if (!found) for (var i=0; i<anchors.length; i++)
if (anchors[i].getAttribute('name').indexOf(section)!=-1) { found=anchors[i]; break; }
if (found) {
// if section heading is collapsed, click to expand it - see [[FoldHeadingsPlugin]]
if (hasClass(found,'foldable') && found.nextSibling.style.display=='none') found.onclick();
// scroll *after* tiddler animation
var delay=config.options.chkAnimate?config.animDuration+100:0;
setTimeout('window.scrollTo('+findPosX(found)+','+findPosY(found)+')',delay);
return found;
}
}
//}}}
/***
!!!!core hijacks
***/
/***
!!!!!createTiddlyLink
***/
//{{{
// [[tiddlername##section]] and [[##section]]
if (!window.createTiddlyLink_section)
window.createTiddlyLink_section=window.createTiddlyLink;
window.createTiddlyLink=function(place,title) {
var t=story.findContainingTiddler(place); var tid=t?t.getAttribute('tiddler'):'';
var parts=title.split(config.textPrimitives.sectionSeparator);
var title=parts[0]; var section=parts[1]; if (section) section=section.trim();
if (!title.length || title.toLowerCase()=='here') title=tid; // default=current tiddler
arguments[1]=title;
var btn=createTiddlyLink_section.apply(this,arguments);
if (section) {
btn.setAttribute('section',section);
if (store.getTiddlerText(title+config.textPrimitives.sectionSeparator+section)===null)
addClass(btn,'tiddlyLinkNonExistingSection');
}
return btn;
}
//}}}
/***
!!!!!onClickTiddlerLink
***/
//{{{
if (!window.onClickTiddlerLink_section)
window.onClickTiddlerLink_section=window.onClickTiddlerLink;
window.onClickTiddlerLink=function(ev) {
var e=ev||window.event; var target=resolveTarget(e); var title=null;
while (target!=null && title==null) {
title=target.getAttribute('tiddlyLink');
section=target.getAttribute('section');
target=target.parentNode;
}
var t=story.findContainingTiddler(target); var tid=t?t.getAttribute('tiddler'):'';
if (title!=tid||!section) // avoid excess scrolling for intra-tiddler links
onClickTiddlerLink_section.apply(this,arguments);
story.scrollToSection(title,section);
return false;
}
//}}}
/***
!!!!! displayTiddler
***/
//{{{
if (!Story.prototype.displayTiddler_section)
Story.prototype.displayTiddler_section=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler)
{
var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
var parts=title.split(config.textPrimitives.sectionSeparator);
var title=parts[0]; var section=parts[1]; if (section) section=section.trim();
if (!title.length || title.toLowerCase()=='here') {
var t=story.findContainingTiddler(place);
title=t?t.getAttribute('tiddler'):'';
}
arguments[1]=title; // default=current tiddler
this.displayTiddler_section.apply(this,arguments);
story.scrollToSection(title,section);
}
//}}}
/***
<html><a name="sampleanchorlink" /></html>This is a sample ''anchor link'': {{{<html><a name="sampleanchorlink" /></html>}}}
!!!!!isExternalLink
***/
//{{{
if (!config.formatterHelpers.isExternalLink_section)
config.formatterHelpers.isExternalLink_section=config.formatterHelpers.isExternalLink;
config.formatterHelpers.isExternalLink=function(link) { // remove section references before testing
var l=link.split(config.textPrimitives.sectionSeparator)[0];
return config.formatterHelpers.isExternalLink_section(l);
}
//}}}
/***
!!!!!tiddler.handler
***/
//{{{
if (!config.macros.tiddler.handler_section)
config.macros.tiddler.handler_section=config.macros.tiddler.handler;
config.macros.tiddler.handler=function(place,macroName,params,wikifier,paramString,tiddler)
{
if (!params[0]) return;
var sep=config.textPrimitives.sectionSeparator;
var parts=params[0].split(sep); var tid=parts[0]; var sec=parts[1]; if (sec) sec=sec.trim();
if ((tid.toLowerCase()=='here'||!tid.length) && sec) { // fixup for 'here##section' and '##section'
var here=story.findContainingTiddler(place)
var tid=here?here.getAttribute('tiddler'):tiddler?tiddler.title:'';
arguments[2][0]=tid+sep+sec;
arguments[4]=paramString.replace(new RegExp('(here)?'+sep+sec),tid+sep+sec);
}
config.macros.tiddler.handler_section.apply(this,arguments);
}
//}}}
/***
!!!!sectionTOC macro
***/
//{{{
config.macros.sectionTOC = {
targetClass: 'sectionTOC',
linkFormat: '[[%0|##%0]]',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var out=[];
var targetClass=params[0]||this.targetClass;
var t=story.findContainingTiddler(place); if (!t) return;
var elems=t.getElementsByTagName('*');
var level=5; // topmost heading level
for (var i=0; i<elems.length; i++) {
var txt=getPlainText(elems[i]).trim();
var link=this.linkFormat.format([txt]);
switch(elems[i].nodeName) {
case 'H1': out.push('#'+link); level=1; break;
case 'H2': out.push('##'+link); level=level<2?level:2; break;
case 'H3': out.push('###'+link); level=level<3?level:3; break;
case 'H4': out.push('####'+link); level=level<4?level:4; break;
case 'H5': out.push('#####'+link); level=level<5?level:5; break;
default: if (hasClass(elems[i],targetClass)) var target=elems[i];
}
}
// trim excess bullet levels
if (level>1) for (var i=0; i<out.length; i++) out[i]=out[i].substr(level-1);
// show numbered list
if (out.length && target) {
if (target.style.display=='none') target.style.display='block';
wikify(out.join('\n'),target);
}
}
}
//}}}
/***
!!!Invoke macro
{{{
<<sectionTOC>>
}}}
***/
// //<<sectionTOC>>
/***
|Name|SectionLinksPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[SectionLinksPlugin]]|
!!!!!Code
***/
//{{{
config.macros.sectionTOC.determineIndexInfo = function (section) {
var indexInfo = null;
if (section) {
section.replace(/^(.*?)(\{)(\d*)(\})$/, function (m, p1, p2, p3, p4) {
if (jQuery.isNumeric(p3)) {
indexInfo = {
section: p1,
index: parseInt(p3) - 1
};
}
});
}
return indexInfo;
};
config.macros.sectionTOC.findIndexedSection = function (section, heads, anchors) {
var found = null;
var indexInfo = config.macros.sectionTOC.determineIndexInfo(section);
if (indexInfo) {
heads = heads ? heads : [];
anchors = anchors ? anchors : [];
var foundCount = 0;
for (var i = 0; i < heads.length; i++) {
if (jQuery(heads[i]).contents().not(jQuery(heads[i]).children()).text().trim() == indexInfo.section) {
if (foundCount === indexInfo.index) {
found = heads[i];
break;
}
foundCount++;
}
}
if (!found) {
foundCount = 0;
for (var i = 0; i < anchors.length; i++) {
if (anchors[i].getAttribute("name") == indexInfo.section) {
if (foundCount === indexInfo.index) {
found = anchors[i];
break;
}
foundCount++;
}
}
}
}
return found;
};
config.macros.sectionTOC.findIndexedSectionText = function (section, text) {
var match = null;
var indexInfo = config.macros.sectionTOC.determineIndexInfo(section);
if (indexInfo) {
var re = new RegExp("(^!{1,6}[ \t]*" + indexInfo.section.escapeRegExp() + "[ \t]*\n)", "mg");
re.lastIndex = 0;
var matchCount = 0;
while ((match = re.exec(text)) != null) {
if (matchCount === indexInfo.index) {
break;
}
matchCount++;
}
}
return match;
};
//}}}
//{{{
(function () {
var code = eval("Story.prototype.scrollToSection").toString();
code = code.replace(/(if\s*?\(found\))/, "found = found ? found : config.macros.sectionTOC.findIndexedSection(section, heads, anchors);\n\t$1");
eval("Story.prototype.scrollToSection = function scrollToSection" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
config.macros.sectionTOC.linkFormat = '[[%0|##%0{%1}]]';
var code = eval("config.macros.sectionTOC.handler").toString();
code = code.replace(/(var)/, "var sectionIndexes = {};\n\t\t$1");
code = code.replace(/var.*?getPlainText.*?;/, "var txt = jQuery(elems[i]).contents().not(jQuery(elems[i]).children()).text().trim();\n\t\t\tsectionIndexes[txt] = (sectionIndexes[txt] == null) ? 1 : sectionIndexes[txt] + 1;");
code = code.replace(/(format\(\[txt)(\]\))/, "$1, sectionIndexes[txt]$2")
eval("config.macros.sectionTOC.handler = function handler" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
(function () {
var code = eval("TiddlyWiki.prototype.getTiddlerText").toString();
code = code.replace(/(if\s*?\(match\)|if\s*\(!match\))/, "match = match ? match : config.macros.sectionTOC.findIndexedSectionText(section, text);\n\t\t$1");
eval("TiddlyWiki.prototype.getTiddlerText = function getTiddlerText" + code.substring(code.indexOf("(")));
if (store.getTiddlerText_TiddlerEncryptionPlugin) {
eval("store.getTiddlerText_TiddlerEncryptionPlugin = function getTiddlerText_TiddlerEncryptionPlugin" + code.substring(code.indexOf("(")));
}
})();
//}}}
/***
|Name|ServerUploadPlugin|
|License|[[TW Notes License]]|
|Requires|[[aes.js]] [[InlineJavascriptPlugin]]|
!!!!!Java Server
Download the Transfer Server: [[TransferServer.jar|data:application/java-archive;base64,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]]
Usage: {{{java -jar TransferServer.jar [<tiddler-filter-file>] <password> [<bind-address> | <port> | <bind-address>:<port>] [<authorized-root-folder>...]}}}
where the optional {{{tiddler-filter-file}}} is a path to a local file which contains the tiddlers to exclude from a given TiddlyWiki, as in the following example:
{{{
notes2023.html
Events
Agenda
notes2024.html
Agenda
DateTime
}}}
Lines starting with a {{{tab}}} character are ignored. In the following example, the [[Agenda]] tiddler will not be excluded from the {{{notes2023.html}}} nor the {{{notes2024.html}}} TidlyWiki files:
{{{
notes2023.html
Events
Agenda
notes2024.html
Agenda
DateTime
}}}
Served file MIME types may be specified within the {{{mime-types.txt}}} file located within the current working directory. The {{{TWN_MIME_TYPES}}} environment variable may be used to modify the name and location of this file. The following example illustrates the structure of this file (see [[FileMIMETypes]]):
{{{
text/plain
.txt .text .vbs .asp .cgi .pl
----
text/html
.htm .html .hta .htx .mht
----
text/comma-separated-values
.csv
----
text/javascript
.js, .mjs
}}}
!!!!!Node.js Server
The following files must be placed within the same folder as the server script
* {{{aes.js}}}: the raw contents of the [[aes.js]] tiddler
* {{{TWSaverHTTPServer.js}}}: the [[Node.js Server|TWSaverHTTPServerPlugin##Node.js Server]] script from [[TWSaverHTTPServerPlugin]]
{{{
const fs = require("fs");
const path = require("path");
const process = require("process");
const Aes = require("./aes.js");
const tws = require("./TWSaverHTTPServer.js")();
var exports = function () {
var NO_BYTES = 65536;
var mimeTypesFile = process.env.TWN_MIME_TYPES || "mime-types.txt";
var mimeTypesFileLastModified = null;
var mimeTypesMap = {};
var tiddlerFilterFile = null;
var tiddlerFilterFileLastModified = null;
var tiddlerFilterMap = {};
var password = "";
var loadMimeTypes = function () {
var stats = ((mimeTypesFile != null) && fs.existsSync(mimeTypesFile)) ? fs.lstatSync(mimeTypesFile) : null;
if ((mimeTypesFile != null) && ((stats == null) || !stats.isFile())) {
mimeTypesFileLastModified = null;
mimeTypesMap = {};
} else if ((mimeTypesFile != null) && (stats != null) && stats.isFile() && (stats.mtimeMs !== mimeTypesFileLastModified)) {
mimeTypesFileLastModified = stats.mtimeMs;
mimeTypesMap = {};
console.log("Loading: " + path.resolve(mimeTypesFile));
var parts = fs.readFileSync(mimeTypesFile, "utf8").split(/\r?\n----\r?\n/);
for (var i = 0; i < parts.length; i++) {
var lines = parts[i].split(/\r?\n/);
var mimeType = lines[0];
var extensions = lines[1];
if (mimeType && extensions) {
extensions = extensions.split(" ");
for (var j = 0; j < extensions.length; j++) {
var extension = extensions[j].trim().toLowerCase();
if (extension) {
mimeTypesMap[extension] = mimeType;
}
}
}
}
}
};
var loadFilteredTiddlers = function () {
var stats = ((tiddlerFilterFile != null) && fs.existsSync(tiddlerFilterFile)) ? fs.lstatSync(tiddlerFilterFile) : null;
if ((tiddlerFilterFile != null) && ((stats == null) || !stats.isFile())) {
tiddlerFilterFileLastModified = null;
tiddlerFilterMap = {};
} else if ((tiddlerFilterFile != null) && (stats != null) && stats.isFile() && (stats.mtimeMs !== tiddlerFilterFileLastModified)) {
tiddlerFilterFileLastModified = stats.mtimeMs;
tiddlerFilterMap = {};
console.log("Loading: " + path.resolve(tiddlerFilterFile));
var filteredTiddlers = null;
var lines = fs.readFileSync(tiddlerFilterFile, "utf8").split(/\r?\n/);
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (!line.length) {
filteredTiddlers = null;
} else if ((line.charAt(0) !== '\t') && (filteredTiddlers == null)) {
filteredTiddlers = tiddlerFilterMap[line];
if (filteredTiddlers == null) {
filteredTiddlers = {};
tiddlerFilterMap[line] = filteredTiddlers;
}
} else if ((line.charAt(0) !== '\t') && (filteredTiddlers != null)) {
filteredTiddlers[line] = true;
}
}
}
};
var filterTiddlyWiki = function (input, output, filteredTiddlers, excludeFilteredTiddlers, tempFileDescriptor, tempTiddlerIndexes, sync, callback) {
var tiddlerIndexes = {};
var fileContent = Buffer.allocUnsafe(NO_BYTES);
var fileContentSize = 0;
var bytes = Buffer.allocUnsafe(NO_BYTES);
var inContent = false;
var filter = false;
var stopFiltering = false;
var lineEnded = false;
var completeLine = true;
var byteCounter = 0;
var tiddlerStart = -1;
var title = null;
var dataFlushed;
var byteIndex = 0;
var noBytes = -1;
(function send(done) {
dataFlushed = true;
try {
while (dataFlushed && (noBytes !== 0)) {
if (byteIndex >= noBytes) {
input.read(bytes, function (bytesRead) {
noBytes = bytesRead;
if (bytesRead === 0) {
done();
} else {
byteIndex = 0;
if (!sync) {
send(done);
}
}
});
if (!sync) {
break;
}
} else {
for (; dataFlushed && (byteIndex < noBytes); byteIndex++) {
var inputByte = bytes[byteIndex];
var endOfLine = inputByte === '\n'.charCodeAt(0);
if ((filteredTiddlers != null) && endOfLine && !lineEnded) {
lineEnded = true;
if (completeLine) {
var line = fileContent.subarray(0, fileContentSize).toString("utf8");
if (line === "<div id=\"storeArea\">") {
inContent = true;
} else if (line === "<!--POST-STOREAREA-->") {
inContent = false;
filter = false;
} else if (inContent && filter && tempTiddlerIndexes) {
if ((line === "<pre></pre>") && tempTiddlerIndexes[title]) {
var buffer = Buffer.allocUnsafe(NO_BYTES);
var position = tempTiddlerIndexes[title][0];
while (position < tempTiddlerIndexes[title][1]) {
var bytesRead = fs.readSync(tempFileDescriptor, buffer, 0, Math.min(buffer.length, tempTiddlerIndexes[title][1] - position), position);
position += bytesRead;
var chunk = Buffer.allocUnsafe(bytesRead);
buffer.copy(chunk, 0, 0, bytesRead);
dataFlushed &= output.write(chunk);
byteCounter += chunk.length;
}
fileContentSize = 0;
} else if (line === "</div>") {
fileContentSize = 0;
stopFiltering = true;
} else {
filter = false;
}
} else if (inContent && filter && (line === "</div>")) {
var chunk = Buffer.from("<pre></pre>\n", "utf8");
chunk = Buffer.concat([chunk, Buffer.from("</div>\n", "utf8")]);
dataFlushed = output.write(chunk);
byteCounter += chunk.length;
fileContentSize = 0;
stopFiltering = true;
} else if (inContent && (line === "</div>") && (tiddlerStart > -1)) {
tiddlerIndexes[title] = [tiddlerStart, byteCounter + fileContentSize + 1];
tiddlerStart = -1;
} else if (inContent && (line.indexOf("<div ") === 0) && ((line.indexOf(" title=\"") > -1) )) { // || (line.indexOf(" tiddler=\"") > -1) )) {
title = (line.indexOf(" title=\"") > -1) ? line.substring(line.indexOf(" title=\"") + " title=\"".length)
: line.substring(line.indexOf(" tiddler=\"") + " tiddler=\"".length); // not supported: chkUsePreForStorage end-of-line separation is required for now
if (title.indexOf('"') > -1) {
title = title.substring(0, title.indexOf('"'));
}
if ((excludeFilteredTiddlers && (filteredTiddlers[title] != null)) || (!excludeFilteredTiddlers && (filteredTiddlers[title] == null))) {
fileContent[fileContentSize++] = inputByte;
var chunk = Buffer.allocUnsafe(fileContentSize);
fileContent.copy(chunk, 0, 0, fileContentSize);
dataFlushed = output.write(chunk);
byteCounter += chunk.length;
fileContentSize = 0;
filter = true;
} else {
tiddlerStart = byteCounter + fileContentSize + 1;
}
}
}
} else if ((filteredTiddlers != null) && !endOfLine && lineEnded) {
if (!filter) {
var chunk = Buffer.allocUnsafe(fileContentSize);
fileContent.copy(chunk, 0, 0, fileContentSize);
dataFlushed = output.write(chunk);
byteCounter += chunk.length;
}
fileContentSize = 0;
lineEnded = false;
completeLine = true;
if (stopFiltering) {
filter = false;
stopFiltering = false;
}
}
fileContent[fileContentSize++] = inputByte;
if (fileContentSize >= NO_BYTES) {
if (tempTiddlerIndexes) {
filter = false;
}
if (!filter) {
var chunk = Buffer.allocUnsafe(fileContentSize);
fileContent.copy(chunk, 0, 0, fileContentSize);
dataFlushed = output.write(chunk);
byteCounter += chunk.length;
}
fileContentSize = 0;
completeLine = false;
}
if (!dataFlushed) {
output.once("drain", function () { send(done); });
}
}
}
}
} catch (error) {
console.log(error);
done(error);
}
})(function (error) {
if (!error && !filter && (fileContentSize > 0)) {
try {
var chunk = Buffer.allocUnsafe(fileContentSize);
fileContent.copy(chunk, 0, 0, fileContentSize);
output.write(chunk);
callback(tiddlerIndexes);
} catch (error) {
callback(tiddlerIndexes, error);
}
} else {
callback(tiddlerIndexes, error);
}
});
};
var requestListener = function (request, response) {
var onRequestData;
var onRequestEnd;
var sendResponse = function (data, contentType, responseHeaders) {
if (onRequestData) {
request.removeListener("data", onRequestData);
onRequestData = null;
}
if (onRequestEnd) {
request.removeListener("end", onRequestEnd);
onRequestEnd = null;
}
if (!response.writableEnded) {
var filteredTiddlers = null;
if (Array.isArray(data)) {
filteredTiddlers = tiddlerFilterMap[data[0]];
}
if ((contentType == null) && (filteredTiddlers != null)) {
contentType = "text/html; charset=UTF-8";
} else if (contentType == null) {
contentType = "text/plain; charset=UTF-8";
}
var contentLength = Array.isArray(data) ? (fs.existsSync(data[1]) ? fs.lstatSync(data[1]).size : 0) : Buffer.byteLength(data);
response.statusCode = 200;
response.setHeader("Server", "Transfer Server");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Content-Type", contentType);
if ((filteredTiddlers == null) || !Object.keys(filteredTiddlers).length) {
response.setHeader("Content-Length", contentLength);
}
if (responseHeaders) {
for (var i = 0; i < responseHeaders.length; i++) {
response.setHeader(responseHeaders[i][0], responseHeaders[i][1]);
}
}
if (Array.isArray(data)) {
var fileDescriptor = null;
try {
fileDescriptor = fs.openSync(data[1]);
filterTiddlyWiki({read: function (bytes, callback) { callback(fs.readSync(fileDescriptor, bytes, 0, bytes.length)); }}, response, filteredTiddlers, true, null, null, true, function (tiddlerIndexes, error) {
try {
if (fileDescriptor != null) {
try {
fs.closeSync(fileDescriptor);
} catch (error) {
console.log(error);
}
}
} finally {
response.end();
}
});
} catch (error) {
console.log(error);
response.end();
}
} else {
response.end(data);
}
}
};
response.on("error", function (error) {
console.log(error);
});
if (request.method === "OPTIONS") {
sendResponse("success");
} else if (request.method === "GET") {
var data = "";
var contentType = null;
loadMimeTypes();
var fileUrl = request.url.trim();
if (fileUrl.indexOf('?') > -1) {
var parameters = fileUrl.substring(fileUrl.indexOf('?') + 1);
fileUrl = fileUrl.substring(0, fileUrl.indexOf('?'));
var parameter = parameters;
if ((parameters.indexOf('&') > -1) && (parameters.indexOf('&') + 1 < parameters.length)) {
parameter = parameters.substring(0, parameters.indexOf('&'));
parameters = parameters.substring(parameters.indexOf('&') + 1);
} else {
parameters = null;
}
if ((parameter.indexOf('=') > -1) && (parameter.indexOf('=') + 1 < parameter.length)) {
parameter = parameter.substring(parameter.indexOf('=') + 1);
contentType = decodeURIComponent(parameter.replace(/\+/g, "%20"));
} else if (fileUrl.indexOf(".") > -1) {
contentType = mimeTypesMap[fileUrl.substring(fileUrl.lastIndexOf(".")).trim().toLowerCase()];
}
} else if (fileUrl.indexOf(".") > -1) {
contentType = mimeTypesMap[fileUrl.substring(fileUrl.lastIndexOf(".")).trim().toLowerCase()];
}
fileUrl = decodeURIComponent(fileUrl.replace(/\+/g, "%20"));
fileUrl = (fileUrl.indexOf("/") === 0) ? fileUrl.substring(1).trim() : fileUrl;
var isIndex = fileUrl.endsWith("/index");
fileUrl = isIndex ? fileUrl.substring(0, fileUrl.lastIndexOf("/index")) : fileUrl;
var file = path.resolve(fileUrl);
if (!path.isAbsolute(fileUrl)
&& tws.authorizedRootFolders.length && (tws.authorizedRootFolders[0] != null)) {
file = path.resolve(tws.authorizedRootFolders[0][0] + fileUrl);
}
var folder = isIndex ? file : path.dirname(file);
var authorizedFolder = tws.checkAuthorizedFolder(folder);
if (!authorizedFolder) {
console.log("Unauthorized folder: " + folder);
} else {
if (isIndex) {
if (fs.existsSync(folder) && fs.lstatSync(folder).isDirectory()) {
console.log("Listing files: " + folder);
try {
var files = fs.readdirSync(folder);
for (var i = 0; i < files.length; i++) {
if ((files[i] !== ".") && (files[i] !== "..")) {
data += files[i] + '\n';
}
}
} catch (error) {
console.log(error);
}
}
} else {
console.log("Sending file: " + file);
data = [fileUrl, file];
loadFilteredTiddlers();
}
}
sendResponse(data, contentType);
} else if (request.method === "POST") {
var chunks = [];
var input = null;
request.on("data", onRequestData = function (chunk) {
if (input != null) {
input.process(chunk);
} else {
chunks.push(chunk);
if (Object.keys(tiddlerFilterMap).length > 0) {
var chunkBuffer = Buffer.concat(chunks);
var fileUrl = chunkBuffer.toString("utf8");
var file;
if (fileUrl.indexOf(" : ") > -1) {
fileUrl = fileUrl.substring(0, fileUrl.indexOf(" : "));
chunk = chunkBuffer.subarray(Buffer.from(fileUrl + " : ").length);
fileUrl = fileUrl.replace(/\\/g, "/");
if ((fileUrl.length > 3) && (fileUrl.indexOf("//") === 0) && (fileUrl.indexOf('/', "//".length) > -1)
&& tws.authorizedRootFolders.length && (tws.authorizedRootFolders[0] != null)) {
fileUrl = fileUrl.substring(fileUrl.indexOf('/', "//".length) + 1);
file = path.resolve(tws.authorizedRootFolders[0][0] + fileUrl);
} else {
file = path.resolve(fileUrl);
}
var folder = path.dirname(file);
var authorizedFolder = tws.checkAuthorizedFolder(folder);
var mainFileUrl = (request.headers["x-twn-mainfile"] || "").replace(/\\/g, "/");
if ((mainFileUrl.length > 3) && (mainFileUrl.indexOf("//") === 0) && (mainFileUrl.indexOf('/', "//".length) > -1)) {
mainFileUrl = mainFileUrl.substring(mainFileUrl.indexOf('/', "//".length) + 1);
} else {
mainFileUrl = fileUrl;
}
var tempFileUrl = null;
var tempFile = null;
var tempFolder = null;
var authorizedTempFolder = true;
var deleteTempFile = false;
if (request.headers["x-twn-tempfilepath"] && tiddlerFilterMap[mainFileUrl]) {
tempFileUrl = request.headers["x-twn-tempfilepath"].replace(/\\/g, "/");
if ((tempFileUrl.length > 3) && (tempFileUrl.indexOf("//") === 0) && (tempFileUrl.indexOf('/', "//".length) > -1)) {
tempFileUrl = tempFileUrl.substring(tempFileUrl.indexOf('/', "//".length) + 1);
tempFile = path.resolve(path.isAbsolute(tempFileUrl) ? tempFileUrl : tws.authorizedRootFolders[0][0] + tempFileUrl);
tempFolder = path.dirname(tempFile);
authorizedTempFolder = tws.checkAuthorizedFolder(tempFolder);
tempFileUrl = tempFile;
deleteTempFile = request.headers["x-twn-deletetempfile"] === "true";
} else {
authorizedTempFolder = false;
}
}
console.log("Saving file: " + file);
if (!authorizedFolder) {
var data = "Unauthorized folder: " + folder;
console.log(" " + data);
sendResponse(data, "text/plain");
} else if (!authorizedTempFolder) {
var data = "Unauthorized temporary file path: " + tempFileUrl;
console.log(" " + data);
sendResponse(data, "text/plain");
} else {
try {
loadFilteredTiddlers();
if (tempFolder && !fs.existsSync(tempFolder)) {
fs.mkdirSync(tempFolder, {recursive: true});
}
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder, {recursive: true});
}
var tempTiddlerIndexes = null;
if ((tempFile != null) && fs.existsSync(file) && !fs.existsSync(tempFile) && tiddlerFilterMap[mainFileUrl]) {
var inputFileDescriptor = fs.openSync(file, "r");
var outputFileDescriptor = fs.openSync(tempFile, "w");
filterTiddlyWiki({read: function (bytes, callback) { callback(fs.readSync(inputFileDescriptor, bytes, 0, bytes.length)); }},
{write: function (chunk) { fs.writeSync(outputFileDescriptor, chunk); return true; }},
tiddlerFilterMap[mainFileUrl], false, null, null, true, function (tiddlerIndexes, error) {
try {
fs.closeSync(inputFileDescriptor);
} catch (error) {
console.log(error);
}
try {
fs.closeSync(outputFileDescriptor);
} catch (error) {
console.log(error);
}
if (error) {
tempTiddlerIndexes = false;
sendResponse(error.message, "text/plain", [["X-TWN-TempFileError", "true"]]);
} else {
tempTiddlerIndexes = tiddlerIndexes;
}
});
} else if ((tempFile != null) && fs.existsSync(tempFile)) {
var inputFileDescriptor = fs.openSync(tempFile, "r");
filterTiddlyWiki({read: function (bytes, callback) { callback(fs.readSync(inputFileDescriptor, bytes, 0, bytes.length)); }},
{write: function (chunk) { return true; }},
{}, true, null, null, true, function (tiddlerIndexes, error) {
try {
fs.closeSync(inputFileDescriptor);
} catch (error) {
console.log(error);
}
if (error) {
tempTiddlerIndexes = false;
sendResponse(error.message, "text/plain");
} else {
tempTiddlerIndexes = tiddlerIndexes;
}
});
}
if (tempTiddlerIndexes !== false) {
input = {
chunk: null,
bytes: null,
callback: null,
process: function (chunk) {
if (this.bytes && this.callback) {
if (chunk == null) {
this.chunk = null;
this.bytes = null;
this.callback(0);
} else if ((chunk.length > 0) && (this.bytes.length >= chunk.length)) {
chunk.copy(this.bytes, 0, 0, chunk.length);
this.chunk = null;
this.bytes = null;
this.callback(chunk.length);
} else if (chunk.length > 0) {
chunk.copy(this.bytes, 0, 0, this.bytes.length);
this.chunk = this.chunk.subarray(this.bytes.length);
this.bytes = null;
this.callback(this.bytes.length);
}
} else {
console.log("Error processing chunk:" + (this.bytes ? "" : " bytes null") + (this.callback ? "" : " callback null"));
}
},
read: function (bytes, callback) {
this.bytes = bytes;
this.callback = callback;
if (this.chunk) {
this.process(this.chunk);
}
}
};
var output = {
fileDescriptor: fs.openSync(file, "w"),
write: function (chunk) {
try {
fs.writeSync(this.fileDescriptor, chunk);
} catch (error) {
console.log(error);
try {
fs.closeSync(this.fileDescriptor);
} catch (error) {
console.log(error);
}
sendResponse(error.message, "text/plain");
}
return true;
}
};
var tempFileDescriptor = ((tempFile != null) && fs.existsSync(tempFile)) ? fs.openSync(tempFile, "r") : null;
filterTiddlyWiki(input, output, tiddlerFilterMap[mainFileUrl], true, tempFileDescriptor, tempFileDescriptor ? tempTiddlerIndexes : null, false, function (tiddlerIndexes, error) {
try {
fs.closeSync(output.fileDescriptor);
} catch (closeSyncError) {
console.log(closeSyncError);
error = error || closeSyncError;
}
if (tempFileDescriptor) {
try {
fs.closeSync(tempFileDescriptor);
} catch (closeSyncError) {
console.log(closeSyncError);
error = error || closeSyncError;
}
if (!error && deleteTempFile) {
console.log("Removing temporary file: " + tempFile);
try {
fs.unlinkSync(tempFile);
if (fs.existsSync(tempFile)) {
console.log(" Cannot remove temporary file: " + tempFile);
}
} catch (error) {
console.log(" Cannot remove temporary file: " + tempFile);
console.log(error);
}
}
}
if (error) {
sendResponse(error.message, "text/plain");
}
});
input.process(chunk);
}
} catch (error) {
console.log(error);
sendResponse(error.message, "text/plain");
}
}
}
}
}
}).on("end", onRequestEnd = function () {
var data = "success";
if (input != null) {
input.process(null);
input = null;
sendResponse(data);
} else {
var parameters = Buffer.concat(chunks).toString();
do {
var parameter = parameters;
if ((parameters.indexOf('&') > -1) && (parameters.indexOf('&') + 1 < parameters.length)) {
parameter = parameters.substring(0, parameters.indexOf('&'));
parameters = parameters.substring(parameters.indexOf('&') + 1);
} else {
parameters = null;
}
if ((parameter.indexOf('=') > -1) && (parameter.indexOf('=') + 1 < parameter.length)) {
parameter = parameter.substring(parameter.indexOf('=') + 1);
}
parameter = decodeURIComponent(parameter.replace(/\+/g, "%20"));
if (password !== "") {
parameter = Aes.Ctr.decrypt(parameter, password, 256);
}
if ((parameter.indexOf("savefilename:") === -1) && (parameter.indexOf("appendfilename:") === -1)
&& (parameter.indexOf("savefilename64:") === -1)
&& (parameter.indexOf("appendfilename64:") === -1)
&& (parameter.indexOf("removefilename:") === -1)) {
console.log("Unable to process request");
data = "failure";
} else if ((parameter.indexOf(':') > -1)
&& (parameter.indexOf(':') + 1 < parameter.length)) {
var fileUrl = parameter.substring(parameter.indexOf(':') + 1);
var fileContent = "";
if ((fileUrl.indexOf('\n') > -1) && (fileUrl.indexOf('\n') + 1 < fileUrl.length)) {
fileContent = fileUrl.substring(fileUrl.indexOf('\n') + 1);
fileUrl = fileUrl.substring(0, fileUrl.indexOf('\n'));
}
fileUrl = fileUrl.replace('\\', '/');
var file = path.resolve(fileUrl);
if (!path.isAbsolute(fileUrl)
&& tws.authorizedRootFolders.length && (tws.authorizedRootFolders[0] != null)) {
file = path.resolve(tws.authorizedRootFolders[0][0] + fileUrl);
}
var folder = path.dirname(file);
var authorizedFolder = tws.checkAuthorizedFolder(folder);
if (!authorizedFolder) {
if (parameter.indexOf("savefilename") === 0) {
console.log("Saving file: " + file);
console.log(" Unauthorized folder: " + folder);
} else if (parameter.indexOf("appendfilename") === 0) {
console.log("Appending file: " + file);
console.log(" Unauthorized folder: " + folder);
} else if (parameter.indexOf("removefilename:") === 0) {
console.log("Removing file: " + file);
console.log(" Unauthorized folder: " + folder);
}
data = "failure";
} else {
if ((parameter.indexOf("savefilename") === 0) || (parameter.indexOf("appendfilename") === 0)) {
console.log(((parameter.indexOf("savefilename") === 0) ? "Saving" : "Appending")
+ " file: " + file);
try {
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder, {recursive: true});
}
fileDescriptor = fs.openSync(file, (parameter.indexOf("appendfilename") === 0) ? "a" : "w");
if ((parameter.indexOf("savefilename64:") === 0)
|| (parameter.indexOf("appendfilename64:") === 0)) {
fs.writeSync(fileDescriptor, atob(fileContent));
} else {
fs.writeSync(fileDescriptor, fileContent);
}
} catch (error) {
console.log(error);
data = "failure";
} finally {
if (fileDescriptor != null) {
try {
fs.closeSync(fileDescriptor);
} catch (error) {
console.log(error);
}
}
}
} else if (parameter.indexOf("removefilename:") === 0) {
console.log("Removing file: " + file);
try {
fs.unlinkSync(file);
if (fs.existsSync(file)) {
console.log(" Cannot remove file: " + file);
}
} catch (error) {
console.log(" Cannot remove file: " + file);
console.log(error);
data = "failure";
}
}
}
}
} while (parameters != null);
sendResponse(data, "text/plain");
}
}).on("error", function (error) {
console.log(error);
sendResponse("failure", "text/plain");
});
}
};
var paramIndex = 3;
if ((process.argv.length > 2) && fs.existsSync(process.argv[2]) && fs.lstatSync(process.argv[2]).isFile()) {
paramIndex = 4;
if (process.argv.length > 3) {
password = process.argv[3];
}
tiddlerFilterFile = process.argv[2];
} else if (process.argv.length > 2) {
password = process.argv[2];
}
tws.start(process.argv, paramIndex, requestListener);
loadMimeTypes();
loadFilteredTiddlers();
};
module.exports = exports;
if (require.main === module) {
exports();
}
}}}
Usage: {{{node TransferServer.js [<tiddler-filter-file>] <password> [<bind-address> | <port> | <bind-address>:<port>] [<authorized-root-folder>...]}}}
see [[tiddler-filter-file|ServerUploadPlugin##Java Server]]
!!!!!PHP Server
;index.php
//{{{
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Authorization, Content-Type");
header("Content-Type: text/plain");
$dir = '';
$dh = opendir(empty($dir) ? '.' : $dir);
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..' && $file != '.htaccess' && $file != 'index.php') {
echo $dir . "$file\n";
}
}
closedir($dh);
?>
//}}}
;.htaccess
{{{
# a2enmod headers
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "Authorization, Content-Type"
Header set Access-Control-Allow-Methods "GET"
</IfModule>
<FilesMatch "\.(html|htm|js|css|vfb|ics|txt)$">
FileETag None
<IfModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</IfModule>
</FilesMatch>
}}}
;update.php
Change the ''$password'' value below to the password used to encrypt uploaded content.
<script label="generate password">
var length = 32;
var lettersOnly = false;
var sequence = "";
for (var i = 0; i < length; i++) {
Math.random();
Math.random();
if (lettersOnly) {
var character = Math.floor(Math.random() * (10 + 26 + 26));
if (character < 10) {
sequence = sequence + character;
} else if (character < 36) {
sequence = sequence + String.fromCharCode(character - 10 + 'A'.charCodeAt(0));
} else {
sequence = sequence + String.fromCharCode(character - 10 - 26 + 'a'.charCodeAt(0));
}
} else {
var character = Math.floor(Math.random() * (126 - 32 + 1));
sequence = sequence + String.fromCharCode(character + 32);
}
}
return "\n{{{\n" + sequence + "\n}}}";
</script>
@@''Note:'' Make sure that the permissions of the directory in which this script will upload content are sufficient in order to allow write access.@@
https://www.movable-type.co.uk/scripts/aes-php.html
<script>
return store.getTiddlerText("ServerUploadPlugin##PHP-script").replace(/\* \//g, "*" + "/");
</script>/%
!!!!!PHP-script
{{{
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Authorization, Content-Type");
?><?php
$password = 'L0ck it up saf3';
$authorizedRootFolders = null; // array('/full/path/to/authorized/folder1/', '/full/path/to/authorized/folder2/');
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
/* AES implementation in PHP * /
/* (c) Chris Veness 2005-2014 www.movable-type.co.uk/scripts * /
/* Right of free use is granted for all commercial or non-commercial use under CC-BY licence. * /
/* No warranty of any form is offered. * /
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
Class Aes
{
/**
* AES Cipher function [§5.1]: encrypt 'input' with Rijndael algorithm
*
* @param input message as byte-array (16 bytes)
* @param w key schedule as 2D byte-array (Nr+1 x Nb bytes) -
* generated from the cipher key by keyExpansion()
* @return ciphertext as byte-array (16 bytes)
* /
public static function cipher($input, $w)
{
$Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
$Nr = count($w) / $Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
$state = array(); // initialise 4xNb byte-array 'state' with input [§3.4]
for ($i = 0; $i < 4 * $Nb; $i++) $state[$i % 4][floor($i / 4)] = $input[$i];
$state = self::addRoundKey($state, $w, 0, $Nb);
for ($round = 1; $round < $Nr; $round++) { // apply Nr rounds
$state = self::subBytes($state, $Nb);
$state = self::shiftRows($state, $Nb);
$state = self::mixColumns($state, $Nb);
$state = self::addRoundKey($state, $w, $round, $Nb);
}
$state = self::subBytes($state, $Nb);
$state = self::shiftRows($state, $Nb);
$state = self::addRoundKey($state, $w, $Nr, $Nb);
$output = array(4 * $Nb); // convert state to 1-d array before returning [§3.4]
for ($i = 0; $i < 4 * $Nb; $i++) $output[$i] = $state[$i % 4][floor($i / 4)];
return $output;
}
/**
* Xor Round Key into state S [§5.1.4].
* /
private static function addRoundKey($state, $w, $rnd, $Nb)
{
for ($r = 0; $r < 4; $r++) {
for ($c = 0; $c < $Nb; $c++) $state[$r][$c] ^= $w[$rnd * 4 + $c][$r];
}
return $state;
}
/**
* Apply SBox to state S [§5.1.1].
* /
private static function subBytes($s, $Nb)
{
for ($r = 0; $r < 4; $r++) {
for ($c = 0; $c < $Nb; $c++) $s[$r][$c] = self::$sBox[$s[$r][$c]];
}
return $s;
}
/**
* Shift row r of state S left by r bytes [§5.1.2].
* /
private static function shiftRows($s, $Nb)
{
$t = array(4);
for ($r = 1; $r < 4; $r++) {
for ($c = 0; $c < 4; $c++) $t[$c] = $s[$r][($c + $r) % $Nb]; // shift into temp copy
for ($c = 0; $c < 4; $c++) $s[$r][$c] = $t[$c]; // and copy back
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
return $s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
}
/**
* Combine bytes of each col of state S [§5.1.3].
* /
private static function mixColumns($s, $Nb)
{
for ($c = 0; $c < 4; $c++) {
$a = array(4); // 'a' is a copy of the current column from 's'
$b = array(4); // 'b' is a·{02} in GF(2^8)
for ($i = 0; $i < 4; $i++) {
$a[$i] = $s[$i][$c];
$b[$i] = $s[$i][$c] & 0x80 ? $s[$i][$c] << 1 ^ 0x011b : $s[$i][$c] << 1;
}
// a[n] ^ b[n] is a·{03} in GF(2^8)
$s[0][$c] = $b[0] ^ $a[1] ^ $b[1] ^ $a[2] ^ $a[3]; // 2*a0 + 3*a1 + a2 + a3
$s[1][$c] = $a[0] ^ $b[1] ^ $a[2] ^ $b[2] ^ $a[3]; // a0 * 2*a1 + 3*a2 + a3
$s[2][$c] = $a[0] ^ $a[1] ^ $b[2] ^ $a[3] ^ $b[3]; // a0 + a1 + 2*a2 + 3*a3
$s[3][$c] = $a[0] ^ $b[0] ^ $a[1] ^ $a[2] ^ $b[3]; // 3*a0 + a1 + a2 + 2*a3
}
return $s;
}
/**
* Generate Key Schedule from Cipher Key [§5.2].
*
* Perform key expansion on cipher key to generate a key schedule.
*
* @param key cipher key byte-array (16 bytes).
* @return key schedule as 2D byte-array (Nr+1 x Nb bytes).
* /
public static function keyExpansion($key)
{
$Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
$Nk = count($key) / 4; // key length (in words): 4/6/8 for 128/192/256-bit keys
$Nr = $Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
$w = array();
$temp = array();
for ($i = 0; $i < $Nk; $i++) {
$r = array($key[4 * $i], $key[4 * $i + 1], $key[4 * $i + 2], $key[4 * $i + 3]);
$w[$i] = $r;
}
for ($i = $Nk; $i < ($Nb * ($Nr + 1)); $i++) {
$w[$i] = array();
for ($t = 0; $t < 4; $t++) $temp[$t] = $w[$i - 1][$t];
if ($i % $Nk == 0) {
$temp = self::subWord(self::rotWord($temp));
for ($t = 0; $t < 4; $t++) $temp[$t] ^= self::$rCon[$i / $Nk][$t];
} else if ($Nk > 6 && $i % $Nk == 4) {
$temp = self::subWord($temp);
}
for ($t = 0; $t < 4; $t++) $w[$i][$t] = $w[$i - $Nk][$t] ^ $temp[$t];
}
return $w;
}
/**
* Apply SBox to 4-byte word w.
* /
private static function subWord($w)
{
for ($i = 0; $i < 4; $i++) $w[$i] = self::$sBox[$w[$i]];
return $w;
}
/**
* Rotate 4-byte word w left by one byte.
* /
private static function rotWord($w)
{
$tmp = $w[0];
for ($i = 0; $i < 3; $i++) $w[$i] = $w[$i + 1];
$w[3] = $tmp;
return $w;
}
// sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
private static $sBox = array(
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16);
// rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
private static $rCon = array(
array(0x00, 0x00, 0x00, 0x00),
array(0x01, 0x00, 0x00, 0x00),
array(0x02, 0x00, 0x00, 0x00),
array(0x04, 0x00, 0x00, 0x00),
array(0x08, 0x00, 0x00, 0x00),
array(0x10, 0x00, 0x00, 0x00),
array(0x20, 0x00, 0x00, 0x00),
array(0x40, 0x00, 0x00, 0x00),
array(0x80, 0x00, 0x00, 0x00),
array(0x1b, 0x00, 0x00, 0x00),
array(0x36, 0x00, 0x00, 0x00));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
?>
<?php
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
/* AES counter (CTR) mode implementation in PHP * /
/* (c) Chris Veness 2005-2014 www.movable-type.co.uk/scripts * /
/* Right of free use is granted for all commercial or non-commercial use under CC-BY licence. * /
/* No warranty of any form is offered. * /
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
Class AesCtr extends Aes
{
/**
* Encrypt a text using AES encryption in Counter mode of operation
* - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
*
* Unicode multi-byte character safe
*
* @param plaintext source text to be encrypted
* @param password the password to use to generate a key
* @param nBits number of bits to be used in the key (128, 192, or 256)
* @return encrypted text
* /
public static function encrypt($plaintext, $password, $nBits)
{
$blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!($nBits == 128 || $nBits == 192 || $nBits == 256)) return ''; // standard allows 128/192/256 bit keys
// note PHP (5) gives us plaintext and password in UTF8 encoding!
// use AES itself to encrypt password to get cipher key (using plain password as source for
// key expansion) - gives us well encrypted key
$nBytes = $nBits / 8; // no bytes in key
$pwBytes = array();
for ($i = 0; $i < $nBytes; $i++) $pwBytes[$i] = ord(substr($password, $i, 1)) & 0xff;
$key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
$key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
// initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
// [2-3] = random, [4-7] = seconds, giving guaranteed sub-ms uniqueness up to Feb 2106
$counterBlock = array();
$nonce = floor(microtime(true) * 1000); // timestamp: milliseconds since 1-Jan-1970
$nonceMs = $nonce % 1000;
$nonceSec = floor($nonce / 1000);
$nonceRnd = floor(rand(0, 0xffff));
for ($i = 0; $i < 2; $i++) $counterBlock[$i] = self::urs($nonceMs, $i * 8) & 0xff;
for ($i = 0; $i < 2; $i++) $counterBlock[$i + 2] = self::urs($nonceRnd, $i * 8) & 0xff;
for ($i = 0; $i < 4; $i++) $counterBlock[$i + 4] = self::urs($nonceSec, $i * 8) & 0xff;
// and convert it to a string to go on the front of the ciphertext
$ctrTxt = '';
for ($i = 0; $i < 8; $i++) $ctrTxt .= chr($counterBlock[$i]);
// generate key schedule - an expansion of the key into distinct Key Rounds for each round
$keySchedule = Aes::keyExpansion($key);
//print_r($keySchedule);
$blockCount = ceil(strlen($plaintext) / $blockSize);
$ciphertxt = array(); // ciphertext as array of strings
for ($b = 0; $b < $blockCount; $b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
for ($c = 0; $c < 4; $c++) $counterBlock[15 - $c] = self::urs($b, $c * 8) & 0xff;
for ($c = 0; $c < 4; $c++) $counterBlock[15 - $c - 4] = self::urs($b / 0x100000000, $c * 8);
$cipherCntr = Aes::cipher($counterBlock, $keySchedule); // -- encrypt counter block --
// block size is reduced on final block
$blockLength = $b < $blockCount - 1 ? $blockSize : (strlen($plaintext) - 1) % $blockSize + 1;
$cipherByte = array();
for ($i = 0; $i < $blockLength; $i++) { // -- xor plaintext with ciphered counter byte-by-byte --
$cipherByte[$i] = $cipherCntr[$i] ^ ord(substr($plaintext, $b * $blockSize + $i, 1));
$cipherByte[$i] = chr($cipherByte[$i]);
}
$ciphertxt[$b] = implode('', $cipherByte); // escape troublesome characters in ciphertext
}
// implode is more efficient than repeated string concatenation
$ciphertext = $ctrTxt . implode('', $ciphertxt);
$ciphertext = base64_encode($ciphertext);
return $ciphertext;
}
/**
* Decrypt a text encrypted by AES in counter mode of operation
*
* @param ciphertext source text to be decrypted
* @param password the password to use to generate a key
* @param nBits number of bits to be used in the key (128, 192, or 256)
* @return decrypted text
* /
public static function decrypt($ciphertext, $password, $nBits)
{
$blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!($nBits == 128 || $nBits == 192 || $nBits == 256)) return ''; // standard allows 128/192/256 bit keys
$ciphertext = base64_decode($ciphertext);
// use AES to encrypt password (mirroring encrypt routine)
$nBytes = $nBits / 8; // no bytes in key
$pwBytes = array();
for ($i = 0; $i < $nBytes; $i++) $pwBytes[$i] = ord(substr($password, $i, 1)) & 0xff;
$key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
$key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
// recover nonce from 1st element of ciphertext
$counterBlock = array();
$ctrTxt = substr($ciphertext, 0, 8);
for ($i = 0; $i < 8; $i++) $counterBlock[$i] = ord(substr($ctrTxt, $i, 1));
// generate key schedule
$keySchedule = Aes::keyExpansion($key);
// separate ciphertext into blocks (skipping past initial 8 bytes)
$nBlocks = ceil((strlen($ciphertext) - 8) / $blockSize);
$ct = array();
for ($b = 0; $b < $nBlocks; $b++) $ct[$b] = substr($ciphertext, 8 + $b * $blockSize, 16);
$ciphertext = $ct; // ciphertext is now array of block-length strings
// plaintext will get generated block-by-block into array of block-length strings
$plaintxt = array();
for ($b = 0; $b < $nBlocks; $b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
for ($c = 0; $c < 4; $c++) $counterBlock[15 - $c] = self::urs($b, $c * 8) & 0xff;
for ($c = 0; $c < 4; $c++) $counterBlock[15 - $c - 4] = self::urs(($b + 1) / 0x100000000 - 1, $c * 8) & 0xff;
$cipherCntr = Aes::cipher($counterBlock, $keySchedule); // encrypt counter block
$plaintxtByte = array();
for ($i = 0; $i < strlen($ciphertext[$b]); $i++) {
// -- xor plaintext with ciphered counter byte-by-byte --
$plaintxtByte[$i] = $cipherCntr[$i] ^ ord(substr($ciphertext[$b], $i, 1));
$plaintxtByte[$i] = chr($plaintxtByte[$i]);
}
$plaintxt[$b] = implode('', $plaintxtByte);
}
// join array of blocks into single plaintext string
$plaintext = implode('', $plaintxt);
return $plaintext;
}
/*
* Unsigned right shift function, since PHP has neither >>> operator nor unsigned ints
*
* @param a number to be shifted (32-bit integer)
* @param b number of bits to shift a to the right (0..31)
* @return a right-shifted and zero-filled by b bits
* /
private static function urs($a, $b)
{
$a &= 0xffffffff;
$b &= 0x1f; // (bounds check)
if ($a & 0x80000000 && $b > 0) { // if left-most bit set
$a = ($a >> 1) & 0x7fffffff; // right-shift one bit & clear left-most bit
$a = $a >> ($b - 1); // remaining right-shifts
} else { // otherwise
$a = ($a >> $b); // use normal right-shift
}
return $a;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * /
?>
<?php
set_time_limit(300);
$success = FALSE;
function checkAuthorizedFolder($fileName, $authorizedRootFolders) {
if (!is_null($authorizedRootFolders)) {
$fileFolderPath = @realpath(dirname($fileName));
if ($fileFolderPath !== FALSE) {
$fileFolderPath = $fileFolderPath . DIRECTORY_SEPARATOR;
foreach ($authorizedRootFolders as $folder) {
if (substr($fileFolderPath, 0, strlen($folder)) === $folder) {
return TRUE;
}
}
}
return FALSE;
}
return TRUE;
}
foreach($_POST as $name => $value) {
$data = $value;
if (!empty($password)) {
$data = AesCtr::decrypt($data, $password, 256);
}
if (!is_null($data) && (strpos($data, 'savefilename:') === 0)) {
$dataLines = explode("\n", $data);
$fileName = str_replace('\\', '/', trim(substr(array_shift($dataLines), strlen('savefilename:'))));
if (checkAuthorizedFolder($fileName, $authorizedRootFolders)) {
$data = implode("\n", $dataLines);
if (@file_put_contents(dirname(__FILE__) . '/' . $fileName, $data) === FALSE) {
$success = FALSE;
break;
}
$success = TRUE;
}
} else if (!is_null($data) && (strpos($data, 'appendfilename:') === 0)) {
$dataLines = explode("\n", $data);
$fileName = str_replace('\\', '/', trim(substr(array_shift($dataLines), strlen('appendfilename:'))));
if (checkAuthorizedFolder($fileName, $authorizedRootFolders)) {
$data = implode("\n", $dataLines);
if (@file_put_contents(dirname(__FILE__) . '/' . $fileName, $data, FILE_APPEND) === FALSE) {
$success = FALSE;
break;
}
$success = TRUE;
}
} else if (!is_null($data) && (strpos($data, 'removefilename:') === 0)) {
$fileName = str_replace('\\', '/', trim(substr($data, strlen('removefilename:'))));
if (checkAuthorizedFolder($fileName, $authorizedRootFolders)) {
if (@unlink(dirname(__FILE__) . '/' . $fileName) === FALSE) {
$success = FALSE;
break;
}
$success = TRUE;
}
}
}
if ($success) {
print('success');
} else {
print('failure');
}
?>
}}}
!!!!!PHP-script-end
%/
!!!!!Code
***/
//{{{
config.macros.serverupload = {
save: function (files, url, password, callback) {
var params = [];
for (var i = 0; i < files.length; i++) {
if (files[i]) {
var data = (files[i].append ? "appendfilename:" : "savefilename:") + files[i].name + "\n" + files[i].data;
data = password ? Aes.Ctr.encrypt(data, password, 256) : data;
params.push({name: "data" + (i + 1), value: data});
}
}
config.macros.serverupload.send(params, url, password, callback);
},
send: function (params, url, password, callback) {
var processedURL = config.macros.serverupload.processURL(url);
var settings = {
type: "POST",
dataType: "text",
url: processedURL.url,
beforeSend: function (xhr) {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
},
data: jQuery.param(params)
};
if (processedURL.username) {
settings.headers = {
"Authorization": "Basic " + btoa(processedURL.username + ":" + processedURL.password)
}
}
jQuery.ajax(settings).done(function (data, textStatus, jqXHR) {
data = data ? data.trim() : data;
if (data === "success") {
callback(true);
} else if (data === "failure") {
callback(false);
} else {
callback(data);
}
}).fail(function (jqXHR, textStatus, errorThrown) {
callback(false);
});
},
remove: function (files, url, password, callback) {
var params = [];
for (var i = 0; i < files.length; i++) {
var data = "removefilename:" + files[i].name;
data = password ? Aes.Ctr.encrypt(data, password, 256) : data;
params.push({name: "data" + (i + 1), value: data});
}
config.macros.serverupload.send(params, url, password, callback);
},
processURL: function (url) {
// https://www.rfc-editor.org/rfc/rfc3986#appendix-B
var username = null;
var password = null;
if (url) {
url.replace(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/, function (match, p1, p2, p3, p4, p5, p6, p7, p8, p9, offset, string) {
if (p4 && (p4.lastIndexOf("@") > -1) && (p4.lastIndexOf(":", p4.lastIndexOf("@")) > -1)) {
username = p4.substring(0, p4.lastIndexOf("@"));
password = username.substring(username.indexOf(":") + 1);
username = username.substring(0, username.indexOf(":"));
url = url.replace(username + ":" + password + "@", "");
}
});
}
return {
url: url,
username: username,
password: password
};
}
}
//}}}
/***
|Description|highlights saving button (bold red by default) and the document title (adds a leading "*") when there are unsaved changes (also toggles {{{hasUnsavedChanges}}} class of the root element for hackability)|
|Version |1.5.1|
|Author |Yakov Litvin|
|Source |https://github.com/YakovL/TiddlyWiki_ShowUnsavedPlugin/blob/master/ShowUnsavedPlugin.js|
|License |[[MIT|https://github.com/YakovL/TiddlyWiki_YL_ExtensionsCollection/blob/master/Common%20License%20(MIT)]]|
<<option chkShowDirtyStory>> show unsaved if any tiddler is opened for editing
Styles applied to unsaved TW can be adjusted in StyleSheetUnsaved
***/
//{{{
config.macros.showDirtyPlugin = {
// styles that highlight save button when there's something to save
showDirtyCss: ".saveChangesButton { font-weight: bold; color: red !important; }",
styleSheetName: "suggestSavingOnDirty",
containerClassName: "hasUnsavedChanges",
showDrity: function(dirty) {
const css = store.getTiddlerText('StyleSheetUnsaved')
if(dirty) {
jQuery('html').addClass(this.containerClassName)
setStylesheet(css, this.styleSheetName)
document.title = "*" + getPageTitle()
} else {
jQuery('html').removeClass(this.containerClassName)
removeStyleSheet(this.styleSheetName)
document.title = getPageTitle()
}
},
checkDirty: function() {
return store.isDirty() ||
(config.options.chkShowDirtyStory && story.areAnyDirty())
},
init: function() {
config.shadowTiddlers.StyleSheetUnsaved = this.showDirtyCss
// add the "saveChangesButton" class to the save changes button
config.macros.saveChanges.SCM_orig_handler = config.macros.saveChanges.handler
config.macros.saveChanges.handler = function(place, macroName, params) {
this.SCM_orig_handler.apply(this, arguments)
place.lastChild.classList.add("saveChangesButton")
}
// regularly check and indicate unsaved
setInterval(function() {
const isDirty = config.macros.showDirtyPlugin.checkDirty()
config.macros.showDirtyPlugin.showDrity(isDirty)
}, 500)
}
}
//}}}
/***
|Name|ShowUnsavedPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[ShowUnsavedPlugin]]|
!!!!!Code
***/
//{{{
(function () {
var code = eval("config.macros.showDirtyPlugin.showDrity").toString();
code = code.replace("const", "var wasDirty = config.macros.showDirtyPlugin.wasDirty; config.macros.showDirtyPlugin.wasDirty = dirty; if (dirty === wasDirty) return; const");
eval("config.macros.showDirtyPlugin.showDrity = function showDrity" + code.substring(code.indexOf("(")));
})();
//}}}
/***
|Name|SliderExtensionPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
(function () {
var code;
code = eval("config.macros.slider.onClickSlider").toString();
code = code.replace("if(", "if ((n.getAttribute('redraw') == 'true') && !isOpen) { var t = n.getAttribute('slidertiddler'); if (t) { n.setAttribute('tiddler', t); var text = store.getTiddlerText(t); if (text) { t = (t.indexOf('##') > -1) ? t.substring(0, t.indexOf('##')) : t; wikify(text, n, null, store.getTiddler(t)); } } } else if ((n.getAttribute('redraw') == 'true') && isOpen) { n.removeAttribute('tiddler'); }; if(");
code = code.replace("config.options[", "if (cookie) config.options[");
code = code.replace("saveOption", "if ((n.getAttribute('redraw') == 'true') && isOpen) { n.innerHTML = ''; }; if (cookie) saveOption");
eval("config.macros.slider.onClickSlider = function onClickSlider" + code.substring(code.indexOf("(")));
code = eval("config.macros.slider.handler").toString();
code = code.replace("panel.", "panel.setAttribute('redraw', redraw || ''); panel.");
code = code.replace('"tiddler"', 'redraw != "true" ? "tiddler" : "slidertiddler"');
code = code.replace("text)", "text && ((redraw != 'true') || (panel.style.display == 'block')))");
eval("config.macros.slider.handler = function handler(redraw, " + code.substring(code.indexOf("(") + 1));
config.macros.slider.sliderHandler = config.macros.slider.handler;
config.macros.slider.handler = function (place, macroName, params, wikifier, paramString, tiddler) {
var args = paramString.parseParams("anon", null, null)[0];
if (args.cookie || args.redraw) {
var sliderParams = args.anon || [];
if (args.cookie && args.cookie[0] == 'false') {
sliderParams.unshift("");
}
this.sliderHandler(args.redraw ? args.redraw[0] : null, place, macroName, sliderParams);
} else {
this.sliderHandler(null, place, macroName, params);
}
}
})();
//}}}
/***
|Name|SnapshotPlugin|
|Source|http://www.TiddlyTools.com/#SnapshotPlugin|
|Documentation|http://www.TiddlyTools.com/#SnapshotPluginInfo|
|Version|1.4.3|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|save or print HTML+CSS image of rendered document content|
This plugin provides a macro as well as tiddler toolbar commands to create a file or browser window containing the //rendered// CSS-and-HTML that is currently being displayed for selected elements of the current document.
!!!!!Documentation
>see [[SnapshotPluginInfo]]
!!!!!Configuration
<<<
<<option chkSnapshotHTMLOnly>> output HTML only (omit CSS)
<<<
!!!!!Revisions
<<<
2011.02.14 1.4.3 fix OSX error: use picker.file.path
2011.01.03 1.4.2 added snapshotSaveViewer toolbar command
2010.12.15 1.4.1 added 'snapshot' class to wrapper
|please see [[SnapshotPluginInfo]] for additional revision details|
2008.04.21 1.0.0 initial release - derived from [[NewDocumentPlugin]] with many improvements...
<<<
!!!!!Code
***/
//{{{
version.extensions.SnapshotPlugin= {major: 1, minor: 4, revision: 3, date: new Date(2011,2,14)};
if (config.options.chkSnapshotHTMLOnly===undefined)
config.options.chkSnapshotHTMLOnly=false;
config.macros.snapshot = {
snapLabel: "save a snapshot",
printLabel: "print a snapshot",
snapPrompt: "save an HTML image",
printPrompt: "print an HTML image",
hereID: "here",
viewerID: "viewer",
storyID: "story",
allID: "all",
askID: "ask",
askTiddlerID: "askTiddler",
askDOMID: "askDOM",
askMsg: "select an element...",
hereItem: "tiddler: '%0'",
viewerItem: "tiddler: '%0' (content only)",
storyItem: "story column (one file)",
storyFilesItem: "story column (multiple files)",
allItem: "entire document",
tiddlerItem: "select a tiddler...",
IDItem: "select a DOM element by ID...",
HTMLItem: "[%0] output HTML only (omit CSS)",
fileMsg: "select or enter a target path/filename",
defaultFilename: "snapshot.html",
okmsg: "snapshot written to %0",
failmsg: "An error occurred while creating %0",
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var printing=params[0]&¶ms[0]=="print"; if (printing) params.shift();
params = paramString.parseParams("anon",null,true,false,false);
var id=getParam(params,"id","here");
var label=getParam(params,"label",printing?this.printLabel:this.snapLabel);
var prompt=getParam(params,"prompt",printing?this.printPrompt:this.snapPrompt);
var btn=createTiddlyButton(place,label,prompt, function(ev){
this.setAttribute("snapID",this.getAttribute("startID"));
config.macros.snapshot.go(this,ev)
});
btn.setAttribute("startID",id);
btn.setAttribute("snapID",id);
btn.setAttribute("printing",printing?"true":"false");
btn.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
},
go: function(here,ev) {
var cms=config.macros.snapshot; // abbreviation
var id=here.getAttribute("snapID");
var printing=here.getAttribute("printing")=="true";
var HTMLOnly=here.getAttribute("HTMLOnly")=="true";
if (id==cms.askID||id==cms.askTiddlerID||id==cms.askDOMID) {
cms.askForID(here,ev);
} else if (id==cms.storyID) {
story.forEachTiddler(function(t,e) {
var out=cms.getsnap(e,e.id,printing,HTMLOnly);
if (printing) cms.printsnap(out);
else cms.savesnap(out,e.getAttribute('tiddler')+'.html');
});
} else {
if (id==cms.allID) id="contentWrapper";
var snapElem=document.getElementById(id);
if (id==cms.hereID || id==cms.viewerID)
var snapElem=story.findContainingTiddler(here);
if (snapElem && hasClass(snapElem,"tiddler") && (id==cms.viewerID || HTMLOnly)) {
// find viewer class element within tiddler element
var nodes=snapElem.getElementsByTagName("*");
for (var i=0; i<nodes.length; i++)
if (hasClass(nodes[i],"viewer")) { snapElem=nodes[i]; break; }
}
if (!snapElem) // not in a tiddler or no viewer element or unknown ID
{ e.cancelBubble=true; if(e.stopPropagation)e.stopPropagation(); return(false); }
// write or print snapshot
var out=cms.getsnap(snapElem,id,printing,HTMLOnly);
if (printing) cms.printsnap(out); else cms.savesnap(out);
}
return false;
},
askForID: function(here,ev) {
var ev = ev ? ev : window.event;
var cms=config.macros.snapshot; // abbreviation
var id=here.getAttribute("snapID");
var indent='\xa0\xa0\xa0\xa0';
var p=Popup.create(here); if (!p) return false; p.className+=' sticky smallform';
var s=createTiddlyElement(p,'select'); s.button=here;
if (id==cms.askID) {
s.options[s.length]=new Option(cms.askMsg,cms.askID);
var tid=story.findContainingTiddler(here);
if(tid) {
var title=tid.getAttribute("tiddler");
if (here.getAttribute("HTMLOnly")!="true")
s.options[s.length]=new Option(indent+cms.hereItem.format([title]),cms.hereID);
s.options[s.length]=new Option(indent+cms.viewerItem.format([title]),cms.viewerID);
}
s.options[s.length]=new Option(indent+cms.tiddlerItem,cms.askTiddlerID);
s.options[s.length]=new Option(indent+cms.IDItem,cms.askDOMID);
s.options[s.length]=new Option(indent+cms.storyItem,"tiddlerDisplay");
s.options[s.length]=new Option(indent+cms.storyFilesItem,cms.storyID);
s.options[s.length]=new Option(indent+cms.allItem,"contentWrapper");
}
if (id==cms.askDOMID) {
s.options[s.length]=new Option(cms.IDItem,cms.askDOMID);
var elems=document.getElementsByTagName("*");
var ids=[];
for (var i=0;i<elems.length;i++)
if (elems[i].id.length && elems[i].className!="animationContainer")
ids.push(elems[i].id);
ids.sort();
for (var i=0;i<ids.length;i++) s.options[s.length]=new Option(indent+ids[i],ids[i]);
}
if (id==cms.askTiddlerID) {
s.options[s.length]=new Option(cms.tiddlerItem,cms.askTiddlerID);
var elems=document.getElementsByTagName("div");
var ids=[];
for (var i=0;i<elems.length;i++) { var id=elems[i].id;
if (id.length && id.substr(0,story.idPrefix.length)==story.idPrefix && id!="tiddlerDisplay")
ids.push(id);
}
ids.sort();
for (var i=0;i<ids.length;i++) s.options[s.length]=new Option(indent+ids[i].substr(story.idPrefix.length),ids[i]);
}
s.options[s.length]=new Option(cms.HTMLItem.format([here.getAttribute("HTMLOnly")=="true"?"\u221a":"_"]),cms.HTMLItem);
s.onchange=function(ev){
var ev = ev ? ev : window.event;
var cms=config.macros.snapshot; // abbreviation
var here=this.button;
if (this.value==cms.HTMLItem) {
config.options.chkSnapshotHTMLOnly=!config.options.chkSnapshotHTMLOnly;
here.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
config.macros.option.propagateOption("chkSnapshotHTMLOnly","checked",
config.options.chkSnapshotHTMLOnly,"input");
} else
here.setAttribute("snapID",this.value);
config.macros.snapshot.go(here,ev);
return false;
};
Popup.show();
ev.cancelBubble=true;
if(ev.stopPropagation)ev.stopPropagation();
return false;
},
getpath: function() {
// get current path
var path=getLocalPath(window.location.href);
var slashpos=path.lastIndexOf("/");
if (slashpos==-1) slashpos=path.lastIndexOf("\\");
if (slashpos!=-1) path=path.substr(0,slashpos+1); // trim filename
return path;
},
getsnap: function(snapElem,id,printing,HTMLOnly) {
var cms=config.macros.snapshot; // abbreviation
var out='<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />';
if (printing)
out+='<base href="file:///'+cms.getpath().replace(/\\/g,'/')+'"></base>\n';
if (!HTMLOnly) {
var styles=document.getElementsByTagName('style');
var fmt='<style>\n/* stylesheet=%0 */\n%1\n\n</style>\n';
for(var i=0; i < styles.length; i++)
out+=fmt.format([styles[i].getAttribute('id'),styles[i].innerHTML]);
}
out+='</head>\n';
var elems=snapElem.getElementsByTagName('input');
for (var i=0; i<elems.length; i++) { var e=elems[i];
if (e.type=='text') e.defaultValue=e.value;
if (e.type=='checkbox') e.defaultChecked=e.checked;
if (e.type=='radiobutton') e.defaultChecked=e.checked;
}
var elems=snapElem.getElementsByTagName('textarea');
for (var i=0; i<elems.length; i++) elems[i].defaultValue=elems[i].value;
var fmt='<body>\n\n<div class="snapshot %0">%1</div>\n\n</body>\n';
out+=fmt.format([(id==cms.viewerID?'tiddler viewer':''),snapElem.innerHTML]);
return '<html>\n'+out+'</html>';
},
printsnap: function(out) {
var win=window.open("","_blank","");
win.document.open();
win.document.writeln(out);
win.document.close();
win.focus(); // bring to front
win.print(); // trigger print dialog
},
savesnap: function(out,target) {
var cms=config.macros.snapshot; // abbreviation
// make sure we are local
if (window.location.protocol!="file:")
{ alert(config.messages.notFileUrlError); return; }
var target=target||cms.askForFilename(cms.fileMsg,cms.getpath(),cms.defaultFilename);
if (!target) return; // cancelled by user
// if specified file does not include a path, assemble fully qualified path and filename
var slashpos=target.lastIndexOf("/"); if (slashpos==-1) slashpos=target.lastIndexOf("\\");
if (slashpos==-1) {
var h=document.location.href;
var cwd=getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf('/')+1)));
target=cwd+target;
}
var link="file:///"+target.replace(/\\/g,'/'); // link for message text
var ok=saveFile(target,convertUnicodeToUTF8(out));
var msg=ok?cms.okmsg.format([target]):cms.failmsg.format([target]);
displayMessage(msg,link);
},
askForFilename: function(msg,path,file) {
if(window.Components) { // moz
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, nsIFilePicker.modeSave);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension='html';
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.path;
}
catch(e) { alert('error during local file access: '+e.toString()) }
}
else { // IE
try { // XP/Vista only
var s = new ActiveXObject('UserAccounts.CommonDialog');
s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
s.FilterIndex=3; // default to HTML files;
s.InitialDir=path;
s.FileName=file;
if (s.showOpen()) var result=s.FileName;
}
catch(e) { var result=prompt(msg,path+file); } // fallback for non-XP IE
}
return result;
}
};
//}}}
// // TOOLBAR DEFINITIONS
//{{{
config.commands.snapshotSave = {
text: "snap",
tooltip: config.macros.snapshot.snapPrompt,
handler: function(ev,src,title) {
src.setAttribute("snapID","ask");
src.setAttribute("printing","false");
src.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
config.macros.snapshot.go(src,ev);
return false;
}
};
config.commands.snapshotSaveViewer = {
text: "snap",
tooltip: config.macros.snapshot.snapPrompt,
handler: function(ev,src,title) {
src.setAttribute("snapID","viewer");
src.setAttribute("printing","false");
src.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
config.macros.snapshot.go(src,ev);
return false;
}
};
config.commands.snapshotPrint = {
text: "print",
tooltip: config.macros.snapshot.printPrompt,
handler: function(ev,src,title) {
src.setAttribute("snapID","ask");
src.setAttribute("printing","true");
src.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
config.macros.snapshot.go(src,ev);
return false;
}
};
config.commands.snapshotPrintViewer = {
text: "print",
tooltip: config.macros.snapshot.printPrompt,
handler: function(ev,src,title) {
src.setAttribute("snapID","viewer");
src.setAttribute("printing","true");
src.setAttribute("HTMLOnly",config.options.chkSnapshotHTMLOnly?"true":"false");
config.macros.snapshot.go(src,ev);
return false;
}
};
//}}}
// // COPIED FROM [[StickyPopupPlugin]] TO ELIMINATE PLUGIN DEPENDENCY
//{{{
if (config.options.chkStickyPopups==undefined) config.options.chkStickyPopups=false;
Popup.stickyPopup_onDocumentClick = function(ev)
{
// if click is in a sticky popup, ignore it so popup will remain visible
var e = ev ? ev : window.event; var target = resolveTarget(e);
var p=target; while (p) {
if (hasClass(p,"popup") && (hasClass(p,"sticky")||config.options.chkStickyPopups)) break;
else p=p.parentNode;
}
if (!p) // not in sticky popup (or sticky popups disabled)... use normal click handling
Popup.onDocumentClick(ev);
return true;
};
try{removeEvent(document,"click",Popup.onDocumentClick);}catch(e){};
try{addEvent(document,"click",Popup.stickyPopup_onDocumentClick);}catch(e){};
//}}}
/***
|Name|SnapshotPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[SnapshotPlugin]]|
!!!!!Code
***/
//{{{
(function () {
var code = eval("config.macros.snapshot.askForFilename").toString();
var newCode = null;
var startPosition = code.indexOf("window.Components");
if (startPosition > -1) {
startPosition = code.indexOf("try", startPosition);
if (startPosition > -1) {
newCode = code.substring(0, startPosition) + ' if ((typeof netscape == "undefined") || !netscape.security || !netscape.security.PrivilegeManager || !netscape.security.PrivilegeManager.enablePrivilege) var result = prompt(config.macros.snapshot.fileMsg, path + file); else ' + code.substring(startPosition);
code = newCode;
}
}
if (newCode != null) {
eval("config.macros.snapshot.askForFilename = function askForFilename" + newCode.substring(newCode.indexOf("(")));
}
})();
//}}}
//{{{
(function () {
if (store.getTiddler("TWSaverHTTPServerPlugin")) {
var code = eval("config.macros.snapshot.savesnap").toString();
var newCode = null;
code = code.replace("if (", "if (false && ");
code = code.replace(",cms.getpath(),", ",window.isLocal()?cms.getpath():(function () {var p = cms.getpath().replace(/\\\\/g, '/').replace(/^\\/+/, ''); return p.substring(p.indexOf('/') + 1);})(),");
code = code.replace("slashpos==-1) {", "window.isLocal() && slashpos==-1) {");
code = code.replace("var link=", "var link=!window.isLocal() ? document.location.protocol + '//' + getLocalPath(decodeURIComponent(document.location.href.substr(0,document.location.href.lastIndexOf('/')+1))) + target + '?t=text/html' : ");
code = code.replace("saveFile(target", "saveFile((window.isLocal() ? '' : getLocalPath(decodeURIComponent(document.location.href.substr(0,document.location.href.lastIndexOf('/')+1)))) + target");
var startPosition = code.indexOf("var ok");
if (startPosition > -1) {
startPosition = code.indexOf(");", startPosition);
if (startPosition > -1) {
var endPosition = code.indexOf("displayMessage", startPosition);
if (endPosition > -1) {
endPosition = code.indexOf(");", endPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + ", function (ok) { " + code.substring(startPosition + 2, endPosition + 2) + " });" + code.substring(endPosition + 2);
}
}
}
}
if (newCode != null) {
eval("config.macros.snapshot.savesnap = function savesnap" + newCode.substring(newCode.indexOf("(")));
}
}
})();
//}}}
/***
|Name|SpreadsheetPlugin|
|License|[[TW Notes License]]|
|Requires|[[jspreadsheet.js]]|
!!!!!Code
***/
//{{{
config.macros.spreadsheet = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var dataTiddler = null;
var section = null;
var source = params[0] || (config.textPrimitives.sectionSeparator + "spreadsheet");
if (source.indexOf(config.textPrimitives.sectionSeparator) === 0) {
dataTiddler = tiddler;
section = source.substring(config.textPrimitives.sectionSeparator.length);
source = tiddler.title + source;
} else if (source.indexOf(config.textPrimitives.sectionSeparator) > 0) {
dataTiddler = source.substring(0, source.indexOf(config.textPrimitives.sectionSeparator));
section = source.substring(source.indexOf(config.textPrimitives.sectionSeparator) + config.textPrimitives.sectionSeparator.length);
} else {
dataTiddler = source;
}
var spreadsheetPlace = jQuery("<span></span>").appendTo(place);
var loadOptions = function () {
var options = null;
try {
eval("options = " + store.getTiddlerText(source));
} catch (e) {}
return options || {};
};
var worksheetsStore = null;
var worksheets = null;
var update = function (options) {
if (typeof dataTiddler === "string") {
if (store.tiddlerExists(dataTiddler)) {
dataTiddler = store.getTiddler(dataTiddler);
} else {
var created = new Date();
dataTiddler = store.saveTiddler(dataTiddler, dataTiddler, "", config.options.txtUserName, created, null, null, false, created, config.options.txtUserName);
}
}
if (!options) {
options = loadOptions();
options.worksheets = [];
if (worksheets) {
for (var i = 0; i < worksheets.length; i++) {
options.worksheets.push({
columns: worksheets[i].getConfig().columns || [],
rows: worksheets[i].getConfig().rows || [],
mergeCells: worksheets[i].getConfig().mergeCells || {},
style: worksheets[i].getStyle() || {},
data: worksheets[i].getData() || []
});
}
}
}
var functions = {};
for (var k in options) {
if (typeof options[k] === "function") {
functions[k] = options[k];
options[k] = k;
}
}
var data = JSON.stringify(options, null, "\t");
for (var k in functions) {
data = data.replace('\t"' + k + '": "' + k + '"', '\t"' + k + '": ' + functions[k].toString());
}
data = "//{{{\n" + data + "\n//}}}";
if (data !== store.getTiddlerText(source)) {
if (section == null) {
dataTiddler.text = data;
} else {
var headerRE = new RegExp("(^!{1,6}[ \t]*" + section.escapeRegExp() + "[ \t]*\n)", "mg");
headerRE.lastIndex = 0;
var match = headerRE.exec(dataTiddler.text);
if (!match) {
dataTiddler.text = dataTiddler.text + (dataTiddler.text.length ? "\n" : "") + "!" + section + "\n" + data;
} else {
var pos = match.index + match[1].length;
endPos = dataTiddler.text.indexOf("\n!", pos);
endPos = (endPos === -1) ? dataTiddler.text.length : endPos;
dataTiddler.text = dataTiddler.text.substring(0, pos) + data + dataTiddler.text.substring(endPos);
}
}
dataTiddler.modifier = config.options.txtUserName;
dataTiddler.modified = new Date();
store.setDirty(true);
}
};
var options = loadOptions();
if (!options.worksheets) {
if (jQuery.isEmptyObject(options)) {
options.tabs = true;
options.toolbar = true;
}
options.worksheets = [{
columns: [{type: "text"}],
rows: [],
mergeCells: {},
style: {},
data: [[]]
}];
update(options);
}
options.root = spreadsheetPlace[0];
var onload = options.onload;
var onchange = options.onchange;
var onundo = options.onundo;
var onredo = options.onredo;
var oncreateworksheet = options.oncreateworksheet;
var ondeleteworksheet = options.ondeleteworksheet;
var oninsertcolumn = options.oninsertcolumn;
var ondeletecolumn = options.ondeletecolumn;
var onmovecolumn = options.onmovecolumn;
var onresizecolumn = options.onresizecolumn;
var onchangeheader = options.onchangeheader;
var oninsertrow = options.oninsertrow;
var ondeleterow = options.ondeleterow;
var onmoverow = options.onmoverow;
var onresizerow = options.onresizerow;
var onmerge = options.onmerge;
var onchangestyle = options.onchangeheader;
options.onload = function (spreadsheet) {
for (var i = 0; i < options.worksheets.length; i++) {
if (options.worksheets[i].rows) {
for (var j = 0; j < options.worksheets[i].rows.length; j++) {
var row = options.worksheets[i].rows[j];
if (row && jQuery.isNumeric(row.height)) {
worksheetsStore[i].setHeight(j, parseInt(row.height));
}
}
}
}
worksheets = worksheetsStore;
if (typeof onload === "function") {
updateData = onload.apply(this, arguments) !== false;
}
};
options.onchange = function (worksheet, cell, x, y, value) {
var updateData = true;
if (typeof onchange === "function") {
updateData = onchange.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onundo = function (worksheet, historyRecord) {
var updateData = true;
if (typeof onundo === "function") {
updateData = onundo.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onredo = function (worksheet, historyRecord) {
var updateData = true;
if (typeof onredo === "function") {
updateData = onredo.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.oncreateworksheet = function (worksheet, worksheetOptions, worksheetNumber) {
var updateData = true;
if (typeof oncreateworksheet === "function") {
updateData = oncreateworksheet.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.ondeleteworksheet = function (worksheet, oldWorksheetNumber) {
var updateData = true;
if (typeof ondeleteworksheet === "function") {
updateData = ondeleteworksheet.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.oninsertcolumn = function (worksheet, columns) {
var updateData = true;
if (typeof oninsertcolumn === "function") {
updateData = oninsertcolumn.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.ondeletecolumn = function (worksheet, removedColumns) {
var updateData = true;
if (typeof ondeletecolumn === "function") {
updateData = ondeletecolumn.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onmovecolumn = function (worksheet, movedColumns) {
var updateData = true;
if (typeof onmovecolumn === "function") {
updateData = onmovecolumn.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onresizecolumn = function (worksheet, column, width, oldWidth) {
var updateData = true;
if (typeof onresizecolumn === "function") {
updateData = onresizecolumn.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onchangeheader = function (worksheet, colIndex, newValue, oldValue) {
var updateData = true;
if (typeof onchangeheader === "function") {
updateData = onchangeheader.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.oninsertrow = function (worksheet, rows) {
var updateData = true;
if (typeof oninsertrow === "function") {
updateData = oninsertrow.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.ondeleterow = function (worksheet, rows) {
var updateData = true;
if (typeof ondeleterow === "function") {
updateData = ondeleterow.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onmoverow = function (worksheet, origin, destination) {
var updateData = true;
if (typeof onmoverow === "function") {
updateData = onmoverow.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onresizerow = function (worksheet, row, height, oldHeight) {
var updateData = true;
if (typeof onresizerow === "function") {
updateData = onresizerow.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onmerge = function (worksheet, merges) {
var updateData = true;
if (typeof onmerge === "function") {
updateData = onmerge.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
options.onchangestyle = function (worksheet, changes) {
var updateData = true;
if (typeof onchangestyle === "function") {
updateData = onchangestyle.apply(this, arguments) !== false;
}
if (updateData && worksheets) {
update.call(this);
}
};
jQuery(spreadsheetPlace).on("dblclick", function (e) {
e.preventDefault();
e.stopPropagation();
});
worksheetsStore = jspreadsheet(jQuery("<div></div>").appendTo(spreadsheetPlace)[0], options);
}
}
//}}}
/***
/% <!DOCTYPE html> %/
<html>
<head>
<title>SyncPlugin</title><link rel="stylesheet" href="/bags/common/tiddlers/profile.css" type="text/css" />
<link rel="alternate" type="application/atom+xml"
title="Atom SyncPlugin" href="http://cdent-t11.tiddlyspace.com/bags/cdent-t11_public/tiddlers/SyncPlugin.atom" />
<link rel="icon" href="/favicon.ico">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="title" content="SyncPlugin" />
<meta name="image" content="/SiteIcon" />
<meta name="description" content="SyncPlugin">
<meta name="keywords" content="systemConfig">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="container">
<div id="header">
<h1>
<a href="http://cdent-t11.tiddlyspace.com/#%5B%5BSyncPlugin%5D%5D" id='title'>SyncPlugin</a>
</h1>
</div>
<div id="article">
<div class="meta" id='links'>
<a class="revisions"
href="/bags/cdent-t11_public/tiddlers/SyncPlugin/revisions">revision</a>
<a class="revision"
href="/bags/cdent-t11_public/tiddlers/SyncPlugin/revisions/927199">
927199</a>
in <a href="/bags/cdent-t11_public/tiddlers">this collection of tiddlers</a>
</div><div id="tiddlertext">
<div class="tiddler">
<div id="text-html" class="main section"><div><table class="twtable"><tbody><tr class="evenRow"><td>Name</td><td><a class="externalLink null" href="http://tiddlyspace.com/bags/cdent-t11_public/tiddlers/SyncPlugin" title="External link to http://tiddlyspace.com/bags/cdent-t11_public/tiddlers/SyncPlugin">SyncPlugin</a></td></tr><tr class="oddRow"><td>Source</td><td><a class="externalLink" href="https://github.com/TiddlyWiki/tiddlywiki/blob/master/plugins/Sync.js" title="External link to https://github.com/TiddlyWiki/tiddlywiki/blob/master/plugins/Sync.js">https://github.com/TiddlyWiki/tiddlywiki/blob/master/plugins/Sync.js</a></td></tr><tr class="evenRow"><td>Version</td><td>1.0.0</td></tr><tr class="oddRow"><td>CoreVersion</td><td>2.6.6</td></tr><tr class="evenRow"><td>Type</td><td>plugin</td></tr><tr class="oddRow"><td>Description</td><td>Synchronise changes with other <a class="externalLink null" href="http://tiddlyspace.com/bags/cdent-t11_public/tiddlers/TiddlyWiki" title="External link to http://tiddlyspace.com/bags/cdent-t11_public/tiddlers/TiddlyWiki">TiddlyWiki</a> files and servers</td></tr></tbody></table><h5>Code</h5><pre>version.extensions.SyncPlugin= {major: 1, minor: 0, revision: 0, date: new Date(2011,11,24)};
// Synchronisation handlers
config.syncers = {};
config.macros.sync = {};
config.commands.syncing = {type: "popup"};
var currSync = null;
config.backstageTasks.push("sync");
merge(config.tasks,{sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'}});
merge(config.macros.sync,{
listViewTemplate: {
columns: [
{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
],
rowClasses: [
],
buttons: [
{caption: "Sync these tiddlers", name: 'sync'}
]},
wizardTitle: "Synchronize with external servers and files",
step1Title: "Choose the tiddlers you want to synchronize",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
syncLabel: "sync",
syncPrompt: "Sync these tiddlers",
hasChanged: "Changed while unplugged",
hasNotChanged: "Unchanged while unplugged",
syncStatusList: {
none: {text: "...", display:'none', className:'notChanged'},
changedServer: {text: "Changed on server", display:null, className:'changedServer'},
changedLocally: {text: "Changed while unplugged", display:null, className:'changedLocally'},
changedBoth: {text: "Changed while unplugged and on server", display:null, className:'changedBoth'},
notFound: {text: "Not found on server", display:null, className:'notFound'},
putToServer: {text: "Saved update on server", display:null, className:'putToServer'},
gotFromServer: {text: "Retrieved update from server", display:null, className:'gotFromServer'}
}
});
merge(config.commands.syncing,{
text: "syncing",
tooltip: "Control synchronisation of this tiddler with a server or external file",
currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
notCurrentlySyncing: "Not currently syncing",
captionUnSync: "Stop synchronising this tiddler",
chooseServer: "Synchronise this tiddler with another server:",
currServerMarker: "\u25cf ",
notCurrServerMarker: " "});
config.commands.syncing.handlePopup = function(popup,title)
{
var me = config.commands.syncing;
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var serverType = tiddler.getServerType();
var serverHost = tiddler.fields["server.host"];
var serverWorkspace = tiddler.fields["server.workspace"];
if(!serverWorkspace)
serverWorkspace = "";
if(serverType) {
var e = createTiddlyElement(popup,"li",null,"popupMessage");
e.innerHTML = me.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
} else {
createTiddlyElement(popup,"li",null,"popupMessage",me.notCurrentlySyncing);
}
if(serverType) {
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,me.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type","");
}
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
createTiddlyElement(popup,"li",null,"popupMessage",me.chooseServer);
var feeds = store.getTaggedTiddlers("systemServer","title");
var t;
for(t=0; t<feeds.length; t++) {
var f = feeds[t];
var feedServerType = store.getTiddlerSlice(f.title,"Type");
if(!feedServerType)
feedServerType = "file";
var feedServerHost = store.getTiddlerSlice(f.title,"URL");
if(!feedServerHost)
feedServerHost = "";
var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
if(!feedServerWorkspace)
feedServerWorkspace = "";
var caption = f.title;
if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
caption = me.currServerMarker + caption;
} else {
caption = me.notCurrServerMarker + caption;
}
btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,me.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type",feedServerType);
btn.setAttribute("server.host",feedServerHost);
btn.setAttribute("server.workspace",feedServerWorkspace);
}
};
config.commands.syncing.onChooseServer = function(e)
{
var tiddler = this.getAttribute("tiddler");
var serverType = this.getAttribute("server.type");
if(serverType) {
store.addTiddlerFields(tiddler,{
"server.type": serverType,
"server.host": this.getAttribute("server.host"),
"server.workspace": this.getAttribute("server.workspace")
});
} else {
store.setValue(tiddler,"server",null);
}
return false;
};
// sync macro
// Sync state.
//# Members:
//# syncList - List of sync objects (title, tiddler, server, workspace, page, revision)
//# wizard - reference to wizard object
//# listView - DOM element of the listView table
config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(!wikifier.isStatic)
this.startSync(place);
};
config.macros.sync.cancelSync = function()
{
currSync = null;
};
config.macros.sync.startSync = function(place)
{
if(currSync)
config.macros.sync.cancelSync();
currSync = {};
currSync.syncList = this.getSyncableTiddlers();
currSync.syncTasks = this.createSyncTasks(currSync.syncList);
this.preProcessSyncableTiddlers(currSync.syncList);
var wizard = new Wizard();
currSync.wizard = 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);
currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
this.processSyncableTiddlers(currSync.syncList);
wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
};
config.macros.sync.getSyncableTiddlers = function()
{
var list = [];
store.forEachTiddler(function(title,tiddler) {
var syncItem = {};
syncItem.serverType = tiddler.getServerType();
syncItem.serverHost = tiddler.fields['server.host'];
if(syncItem.serverType && syncItem.serverHost) {
syncItem.adaptor = new config.adaptors[syncItem.serverType]();
syncItem.serverHost = syncItem.adaptor.fullHostName(syncItem.serverHost);
syncItem.serverWorkspace = tiddler.fields['server.workspace'];
syncItem.tiddler = tiddler;
syncItem.title = tiddler.title;
syncItem.isTouched = tiddler.isTouched();
syncItem.selected = syncItem.isTouched;
syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
syncItem.status = syncItem.syncStatus.text;
list.push(syncItem);
}
});
list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
return list;
};
config.macros.sync.preProcessSyncableTiddlers = function(syncList)
{
var i;
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
si.serverUrl = si.adaptor.generateTiddlerInfo(si.tiddler).uri;
}
};
config.macros.sync.processSyncableTiddlers = function(syncList)
{
var i;
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
if(si.syncStatus.display)
si.rowElement.style.display = si.syncStatus.display;
if(si.syncStatus.className)
si.rowElement.className = si.syncStatus.className;
}
};
config.macros.sync.createSyncTasks = function(syncList)
{
var i,syncTasks = [];
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
var j,r = null;
for(j=0; j<syncTasks.length; j++) {
var cst = syncTasks[j];
if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
r = cst;
}
if(r) {
si.syncTask = r;
r.syncItems.push(si);
} else {
si.syncTask = this.createSyncTask(si);
syncTasks.push(si.syncTask);
}
}
return syncTasks;
};
config.macros.sync.createSyncTask = function(syncItem)
{
var st = {};
st.serverType = syncItem.serverType;
st.serverHost = syncItem.serverHost;
st.serverWorkspace = syncItem.serverWorkspace;
st.syncItems = [syncItem];
var getTiddlerListCallback = function(context,sycnItems) {
var me = config.macros.sync;
if(!context.status) {
displayMessage(context.statusText);
return false;
}
syncItems = context.userParams;
var i,tiddlers = context.tiddlers;
for(i=0; i<syncItems.length; i++) {
var si = syncItems[i];
var f = tiddlers.findByField("title",si.title);
if(f !== null) {
if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
si.syncStatus = me.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
}
} else {
si.syncStatus = me.syncStatusList.notFound;
}
me.updateSyncStatus(si);
}
return true;
};
var openWorkspaceCallback = function(context,syncItems) {
if(context.status) {
context.adaptor.getTiddlerList(context,syncItems,getTiddlerListCallback);
return true;
}
displayMessage(context.statusText);
return false;
};
var context = {host:st.serverHost,workspace:st.serverWorkspace};
syncItem.adaptor.openHost(st.serverHost);
syncItem.adaptor.openWorkspace(st.serverWorkspace,context,st.syncItems,openWorkspaceCallback);
return st;
};
config.macros.sync.updateSyncStatus = function(syncItem)
{
var e = syncItem.colElements["status"];
jQuery(e).empty();
createTiddlyText(e,syncItem.syncStatus.text);
syncItem.rowElement.style.display = syncItem.syncStatus.display;
if(syncItem.syncStatus.className)
syncItem.rowElement.className = syncItem.syncStatus.className;
};
config.macros.sync.doSync = function(e)
{
var me = config.macros.sync;
var getTiddlerCallback = function(context,syncItem) {
if(syncItem) {
var tiddler = context.tiddler;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
syncItem.syncStatus = me.syncStatusList.gotFromServer;
me.updateSyncStatus(syncItem);
}
};
var putTiddlerCallback = function(context,syncItem) {
if(syncItem) {
store.resetTiddler(context.title);
syncItem.syncStatus = me.syncStatusList.putToServer;
me.updateSyncStatus(syncItem);
}
};
var rowNames = ListView.getSelectedRows(currSync.listView);
var i,sl = me.syncStatusList;
for(i=0; i<currSync.syncList.length; i++) {
var si = currSync.syncList[i];
if(rowNames.indexOf(si.title) != -1) {
var errorMsg = "Error in doSync: ";
try {
var r = true;
switch(si.syncStatus) {
case sl.changedServer:
var context = {"workspace": si.serverWorkspace};
r = si.adaptor.getTiddler(si.title,context,si,getTiddlerCallback);
break;
case sl.notFound:
case sl.changedLocally:
case sl.changedBoth:
r = si.adaptor.putTiddler(si.tiddler,null,si,putTiddlerCallback);
break;
default:
break;
}
if(!r)
displayMessage(errorMsg + r);
} catch(ex) {
if(ex.name == "TypeError")
displayMessage("sync operation unsupported: " + ex.message);
else
displayMessage(errorMsg + ex.message);
}
}
}
return false;
};
</pre></div></div>
<dl class="meta section">
<dt>bag</dt>
<dd><span class="bag">cdent-t11_public</span>
</dd>
<dt>created</dt><dd class="dtstart meta-created" data-timestamp="20121104161351">Sun, 04 Nov 2012 16:13:51 GMT</dd>
<dt>creator</dt><dd><a class="meta-creator" href="http://cdent.tiddlyspace.com/">cdent</a></dd>
<dt>modified</dt><dd class="meta-modified" data-timestamp="20121104161448">Sun, 04 Nov 2012 16:14:48 GMT</dd>
<dt>modifier</dt><dd><a href="http://cdent.tiddlyspace.com/" class="meta-modifier">cdent</a></dd><dt>tags</dt><dd class="meta-tag">
<a class="tag" href='/search?q=tag:"systemConfig"'>systemConfig</a>
</dd><dt class="hidden">_hash</dt>
<dd class="hidden meta-_hash">9ae3101254f0343ffed56f8d34befb03d4fb6312</dd></dl>
</div>
</div>
</div>
<div id="footer">
Brought to you by <a href="http://tiddlyspace.com">TiddlySpace</a>.
</div>
</div>
<script type="text/javascript" src="/bags/common/tiddlers/jquery.js"></script>
<script type="text/javascript" src="/bags/common/tiddlers/_reply-loader.js"></script>
<script type="text/javascript" src="/bags/common/tiddlers/backstage.js"></script>
</body>
</html>
/% ***/
version.extensions.SyncPlugin= {major: 1, minor: 0, revision: 0, date: new Date(2011,11,24)};
// Synchronisation handlers
config.syncers = {};
config.macros.sync = {};
config.commands.syncing = {type: "popup"};
var currSync = null;
config.backstageTasks.push("sync");
merge(config.tasks,{sync: {text: "sync", tooltip: "Synchronise changes with other TiddlyWiki files and servers", content: '<<sync>>'}});
merge(config.macros.sync,{
listViewTemplate: {
columns: [
{name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'},
{name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'},
{name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'},
{name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'},
{name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'}
],
rowClasses: [
],
buttons: [
{caption: "Sync these tiddlers", name: 'sync'}
]},
wizardTitle: "Synchronize with external servers and files",
step1Title: "Choose the tiddlers you want to synchronize",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
syncLabel: "sync",
syncPrompt: "Sync these tiddlers",
hasChanged: "Changed while unplugged",
hasNotChanged: "Unchanged while unplugged",
syncStatusList: {
none: {text: "...", display:'none', className:'notChanged'},
changedServer: {text: "Changed on server", display:null, className:'changedServer'},
changedLocally: {text: "Changed while unplugged", display:null, className:'changedLocally'},
changedBoth: {text: "Changed while unplugged and on server", display:null, className:'changedBoth'},
notFound: {text: "Not found on server", display:null, className:'notFound'},
putToServer: {text: "Saved update on server", display:null, className:'putToServer'},
gotFromServer: {text: "Retrieved update from server", display:null, className:'gotFromServer'}
}
});
merge(config.commands.syncing,{
text: "syncing",
tooltip: "Control synchronisation of this tiddler with a server or external file",
currentlySyncing: "<div>Currently syncing via <span class='popupHighlight'>'%0'</span> to:</"+"div><div>host: <span class='popupHighlight'>%1</span></"+"div><div>workspace: <span class='popupHighlight'>%2</span></"+"div>", // Note escaping of closing <div> tag
notCurrentlySyncing: "Not currently syncing",
captionUnSync: "Stop synchronising this tiddler",
chooseServer: "Synchronise this tiddler with another server:",
currServerMarker: "\u25cf ",
notCurrServerMarker: " "});
config.commands.syncing.handlePopup = function(popup,title)
{
var me = config.commands.syncing;
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var serverType = tiddler.getServerType();
var serverHost = tiddler.fields["server.host"];
var serverWorkspace = tiddler.fields["server.workspace"];
if(!serverWorkspace)
serverWorkspace = "";
if(serverType) {
var e = createTiddlyElement(popup,"li",null,"popupMessage");
e.innerHTML = me.currentlySyncing.format([serverType,serverHost,serverWorkspace]);
} else {
createTiddlyElement(popup,"li",null,"popupMessage",me.notCurrentlySyncing);
}
if(serverType) {
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
var btn = createTiddlyButton(createTiddlyElement(popup,"li"),this.captionUnSync,null,me.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type","");
}
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
createTiddlyElement(popup,"li",null,"popupMessage",me.chooseServer);
var feeds = store.getTaggedTiddlers("systemServer","title");
var t;
for(t=0; t<feeds.length; t++) {
var f = feeds[t];
var feedServerType = store.getTiddlerSlice(f.title,"Type");
if(!feedServerType)
feedServerType = "file";
var feedServerHost = store.getTiddlerSlice(f.title,"URL");
if(!feedServerHost)
feedServerHost = "";
var feedServerWorkspace = store.getTiddlerSlice(f.title,"Workspace");
if(!feedServerWorkspace)
feedServerWorkspace = "";
var caption = f.title;
if(serverType == feedServerType && serverHost == feedServerHost && serverWorkspace == feedServerWorkspace) {
caption = me.currServerMarker + caption;
} else {
caption = me.notCurrServerMarker + caption;
}
btn = createTiddlyButton(createTiddlyElement(popup,"li"),caption,null,me.onChooseServer);
btn.setAttribute("tiddler",title);
btn.setAttribute("server.type",feedServerType);
btn.setAttribute("server.host",feedServerHost);
btn.setAttribute("server.workspace",feedServerWorkspace);
}
};
config.commands.syncing.onChooseServer = function(e)
{
var tiddler = this.getAttribute("tiddler");
var serverType = this.getAttribute("server.type");
if(serverType) {
store.addTiddlerFields(tiddler,{
"server.type": serverType,
"server.host": this.getAttribute("server.host"),
"server.workspace": this.getAttribute("server.workspace")
});
} else {
store.setValue(tiddler,"server",null);
}
return false;
};
// sync macro
// Sync state.
//# Members:
//# syncList - List of sync objects (title, tiddler, server, workspace, page, revision)
//# wizard - reference to wizard object
//# listView - DOM element of the listView table
config.macros.sync.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(!wikifier.isStatic)
this.startSync(place);
};
config.macros.sync.cancelSync = function()
{
currSync = null;
};
config.macros.sync.startSync = function(place)
{
if(currSync)
config.macros.sync.cancelSync();
currSync = {};
currSync.syncList = this.getSyncableTiddlers();
currSync.syncTasks = this.createSyncTasks(currSync.syncList);
this.preProcessSyncableTiddlers(currSync.syncList);
var wizard = new Wizard();
currSync.wizard = 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);
currSync.listView = ListView.create(listWrapper,currSync.syncList,this.listViewTemplate);
this.processSyncableTiddlers(currSync.syncList);
wizard.setButtons([{caption: this.syncLabel, tooltip: this.syncPrompt, onClick: this.doSync}]);
};
config.macros.sync.getSyncableTiddlers = function()
{
var list = [];
store.forEachTiddler(function(title,tiddler) {
var syncItem = {};
syncItem.serverType = tiddler.getServerType();
syncItem.serverHost = tiddler.fields['server.host'];
if(syncItem.serverType && syncItem.serverHost) {
syncItem.adaptor = new config.adaptors[syncItem.serverType]();
syncItem.serverHost = syncItem.adaptor.fullHostName(syncItem.serverHost);
syncItem.serverWorkspace = tiddler.fields['server.workspace'];
syncItem.tiddler = tiddler;
syncItem.title = tiddler.title;
syncItem.isTouched = tiddler.isTouched();
syncItem.selected = syncItem.isTouched;
syncItem.syncStatus = config.macros.sync.syncStatusList[syncItem.isTouched ? "changedLocally" : "none"];
syncItem.status = syncItem.syncStatus.text;
list.push(syncItem);
}
});
list.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : +1);});
return list;
};
config.macros.sync.preProcessSyncableTiddlers = function(syncList)
{
var i;
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
si.serverUrl = si.adaptor.generateTiddlerInfo(si.tiddler).uri;
}
};
config.macros.sync.processSyncableTiddlers = function(syncList)
{
var i;
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
if(si.syncStatus.display)
si.rowElement.style.display = si.syncStatus.display;
if(si.syncStatus.className)
si.rowElement.className = si.syncStatus.className;
}
};
config.macros.sync.createSyncTasks = function(syncList)
{
var i,syncTasks = [];
for(i=0; i<syncList.length; i++) {
var si = syncList[i];
var j,r = null;
for(j=0; j<syncTasks.length; j++) {
var cst = syncTasks[j];
if(si.serverType == cst.serverType && si.serverHost == cst.serverHost && si.serverWorkspace == cst.serverWorkspace)
r = cst;
}
if(r) {
si.syncTask = r;
r.syncItems.push(si);
} else {
si.syncTask = this.createSyncTask(si);
syncTasks.push(si.syncTask);
}
}
return syncTasks;
};
config.macros.sync.createSyncTask = function(syncItem)
{
var st = {};
st.serverType = syncItem.serverType;
st.serverHost = syncItem.serverHost;
st.serverWorkspace = syncItem.serverWorkspace;
st.syncItems = [syncItem];
var getTiddlerListCallback = function(context,sycnItems) {
var me = config.macros.sync;
if(!context.status) {
displayMessage(context.statusText);
return false;
}
syncItems = context.userParams;
var i,tiddlers = context.tiddlers;
for(i=0; i<syncItems.length; i++) {
var si = syncItems[i];
var f = tiddlers.findByField("title",si.title);
if(f !== null) {
if(tiddlers[f].fields['server.page.revision'] > si.tiddler.fields['server.page.revision']) {
si.syncStatus = me.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer'];
}
} else {
si.syncStatus = me.syncStatusList.notFound;
}
me.updateSyncStatus(si);
}
return true;
};
var openWorkspaceCallback = function(context,syncItems) {
if(context.status) {
context.adaptor.getTiddlerList(context,syncItems,getTiddlerListCallback);
return true;
}
displayMessage(context.statusText);
return false;
};
var context = {host:st.serverHost,workspace:st.serverWorkspace};
syncItem.adaptor.openHost(st.serverHost);
syncItem.adaptor.openWorkspace(st.serverWorkspace,context,st.syncItems,openWorkspaceCallback);
return st;
};
config.macros.sync.updateSyncStatus = function(syncItem)
{
var e = syncItem.colElements["status"];
jQuery(e).empty();
createTiddlyText(e,syncItem.syncStatus.text);
syncItem.rowElement.style.display = syncItem.syncStatus.display;
if(syncItem.syncStatus.className)
syncItem.rowElement.className = syncItem.syncStatus.className;
};
config.macros.sync.doSync = function(e)
{
var me = config.macros.sync;
var getTiddlerCallback = function(context,syncItem) {
if(syncItem) {
var tiddler = context.tiddler;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created);
syncItem.syncStatus = me.syncStatusList.gotFromServer;
me.updateSyncStatus(syncItem);
}
};
var putTiddlerCallback = function(context,syncItem) {
if(syncItem) {
store.resetTiddler(context.title);
syncItem.syncStatus = me.syncStatusList.putToServer;
me.updateSyncStatus(syncItem);
}
};
var rowNames = ListView.getSelectedRows(currSync.listView);
var i,sl = me.syncStatusList;
for(i=0; i<currSync.syncList.length; i++) {
var si = currSync.syncList[i];
if(rowNames.indexOf(si.title) != -1) {
var errorMsg = "Error in doSync: ";
try {
var r = true;
switch(si.syncStatus) {
case sl.changedServer:
var context = {"workspace": si.serverWorkspace};
r = si.adaptor.getTiddler(si.title,context,si,getTiddlerCallback);
break;
case sl.notFound:
case sl.changedLocally:
case sl.changedBoth:
r = si.adaptor.putTiddler(si.tiddler,null,si,putTiddlerCallback);
break;
default:
break;
}
if(!r)
displayMessage(errorMsg + r);
} catch(ex) {
if(ex.name == "TypeError")
displayMessage("sync operation unsupported: " + ex.message);
else
displayMessage(errorMsg + ex.message);
}
}
}
return false;
};
// // %/
//{{{
var code = eval("ajaxReq").toString();
var newCode = null;
var startPosition = code.indexOf("window.netscape.security.PrivilegeManager.enablePrivilege");
if (startPosition > -1) {
var endPosition = code.indexOf(';', startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + " try { " + code.substring(startPosition, endPosition + 1) + " } catch (e) {} " + code.substring(endPosition + 1);
code = newCode;
}
}
if (newCode != null) {
eval("ajaxReq = function ajaxReq" + newCode.substring(newCode.indexOf("(")));
}
var code = eval("config.macros.sync.createSyncTask").toString();
var newCode = null;
var startPosition = code.indexOf("displayMessage");
if (startPosition > -1) {
var endPosition = code.indexOf(';', startPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + ' displayMessage(context.statusText + " (source: " + context.host + ")"); ' + code.substring(endPosition + 1);
code = newCode;
}
}
if (newCode != null) {
eval("config.macros.sync.createSyncTask = function createSyncTask" + newCode.substring(newCode.indexOf("(")));
}
//}}}
|''Description:''|GPL Licensed Tiddlers|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/gpl.html|
|''Workspace:''|Main|
|''Description:''|Games|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/games.html|
|''Workspace:''|Main|
|''Description:''|Notes|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/notes.html|
|''Workspace:''|Main|
|''Description:''|Notes Extensions|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/notes-ext.html|
|''Workspace:''|Main|
|''Description:''|iTW|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/itw.html|
|''Workspace:''|Main|
|''Description:''|iTW Notes|
|''Type:''|file|
|''URL:''|https://galasoft.com/tiddlywiki/itwnotes.html|
|''Workspace:''|Main|
[[TW Notes|https://sourceforge.net/p/gsutil/code/ci/master/tree/src/html/tiddlywiki/]] is a collection of tiddlers and code taken from various sources. Please see each individual tiddler for its applicable restrictions and licenses. Any tiddler that does not contain a restriction or license statement falls under the following [[BSD license|https://opensource.org/license/bsd-3-clause]]:
Copyright (c) 2010-2025, Galasoft Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the Galasoft Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<!--{{{-->
<form action='javascript:;' style="white-space:nowrap">
<input type="hidden" name="title" value="">
<input type="hidden" name="section" value="">
<input type="text" name="newsection" value="" autocomplete="off" style="width:61%"
onchange="return config.macros.editSection.changed(this,event);">
<input type=button value="save" style="width:12%"
onclick="return config.macros.editSection.saveForm(this,event)">
<input type=button value="cancel" style="width:12%"
onclick="return config.macros.editSection.cancel(this,event)">
<input type=button value="delete" style="width:12%"
onclick="return config.macros.editSection.remove(this,event)">
<div macro="tiddler QuickEditToolbar"></div>
<textarea name="content" rows="15" cols="80" autocomplete="off"
onchange="return config.macros.editSection.changed(this,event)"></textarea>
<span macro='imagePaste jQuery(place).parent().find("textarea").eq(0)'>
</form>
<!--}}}-->
/***
|Name|TWNotesInitPlugin|
|License|[[TW Notes License]]|
!!!!!CustomScripts
{{{
<script>
</script>
}}}
!!!!!EditTemplate
{{{
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='keyboard'></span><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span><span macro='imagePaste'></span></div>
<!--}}}-->
}}}
!!!!!MainMenu
{{{
[[Notes]]
<<matchTags "[[%0]]" "\n" sort:+title notes AND NOT Trash>>
[[GettingStarted]]
}}}
!!!!!MarkupPostBody
{{{
<script type="text/javascript">
(function () {
window.loadCookies = function () {};
window.removeCookie = function (name) {};
window.saveCookie = function (name) {};
})();
</script>
}}}
!!!!!MarkupPostHead
{{{
<!--{{{-->
<script type="text/javascript">
var OriginalJSON = JSON;
var OriginalDate = Date;
OriginalDate.prototype.getTimezoneOffsetOverride_base = OriginalDate.prototype.getTimezoneOffset;
OriginalDate.prototype.getTimezoneOffset = function getTimezoneOffset () {
if (!isNaN(parseFloat(this["overriddentimezoneoffset"])) && isFinite(this["overriddentimezoneoffset"])) {
return this["overriddentimezoneoffset"];
} else if (this["overriddentimezone"]) {
if (this["overriddentimezone"] == "ET") {
var daylightSavingTimeStart = new OriginalDate(this.getFullYear(), 3 - 1, 8, 2, 0, 0, 0);
daylightSavingTimeStart.setDate(daylightSavingTimeStart.getDate() + ((7 - daylightSavingTimeStart.getDay()) % 7));
var daylightSavingTimeEnd = new OriginalDate(this.getFullYear(), 11 - 1, 1, 2, 0, 0, 0);
daylightSavingTimeEnd.setDate(daylightSavingTimeEnd.getDate() + ((7 - daylightSavingTimeEnd.getDay()) % 7));
if ((this.getTime() >= daylightSavingTimeStart.getTime()) && (this.getTime() <= daylightSavingTimeEnd.getTime())) {
return 240;
} else {
return 300;
}
}
}
return this.getTimezoneOffsetOverride_base();
}
Date = function (year, month, day, hours, minutes, seconds, milliseconds) {
for (var dateComponentKey in Date.prototype) {
OriginalDate.prototype[dateComponentKey] = Date.prototype[dateComponentKey];
}
for (var dateComponentKey in Date) {
OriginalDate[dateComponentKey] = Date[dateComponentKey];
}
var dateTimeTiddlerName = "DateTime Data";
var newDate = null;
if (typeof year === 'undefined') {
newDate = new OriginalDate();
if ((typeof store !== 'undefined') && store) {
var value = store.getValue(dateTimeTiddlerName, "overriddenyear");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setYear(Number(value));
value = store.getValue(dateTimeTiddlerName, "overriddenmonth");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setMonth(Number(value) - 1);
value = store.getValue(dateTimeTiddlerName, "overriddenday");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setDate(Number(value));
value = store.getValue(dateTimeTiddlerName, "overriddenhours");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setHours(Number(value));
value = store.getValue(dateTimeTiddlerName, "overriddenminutes");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setMinutes(Number(value));
value = store.getValue(dateTimeTiddlerName, "overriddenseconds");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setSeconds(Number(value));
value = store.getValue(dateTimeTiddlerName, "overriddenmilliseconds");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate.setMilliseconds(Number(value));
}
} else if (typeof month === 'undefined') {
newDate = new OriginalDate(year);
} else if (typeof day === 'undefined') {
newDate = new OriginalDate(year, month);
} else if (typeof hours === 'undefined') {
newDate = new OriginalDate(year, month, day);
} else if (typeof minutes === 'undefined') {
newDate = new OriginalDate(year, month, day, hours);
} else if (typeof seconds === 'undefined') {
newDate = new OriginalDate(year, month, day, hours, minutes);
} else if (typeof milliseconds === 'undefined') {
newDate = new OriginalDate(year, month, day, hours, minutes, seconds);
} else {
newDate = new OriginalDate(year, month, day, hours, minutes, seconds, milliseconds);
}
if ((typeof store !== 'undefined') && store) {
var value = store.getValue(dateTimeTiddlerName, "overriddentimezoneoffset");
if (!isNaN(parseFloat(value)) && isFinite(value)) newDate["overriddentimezoneoffset"] = Number(value);
value = store.getValue(dateTimeTiddlerName, "overriddentimezone");
if (value) newDate["overriddentimezone"] = value;
}
return newDate;
}
Date.now = OriginalDate.now;
Date.UTC = OriginalDate.UTC;
Date.prototype = OriginalDate.prototype;
</script>
<!--}}}-->
}}}
!!!!!MarkupPreHead
{{{
<!--{{{-->
<link rel="alternate" type="application/rss+xml" title="RSS" href="index.xml">
<link rel="shortcut icon" href="data:;base64,AAABAAEAEBAAAAAAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5/////////////a2tra2tra2tra2tr////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5/////////////////////a2tra2tra2tra2tr////MzMz///+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////////////////////////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////////////////////////////MzMz///+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr////////////////////////////////////////////////////////////////ETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwv2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw8AAOLpAADg6QAAf18AAP//AAD//wAA//8AAP//AAD//wAAAIAAAP//AAD/fwAA//8AAACAAAD//wAA//8AAOHp" type="image/vnd.microsoft.icon" />
<link rel="icon" href="data:;base64,AAABAAEAEBAAAAAAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5/////////////a2tra2tra2tra2tr////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5/////////////////////a2tra2tra2tra2tr////MzMz///+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////////////////////////MzMz////////////////////////////////////////a2tra2tra2tra2tr///////////+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////////////////////////////MzMz///+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5/////a2tra2tra2tra2tr////////////////////////////////////////////////////////////////ETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPETQPVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfVXgfmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwvmbwv2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw/2fw8AAOLpAADg6QAAf18AAP//AAD//wAA//8AAP//AAD//wAAAIAAAP//AAD/fwAA//8AAACAAAD//wAA//8AAOHp" type="image/vnd.microsoft.icon" />
<!--}}}-->
}}}
!!!!!Notes
{{{
<script>
if (config.macros.fileDrop && config.macros.fileDrop.fileDropSupported()) {
return "//New tiddlers may also be created by dropping files " + (config.macros.fileDrop.folderDropSupported() ? "" : "or folders ") + "on this document.//\n\n";
}
</script><<tiddler CustomScripts>>[[Trash]]
-------
<<calendar thismonth tag:"(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT attachment AND NOT Trash">>
<<showReminders format:"DIFF: TITLE" leadtime:7 limit tag:"(NOT meeting OR NOT cancelled) AND (NOT task OR NOT done) AND NOT attachment AND NOT Trash">>
<<matchTags "[[%0]]" "\n" sort:+title important AND (NOT task OR NOT done) AND NOT attachment AND NOT Trash>>
-------
<<list filter "[tag[notes AND NOT Trash]]">>
-------
<<option chkAutoSave>> AutoSave [[TiddlyWiki Markup]]
}}}
!!!!!ToolbarCommands
{{{
|~ViewToolbar|decryptThis closeTiddler closeOthers +editTiddler > fields syncing permalink references jump copyTiddler snapshotSave undoChanges|
|~EditToolbar|+saveTiddler -cancelTiddler copyTiddler deleteTiddler|
}}}
!!!!!ViewTemplate
{{{
<!--{{{-->
<div class='toolbar' role='navigation' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view created date "YYYY-0MM-0DD 0hh:0mm"'></span> / <span macro='view modified date "YYYY-0MM-0DD 0hh:0mm"'></span></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='tagClear'></div>
<span macro='reciprocalTags Tags'></span>
<span macro='relatedTiddlers Related'></span>
<span macro='calc Calculator'></span>
<span macro="checkbox important">important</span>
<!--}}}-->
}}}
!!!!!Code
***/
//{{{
config.TWNotesInitPlugin = {
shadowTiddlers: {
CustomScripts: {
requires: ["InlineJavascriptPlugin"]
},
DefaultTiddlers: {
requires: ["CalendarPlugin", "InlineJavascriptPlugin", "MatchTagsPlugin", "ReminderMacros"],
content: function () {
return "[[Notes]]";
}
},
EditTemplate: {
requires: [],
content: function () {
var t = store.getTiddlerText("TWNotesInitPlugin##EditTemplate");
if (!store.tiddlerExists("KeyboardPlugin")) {
t = t.replace("<span macro='keyboard'></span>", "");
}
if (!store.tiddlerExists("ImagePastePlugin")) {
t = t.replace("<span macro='imagePaste'></span>", "");
}
return t.substring(t.indexOf("{{{") + "{{{".length, t.lastIndexOf("}}}")).trim();
}
},
MainMenu: {
requires: ["CalendarPlugin", "InlineJavascriptPlugin", "MatchTagsPlugin", "ReminderMacros"]
},
MarkupPostBody: {
requires: []
},
MarkupPostHead: {
requires: []
},
MarkupPreHead: {
requires: []
},
Notes: {
requires: ["CalendarPlugin", "InlineJavascriptPlugin", "MatchTagsPlugin", "ReminderMacros"]
},
SiteSubtitle: {
requires: [],
content: function () {
return "a TiddlyWiki for taking notes";
}
},
SiteTitle: {
requires: [],
content: function () {
return "TW Notes";
}
},
ToolbarCommands: {
requires: [],
content: function () {
var t = store.getTiddlerText("TWNotesInitPlugin##ToolbarCommands");
if (!store.tiddlerExists("SyncPlugin")) {
t = t.replace(" syncing", "");
}
return t.substring(t.indexOf("{{{") + "{{{".length, t.lastIndexOf("}}}")).trim();
}
},
ViewTemplate: {
requires: [],
content: function () {
var t = store.getTiddlerText("TWNotesInitPlugin##ViewTemplate");
if (!store.tiddlerExists("ReciprocalTagsPlugin")) {
t = t.substring(0, t.lastIndexOf("<span", t.indexOf("reciprocalTags"))) + t.substring(t.indexOf("<span", t.indexOf("reciprocalTags")));
}
if (!store.tiddlerExists("RelatedTiddlersPlugin")) {
t = t.substring(0, t.lastIndexOf("<span", t.indexOf("relatedTiddlers"))) + t.substring(t.indexOf("<span", t.indexOf("relatedTiddlers")));
}
if (!store.tiddlerExists("CalculatorPlugin")) {
t = t.substring(0, t.lastIndexOf("<span", t.indexOf("calc"))) + t.substring(t.indexOf("<span", t.indexOf("calc")));
}
if (!store.tiddlerExists("CheckboxPlugin")) {
if (t.lastIndexOf(" ", t.indexOf("checkbox")) > -1) {
t = t.substring(0, t.lastIndexOf(" ", t.indexOf("checkbox"))) + t.substring(t.indexOf("</span>", t.indexOf("checkbox")) + "</span>".length + 1);
} else {
t = t.substring(0, t.lastIndexOf("<span", t.indexOf("checkbox"))) + t.substring(t.indexOf("</span>", t.indexOf("checkbox")) + "</span>".length + 1);
}
}
return t.substring(t.indexOf("{{{") + "{{{".length, t.lastIndexOf("}}}")).trim();
}
}
},
backstageTasks: [{text: "date time", tooltip: "", tiddler: "DateTime"}, {text: "cleanup", tooltip: "", tiddler: "Cleanup"}],
initialized: false,
init: function() {
var twnip = config.TWNotesInitPlugin;
for (var shadowTiddlerTitle in twnip.shadowTiddlers) {
var shadowTiddlerDef = twnip.shadowTiddlers[shadowTiddlerTitle];
if (shadowTiddlerDef) {
var hasRequirements = true;
for (var i = 0; i < shadowTiddlerDef.requires.length; i++) {
hasRequirements &= store.tiddlerExists(shadowTiddlerDef.requires[i]);
}
if (hasRequirements) {
var shadowTiddler;
if (shadowTiddlerDef.content) {
shadowTiddler = shadowTiddlerDef.content();
shadowTiddler = store.getTiddlerText(shadowTiddler) || shadowTiddler;
} else {
shadowTiddler = store.getTiddlerText("TWNotesInitPlugin##" + shadowTiddlerTitle);
if (shadowTiddler) {
shadowTiddler = shadowTiddler.substring(shadowTiddler.indexOf("{{{") + "{{{".length, shadowTiddler.lastIndexOf("}}}")).trim();
}
}
if (shadowTiddler != null) {
config.shadowTiddlers[shadowTiddlerTitle] = shadowTiddler;
}
}
}
}
for (var i = 0; i < twnip.backstageTasks.length; i++) {
if (store.tiddlerExists(twnip.backstageTasks[i].tiddler) && (config.backstageTasks.indexOf(twnip.backstageTasks[i].tiddler) === -1)) {
config.tasks[twnip.backstageTasks[i].tiddler] = {text: twnip.backstageTasks[i].text, tooltip: twnip.backstageTasks[i].tooltip, content: "[[" + twnip.backstageTasks[i].tiddler + "]]\n\n" + store.getTiddlerText(twnip.backstageTasks[i].tiddler)};
config.backstageTasks[config.backstageTasks.length] = twnip.backstageTasks[i].tiddler;
}
}
if (store.tiddlerExists("ImagePastePlugin")) {
var optionsPanel = store.getTiddlerText("OptionsPanel");
if (optionsPanel) {
var index = optionsPanel.lastIndexOf("<<option ");
if (index > -1) {
index = optionsPanel.indexOf("]]", index);
if (index > -1) {
index += 2;
optionsPanel = optionsPanel.substring(0, index) + "\n<<option chkPasteImages>> [[EnableImagePasting]]" + optionsPanel.substring(index);
config.shadowTiddlers["OptionsPanel"] = optionsPanel;
}
}
}
}
config.options.txtUserName = "YourName";
config.options.chkAutoSave = false;
config.options.chkSaveBackups = true;
config.options.txtBackupFolder = "backup";
config.options.chkUpdateTaggedTiddlersOnSaveChanges = true;
config.options.chkDisableNonExistingWikiLinks = true;
config.options.chkDisableWikiLinks = true;
config.options.chkEnableUndo = true;
config.options.chkSearchTitlesFirst = false;
config.options.chkDatePopupHideCreated = true;
config.options.chkDatePopupHideChanged = true;
config.options.chkDatePopupHideTagged = false;
config.options.chkDatePopupHideReminders = false;
config.options.chkDisplayWeekNumbers = true;
config.options.chkEnhanceReminderPerformance = false;
config.options.chkPasteImages = true;
config.newMeansNewForJournalsToo = false;
if (!twnip.initialized) {
var ccFieldsHandlePopup = config.commands.fields.handlePopup;
config.commands.fields.handlePopup = function (popup, title) {
if (popup) {
jQuery(popup).on("click", function (event) {
event.stopPropagation();
event.preventDefault();
});
}
ccFieldsHandlePopup.call(this, popup, title);
if (popup && store.tiddlerExists("CopyToClipboardPlugin")) {
jQuery(popup).find("tbody").find("td").each(function () {
var column = jQuery(this);
var value = column.text();
column.empty();
wikify('<clip title=""><nowiki>' + value + '</nowiki></clip>', column[0]);
});
}
};
config.customCommands = {};
var cmToolbarCustomCommand = config.macros.toolbar.customCommand;
config.macros.toolbar.customCommand = function (place, commandName, wikifier, tiddler) {
if (config.customCommands[commandName] && jQuery.isFunction(config.customCommands[commandName].init)) {
var command = config.customCommands[commandName].init(place, commandName, wikifier, tiddler);
if (command && command[0]) {
command[0].onclick = null;
command.on("click", function (e) {
e.preventDefault();
e.stopPropagation();
var command = config.customCommands[this.getAttribute("commandName")];
return command.handler(e.originalEvent, this, this.getAttribute("tiddler"));
});
}
} else {
cmToolbarCustomCommand.call(this, place, commandName, wikifier, tiddler);
}
};
var forEachFieldCode = eval("TiddlyWiki.prototype.forEachField").toString();
forEachFieldCode = forEachFieldCode.replace(/(var\s[\s\S]*?var\s.*?;)([\s\S]*?\{)([\s\S]*?,\s*)(n[\s\S]*?\))/, "$1\n\tvar fieldOrder = [];\n\tfor (var n in t.fields) fieldOrder[fieldOrder.length] = n;\n\tfieldOrder.sort();\n\tfor (var i = 0; i < fieldOrder.length; i++) {$3fieldOrder[i],t.fields[fieldOrder[i]])");
eval("TiddlyWiki.prototype.forEachField = function forEachField" + forEachFieldCode.substring(forEachFieldCode.indexOf("(")));
var onGetTiddlerCode = eval("config.macros.importTiddlers.onGetTiddler").toString();
onGetTiddlerCode = onGetTiddlerCode.replace("tiddler.created", "tiddler.created, tiddler.creator");
onGetTiddlerCode = onGetTiddlerCode.replace("store.setValue(tiddler.title, 'server', null)", "if (tiddler.fields && tiddler.fields['changecount']) { var importedTiddler = store.fetchTiddler(tiddler.title); if (!importedTiddler.fields) importedTiddler.fields = {}; importedTiddler.fields['changecount'] = tiddler.fields['changecount']; } store.setValue(tiddler.title, 'server', null)");
eval("config.macros.importTiddlers.onGetTiddler = function onGetTiddler" + onGetTiddlerCode.substring(onGetTiddlerCode.indexOf("(")));
var original = window.originalHTML || recreateOriginal();
var posDiv = locateStoreArea(original);
if (posDiv) {
window.originalHTML = original.substr(0, posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
}
var sliderHandlerCode = eval(config.macros.slider.sliderHandler ? "config.macros.slider.sliderHandler" : "config.macros.slider.handler").toString();
sliderHandlerCode = sliderHandlerCode.replace(/(store\.getTiddler\(.*?\))/, "(!store.tiddlerExists(params[1]) && (params[1].indexOf('##') > -1)) ? store.getTiddler(params[1].substring(0, params[1].indexOf('##'))) : store.getTiddler(params[1])");
eval((config.macros.slider.sliderHandler ? "config.macros.slider.sliderHandler = function handler" : "config.macros.slider.handler = function handler") + sliderHandlerCode.substring(sliderHandlerCode.indexOf("(")));
var genericCreateCode = eval("config.macros.option.genericCreate").toString();
if (genericCreateCode.indexOf("createTiddlyText") === -1) {
var initialGenericCreate = config.macros.option.genericCreate;
genericCreateCode = genericCreateCode.replace("return", "if (desc != 'no') createTiddlyText(place, ' ' + (config.optionsDesc[opt] || opt)); return");
eval("config.macros.option.genericCreate = " + genericCreateCode);
for (var k in config.macros.option.types) {
if (config.macros.option.types[k].create === initialGenericCreate) {
config.macros.option.types[k].create = config.macros.option.genericCreate;
}
}
}
// fix for js/Strings.js included in TiddlyWiki 2.9.2: https://github.com/TiddlyWiki/TiddlyWiki/commit/bae3ff2c27297030fd3f60788de1c2b73d298986
var parseParamsCode = eval("String.prototype.parseParams").toString();
parseParamsCode = parseParamsCode.replace("cascadeDefaults", "cascadeDefaults, allowEscaping");
parseParamsCode = parseParamsCode.replace(/(n\s*=\s*|return\s+)(match\[.*?\])(\.replace.*?\))/g, "$1(allowEscaping === false) ? $2 : $2$3");
eval("String.prototype.parseParams = " + parseParamsCode);
}
twnip.initialized = true;
loadOptions();
}
};
//}}}
//{{{
// config.TWNotesInitPlugin.init() is called before the config.macros[m].init() functions are called
config.TWNotesInitPlugin.init();
//}}}
/***
|Name|TWSaverHTTPServerPlugin|
|License|[[TW Notes License]]|
!!!!!Java Server
Download the TW Saver HTTP Server: [[TWSaverHTTPServer.jar|data:application/java-archive;base64,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]]
Usage: {{{java -jar TWSaverHTTPServer.jar [<bind-address> | <port> | <bind-address>:<port>] [<authorized-root-folder>...]}}}
!!!!!Node.js Server
{{{
const dns = require("dns");
const fs = require("fs");
const http = require("http");
const path = require("path");
const process = require("process");
var exports = function () {
var hostname = null;
var port = 6942;
var authorizedRootFolders = [];
var checkAuthorizedFolder = function (folder) {
var authorizedFolder = false;
var folderPath = (folder + path.sep).toLowerCase();
for (var i = 0; i < authorizedRootFolders.length; i++) {
if ((authorizedRootFolders[i] === null)
|| (folderPath.indexOf(authorizedRootFolders[i][1]) === 0)) {
authorizedFolder = true;
break;
}
}
return authorizedFolder;
};
var requestListener = function (request, response) {
var onRequestData;
var onRequestEnd;
var onRequestError;
var sendResponse = function (data) {
request.removeListener("data", onRequestData);
request.removeListener("end", onRequestEnd);
request.removeListener("error", onRequestError);
response.statusCode = 200;
response.setHeader("Server", "TW Saver HTTP Server");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Content-Type", "text/plain");
response.end(data);
};
response.on("error", function (error) {
console.log(error);
});
var chunks = [];
var fileDescriptor = null;
onRequestData = function (chunk) {
if (fileDescriptor == null) {
chunks.push(chunk);
var chunkBuffer = Buffer.concat(chunks);
var fileUrl = chunkBuffer.toString();
if (fileUrl.indexOf(" : ") > -1) {
fileUrl = fileUrl.substring(0, fileUrl.indexOf(" : "));
chunk = chunkBuffer.subarray(Buffer.from(fileUrl + " : ").length);
fileUrl = fileUrl.replace(/\\/g, "/");
var file = path.resolve(fileUrl);
var folder = path.dirname(file);
var authorizedFolder = checkAuthorizedFolder(folder);
console.log("Saving file: " + file);
if (!authorizedFolder) {
var data = "Unauthorized folder: " + folder;
console.log(" " + data);
sendResponse(data);
} else {
try {
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder, {recursive: true});
}
fileDescriptor = fs.openSync(file, "w");
} catch (error) {
onRequestError(error);
}
}
}
}
if (fileDescriptor != null) {
try {
fs.writeSync(fileDescriptor, chunk);
} catch (error) {
onRequestError(error);
}
}
};
onRequestEnd = function () {
try {
if (fileDescriptor != null) {
fs.closeSync(fileDescriptor);
}
fileDescriptor = null;
sendResponse("success");
} catch (error) {
onRequestError(error);
}
};
onRequestError = function (error) {
console.log(error);
if (fileDescriptor != null) {
try {
fs.closeSync(fileDescriptor);
} catch (e) {
console.log(e);
}
}
fileDescriptor = null;
sendResponse(error.message);
};
request.on("data", onRequestData);
request.on("end", onRequestEnd);
request.on("error", onRequestError);
}
var loadConfig = function (args, index, callback) {
var argsIndex = index || 2;
var argHostname = null;
var argPort = null;
if (args && (args.length > argsIndex)) {
if (args[argsIndex].indexOf(':') > 0) {
argHostname = args[argsIndex].substring(0, args[argsIndex].lastIndexOf(':'));
var argPort = parseInt(args[argsIndex].substring(args[argsIndex].lastIndexOf(':') + 1));
argPort = Number.isNaN(argPort) ? null : argPort;
} else {
if ((args[argsIndex].indexOf(".") > -1) || Number.isNaN(argPort = parseInt(args[argsIndex]))) {
argPort = null;
argHostname = args[argsIndex];
}
}
}
var validateHost = function (err, address, family) {
if (!err) {
hostname = argHostname;
port = (argPort == null) ? port : argPort;
argsIndex++;
} else if (argPort != null) {
port = argPort;
argsIndex++;
}
var authorizedFolders = [];
if (argsIndex >= args.length) {
authorizedFolders.push(path.resolve(".") + path.sep);
} else {
for (; argsIndex < args.length; argsIndex++) {
var arg = args[argsIndex].trim();
if (arg === "*") {
authorizedFolders = [null];
break;
} else {
var folderPath = arg;
if (fs.existsSync(folderPath) && fs.lstatSync(folderPath).isDirectory()) {
folderPath = path.resolve(args[argsIndex]) + path.sep;
if (authorizedFolders.indexOf(folderPath) === -1) {
authorizedFolders.push(folderPath);
}
}
}
}
if (authorizedFolders.length && (authorizedFolders[0] != null)) {
for (var i = 0; i < authorizedFolders.length; i++) {
var folderPath = authorizedFolders[i];
for (var j = 0; j < authorizedFolders.length; j++) {
if ((folderPath !== authorizedFolders[j]) && (folderPath.indexOf(authorizedFolders[j]) === 0)) {
authorizedFolders.splice(i, 1);
i--;
break;
}
}
}
}
}
for (var i = 0; i < authorizedFolders.length; i++) {
authorizedRootFolders[i] = (authorizedFolders[i] != null)
? [authorizedFolders[i], authorizedFolders[i].toLowerCase()]
: authorizedFolders[i];
}
callback(authorizedFolders);
};
if (argHostname) {
dns.lookup(argHostname, validateHost);
} else {
validateHost("nohostname");
}
};
var start = function (args, index, listener) {
requestListener = listener || requestListener;
loadConfig(args, index, function (authorizedFolders) {
if (!authorizedFolders.length) {
console.log("Authorized root folders: none");
} else if (authorizedFolders[0] === null) {
console.log("Authorized root folders: all");
} else {
console.log("Authorized root folders:");
for (var i = 0; i < authorizedFolders.length; i++) {
console.log(" " + authorizedFolders[i]);
}
}
console.log();
http.createServer(requestListener).listen(port, hostname || "0.0.0.0", function () {
if (hostname != null) {
console.log("Listening on");
console.log(" address: " + hostname);
console.log(" port: " + port);
} else {
console.log("Listening on port: " + port);
}
console.log();
});
});
};
return {
authorizedRootFolders: authorizedRootFolders,
checkAuthorizedFolder: checkAuthorizedFolder,
start: start
};
};
module.exports = exports;
if (require.main === module) {
exports().start(process.argv);
}
}}}
Usage: {{{node TWSaverHTTPServer.js [<bind-address> | <port> | <bind-address>:<port>] [<authorized-root-folder>...]}}}
!!!!!Code
***/
//{{{
if (config.options.txtTWSaverHTTPServerURL === undefined) {
config.options.txtTWSaverHTTPServerURL = "http://localhost:6942";
}
if (config.options.txtTWSaverHTTPServerAdditionalSavePaths === undefined) {
config.options.txtTWSaverHTTPServerAdditionalSavePaths = "";
}
if (config.options.txtTWSaverHTTPServerTempFolderPath === undefined) {
config.options.txtTWSaverHTTPServerTempFolderPath = "";
}
if (config.options.chkTWSaverHTTPServerSmallBackups === undefined) {
config.options.chkTWSaverHTTPServerSmallBackups = false;
}
merge(config.optionsDesc, {
txtTWSaverHTTPServerURL: "The URL of the TW Saver HTTP Server (blank to disable)",
txtTWSaverHTTPServerAdditionalSavePaths: "Full paths, including file names, of additional locations in which to save the main document, separated by spaces and forward slashes ( / )",
txtTWSaverHTTPServerTempFolderPath: "The path to the folder used to store temporary files (blank to use the TiddlyWiki's current folder)",
chkTWSaverHTTPServerSmallBackups: "Save backups that contain only the initial versions of modified or deleted tiddlers and a list of any added tiddlers"
});
//}}}
//{{{
(function() {
var windowSaveMain = window.saveMain;
function formatLocalPathLink(localPath) {
var link = "";
if (localPath) {
localPath = localPath.replace(/\\/g, "/");
if (window.isLocal()) {
if (localPath.indexOf("//") === 0) {
link = "file:" + localPath;
} else if (localPath.indexOf("/") === 0) {
link = "file://" + localPath;
} else {
link = "file:///" + localPath;
}
} else {
link = document.location.protocol + "//" + localPath.trim().replace(/^\/+/, "") + "?t=text/html";
}
}
return link;
}
var savingInProgressWarning = function (e) {
return "--------------------------------\n\nSaving in progress. If you continue it will be interrupted.\n\n--------------------------------";
};
var isSaving = false;
var mainFile = null;
var tempFilePath = null;
var deleteTempFile = false;
var usePreForStorage = config.options.chkUsePreForStorage;
window.saveMain = function (localPath, original, posDiv, callback) {
if (isSaving) {
if (confirm("Saving in progress. Save anyway?")) {
isSaving = false;
mainFile = null;
tempFilePath = null;
deleteTempFile = false;
}
}
if (isSaving) {
config.saveByDownload = true;
} else {
var onbeforeunload = "none";
if (window.onbeforeunload !== savingInProgressWarning) {
onbeforeunload = window.onbeforeunload;
window.onbeforeunload = savingInProgressWarning;
}
isSaving = true;
mainFile = localPath;
tempFilePath = null;
deleteTempFile = false;
var smallBackups = config.options.chkTWSaverHTTPServerSmallBackups;
var additionalSavePaths = config.options.txtTWSaverHTTPServerAdditionalSavePaths;
usePreForStorage = config.options.chkUsePreForStorage;
try {
var revised = updateOriginal(original, posDiv, localPath);
displayMessage("Saving...");
setTimeout(function () {
deleteTempFile = !additionalSavePaths && !config.options.chkSaveBackups;
saveFile(localPath, revised, function (save, content, savedByTWSaverHTTPServer) {
if (save) {
window.originalHTML = content;
if (!config.saveByManualDownload) {
if (!savedByTWSaverHTTPServer && config.saveByDownload) { //# set by HTML5DownloadSaveFile()
var link = getDataURI(revised);
var msg = config.messages.mainDownload;
} else {
var link = formatLocalPathLink(localPath);
var msg = config.messages.mainSaved;
}
displayMessage(msg, link);
}
store.setDirty(false);
var saveAdditional = function (paths, index) {
if (!paths) {
paths = additionalSavePaths;
if (!paths) {
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
isSaving = false;
mainFile = null;
tempFilePath = null;
deleteTempFile = false;
if (callback) {
callback();
}
} else {
paths = paths.split(/ \/ /g);
saveAdditional(paths, 0);
}
} else {
if (index < paths.length) {
displayMessage("Saving additional copy...");
setTimeout(function () {
deleteTempFile = index === paths.length - 1;
saveFile(paths[index], revised, function (save, content, savedByTWSaverHTTPServer) {
if (save) {
displayMessage("Additional copy saved", formatLocalPathLink(paths[index]));
} else {
alert("Failed to save " + paths[index]);
}
saveAdditional(paths, index + 1)
});
}, 0);
} else {
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
isSaving = false;
mainFile = null;
tempFilePath = null;
deleteTempFile = false;
if (callback) {
callback();
}
}
}
};
if (savedByTWSaverHTTPServer && config.options.chkSaveBackups) {
deleteTempFile = !additionalSavePaths;
var backup = original;
if (smallBackups && usePreForStorage) {
var createSmallBackup = function (backup, revised, postMessageFct) {
var backupStore = locateStoreArea(backup);
var revisedStore = locateStoreArea(revised);
if (backupStore && revisedStore) {
var tiddlerStart = '<div title="';
var startIndex;
var endIndex;
var noBackupTiddlers = -1;
var noRevisedTiddlers = -1;
if (postMessageFct) {
noBackupTiddlers = 0;
startIndex = backup.indexOf(tiddlerStart, backupStore[0]);
while ((startIndex > -1) && (startIndex < backupStore[1])) {
noBackupTiddlers++;
startIndex = backup.indexOf(tiddlerStart, startIndex + 1);
}
noRevisedTiddlers = 0;
startIndex = revised.indexOf(tiddlerStart, revisedStore[0]);
while ((startIndex > -1) && (startIndex < revisedStore[1])) {
noRevisedTiddlers++;
startIndex = revised.indexOf(tiddlerStart, startIndex + 1);
}
}
var noProcessed = 0;
var updatedPercent = -1;
var updateProcessed = function () {
noProcessed++;
if (postMessageFct && ((noBackupTiddlers + noRevisedTiddlers) > 0)) {
var percent = Math.round(noProcessed * 100 / (noBackupTiddlers + noRevisedTiddlers));
if (updatedPercent < percent) {
postMessageFct({percent: percent});
updatedPercent = percent;
}
}
};
var addedTiddlers = [];
startIndex = revised.indexOf(tiddlerStart, revisedStore[0]);
while ((startIndex > -1) && (startIndex < revisedStore[1])) {
endIndex = revised.indexOf('"', startIndex + tiddlerStart.length);
if (endIndex > -1) {
var backupIndex = backup.indexOf(revised.substring(startIndex, endIndex + 1), backupStore[0]);
if ((backupIndex == -1) || (backupIndex >= backupStore[1])) {
addedTiddlers[addedTiddlers.length] = revised.substring(startIndex + tiddlerStart.length, endIndex);
}
}
startIndex = revised.indexOf(tiddlerStart, startIndex + 1);
updateProcessed();
}
startIndex = backup.indexOf(tiddlerStart, backupStore[0]);
var cutStartIndex = startIndex;
while ((startIndex > -1) && (startIndex < backupStore[1])) {
endIndex = backup.indexOf(tiddlerStart, startIndex + 1);
endIndex = ((endIndex == -1) || (endIndex > backupStore[1])) ? backupStore[1] : endIndex;
if (revised.indexOf(backup.substring(startIndex, endIndex)) > -1) {
startIndex = endIndex;
} else {
if (cutStartIndex <= startIndex) {
backup = backup.substring(0, cutStartIndex) + backup.substring(startIndex);
backupStore = locateStoreArea(backup);
startIndex = cutStartIndex + (endIndex - startIndex);
}
cutStartIndex = startIndex;
}
updateProcessed();
}
if (cutStartIndex < startIndex) {
backup = backup.substring(0, cutStartIndex) + backup.substring(startIndex);
}
startIndex = backup.indexOf("<script");
if (startIndex > -1) {
var added = "<!-- Added Tiddlers\n";
for (var i = 0; i < addedTiddlers.length; i++) {
added += addedTiddlers[i] + "\n";
}
added += "-->\n"
backup = backup.substring(0, startIndex) + added + backup.substring(startIndex);
}
if (postMessageFct && (updatedPercent < 100)) {
postMessageFct({percent: 100});
}
return backup;
} else {
return "";
}
}
if (window.Worker) {
var smallBackupMessage = "Creating small backup";
displayMessage(smallBackupMessage);
var blob = new Blob(
["onmessage = function (e) { eval(e.data.env); var workerResult = createSmallBackup(e.data.backup, e.data.revised, postMessage); postMessage(workerResult); }"],
{type: "text/javascript"});
var worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = function (e) {
if (typeof e.data === "string") {
if (e.data) {
displayMessage("Saving backup...");
setTimeout(function () {
saveBackup(localPath, e.data, function () {
saveAdditional();
});
}, 0);
} else {
saveAdditional();
}
} else {
var smallBackupMessagePlace = jQuery("#messageArea").children(".messageArea__text").filter(function (index) {
return jQuery(this).text().indexOf(smallBackupMessage) === 0;
});
if (smallBackupMessagePlace.length) {
smallBackupMessagePlace.text(smallBackupMessage + " " + e.data.percent + "%");
} else {
displayMessage(smallBackupMessage + " " + e.data.percent + "%");
}
}
}
try {
worker.postMessage({
env: "var startSaveArea = '" + startSaveArea + "'"
+ "; var startSaveAreaRE = " + startSaveAreaRE.toString()
+ "; var endSaveArea = '" + endSaveArea + "'"
+ "; var endSaveAreaCaps = '" + endSaveAreaCaps + "'"
+ "; var locateStoreArea = " + locateStoreArea.toString()
+ "; var createSmallBackup = " + createSmallBackup.toString(),
backup: backup,
revised: revised
});
} catch (e) {
alert(e);
}
} else {
displayMessage("Saving backup...");
setTimeout(function () {
try {
backup = createSmallBackup(backup, revised);
} catch (e) {
alert(e);
}
if (backup) {
saveBackup(localPath, backup, function () {
saveAdditional();
});
} else {
saveAdditional();
}
}, 0);
}
} else {
displayMessage("Saving backup...");
setTimeout(function () {
saveBackup(localPath, backup, function () {
saveAdditional();
});
}, 0);
}
} else {
saveAdditional();
}
} else {
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
isSaving = false;
mainFile = null;
tempFilePath = null;
deleteTempFile = false;
alert(config.messages.mainFailed);
if (callback) {
callback();
}
}
});
}, 0);
} catch (ex) {
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
isSaving = false;
mainFile = null;
tempFilePath = null;
deleteTempFile = false;
showException(ex);
if (callback) {
callback();
}
}
}
};
var windowSaveBackup = window.saveBackup;
window.saveBackup = function (localPath, original, callback) {
var backupFolder = config.options.txtBackupFolder;
var backupPath = getBackupPath(localPath);
if (backupFolder && (backupPath.indexOf(backupFolder) > -1) && ((backupFolder.indexOf("/") == 0) || (backupFolder.indexOf("\\") == 0) || (backupFolder.indexOf(":") > -1))) {
backupPath = backupPath.substring(backupPath.indexOf(backupFolder));
}
var backup = copyFile(backupPath, localPath);
if (!backup) {
backup = saveFile(backupPath, original, function (backup) {
if (backup) {
displayMessage(config.messages.backupSaved, formatLocalPathLink(backupPath));
if (callback) {
callback();
}
} else {
alert(config.messages.backupFailed);
if (callback) {
callback();
}
}
});
} else {
if (callback) {
callback();
}
}
};
var windowSaveFile = window.saveFile;
window.saveFile = function (fileUrl, content, callback) {
callback = callback || function () {};
if (config.options.txtTWSaverHTTPServerURL) {
config.saveByDownload = true;
var postRequest = function (callback) {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState == 4 /* XMLHttpRequest.DONE */ ) {
callback(request.responseText, request.getResponseHeader("X-TWN-TempFileError"));
}
};
try {
request.open("POST", config.options.txtTWSaverHTTPServerURL, true);
if (usePreForStorage) {
if (mainFile != null) {
request.setRequestHeader("X-TWN-MainFile", mainFile);
}
if (tempFilePath != null) {
request.setRequestHeader("X-TWN-TempFilePath", tempFilePath);
}
if (deleteTempFile) {
request.setRequestHeader("X-TWN-DeleteTempFile", "true");
}
}
request.send(fileUrl + " : " + content);
} catch (e) {
callback("" + e);
}
};
var onbeforeunload = "none";
if (window.onbeforeunload !== savingInProgressWarning) {
onbeforeunload = window.onbeforeunload;
window.onbeforeunload = savingInProgressWarning;
}
var saveTWRetries = 0;
var popup = null;
var popupTimer = (new Date()).getTime();
var saveCanceled = false;
var saveTW = function (resetTempFilePath) {
if (resetTempFilePath && (mainFile != null)) {
var backupFolder = config.options.txtBackupFolder;
config.options.txtBackupFolder = config.options.txtTWSaverHTTPServerTempFolderPath || "";
tempFilePath = getBackupPath(mainFile, "temp");
config.options.txtBackupFolder = backupFolder;
}
postRequest(function (result, tempFileError) {
resetTempFilePath = !!tempFileError;
var hidePopup = function () {
if (popup != null) {
jQuery(popup).css("display", "none");
document.body.removeChild(popup);
popup = null;
removeStyleSheet("TWSaverHTTPServerPluginModal");
}
popupTimer = (new Date()).getTime();
}
if (result == "success") {
hidePopup();
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
callback(true, content, true);
return true;
} else {
if (!saveCanceled && !result) {
saveTWRetries++;
if ((popup === null) && ((new Date()).getTime() - popupTimer >= 5000)) {
setStylesheet("\
.TWSaverHTTPServerPluginModal {\
display: none;\
position: fixed;\
z-index: 9999999;\
padding-top: 100px;\
left: 0;\
top: 0;\
width: 100%;\
height: 100%;\
overflow: auto;\
background-color: rgb(0,0,0);\
background-color: rgba(0,0,0,0.4);\
}\
.TWSaverHTTPServerPluginModal-content {\
background-color: #fefefe;\
margin: auto;\
padding: 0px;\
border: 1px solid #888;\
width: 400px;\
}\
.TWSaverHTTPServerPluginModal-header {\
padding: 2px 16px;\
color: white;\
}\
.TWSaverHTTPServerPluginModal-body {\
padding: 2px 16px;\
}\
.TWSaverHTTPServerPluginModal-footer {\
padding: 2px 16px;\
color: white;\
}\
.TWSaverHTTPServerPluginModalClose {\
color: #aaaaaa;\
float: right;\
font-size: 28px;\
font-weight: bold;\
}\
.TWSaverHTTPServerPluginModalClose:hover, .TWSaverHTTPServerPluginModalClose:focus {\
color: #000;\
text-decoration: none;\
cursor: pointer;\
}", "TWSaverHTTPServerPluginModal");
popup = document.getElementById("TWSaverHTTPServerPluginPopup");
if (popup) {
document.body.removeChild(popup);
}
popup = createTiddlyElement(document.body, "div", "TWSaverHTTPServerPluginPopup", "TWSaverHTTPServerPluginModal");
jQuery(popup).html("\
<div class='TWSaverHTTPServerPluginModal-content'>\
<div class='TWSaverHTTPServerPluginModal-header'>\
<span class='TWSaverHTTPServerPluginModalClose'>×</span>\
<p> </p>\
</div>\
<div class='TWSaverHTTPServerPluginModal-body'>\
<p>Save retry: <span id='TWSaverHTTPServerPluginModalSaveRetry'></span></p>\
</div>\
<div class='TWSaverHTTPServerPluginModal-footer'>\
<span class='TWSaverHTTPServerPluginModalClose'><button id='TWSaverHTTPServerPluginModalCancel'>Cancel TW Saver HTTP Server saving</button></span>\
<p> </p>\
</div>\
</div>");
jQuery(popup).find(".TWSaverHTTPServerPluginModalClose").on("click", hidePopup);
jQuery(popup).find("#TWSaverHTTPServerPluginModalCancel").on("click", function () {
hidePopup();
saveCanceled = true;
});
jQuery(popup).css("display", "block");
}
jQuery("#TWSaverHTTPServerPluginModalSaveRetry").text(saveTWRetries);
setTimeout(function () {
saveTW(resetTempFilePath);
}, 500);
} else if (!saveCanceled && confirm(result + "\nChoose OK to try again.")) {
hidePopup();
saveTW(resetTempFilePath);
} else {
hidePopup();
if (onbeforeunload !== "none") {
window.onbeforeunload = onbeforeunload;
}
if (confirm("An error occurred while saving the file with the TW Saver HTTP Server.\nChoose OK to try other saving methods.")) {
config.saveByDownload = false;
if (windowSaveFile) {
var result = windowSaveFile(fileUrl, content);
callback(result, content, false);
return result;
}
callback(false, content, false);
return false;
} else {
callback(false, content, false);
return false;
}
}
}
});
};
saveTW(tempFilePath == null);
return true;
} else {
if (windowSaveFile) {
var result = windowSaveFile(fileUrl, content);
callback(result, content);
return result;
}
callback(false, content, false);
return false;
}
};
})();
//}}}
/***
|Name|TaskTimerPlugin|
|Source|http://www.TiddlyTools.com/#TaskTimerPlugin|
|Documentation|http://www.TiddlyTools.com/#TaskTimerPluginInfo|
|Version|1.4.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|'timer' button automatically writes start/end/elapsed time into tiddler content|
Quickly generate 'timed task' logs that can be used for status reports, billing purposes, etc.
!!!!!Documentation
> see [[TaskTimerPluginInfo]]
!!!!!Configuration
> see [[TaskTimerPluginConfig]]
!!!!!Revisions
<<<
2008.11.10 [1.4.1] in elapsed time calculation, truncate start/stop times to nearest second (avoids 1-second 'round-down' error)
|please see [[TaskTimerPluginInfo]] for additional revision details|
2007.03.14 [0.5.0] converted from inline script
<<<
!!!!!Code
***/
//{{{
version.extensions.TaskTimerPlugin= {major: 1, minor:4, revision: 1, date: new Date(2008,11,10)};
config.macros.taskTimer = {
label: "start timer",
title: "press to start the task timer",
format: "|%4|%0|%1|%2|%3|\\n", // note: double-backslash-en, also date is %4 (for backward compatibility)
defText: " ", // default description text
todayKeyword: "today",
todayFormat: "0MM/0DD/YYYY", // default format - superceded by CalendarPlugin, DatePlugin, or DatePluginConfig
datestampFormat: "YYYY-0MM-0DD", // date stamp format
buttonFormat: "%0 - %2", // timer button formats: %0=current time, %1=start time, %2=elapsed time
defHeader: "|//Date//|//Description//|//Started//|//Stopped//|//Elapsed//|\n",
defTarget: "ActivityReport",
descrMsg: "Enter a short description for this activity. Press [cancel] to continue timer.",
askMsg: "Enter the title of a tiddler in which to record this activity. Press [cancel] to continue timer.",
errMsg: "'%0' is not a valid tiddler title. Please try again...\n\n",
createdMsg: "'%0' has been created",
updatedMsg: "'%0' has been updated",
marker: "/%"+"tasktimer"+"%/",
tag: "task",
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var target=params.shift(); // get optional target tiddler title
if (!target) target="";
var format=this.format; if (params[0]) format=params.shift(); // get optional output format
var descrMsg=this.descrMsg; if (params[0]) descrMsg=params.shift(); // get optional message text
var defText=this.defText; if (params[0]) defText=params.shift(); // get optional default text
var onclick="config.macros.taskTimer.toggle(this,'"+target+"','"+format+"','"+descrMsg+"','"+defText+"')";
createTiddlyElement(place,"span").innerHTML =
'<input type="button" value="start timer" title="'+this.title+'" onclick="'+onclick+'">';
},
toggle: function(here,target,format,msg,defText) {
if (!target || !target.length || target=="here") {
var tid=story.findContainingTiddler(here);
target=tid?tid.getAttribute("tiddler"):"ask";
}
if (!here.running) { // not running... start timer...
here.startTime=new Date();
var now=here.startTime.formatString("0hh:0mm:0ss");
here.title=(here.target||target)+" - started at "+now;
here.value=this.buttonFormat.format([now,now,"00:00:00"]);
here.id=new Date().getTime()+Math.random().toString(); // unique ID
here.ticker=setTimeout("config.macros.taskTimer.tick('"+here.id+"')",500);
here.running=true;
} else {
if (target=="ask") {
target=prompt(this.askMsg,here.target||this.defTarget);
while (target && !target.trim().length)
target=prompt(this.errMsg.format([target])+this.askMsg,here.target||this.defTarget);
if (!target) return; // user cancelled input... continue timer
}
var txt=prompt(msg,defText); // get description from user
if (!txt) return; // user cancelled input... continue timer
if (target==this.todayKeyword || target.substr(0,this.todayKeyword.length+1)==this.todayKeyword+":")
target=(new Date()).formatString(this.getJournalFormat(target));
here=document.getElementById(here.id); // RE-get button element after timer has stopped...
clearTimeout(here.ticker);
here.target=target;
var before=this.defHeader;
var after=this.marker+"\n";
var tiddler=store.getTiddler(here.target);
if (tiddler && tiddler.text.length) {
var pos=tiddler.text.indexOf(this.marker);
if (pos==-1) pos=tiddler.text.length; // no marker, append content to end
var before=tiddler.text.substr(0,pos); // everything up to marker
if (before.length&&before.substr(before.length-1)!="\n") before+="\n"; // start on a new line
var after=tiddler.text.substr(pos); // marker+everything else
}
var now=new Date(Math.floor(new Date()/1000)*1000);
var then=new Date(Math.floor(here.startTime/1000)*1000);
var diff=new Date(now-then);
var s=diff.getUTCSeconds(); if (s<10) s="0"+s;
var m=diff.getUTCMinutes(); if (m<10) m="0"+m;
var h=diff.getUTCHours(); if (h<10) h="0"+h;
var start=then.formatString("0hh:0mm:0ss");
var stop=now.formatString("0hh:0mm:0ss");
var elapsed=h+":"+m+":"+s;
var dateStamp=now.formatString(config.macros.taskTimer.datestampFormat);
var newtxt=before+format.format([txt,start,stop,elapsed,dateStamp])+after;
var newtags=(tiddler?tiddler.tags:['task']); // include 'task' tag when creating new tiddlers
store.saveTiddler(here.target,here.target,newtxt,config.options.txtUserName,new Date(),newtags,tiddler?tiddler.fields:null);
if (!tiddler) displayMessage(this.createdMsg.format([here.target]));
else displayMessage(this.updatedMsg.format([here.target]));
here.running=false;
here.value=this.label;
here.title=this.title;
var tid=story.findContainingTiddler(here);
if (!tid || tid.getAttribute("tiddler")!=target) // display target tiddler, but only when button is not IN the target tiddler
{ story.displayTiddler(story.findContainingTiddler(here),here.target); story.refreshTiddler(here.target,1,true); }
}
},
tick: function(id) {
var here=document.getElementById(id); if (!here) return;
var now=new Date();
var diff=new Date(now-here.startTime);
var s=diff.getUTCSeconds(); if (s<10) s="0"+s;
var m=diff.getUTCMinutes(); if (m<10) m="0"+m;
var h=diff.getUTCHours(); if (h<10) h="0"+h;
var elapsed=h+":"+m+":"+s;
now=now.formatString("0hh:0mm:0ss");
var start=here.startTime.formatString("0hh:0mm:0ss");
here.value=this.buttonFormat.format([now,start,elapsed]);
here.ticker=setTimeout("config.macros.taskTimer.tick('"+id+"')",500);
},
getJournalFormat: function(target) {
var fmt=target.split(":"); fmt.shift(); fmt=fmt.join(":");
if (!fmt || !fmt.length) { // if date format was not specified
if (config.macros.date) // if installed, use default from DatePlugin
fmt=config.macros.date.linkformat;
if (config.macros.calendar) { // if installed, use default from CalendarPlugin
if (!config.macros.date) // hard-coded calendar fallback if no DatePlugin
fmt=config.macros.calendar.tiddlerformat;
else // journalDateFmt is set when calendar is rendered with DatePlugin
fmt=config.macros.calendar.journalDateFmt;
}
}
if (!fmt) { // if not specified and no DatePlugin/CalendarPlugin
// get format from <<newJournal>> in SideBarOptions
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]) fmt = pa[0]; }
}
if (!fmt) var fmt=this.todayFormat; // no "newJournal"... final fallback.
return fmt;
}
}
//}}}
/***
|Name|TaskTimerPluginConfig|
|Source|http://www.TiddlyTools.com/#TaskTimerPluginConfig|
|Documentation|http://www.TiddlyTools.com/#TaskTimerPluginInfo|
|Version|1.3.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|formats other optional settings for TaskTimerPlugin|
***/
//{{{
// default target tiddler title (when 'ask' option is used)
config.macros.taskTimer.defTarget="ActivityReport";
// table heading (when creating **new** target tiddlers only)
config.macros.taskTimer.defHeader="|//Date//|//Description//|//Started//|//Stopped//|//Elapsed//|\n";
// note: double-backslash-en, also datestamp is %4 (for backward compatibility)
config.macros.taskTimer.format="|%4|%0|%1|%2|%3|\\n";
// date stamp format (used with %4, above)
config.macros.taskTimer.datestampFormat="YYYY-0MM-0DD";
// default description text - note: do not use empty string (e.g., "")
config.macros.taskTimer.defText=" ";
// format for target tiddler title (when "today" option is used)
// otherwise, value is superceded by CalendarPlugin, DatePlugin, DatePluginConfig,
// or format from <<newJournal>> macro embedded in SideBarOptions
config.macros.taskTimer.todayFormat="0MM/0DD/YYYY";
// marker for locating 'insertion point' in target tiddler
config.macros.taskTimer.marker="/%"+"tasktimer"+"%/"; //
// default tag (when creating **new** target tiddlers only)
config.macros.taskTimer.tag="task";
//}}}
/***
|Name|TaskTimerPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[TaskTimerPlugin]]|
!!!!!Code
***/
//{{{
config.macros.taskTimer.timerField = "tasktimerpluginoverride";
config.macros.taskTimer.timerFieldPause = "tasktimerpluginoverridepause";
//}}}
//{{{
config.macros.taskTimer.toggleOverride_base = config.macros.taskTimer.toggle;
config.macros.taskTimer.toggle = function (here, target, format, msg, defText) {
if (!here.running) {
config.macros.taskTimer.updateTimerField("", here, target, config.macros.taskTimer.timerFieldPause);
config.macros.taskTimer.updateTimerField(config.macros.taskTimer.startTimer(null, here, target), here, target);
} else {
here.pauseTime = (new Date()).getTime();
clearTimeout(here.ticker);
here.pauseLength = config.macros.taskTimer.updateTimerField(null, here, target, config.macros.taskTimer.timerFieldPause);
if (here.pauseLength.length) {
here.pauseLength = parseInt(here.pauseLength);
if (isNaN(here.pauseLength)) {
here.pauseLength = 0;
}
} else {
here.pauseLength = 0;
}
var startTime = config.macros.taskTimer.updateTimerField("", here, target);
config.macros.taskTimer.toggleOverride_base(here, target, format, msg, defText);
if (here.running) {
here.pauseLength = here.pauseLength + ((new Date()).getTime() - here.pauseTime);
config.macros.taskTimer.updateTimerField(here.pauseLength, here, target, config.macros.taskTimer.timerFieldPause);
config.macros.taskTimer.updateTimerField(startTime, here, target);
here.ticker = setTimeout("config.macros.taskTimer.tick('" + here.id + "')", 500);
}
}
};
config.macros.taskTimer.updateTimerField = function (value, here, target, name) {
name = name ? name : config.macros.taskTimer.timerField;
var previousValue;
if (!target || !target.length || (target === "here")) {
var tid = story.findContainingTiddler(here);
target = tid ? tid.getAttribute("tiddler") : null;
}
if (target) {
var tiddler = store.getTiddler(target);
if (tiddler) {
previousValue = tiddler.fields[name];
if ((value != null) && (tiddler.fields[name] !== value)) {
tiddler.fields[name] = "" + value;
tiddler.modifier = config.options.txtUserName;
tiddler.modified = new Date();
store.setDirty(true);
}
}
}
return (previousValue != null) ? previousValue : "";
};
//}}}
//{{{
(function () {
// https://stackoverflow.com/questions/9645803/whats-the-replacement-for-browser
if (!jQuery.browser) {
var uaMatch = function (ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
return {
browser: match[1] || "",
version: match[2] || "0"
};
};
var matched = uaMatch(navigator.userAgent),
browser = {};
if (matched.browser) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if (browser.chrome) {
browser.webkit = true;
} else if (browser.webkit) {
browser.safari = true;
}
jQuery.browser = browser;
}
})();
//}}}
//{{{
(function () {
var code;
code = eval("config.macros.taskTimer.handler").toString();
code = code.replace(/createTiddlyElement[\s\S]*?;/,
'var span = createTiddlyElement(place,"span");\n' +
'var attributes = {};\n' +
'attributes["type"] = "button";\n' +
'attributes["value"] = "start timer";\n' +
'attributes["title"] = this.title;\n' +
'attributes["onclick"] = (jQuery.browser.msie && (parseInt(jQuery.browser.version, 10) < 8)) ? function() { eval(onclick); } : onclick;\n' +
'var inputField = createTiddlyElement(span, "input", null, null, null, attributes);\n' +
'var storedTime = config.macros.taskTimer.updateTimerField(null, inputField, target);\n' +
'if (storedTime.length) {\n' +
' storedTime = parseInt(storedTime);\n' +
' if (!isNaN(storedTime)) {\n' +
' config.macros.taskTimer.startTimer(new Date(storedTime), inputField, target);\n' +
' }\n' +
'}\n'
);
eval("config.macros.taskTimer.handler = function handler" + code.substring(code.indexOf("(")));
code = eval("config.macros.taskTimer.tick").toString();
code = code.replace("now-here.startTime", "now - here.startTime - (here.pauseLength ? here.pauseLength : 0)");
eval("config.macros.taskTimer.tick = function tick" + code.substring(code.indexOf("(")));
code = eval("config.macros.taskTimer.toggleOverride_base").toString();
code = code.replace("new Date()/1000", "(here.pauseTime ? here.pauseTime : new Date()) / 1000");
code = code.replace("now-then", "now - then - (here.pauseLength ? (Math.floor(here.pauseLength / 1000) * 1000) : 0)");
eval("config.macros.taskTimer.toggleOverride_base = function toggleOverride_base" + code.substring(code.indexOf("(")));
code =
'config.macros.taskTimer.startTimer = function (startTime, here, target) {\n' +
' if (!target || !target.length || (target === "here")) {\n' +
' var tid=story.findContainingTiddler(here);\n' +
' target = tid ? tid.getAttribute("tiddler") : "";\n' +
' }\n' +
' here.startTime = startTime ? startTime : new Date();\n' +
code.substring(code.indexOf("var now"), code.indexOf("true;") + "true;".length) + '\n' +
' return here.startTime.getTime();\n' +
'}';
eval(code);
})();
//}}}
/***
|Name|TiddlerEncryptionPlugin|
|Author|Lyall Pearce|
|Source|http://www.Remotely-Helpful.com/TiddlyWiki/TiddlerEncryptionPlugin.html|
|License|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|Version|3.2.2|
|~CoreVersion|2.4.0|
|Requires|None|
|Overrides|store.getSaver().externalizeTiddler(), store.getTiddler() and store.getTiddlerText()|
|Description|Encrypt/Decrypt Tiddlers with a Password key|
!!!!!Usage
<<<
* Tag a tiddler with Encrypt(prompt)
** Consider the 'prompt' something to help you remember the password with. If multiple tiddlers can be encrypted with the same 'prompt' and you will only be asked for the password once.
* Upon save, the Tiddler will be encrypted and the tag replaced with Decrypt(prompt).
** Failure to encrypt (by not entering a password) will leave the tiddler unencrypted and will leave the Encrypt(prompt) tag in place. This means that the next time you save, you will be asked for the password again.
** To have multiple tiddlers use the same password - simply use the same 'prompt'.
** Tiddlers that are encrypted may be automatically tagged 'excludeSearch' as there is no point in searching encrypted data - this is configurable by an option - you still may want to search the titles of encrypted tiddlers
** Tiddlers that are encrypted may be automatically tagged 'excludeLists', if you have them encrypted, you may also want to keep them 'hidden' - this is configurable by an option.
** Automatic removal of excludeLists and excludeSearch tags is performed, if the above two options are set, only if these two tags are the last 2 tags for a tiddler, if they are positioned somewhere else in the tags list, they will be left in place, meaning that the decrypted tiddler will not be searchable and/or will not appear in lists.
** Encrypted tiddlers are stored as displayable hex, to keep things visibly tidy, should you display an encrypted tiddler. There is nothing worse than seeing a pile of gobbledy gook on your screen. Additionally, the encrypted data is easily cut/paste/emailed if displayed in hex form.
* Tiddlers are decrypted only if you click the decrypt button or the decryptAll button, not when you load the TiddlyWiki
** If you don't display a tiddler, you won't have the option to decrypt it (unless you use the {{{<<EncryptionDecryptAll>>}}} macro)
** Tiddlers will re-encrypt automatically on save.
** Decryption of Tiddlers does not make your TiddlyWiki 'dirty' - you will not be asked to save if you leave the page.
* Errors are reported via diagnostic messages.
** Empty passwords, on save, will result in the tiddler being saved unencrypted - this should only occur with new tiddlers, decrypted tiddlers or with tiddlers who have had their 'prompt' tag changed.
** Encrypted tiddlers know if they are decrypted successfully - failure to decrypt a tiddler will ''not'' lose your data.
** Editing of an encrypted (that has not been unencrypted) tiddler will result in loss of that tiddler as the SHA1 checksums will no longer match, upon decryption. To this end, it is best that you do not check the option. You can, however edit an encrypted tiddler tag list - just do ''not'' change the tiddler contents.
** To change the password on a Tiddler, change the Encrypt('prompt') tag to a new prompt value, after decrypting the tiddler.
** You can edit the tags of an encrypted tiddler, so long as you do not edit the text.
** To change the password for all tiddlers of a particular prompt, use the {{{<<EncryptionChangePassword ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro.
** To decrypt all tiddlers of a particular "prompt string", use the {{{<<EncryptionDecryptAll ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro - this will make tiddlers encrypted with "prompt string" searchable - or prompt for all 'prompt strings', if none is supplied.
<<<
!!!!!Configuration
<<<
Useful Buttons:
<<EncryptionChangePassword>> - Change passwords of encrypted tiddlers.
<<EncryptionDecryptAll>> - Decrypt ALL tiddlers - enables searching contents of encrypted tiddlers.
<<option chkExcludeEncryptedFromSearch>> - If set, Encrypted Tiddlers are excluded from searching by tagging with excludeSearch. If Clear, excludeSearch is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Searching of Encrypted Tiddlers is only meaningful for the Title and Tags.
<<option chkExcludeEncryptedFromLists>> - If set, Encrypted Tiddlers are excluded from lists by tagging with excludeLists. If Clear, excludeLists is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Preventing encrypted tiddlers from appearing in lists effectively hides them.
<<option chkShowDecryptButtonInContent>> - If set, Encrypted Tiddlers content is replaced by <<EncryptionDecryptThis>> button. This has consequences, in the current version as, if you edit the tiddler without decrypting it, you lose the contents.
<<<
!!!!!Revision History
<<<
* 3.2.2 - added tiddler.changed() calls whenever plugin changes tiddler content as per recommendations by Udo.
* 3.2.1 - Returned the <<EncryptionDecryptThis>> button as an option.
* 3.2.0 - Ditched the 'Decrypt' button showing up in the tiddler contents if the tiddler is encrypted. It caused too much pain if you edit the tiddler without decrypting it - you lost your data as it was replaced by a Decrypt Macro call! Additionally, a 'decrypt' button will now appear in the toolbar, just before the edit button, if the tiddler is encrypted. This button only appears if using core TiddlyWiki version 2.4 or above.
* 3.1.1 - Obscure bug whereby if an encrypted tiddler was a certain length, it would refuse to decrypt.
* 3.1.0 - When creating a new Encrypt(prompt) tiddler and you have not previously decrypted a tiddler with the same prompt, on save, you will be prompted for the password to encrypt the tiddler. Prior to encrypting, an attempt to decrypt all other tiddlers with the same prompt, is performed. If any tiddler fails to decrypt, the save is aborted - this is so you don't accidentally have 2 (or more!) passwords for the same prompt. Either you enter the correct password, change the prompt string and try re-saving or you cancel (and the tiddler is saved unencrypted).
* 3.0.1 - Allow Enter to be used for password entry, rather than having to press the OK button.
* 3.0.0 - Major revamp internally to support entry of passwords using forms such that passwords are no longer visible on entry. Completely backward compatible with old encrypted tiddlers. No more using the javascript prompt() function.
<<<
!!!!!Additional work
***/
//{{{
version.extensions.TiddlerEncryptionPlugin = {major: 3, minor: 2, revision: 2, date: new Date(2012,05,17)};
// where I cache the passwords - for want of a better place.
config.encryptionPasswords = new Array();
config.encryptionReEnterPasswords = false;
if(config.options.chkExcludeEncryptedFromSearch == undefined) config.options.chkExcludeEncryptedFromSearch = false;
if(config.options.chkExcludeEncryptedFromLists == undefined) config.options.chkExcludeEncryptedFromLists = false;
if(config.options.chkShowDecryptButtonInContent == undefined) config.options.chkShowDecryptButtonInContent = false;
config.macros.EncryptionChangePassword = {};
config.macros.EncryptionChangePassword.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Change Passwords",
(params[1] && params[1].length > 0) ? params[1] : "Change Passwords" + (params[2] ? " for prompt "+params[2] : ""),
onClickEncryptionChangePassword,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("promptString", params[2]);
}
};
config.macros.EncryptionDecryptAll = {};
config.macros.EncryptionDecryptAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Decrypt All",
(params[1] && params[1].length > 0) ? params[1] : "Decrypt All Tiddlers" + ((params[2] && params[2].length > 0) ? " for prompt "+params[2] : " for a given 'prompt string'"),
onClickEncryptionDecryptAll,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("promptString", params[2]);
}
};
config.macros.EncryptionDecryptThis = {};
config.macros.EncryptionDecryptThis.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Decrypt",
(params[1] && params[1].length > 0) ? params[1] : "Decrypt this Tiddler",
onClickEncryptionDecryptThis,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("theTiddler", params[2]);
}
};
// toolbar button to decrypt tiddlers.
config.commands.decryptThis = {
text: "decrypt",
tooltip: "Decrypt this tiddler",
isEnabled : function(tiddler) {
// Only show decrypt button if tiddler is tagged as Decrypt(
if(tiddler.tags.join().indexOf('Decrypt(') == -1) {
return false;
} else {
return true;
}
},
handler: function(event, src, title) {
encryptionGetAndDecryptTiddler(title);
return false;
}
};
// core version 2.4 or above get a 'decrypt' button in the toolbar.
if(config.shadowTiddlers && config.shadowTiddlers.ToolbarCommands && config.shadowTiddlers.ToolbarCommands.indexOf('decryptThis') == -1) {
// put our toolbar button in before the edit button.
// won't work if editTiddler is not the default item (prefixed with plus)
config.shadowTiddlers.ToolbarCommands.replace(/\+editTiddler/,'decryptThis +editTiddler');
}
// Called by the EncryptionChangePassword macro/button
// Also invoked by the callback for password entry
function onClickEncryptionChangePassword(eventObject) {
var promptString;
if(!promptString && this.getAttribute) {
promptString = this.getAttribute("promptString");
}
// I do call this function directly
if(!promptString && typeof(eventObject) == "string") {
promptString = eventObject;
}
if(!promptString) {
promptString = prompt("Enter 'prompt string' to change password for:","");
}
if(!promptString) {
return;
}
if(! config.encryptionPasswords[promptString]) {
var changePasswordContext = {changePasswordPromptString: promptString,
callbackFunction: MyChangePasswordPromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(promptString,"",changePasswordContext);
return;
// Callback function will re-invoke this function
}
// Decrypt ALL tiddlers for that prompt
onClickEncryptionDecryptAll(promptString);
// Now ditch the cached password, this will force the re-request for the new password, on save.
displayMessage("Save TiddlyWiki to set new password for '"+promptString+"'");
config.encryptionPasswords[promptString] = null;
// mark store as dirty so a save will be requrested.
store.setDirty(true);
autoSaveChanges();
return;
};
// Called by the password entry form when the user clicks 'OK' button.
function MyChangePasswordPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
onClickEncryptionChangePassword(context.changePasswordPromptString);
return;
}
// Called by the EncryptionDecryptThis macro/button
function onClickEncryptionDecryptThis() {
var theTiddler = this.getAttribute("theTiddler");
if(!theTiddler) {
return;
}
encryptionGetAndDecryptTiddler(theTiddler);
return;
};
function encryptionGetAndDecryptTiddler(title) {
config.encryptionReEnterPasswords = true;
try {
theTiddler = store.getTiddler(title);
config.encryptionReEnterPasswords = false;
story.refreshAllTiddlers();
} catch (e) {
if(e == "DecryptionFailed") {
displayMessage("Decryption failed");
return;
}
} // catch
return;
};
// called by the EncryptionDecryptAlll macro/button
// Also called by the callback after the user clicks 'OK' button on the password entry form
function onClickEncryptionDecryptAll(eventObject) {
var promptString;
if(!promptString && this.getAttribute) {
promptString = this.getAttribute("promptString");
}
// I do call this function directly
if(!promptString && typeof(eventObject) == "string") {
promptString = eventObject;
}
if(!promptString) {
promptString = "";
}
// Loop through all tiddlers, looking to see if there are any Decrypt(promptString) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally do the decryptAll functionality, which will then
// be able to decrypt all the required tiddlers, without prompting.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
var tagToSearchFor="Decrypt("+promptString;
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(!promptGenerated && tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPromptString=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPromptString]) {
// no password cached, prompt and cache it, rather than decryptAll
// callback from prompting form will resume decryptAll attempt.
var decryptAllContext = {decryptAllPromptString: promptString,
callbackFunction: MyDecryptAllPromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(passwordPromptString,"",decryptAllContext);
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
// Now do the decrypt all functionality
try {
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
try {
CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
} catch (e) {
displayMessage("Decryption of '"+tiddler.title+"' failed.");
// throw e;
}
} // if(tiddler.tags
} // for
} // if
}); // store.forEachTiddler
displayMessage("All tiddlers" + (promptString != "" ? " for '"+promptString+"'" : "") + " have been decrypted");
} catch (e) {
if(e == "DecryptionFailed") {
return;
}
} // catch
}
return;
};
function MyDecryptAllPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// restart the decryptAll process again after the user has entered a password.
onClickEncryptionDecryptAll(context.decryptAllPromptString);
return;
}
saveChanges_TiddlerEncryptionPlugin = saveChanges;
saveChanges = function(onlyIfDirty,tiddlers) {
// Loop through all tiddlers, looking to see if there are any Encrypt(string) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally call the original saveChanges() function, which will then
// be able to save the tiddlers.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
if(!promptGenerated && tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf("Encrypt(") == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPrompt]) {
// no password cached, prompt and cache it, rather than save
var saveContext = {onlyIfDirty: onlyIfDirty,
tiddlers: tiddlers,
callbackFunction: MySavePromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(passwordPrompt,"",saveContext);
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
saveChanges_TiddlerEncryptionPlugin(onlyIfDirty,tiddlers);
}
return;
}
function MySavePromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// validate the password entered by attempting to decrypt all tiddlers
// with the same encryption prompt string.
onClickEncryptionDecryptAll(context.passwordPrompt);
// restart the save process again
saveChanges(context.onlyIfDirty, context.tiddlers);
return;
}
store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin = store.getSaver().externalizeTiddler;
store.getSaver().externalizeTiddler = function(store, tiddler) {
// Ok, got the tiddler, track down the passwordPrompt in the tags.
// track down the Encrypt(passwordPrompt) tag
if(tiddler && tiddler.tags) {
for(var g=0; g<tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf("Encrypt(") == 0) {
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Encrypt this tiddler!
var decryptedSHA1 = Crypto.hexSha1Str(tiddler.text);
var password = GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
var encryptedText = TEAencrypt(tiddler.text, password);
encryptedText = StringToHext_TiddlerEncryptionPlugin(encryptedText);
tiddler.text = "Encrypted("+decryptedSHA1+")\n"+encryptedText;
// Replace the Tag with the Decrypt() tag
tiddler.tags[g]="Decrypt("+passwordPrompt+")";
// prevent searches on encrypted tiddlers, still nice to search on title though.
if(config.options.chkExcludeEncryptedFromSearch == true) {
tiddler.tags.push("excludeSearch");
}
// prevent lists of encrypted tiddlers
if(config.options.chkExcludeEncryptedFromLists == true) {
tiddler.tags.push("excludeLists");
}
tiddler.changed();
// let the store know it's dirty
store.setDirty(tiddler.title, true);
} else {
// do not encrypt - no password entered
}
break;
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if(tiddler.tags...
// Then, finally, do the save by calling the function we override.
return store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin(store, tiddler);
};
function CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler) {
if(tiddler && tiddler.tags) {
for(var g=0; g<tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf("Decrypt(") == 0) {
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
if(tiddler.text.substr(0,10) == "Encrypted(") {
var closingSHA1Bracket = tiddler.text.indexOf(")");
var decryptedSHA1 = tiddler.text.substring(10, closingSHA1Bracket);
// Ok, tagged with Decrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Decrypt this tiddler!
var decryptedText = tiddler.text.substr(closingSHA1Bracket+2);
decryptedText = HexToString_TiddlerEncryptionPlugin(decryptedText);
// prompt("Decryption request for Tiddler '"+tiddler.title+"'");
var password = GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
decryptedText = TEAdecrypt(decryptedText, password );
var thisDecryptedSHA1 = Crypto.hexSha1Str(decryptedText);
if(decryptedSHA1 == thisDecryptedSHA1) {
tiddler.text = decryptedText;
// Replace the Tag with the Encrypt() tag
tiddler.tags[g]="Encrypt("+passwordPrompt+")";
if(tiddler.tags[tiddler.tags.length-1] == 'excludeLists') {
// Remove exclude lists only if it's the last entry
// as it's automatically put there by encryption
tiddler.tags.length--;
}
if(tiddler.tags[tiddler.tags.length-1] == 'excludeSearch') {
// Remove exclude search only if it's the last entry
// as it's automatically put there by encryption
tiddler.tags.length--;
}
tiddler.changed();
} else {
// Did not decrypt, discard the password from the cache
config.encryptionPasswords[passwordPrompt] = null;
config.encryptionReEnterPasswords = false;
throw "DecryptionFailed";
}
} else {
// no password supplied, dont bother trying to decrypt
config.encryptionReEnterPasswords = false;
throw "DecryptionFailed";
}
} else {
// Tagged as encrypted but not expected format, just leave it unchanged
}
break; // out of for loop
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if (tiddler && tags)
return tiddler;
};
store.getTiddler_TiddlerEncryptionPlugin = store.getTiddler;
store.getTiddler = function(title) {
var tiddler = store.getTiddler_TiddlerEncryptionPlugin(title);
if(tiddler) { // shadow tiddlers are not expected to be encrypted.
try {
return CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
} catch (e) {
if (config.options.chkShowDecryptButtonInContent == true) {
if(e == "DecryptionFailed") {
var tiddler = store.getTiddler("DecryptionFailed");
if(!tiddler) {
tiddler = new Tiddler();
tiddler.set(title,
"<<EncryptionDecryptThis \"Decrypt\" \"Decrypt this tiddler\" \""+title+"\">>",
config.views.wikified.shadowModifier,
version.date,[],version.date);
}
return tiddler;
} // if(e)
}
return(tiddler);
} // catch
} // if(tiddler) {
return null;
};
store.getTiddlerText_TiddlerEncryptionPlugin = store.getTiddlerText;
store.getTiddlerText = function(title,defaultText) {
// Simply retrieve the tiddler, normally, if it requires decryption, it will be decrypted
var decryptedTiddler = store.getTiddler(title);
if(decryptedTiddler) {
return decryptedTiddler.text;
}
//Ok, rather than duplicate all the core code, the above code should fail if we reach here
// let the core code take over.
return store.getTiddlerText_TiddlerEncryptionPlugin(title,defaultText);
};
// Given a prompt, search our cache to see if we have already entered the password.
// Can return null if the user enters nothing.
function MyPrompt_TiddlerEncryptionPlugin(promptString,defaultValue,context) {
if(!context) {
context = {};
}
context.passwordPrompt = promptString;
PasswordPrompt.prompt(MyPromptCallback_TiddlerEncryptionPlugin, context);
return;
}
function MyPromptCallback_TiddlerEncryptionPlugin(context) {
if(context.callbackFunction) {
context.callbackFunction(context);
} else {
config.encryptionPasswords[context.passwordPrompt] = context.password;
story.refreshAllTiddlers(true);
}
return;
}
function GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString) {
if(!config.encryptionPasswords[promptString]) {
config.encryptionPasswords[promptString] = MyPrompt_TiddlerEncryptionPlugin(promptString, "");
}
return config.encryptionPasswords[promptString]; // may be null, prompt can be cancelled.
}
function GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(promptString) {
if(config.encryptionReEnterPasswords) {
return GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString);
} else {
return config.encryptionPasswords[promptString];
}
}
// Make the encrypted tiddlies look a little more presentable.
function StringToHext_TiddlerEncryptionPlugin(theString) {
var theResult = "";
for(var i=0; i<theString.length; i++) {
var theHex = theString.charCodeAt(i).toString(16);
if(theHex.length<2) {
theResult += "0"+theHex;
} else {
theResult += theHex;
}
if(i && i % 32 == 0)
theResult += "\n";
}
return theResult;
}
function HexToString_TiddlerEncryptionPlugin(theString) {
var theResult = "";
for(var i=0; i<theString.length; i+=2) {
if(theString.charAt(i) == "\n") {
i--; // cause us to skip over the newline and resume
continue;
}
theResult += String.fromCharCode(parseInt(theString.substr(i, 2),16));
}
return theResult;
}
//
// Heavily leveraged from http://trac.tiddlywiki.org/browser/Trunk/contributors/SaqImtiaz/verticals/Hesperian/PasswordPromptPlugin.js Revision 5635
//
PasswordPrompt ={
prompt : function(callback,context){
if (!context) {
context = {};
}
var box = createTiddlyElement(document.getElementById("contentWrapper"),'div','passwordPromptBox');
box.innerHTML = store.getTiddlerText('PasswordPromptTemplate');
box.style.position = 'absolute';
this.center(box);
document.getElementById('promptDisplayField').value = context.passwordPrompt;
var passwordInputField = document.getElementById('passwordInputField');
passwordInputField.onkeyup = function(ev) {
var e = ev || window.event;
if(e.keyCode == 10 || e.keyCode == 13) { // Enter
PasswordPrompt.submit(callback, context);
}
};
passwordInputField.focus();
document.getElementById('passwordPromptSubmitBtn').onclick = function(){PasswordPrompt.submit(callback,context);};
document.getElementById('passwordPromptCancelBtn').onclick = function(){PasswordPrompt.cancel(callback,context);};
},
center : function(el){
var size = this.getsize(el);
el.style.left = (Math.round(findWindowWidth()/2) - (size.width /2) + findScrollX())+'px';
el.style.top = (Math.round(findWindowHeight()/2) - (size.height /2) + findScrollY())+'px';
},
getsize : function (el){
var x = {};
x.width = el.offsetWidth || el.style.pixelWidth;
x.height = el.offsetHeight || el.style.pixelHeight;
return x;
},
submit : function(cb,context){
context.passwordPrompt = document.getElementById('promptDisplayField').value;
context.password = document.getElementById('passwordInputField').value;
var box = document.getElementById('passwordPromptBox');
box.parentNode.removeChild(box);
cb(context);
return false;
},
cancel : function(cb,context){
var box = document.getElementById('passwordPromptBox');
box.parentNode.removeChild(box);
return false;
},
setStyles : function(){
setStylesheet(
"#passwordPromptBox dd.submit {margin-left:0; font-weight: bold; margin-top:1em;}\n"+
"#passwordPromptBox dd.submit .button {padding:0.5em 1em; border:1px solid #ccc;}\n"+
"#passwordPromptBox dt.heading {margin-bottom:0.5em; font-size:1.2em;}\n"+
"#passwordPromptBox {border:1px solid #ccc;background-color: #eee;padding:1em 2em;}",'passwordPromptStyles');
},
template : '<form action="" onsubmit="return false;" id="passwordPromptForm">\n'+
' <dl>\n'+
' <dt class="heading">Please enter the password:</dt>\n'+
' <dt>Prompt:</dt>\n'+
' <dd><input type="text" readonly id="promptDisplayField" class="display"/></dd>\n'+
' <dt>Password:</dt>\n'+
' <dd><input type="password" tabindex="1" class="input" id="passwordInputField"/></dd>\n'+
' <dd class="submit">\n'+
' <a tabindex="2" href="javascript:;" class="button" id="passwordPromptSubmitBtn">OK</a>\n'+
' <a tabindex="3" href="javascript:;" class="button" id="passwordPromptCancelBtn">Cancel</a>\n'+
' </dd>\n'+
' </dl>\n'+
'</form>',
init : function(){
config.shadowTiddlers.PasswordPromptTemplate = this.template;
this.setStyles();
}
};
PasswordPrompt.init();
// http://www.movable-type.co.uk/scripts/tea-block.html
//
// TEAencrypt: Use Corrected Block TEA to encrypt plaintext using password
// (note plaintext & password must be strings not string objects)
//
// Return encrypted text as string
//
function TEAencrypt(plaintext, password)
{
if (plaintext.length == 0) return(''); // nothing to encrypt
// 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but keep
// spaces as spaces (not '%20') so encrypted text doesn't grow too long (quick & dirty)
var asciitext = escape(plaintext).replace(/%20/g,' ');
var v = strToLongs(asciitext); // convert string to array of longs
if (v.length <= 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding a null
var k = strToLongs(password.slice(0,16)); // simply convert first 16 chars of password as key
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = 0;
while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word
sum += delta;
e = sum>>>2 & 3;
for (var p = 0; p < n; p++) {
y = v[(p+1)%n];
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
z = v[p] += mx;
}
}
var ciphertext = longsToStr(v);
return escCtrlCh(ciphertext);
}
//
// TEAdecrypt: Use Corrected Block TEA to decrypt ciphertext using password
//
function TEAdecrypt(ciphertext, password)
{
if (ciphertext.length == 0) return('');
var v = strToLongs(unescCtrlCh(ciphertext));
var k = strToLongs(password.slice(0,16));
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;
while (sum != 0) {
e = sum>>>2 & 3;
for (var p = n-1; p >= 0; p--) {
z = v[p>0 ? p-1 : n-1];
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
y = v[p] -= mx;
}
sum -= delta;
}
var plaintext = longsToStr(v);
// strip trailing null chars resulting from filling 4-char blocks:
plaintext = plaintext.replace(/\0+$/,'');
return unescape(plaintext);
}
// supporting functions
function strToLongs(s) { // convert string to array of longs, each containing 4 chars
// note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
var l = new Array(Math.ceil(s.length/4));
for (var i=0; i<l.length; i++) {
// note little-endian encoding - endianness is irrelevant as long as
// it is the same in longsToStr()
l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) +
(s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
}
return l; // note running off the end of the string generates nulls since
} // bitwise operators treat NaN as 0
function longsToStr(l) { // convert array of longs back to string
var a = new Array(l.length);
for (var i=0; i<l.length; i++) {
a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF,
l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
}
return a.join(''); // use Array.join() rather than repeated string appends for efficiency
}
function escCtrlCh(str) { // escape control chars etc which might cause problems with encrypted texts
return str.replace(/[\0\t\n\v\f\r\xa0'"!]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
}
function unescCtrlCh(str) { // unescape potentially problematic nulls and control characters
return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
}
//}}}
/***
|Name|TiddlerEncryptionPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[TiddlerEncryptionPlugin]]|
!!!!!Code
***/
//{{{
PasswordPrompt.promptOverride_base = PasswordPrompt.prompt;
PasswordPrompt.prompt = function (callback, context) {
jQuery("#passwordPromptBox").remove();
PasswordPrompt.promptOverride_base(callback, context);
jQuery("#passwordPromptBox").css("z-index", "999999");
};
//}}}
//{{{
store.getTiddlerText = function (title, defaultText) {
if (title) {
var tiddlerTitle = title;
var pos = tiddlerTitle.indexOf(config.textPrimitives.sectionSeparator);
if (pos > -1) {
tiddlerTitle = tiddlerTitle.substring(0, pos);
}
store.getTiddler(tiddlerTitle);
pos = tiddlerTitle.indexOf(config.textPrimitives.sliceSeparator);
if (pos > -1) {
tiddlerTitle = tiddlerTitle.substring(0, pos);
store.getTiddler(tiddlerTitle);
}
}
return store.getTiddlerText_TiddlerEncryptionPlugin(title, defaultText);
};
//}}}
//{{{
(function () {
var code = eval("PasswordPrompt.cancel").toString();
code = code.replace("return", "if (context && context.cancelCallbackFunction) context.cancelCallbackFunction(context);\nreturn");
eval("PasswordPrompt.cancel = function cancel" + code.substring(code.indexOf("(")));
})();
//}}}
//{{{
PasswordPrompt.decrypt = function (tiddler, callback) {
callback = callback || function () {};
var passwordPrompt = null;
tiddler = (typeof tiddler === "string") ? store.fetchTiddler(tiddler) : tiddler;
(function retry() {
if (!(tiddler instanceof Tiddler)) {
callback("", retry);
} else {
try {
CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
callback("decrypted", retry);
} catch (e) {
if (e !== "DecryptionFailed") {
throw e;
} else {
if (passwordPrompt == null) {
passwordPrompt = "";
for (var i = 0; tiddler.tags && !passwordPrompt && (i < tiddler.tags.length); i++) {
if (tiddler.tags[i].indexOf("Decrypt(") === 0) {
passwordPrompt = tiddler.tags[i].substring("Decrypt(".length, tiddler.tags[i].lastIndexOf(")"));
}
}
}
MyPrompt_TiddlerEncryptionPlugin(passwordPrompt, "", {
callbackFunction: function (result) {
if (result.password) {
config.encryptionPasswords[result.passwordPrompt] = result.password;
}
try {
CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
callback("decrypted", retry);
} catch (e) {
if (e !== "DecryptionFailed") {
throw e;
} else {
callback("failed", retry);
}
}
},
cancelCallbackFunction: function (result) {
callback("canceled", retry);
}
});
}
}
}
})();
};
//}}}
//{{{
config.macros.tiddlerPreviewPopup = {
showDelayed: function (jsEvent, title, link, timeout, callback) {
return window.setTimeout(function() { config.macros.tiddlerPreviewPopup.show(jsEvent, title, link, callback); }, timeout);
},
show: function (jsEvent, title, link, callback) {
var tiddler = store.getTiddler(title);
var text = tiddler ? tiddler.text : null;
text = link ? ("[[" + link + "|" + title + "]]" + ((text && text.length) ? "\n\n" + text : "")) : text;
if ((text && text.length) || callback) {
var popup = Popup.create(jsEvent.target, null, "sticky popup");
if (popup) {
popup.style.width = "50%";
popup.style.padding = ".5em";
var div = createTiddlyElement(popup, "DIV", null, "popupPreview viewer");
div.style.overflow = "auto";
div.style.whiteSpace = "normal";
div.style[config.browser.isIE ? "height" : "maxHeight"] = "10em";
if (callback) {
createTiddlyButton(div, callback.label, callback.prompt, callback.onClick, "button tpppcallback");
text = "\n\n" + ((text && text.length) ? text : "")
}
wikify(text, div, false, tiddler);
var disableControls = function () {
jQuery(div).find("input, textarea, button, select").attr("disabled", "disabled");
jQuery(div).find("a.button").not(".tpppcallback").attr("onclick", "").unbind("click");
jQuery(div).find("a.tab").click(function (e) {
disableControls();
});
};
disableControls();
jQuery(div).mouseleave(function() {
Popup.remove();
});
Popup.place(Popup.stack[Popup.stack.length - 1].root, Popup.stack[Popup.stack.length - 1].popup, "top", null, {
x: jsEvent.pageX - findPosX(Popup.stack[Popup.stack.length - 1].root),
y: jsEvent.pageY - findPosY(Popup.stack[Popup.stack.length - 1].root)});
} else {
Popup.remove();
}
} else {
Popup.remove();
}
jsEvent.stopImmediatePropagation();
jsEvent.preventDefault();
}
}
//}}}
//{{{
config.messages.tiddlerLinkTooltip = "%0 - %1, %2 / %3";
//}}}
//{{{
Tiddler.prototype.getSubtitle = function () {
var formatDateString = function(date) {
return date.getFullYear() + "-"
+ (date.getMonth() < 9 ? "0" : "") + (date.getMonth() + 1) + "-"
+ (date.getDate() < 10 ? "0" : "") + date.getDate() + " "
+ (date.getHours() < 10 ? "0" : "") + date.getHours() + ":"
+ (date.getMinutes() < 10 ? "0" : "") + date.getMinutes();
}
var modifier = this.modifier;
if (!modifier) {
modifier = config.messages.subtitleUnknown || "";
}
var created = this.created;
if (created) {
created = formatDateString(created);
} else {
created = config.messages.subtitleUnknown || "";
}
var modified = this.modified;
if (modified) {
modified = formatDateString(modified);
} else {
modified = config.messages.subtitleUnknown || "";
}
var f = config.messages.tiddlerLinkTooltip || "%0 - %1, %2 / %3";
return f.format([this.title, modifier, created, modified]);
}
//}}}
http://tiddlywiki.org/wiki/TiddlyWiki_Markup/Tiddler
!Inline Formatting
|!Option|!Syntax|!Output|
|bold font|{{{''bold''}}}|''bold''|
|italic type|{{{//italic//}}}|//italic//|
|underlined text|{{{__underlined__}}}|__underlined__|
|strikethrough text|{{{--strikethrough--}}}|--strikethrough--|
|superscript text|{{{^^super^^script}}}|^^super^^script|
|subscript text|{{{~~sub~~script}}}|~~sub~~script|
|highlighted text|{{{@@highlighted@@}}}|@@highlighted@@|
|preformatted text|<html><code>{{{preformatted}}}</code></html>|{{{preformatted}}}|
!Block Elements
!!Headings
{{{
!Heading 1
!!Heading 2
!!!Heading 3
!!!!Heading 4
!!!!!Heading 5
}}}
<<<
!Heading 1
!!Heading 2
!!!Heading 3
!!!!Heading 4
!!!!!Heading 5
<<<
!!Lists
{{{
* unordered list, level 1
** unordered list, level 2
*** unordered list, level 3
# ordered list, level 1
## ordered list, level 2
### ordered list, level 3
; definition list, term
: definition list, description
*; unordered list, header
** unordered list, level 1
*** unordered list, level 2
**** unordered list, level 3
# ordered
#* unordered
#*;term
#*:definition
# First item
#* First item child
#* <html> First item child <br/>
That spans several lines
</html>
# Second item
}}}
<<<
* unordered list, level 1
** unordered list, level 2
*** unordered list, level 3
# ordered list, level 1
## ordered list, level 2
### ordered list, level 3
; definition list, term
: definition list, description
*; unordered list, header
** unordered list, level 1
*** unordered list, level 2
**** unordered list, level 3
# ordered /% http://tiddlywiki.tiddlyspace.com/bags/tiddlywiki_public/tiddlers/TiddlyWiki%20Markup/revisions/1082998 %/
#* unordered
#*;term
#*:definition
# First item /% https://groups.google.com/forum/#!topic/tiddlywiki/2RI6GJnrX1g %/
#* First item child
#* <html> First item child <br/>
That spans several lines
</html>
# Second item
<<<
!!Blockquotes
{{{
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3
<<<
blockquote
<<<
}}}
<<<
> blockquote, level 1
>> blockquote, level 2
>>> blockquote, level 3
> blockquote
<<<
!!Preformatted Text
<html><pre>
{{{
preformatted (e.g. code)
}}}
</pre></html>
<<<
{{{
preformatted (e.g. code)
}}}
<<<
!!Tables
{{{
|CssClass|k
|!heading column 1|!heading column 2|
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| ... |
|~| ... |
|CssProperty:value;...| ... |
|caption|c
}}}
''Annotation:''
* The {{{>}}} marker creates a "colspan", causing the current cell to merge with the one to the right.
* The {{{~}}} marker creates a "rowspan", causing the current cell to merge with the one above.
<<<
|CssClass|k
|!heading column 1|!heading column 2|
|row 1, column 1|row 1, column 2|
|row 2, column 1|row 2, column 2|
|>|COLSPAN|
|ROWSPAN| ... |
|~| ... |
|CssProperty:value;...| ... |
|caption|c
<<<
!!Images /% TODO %/
cf. [[Image Formatting]]
!Hyperlinks
* [[WikiWords|WikiWord]] are automatically transformed to hyperlinks to the respective tiddler
** the automatic transformation can be suppressed by preceding the respective WikiWord with a tilde ({{{~}}}): {{{~WikiWord}}}
* [[PrettyLinks]] are enclosed in square brackets and contain the desired tiddler name: {{{[[tiddler name]]}}}
** optionally, a custom title or description can be added, separated by a pipe character ({{{|}}}): {{{[[title|target]]}}}<br>''N.B.:'' In this case, the target can also be any website (i.e. URL).
!Custom Styling
* {{{@@CssProperty:value;CssProperty:value;...@@}}}<br>''N.B.:'' CSS color definitions should use lowercase letters to prevent the inadvertent creation of WikiWords.
* <html><code>{{customCssClass{...}}}</code></html>
* raw HTML can be inserted by enclosing the respective code in HTML tags: {{{<html> ... </html>}}}
!Special Markers
* {{{<br>}}} forces a manual line break
* {{{----}}} creates a horizontal ruler
* [[HTML entities|HtmlEntities]]
* {{{<<macroName>>}}} calls the respective [[macro|Macros]]
* To hide text within a tiddler so that it is not displayed, it can be wrapped in {{{/%}}} and {{{%/}}}.<br/>This can be a useful trick for hiding drafts or annotating complex markup.
* To prevent wiki markup from taking effect for a particular section, that section can be enclosed in three double quotes: e.g. {{{"""WikiWord"""}}}.
//{{{
config.macros.timesheet = {
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
if (wikifier && tiddler) {
params = paramString.parseParams("anon", null, true, false, false);
var label = getParam(params, "label", "calculate");
var prompt = getParam(params, "prompt", "Calculate timesheet");
var accessKey = getParam(params, "accessKey", null);
var timesheetSelector = getParam(params, "selector", "table.timesheet");
var result = getParam(params, "result", null);
var timeColIndex = getParam(params, "index", 7);
timeColIndex = jQuery.isNumeric(timeColIndex) ? timeColIndex | 0 : 7;
var calculateTimesheet = function () {
var processedTimesheets = [];
var timesheets = jQuery(timesheetSelector);
if (timesheets && timesheets.length) {
for (var tIndex = 0; tIndex < timesheets.length; tIndex++) {
var timesheet = jQuery(timesheets[tIndex]);
var totalHours = 0;
var trs = timesheet.find("tr").not(".timesheet-total-row");
var rows = [];
var cells = [];
var multipliers = [];
processedTimesheets[processedTimesheets.length] = {timesheet: timesheet, rows: rows, cells: cells, multipliers: multipliers, total: 0};
for (var i = 0; i < trs.length; i++) {
rows[i] = [];
cells[i] = [];
}
trs.each(function(i, tr) {
var colIndex = 1;
multipliers[i] = ((multipliers[i] === undefined) || (multipliers[i] === null)) ? [1] : multipliers[i];
jQuery(tr).find("td").each(function(j, td) {
td = jQuery(td);
while ((rows[i][colIndex] !== undefined) && (rows[i][colIndex] !== null)) {
colIndex++;
}
if (!td.hasClass("timesheet-calculated")) {
cells[i][colIndex] = td;
} else {
cells[i][0] = td;
}
if (!td.hasClass("timesheet-calculated")) {
var isMultiplier = (colIndex >= timeColIndex) && jQuery.isNumeric(td.text()) && (!jQuery.isNumeric(td.attr("colspan")) || (parseInt(td.attr("colspan")) != 2));
var value = td.text();
if (isMultiplier) {
multipliers[i] = [parseFloat(value.trim()), td];
value = "0";
} else if (colIndex >= timeColIndex) {
value = value.trim();
if (!jQuery.isNumeric(value) && !(/^\d?\d:\d\d$/).test(value)) {
value = "0";
}
}
var rowspan = jQuery.isNumeric(td.attr("rowspan")) ? parseInt(td.attr("rowspan")) : 1;
for (var k = i; k < i + rowspan; k++) {
rows[k][colIndex] = value;
if (isMultiplier) {
multipliers[k] = multipliers[i];
}
}
colIndex++;
}
});
});
for (var i = 0; i < rows.length; i++) {
var hours = 0;
for (var j = timeColIndex; j < rows[i].length; j++) {
if (jQuery.isNumeric(rows[i][j])) {
hours += parseFloat(rows[i][j]) * multipliers[i][0];
} else if ((j + 1 < rows[i].length) && !jQuery.isNumeric(rows[i][j + 1])) {
try {
hours += (
((parseInt(rows[i][j + 1].substring(0, rows[i][j + 1].indexOf(":"))) * 60)
+ parseInt(rows[i][j + 1].substring(rows[i][j + 1].indexOf(":") + 1)))
-
((parseInt(rows[i][j].substring(0, rows[i][j].indexOf(":"))) * 60)
+ parseInt(rows[i][j].substring(rows[i][j].indexOf(":") + 1)))
) / 60 * multipliers[i][0];
} catch (e) {}
j++;
}
}
totalHours += hours;
rows[i][0] = hours;
if (!cells[i][0] && (rows[i][timeColIndex] !== undefined) && (rows[i][timeColIndex] !== null)) {
cells[i][0] = jQuery("<td align='right' class='timesheet-calculated'></td>");
var timeStart = null;
for (var j = timeColIndex; j < cells[i].length; j++) {
if (cells[i][j]) {
timeStart = cells[i][j];
break;
}
}
if (!timeStart) {
jQuery(trs[i]).append(cells[i][0]);
} else {
timeStart.before(cells[i][0]);
}
}
if (cells[i][0]) {
cells[i][0].text(hours.toFixed(2));
}
}
if (trs.length > 0) {
var totalRow = timesheet.find("tr.timesheet-total-row");
if (!totalRow.length) {
totalRow = jQuery("<tr class='timesheet-total-row'><td colspan='" + (timeColIndex - 1) + "'></td><td align='right' class='timesheet-calculated'></td></tr>");
jQuery(trs[trs.length - 1]).after(totalRow);
}
cells[cells.length] = [totalRow.find(".timesheet-calculated")];
cells[cells.length - 1][0].text(totalHours.toFixed(2));
}
processedTimesheets[processedTimesheets.length - 1].total = totalHours;
}
}
if (result) {
var timesheets = processedTimesheets;
eval(result);
}
};
createTiddlyButton(place, label, prompt, calculateTimesheet, null, null, accessKey);
}
}
}
//}}}
/***
|Name|TimestampedDownloadSaverPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.chkEnableTimestampedDownloadSaverPlugin === undefined) {
config.options.chkEnableTimestampedDownloadSaverPlugin = true;
}
merge(config.optionsDesc, {
chkEnableTimestampedDownloadSaverPlugin: "Add timestamps to tiddlers saved by download"
});
//}}}
//{{{
config.macros.timestampedDownloadSaver = {
HTML5DownloadSaveFile: window.HTML5DownloadSaveFile,
manualSaveFile: window.manualSaveFile,
saveWithTimestamp: function (filePath, content, saveFile) {
if (config.options.chkEnableTimestampedDownloadSaverPlugin) {
var slashpos = filePath.lastIndexOf("/");
if (slashpos == -1) {
slashpos = filePath.lastIndexOf("\\");
}
var filename = filePath.substr(slashpos + 1);
var extension = filename.substr(filename.lastIndexOf("."));
filename = filename.substr(0, filename.lastIndexOf("."));
filename = (/.*\.\d{8}\.\d{10}$/).test(filename) ? filename.substring(0, filename.length - 20) : filename;
filePath = filePath.substr(0, slashpos + 1)
+ filename
+ "." + (new Date()).convertToYYYYMMDDHHMMSSMMM()
+ extension;
}
return saveFile(filePath, content);
}
};
window.HTML5DownloadSaveFile = function (filePath, content) {
return config.macros.timestampedDownloadSaver.saveWithTimestamp(filePath, content, config.macros.timestampedDownloadSaver.HTML5DownloadSaveFile);
}
window.manualSaveFile = function (filePath, content) {
return config.macros.timestampedDownloadSaver.saveWithTimestamp(filePath, content, config.macros.timestampedDownloadSaver.manualSaveFile);
}
//}}}
/***
|Name|TransferTiddlerPlugin|
|License|[[TW Notes License]]|
|Requires|[[CollatorPlugin]] [[GUIDPlugin]] [[ServerUploadPlugin]] [[UpdateTaggedTiddlersOnSaveChangesPlugin]]|
!!!!!Documentation
Configure either the [[Java Server|ServerUploadPlugin##Java Server]], the [[Node.js Server|ServerUploadPlugin##Node.js Server]], or the [[PHP Server|ServerUploadPlugin##PHP Server]] from [[ServerUploadPlugin]].
Sample options
{{{
txtTransferTiddlerIndexURL: https://mydomain.tld/somefolder/index.php / http://localhost:6943/tiddlers/index
txtTransferTiddlerBaseURL: https://mydomain.tld/somefolder / http://localhost:6943/tiddlers
}}}
Sample [[TransferTiddlerConfig]]
{{{
base: https://mydomain.tld/somefolder
url: https://mydomain.tld/upload/update.php
password: L0ck it up saf3
path: ../somefolder
base2: http://localhost:6943/tiddlers
url2: http://localhost:6943/upload
password2: L0ck it up saf3
path2: tiddlers
}}}
!!!!!Code
***/
//{{{
config.shadowTiddlers.ToolbarCommands = config.shadowTiddlers.ToolbarCommands.replace(/>/, "> transferTiddler");
//}}}
//{{{
config.commands.transferTiddler = {
text: "send",
tooltip: "Upload this tiddler",
handler: function (event, src, title) {
var cmt = config.macros.transfer;
var cms = config.macros.serverupload;
cmt.loadConfig(cmt.defaultConfig, function (url, password, path) {
var tiddler = store.getTiddler(title);
if (tiddler) {
if (config.options.chkAutoAddGUID) {
config.macros.guid.addMissing(true);
}
var guid = tiddler.fields["guid"] ? tiddler.fields["guid"] : config.macros.guid.generate();
var tiddlerInfo = {
name: path + guid + "_i",
data: JSON.stringify({
title: title,
modified: tiddler.modified.convertToYYYYMMDDHHMM()
})
};
var dirty = store.isDirty();
var tiddlerDiv = tiddler.saveToDiv();
var tiddlerDivLength = tiddlerDiv.length;
store.setDirty(dirty);
var sendingMessage = "Sending tiddler: " + title;
var chunkSize = 20000;
var sent = 0;
var append = false;
var sendResult = true;
(function send() {
if (sendResult && tiddlerDiv.length) {
cms.save([append ? null : tiddlerInfo, {name: path + guid + "_t", data: (tiddlerDiv.length > chunkSize) ? tiddlerDiv.substring(0, chunkSize) : tiddlerDiv, append: append}], url, password, function (result) {
if (typeof result === "boolean") {
sendResult = result;
sent += chunkSize;
tiddlerDiv = (tiddlerDiv.length > chunkSize) ? tiddlerDiv.substring(chunkSize) : "";
if (append) {
var percent = Math.round(tiddlerDiv.length ? (sent * 100 / tiddlerDivLength) : 100);
var sendingMessagePlace = jQuery("#messageArea").children(".messageArea__text").filter(function (index) {
return jQuery(this).text().indexOf(sendingMessage) === 0;
});
if (sendingMessagePlace.length) {
sendingMessagePlace.text(sendingMessage + " " + percent + "%");
} else {
displayMessage(sendingMessage + " " + percent + "%");
}
}
append = true;
send();
}
});
} else {
displayMessage(sendResult ? "Tiddler sent: " + title : "Failed to send tiddler: " + title);
}
})();
}
});
return false;
}
}
//}}}
//{{{
if (config.options.txtTransferTiddlerIndexURL === undefined) {
config.options.txtTransferTiddlerIndexURL = "https://mydomain.tld/somefolder/index.php";
}
if (config.options.txtTransferTiddlerBaseURL === undefined) {
config.options.txtTransferTiddlerBaseURL = "https://mydomain.tld/somefolder";
}
merge(config.optionsDesc, {
txtTransferTiddlerIndexURL: "URLs of the TransferTiddlerPlugin tiddler index PHP script or Java server, separated by spaces and forward slashes ( / )",
txtTransferTiddlerBaseURL: "Base URLs of the tiddlers uploaded by TransferTiddlerPlugin, separated by spaces and forward slashes ( / )"
});
//}}}
//{{{
config.annotations.TransferTiddlerConfig = "This tiddler is used to store configuration options for the TransferTiddlerPlugin";
config.shadowTiddlers.TransferTiddlerConfig =
"\n" +
config.optionsDesc.txtTransferTiddlerIndexURL + ": <<option txtTransferTiddlerIndexURL>>\n" +
"\n" +
config.optionsDesc.txtTransferTiddlerBaseURL + ": <<option txtTransferTiddlerBaseURL>>\n" +
"\n" +
"base: //the transfer tiddler base URL for which this configuration applies//\n" +
"url: //URL of the [[ServerUploadPlugin]] PHP script or Java server//\n" +
"password: //password of the [[ServerUploadPlugin]] PHP script or Java server//\n" +
"path: //path of the server directory in which the [[ServerUploadPlugin]] PHP script or Java server will upload content//\n" +
"\n" +
"base2: //the second transfer tiddler base URL for which this configuration applies//\n" +
"url2: //URL of the second [[ServerUploadPlugin]] PHP script or Java server//\n" +
"password2: //password of the second [[ServerUploadPlugin]] PHP script or Java server//\n" +
"path2: //path of the server directory in which the second [[ServerUploadPlugin]] PHP script or Java server will upload content//\n" +
"\n" +
"..."
//}}}
//{{{
config.annotations.TransferTiddler = "This tiddler is used to provide access to downloading and deleting uploaded tiddlers";
config.shadowTiddlers.TransferTiddler =
config.optionsDesc.txtTransferTiddlerIndexURL + ": <<option txtTransferTiddlerIndexURL>>\n" +
"\n" +
config.optionsDesc.txtTransferTiddlerIndexURL + ": <<option txtTransferTiddlerBaseURL>>\n" +
"\n" +
"[[TransferTiddlerPlugin]] tiddler upload and management configuration options: [[TransferTiddlerConfig]]\n\n\n" +
"<<transfer>>\n\n" +
"<<transfer config>>\n" +
"<<transfer manage>>";
if (config.tasks && !config.tasks.transfer) {
config.tasks.transfer = {
text: "transfer",
tooltip: "Download tiddlers and manage uploaded tiddlers",
content: store.getTiddlerText("TransferTiddler")
}
config.backstageTasks.push("transfer");
}
//}}}
//{{{
config.macros.transfer = {
configs: {},
defaultConfig: "",
configTiddlerText: "",
loadConfig: function (key, callback) {
var cmt = config.macros.transfer;
if (store.getTiddlerText("TransferTiddlerConfig") !== cmt.configTiddlerText) {
cmt.configs = {};
cmt.defaultConfig = "";
cmt.configTiddlerText = "";
var settings = store.calcAllSlices("TransferTiddlerConfig");
var keys = ["base", "url", "password", "path"];
var configMap = {};
for (var k in settings) {
for (var i = 0; i < keys.length; i++) {
if (k.indexOf(keys[i]) === 0) {
var configKey = k.substring(keys[i].length);
var c = configMap[configKey];
if (!c) {
c = {};
configMap[configKey] = c;
}
c[keys[i]] = settings[k].trim();
break;
}
}
}
if (!cmt.defaultConfig && config.options.txtTransferTiddlerBaseURL) {
cmt.defaultConfig = config.options.txtTransferTiddlerBaseURL.split(/ \/ /g)[0].trim();
}
if (!cmt.defaultConfig && configMap[""] && configMap[""].base) {
cmt.defaultConfig = configMap[""].base;
}
var baseConfigs = {};
for (var k in configMap) {
if (configMap[k].base) {
baseConfigs[configMap[k].base] = configMap[k];
if (!cmt.defaultConfig) {
cmt.defaultConfig = configMap[k].base;
}
}
}
cmt.configs = baseConfigs;
cmt.configTiddlerText = store.getTiddlerText("TransferTiddlerConfig");
}
key = key || cmt.defaultConfig;
var transferConfig = cmt.configs[key] || {};
var url = transferConfig.url;
var password = transferConfig.password;
var path = transferConfig.path;
if (url && (/^https?:\/\//i).test(url) && path) {
if ((path.lastIndexOf("/") != path.length - 1) && (path.lastIndexOf("\\") != path.length - 1)) {
path += (path.indexOf("/") > -1) ? "/" : "\\";
}
callback(url, password, path);
} else {
displayMessage("Unable to load server configurations");
}
},
loadIndex: function (callback) {
var cms = config.macros.serverupload;
var indexURLs = config.options.txtTransferTiddlerIndexURL.split(/ \/ /g);
var baseURLs = config.options.txtTransferTiddlerBaseURL.split(/ \/ /g);
var indexURLIndex = 0;
var files = [];
var fileIndex = 0;
var tiddlerInfos = [];
loadFiles(function () {
tiddlerInfos.sort(function (a, b) {
return config.macros.collator.compare(a.title, b.title);
});
callback(tiddlerInfos, files.length);
});
function loadFiles(finish) {
var next = loadFiles;
if (indexURLIndex >= indexURLs.length) {
finish();
} else {
var baseURL = (baseURLs[(indexURLIndex < baseURLs.length) ? indexURLIndex : baseURLs.length - 1] || "").trim();
var indexURL = (indexURLs[indexURLIndex++] || "").trim();
var processedURL = cms.processURL(indexURL);
var settings = {
method: "GET",
dataType: "text",
url: processedURL.url + "?" + (new Date()).getTime()
};
if (processedURL.username) {
settings.headers = {
"Authorization": "Basic " + btoa(processedURL.username + ":" + processedURL.password)
}
}
jQuery.ajax(settings).done(function (data, textStatus, jqXHR) {
if (data) {
var fileList = data.split("\n");
for (var i = 0; i < fileList.length; i++) {
if (fileList[i].lastIndexOf("_i") === fileList[i].length - 2) {
var tiddlerBaseURL = baseURL;
var separator = (fileList[i].indexOf("/") > -1) ? "/" : ((fileList[i].indexOf("\\") > -1) ? "\\" : null);
if (separator) {
tiddlerBaseURL = tiddlerBaseURL.replace(/[/\\]/g, separator);
tiddlerBaseURL = tiddlerBaseURL + ((tiddlerBaseURL.lastIndexOf(separator) !== tiddlerBaseURL.length - 1) ? separator : "") + fileList[i].substring(0, fileList[i].lastIndexOf(separator) + 1);
fileList[i] = fileList[i].substring(fileList[i].lastIndexOf(separator) + 1)
}
files.push([baseURL, tiddlerBaseURL, fileList[i]]);
}
}
}
processFiles(function () {
next(finish);
});
}).fail(function (jqXHR, textStatus, errorThrown) {
displayMessage("Error loading index" + (indexURLs.length ? ": " + processedURL.url : ""));
next(finish);
});
}
}
function processFiles(finish) {
var next = processFiles;
if (fileIndex >= files.length) {
finish();
} else {
var file = files[fileIndex++];
var guid = file[2].substring(0, file[2].length - 2);
var processedURL = cms.processURL(file[1]);
var settings = {
method: "GET",
dataType: "text",
url: processedURL.url + ((processedURL.url.lastIndexOf("/") !== processedURL.url.length - 1) ? "/" : "") + file[2] + "?" + (new Date()).getTime()
};
if (processedURL.username) {
settings.headers = {
"Authorization": "Basic " + btoa(processedURL.username + ":" + processedURL.password)
}
}
jQuery.ajax(settings).done(function (data, textStatus, jqXHR) {
if (data) {
try {
var tiddlerInfo = JSON.parse(data);
tiddlerInfo.modified = Date.convertFromYYYYMMDDHHMMSS(tiddlerInfo.modified);
tiddlerInfo.guid = guid;
tiddlerInfo.file = file;
tiddlerInfos.push(tiddlerInfo);
next(finish);
} catch (e) {
displayMessage("Error processing " + guid);
next(finish);
}
} else {
displayMessage("No data found for " + guid);
next(finish);
}
}).fail(function (jqXHR, textStatus, errorThrown) {
displayMessage("Error loading information for " + guid);
next(finish);
});
}
}
},
handler: function (place, macroName, params, wikifier, paramString, tiddler) {
var cmt = config.macros.transfer;
var cms = config.macros.serverupload;
if (params[0] === "config") {
var panel = createTiddlyElement(null, "div", null, "sliderPanel");
panel.style.display = "block";
createTiddlyButton(place, "Configure Server", "Configure the server to which to send tiddlers", function (e) {
if (panel.children("select").length) {
panel.empty();
} else {
panel.empty();
cmt.loadConfig(null, function (url, password, path) {
var select = jQuery("<select>");
panel.append(select);
for (var k in cmt.configs) {
select.append(jQuery("<option>").attr("value", k).text(cms.processURL(k).url));
}
if (cmt.defaultConfig) {
select.val(cmt.defaultConfig);
}
select.on("change", function () {
cmt.defaultConfig = jQuery(this).val();
});
});
}
});
place.appendChild(panel);
panel = jQuery(panel);
} else if (params[0] === "manage") {
var panel = createTiddlyElement(null, "div", null, "sliderPanel");
panel.style.display = "block";
createTiddlyButton(place, "Manage Tiddlers", "Delete selected tiddlers from server", function (e) {
panel.empty();
cmt.loadIndex(function (files, total) {
if (files.length) {
var list = [];
panel.append("<br>");
for (var i = 0; i < files.length; i++) {
list.push({
guid: files[i].guid,
base: files[i].file[0],
checkbox: jQuery("<label></label>", {text: " " + files[i].title, title: cms.processURL(files[i].file[0]).url})
.appendTo(panel)
.prepend("<input type='checkbox' />").find("input").attr("title", cms.processURL(files[i].file[0]).url)
});
panel.append("<br>");
}
panel.append("<br>");
createTiddlyButton(panel[0], "delete", "Delete the selected tiddlers", function (e) {
var selected = [];
for (var i = 0; i < list.length; i++) {
if (list[i].checkbox.is(":checked")) {
selected.push({name: list[i].guid + "_i", base: list[i].base});
selected.push({name: list[i].guid + "_t", base: list[i].base});
}
}
if (selected.length > 0) {
if (confirm("Delete the selected tiddlers from server?")) {
var selectedByURLMap = {};
for (var i = 0; i < selected.length; i++) {
cmt.loadConfig(selected[i].base, function (url, password, path) {
selected[i].url = url;
selected[i].password = password;
selected[i].name = path + selected[i].name;
var selectedByURL = selectedByURLMap[url];
if (!selectedByURL) {
selectedByURL = [];
selectedByURLMap[url] = selectedByURL;
}
selectedByURL.push(selected[i]);
});
}
var selectedByURLList = [];
for (var k in selectedByURLMap) {
selectedByURLList.push(selectedByURLMap[k]);
}
var selectedByURLIndex = 0;
var removalResult = true;
function remove(finish) {
var next = remove;
if (selectedByURLIndex >= selectedByURLList.length) {
finish();
} else {
var selectedByURL = selectedByURLList[selectedByURLIndex++];
if (selectedByURL.length) {
cms.remove(selectedByURL, selectedByURL[0].url, selectedByURL[0].password, function (result) {
if (typeof result === "boolean") {
removalResult &= result;
}
next(finish);
});
} else {
next(finish);
}
}
}
if (selectedByURLList.length) {
remove(function () {
displayMessage(removalResult ? "Tiddlers deleted" : "Failed to delete tiddlers");
if (removalResult) {
panel.empty();
}
});
}
}
} else {
alert("No tiddlers selected");
panel.empty();
}
});
}
});
});
place.appendChild(panel);
panel = jQuery(panel);
} else {
createTiddlyButton(place, "Download Tiddlers", "Download tiddlers from server", function (e) {
cmt.loadIndex(function (files, total) {
var fileIndex = 0;
var loaded = 0;
var tiddlerGUIDMap = {};
store.forEachTiddler(function (title, tiddler) {
if (tiddler.fields["guid"]) {
tiddlerGUIDMap[tiddler.fields["guid"]] = tiddler;
}
});
processFiles(function () {
if (loaded > 0) {
store.notifyAll();
}
displayMessage("Loaded " + loaded + " of " + total + " tiddlers");
});
function processFiles(finish) {
var next = processFiles;
if (fileIndex < files.length) {
var tiddlerInfo = files[fileIndex++];
var guid = tiddlerInfo.guid;
var tiddler = tiddlerGUIDMap[guid] ? tiddlerGUIDMap[guid] : store.getTiddler(tiddlerInfo.title);
if (tiddler && tiddler.fields && tiddler.fields.guid && (tiddler.fields.guid != guid) && (tiddler.title === tiddlerInfo.title)) {
tiddler = null;
}
if (!tiddler || (tiddler.modified.getTime() < tiddlerInfo.modified.getTime())) {
if (confirm((tiddler ? "Update" : "Add") + " tiddler '" + tiddlerInfo.title + "'\n\nOK to proceed?")) {
loadTiddler(guid, tiddlerInfo, tiddler, function () {
next(finish);
});
} else {
next(finish);
}
} else {
next(finish);
}
} else {
finish();
}
}
function loadTiddler(guid, tiddlerInfo, tiddler, finish) {
var processedURL = cms.processURL(tiddlerInfo.file[1]);
var settings = {
method: "GET",
dataType: "text",
url: processedURL.url + ((processedURL.url.lastIndexOf("/") != processedURL.url.length - 1) ? "/" : "") + guid + "_t?" + (new Date()).getTime()
};
if (processedURL.username) {
settings.headers = {
"Authorization": "Basic " + btoa(processedURL.username + ":" + processedURL.password)
}
}
jQuery.ajax(settings).done(function (data, textStatus, jqXHR) {
if (data) {
var loadedTiddler = jQuery(data);
var tiddlerTitle = null;
if (tiddler) {
tiddlerTitle = tiddler.title;
var changecount = 0;
if (jQuery.isNumeric(tiddler.fields["changecount"])) {
changecount = parseInt(tiddler.fields["changecount"], 10);
}
store.deleteTiddler(tiddlerTitle);
var loadedTiddlerChangecount = loadedTiddler.attr("changecount");
if (jQuery.isNumeric(loadedTiddlerChangecount)) {
loadedTiddlerChangecount = parseInt(loadedTiddlerChangecount, 10);
} else {
loadedTiddlerChangecount = 0;
}
loadedTiddler.attr("changecount", ((changecount > loadedTiddlerChangecount) ? changecount : loadedTiddlerChangecount) + 1);
}
var loadedTiddlerTitleIndex = 0;
var loadedTiddlerTitle = tiddlerInfo.title;
if ((/ \(\d+\)$/).test(loadedTiddlerTitle)) {
loadedTiddlerTitleIndex = parseInt(loadedTiddlerTitle.substring(loadedTiddlerTitle.lastIndexOf("(") + 1, loadedTiddlerTitle.length - 1));
loadedTiddlerTitle = loadedTiddlerTitle.substring(0, loadedTiddlerTitle.lastIndexOf("(") - 1);
}
while (store.getTiddler(loadedTiddlerTitle)) {
loadedTiddlerTitleIndex++;
loadedTiddlerTitle = tiddlerInfo.title + " (" + loadedTiddlerTitleIndex + ")";
}
loadedTiddler.attr("title", loadedTiddlerTitle);
store.getLoader().loadTiddlers(store, loadedTiddler);
if ((tiddlerTitle !== null) && config.commands.saveTiddler.updateTaggedTiddlers) {
config.commands.saveTiddler.updateTaggedTiddlers.update(tiddlerTitle, loadedTiddlerTitle);
}
story.refreshTiddler(loadedTiddlerTitle, null, true);
loaded++;
finish();
} else {
displayMessage("No data found for tiddler '" + tiddlerInfo.title + "' (" + guid + ")");
finish();
}
}).fail(function (jqXHR, textStatus, errorThrown) {
displayMessage("Error loading tiddler '" + tiddlerInfo.title + "' (" + guid + ")");
finish();
});
}
});
});
}
}
}
//}}}
/***
|Name:|TrashPlugin|
|Version:|1.2.0|
|Source:|http://www.TiddlyTools.com/#TrashPlugin|
|Author:|Eric Shulman|
|OriginalSource:|http://ido-xp.tiddlyspot.com/#TrashPlugin|
|OriginalAuthor:|Ido Magal (idoXatXidomagalXdotXcom)|
|License:|[[BSD open source license]]|
|CoreVersion:|2.1.0|
|Description|add 'Trash' tag to tiddlers instead of deleting them|
!!!!!Documentation
<<<
When TrashPlugin is installed and you click on the 'delete' command in the tiddler toolbar, rather than directly removing the tiddler from the system, it will be tagged with the following tags:
{{{
Trash excludeLists excludeMissing excludeSearch systemConfigDisable
}}}
As a result, although the tiddler still exists within the document, it is ''hidden from view and will not be searched or invoked as a plugin.''
*{{block{
To view a list of all tiddlers tagged with {{{Trash}}}, simply open the [[Trash]] tiddler (aka, the "trash can").}}}
*{{block{
To reclaim a tiddler from the [[Trash]], click on a title in the trash can to open that tiddler. Then, edit it to remove the Trash tag (as well as the other tags noted above).}}}
*{{block{
To empty the trash can (i.e. actually //delete// the tiddlers), click on the ''//"empty trash"//'' button that appears in the [[Trash]] tiddler. You can also add this button to your [[SideBarOptions]] or any other desired location by using the following macro:
{{{
<<emptyTrash>>
}}}
}}}
*{{block{
To ''bypass the trash can'' and use the normal delete handling (with the usual confirmation messages, if chkConfirmDelete is enabled), hold CTRL while clicking 'delete'}}}
*{{block{
To ''bypass both the trash can //and// the confirmation message'' and //immediately delete// the tiddler without any further interaction, hold CTRL+SHIFT while clicking 'delete'}}}
<<<
!!!!!Revisions
<<<
2009.05.20 [1.2.0] documentation rewrite and code cleanup/reduction
2009.05.12 [1.1.0.5] refactored code to add entry point: {{{config.commands.deleteTiddler.sendToTrash(title)}}}
2008.11.14 [1.1.0.4] added SHIFT-CLICK = bypass trash and delete immediately WITHOUT CONFIRMATION
2008.10.14 [1.1.0.3] return FALSE from emptyTrash() handler (fixes IE page transition error)
2008.05.18 [1.1.0.2] when creating the Trash tiddler, pass an empty tags array [] instead of a null value, so other plugins (e.g., InstantTimestampPlugin) won't fail
2006.12.21 [1.1.0.1] only call setDirty() when actually removing tiddlers from trash
2006.12.12 [1.1.0.0] added movedMsg (feedback when tiddler is tagged as Trash). Make sure tiddler actually exists before tagging it with 'Trash'. Fetch correct tiddler before checking for 'systemConfig' tag
2006.12.11 [1.0.3.1] Don't create Trash tiddler until needed. Remove Trash tiddler when no trash remains. Don't tag Trash tiddler with 'TrashPlugin'. Moved all user-visible strings to variables so they can be translated by 'lingo' plugins. Use displayMessage() instead of alert()
2006.12.11 [1.0.3] Fixed broken reference to core deleteTiddler. Now storing reference to core deleteTiddler in emptyTrash macro. Reduced deleteTiddler hijacking to only the handler.
2006.12.11 [1.0.2] EmptyTrash now uses removeTiddler instead of deleteTiddler. Supports trashing systemConfig tiddlers (adds systemConfigDisable tag).
2006.12.10 [1.0.1] Replaced TW version with proper Core reference. Now properly hijacking deleteTiddler command.
2006.12.10 [1.0.0] First draft.
<<<
!!!!!Code
***/
//{{{
version.extensions.TrashPlugin= {major: 1, minor: 2, revision: 0, date: new Date(2009,5,20)};
//}}}
//{{{
config.macros.emptyTrash = {
tag: 'Trash',
movedMsg: "'%0' has been tagged as %1",
label: 'empty trash',
tooltip: 'Delete all items tagged as %0',
tooltipOlder: 'Delete items tagged as %0 that are older than %1 days old',
emptyMsg: 'The trash is empty',
noneToDeleteMsg: 'There are no items in the trash older than %0 days',
confirmMsg: "The following tiddlers will be deleted:\n\n'%0'\n\nOK to proceed?",
deletedMsg: "Deleted '%0'",
handler: function ( place,macroName,params,wikifier,paramString,tiddler ) {
var namedParams = (paramString.parseParams(daysOld))[0];
var daysOld = namedParams['daysOld'] ? namedParams['daysOld'][0] : 0; // default
var buttonTitle = namedParams['title'] ? namedParams['title'][0] : this.label;
var buttonTip=this.tooltip.format([this.tag])
if (daysOld) buttonTip=this.tooltipOlder.format([this.tag,daysOld])
var b=createTiddlyButton(place,buttonTitle,buttonTip,this.emptyTrash);
b.setAttribute('daysOld',daysOld);
},
emptyTrash: function() {
var cme=config.macros.emptyTrash; // abbrev
var daysOld=this.getAttribute('daysOld');
var compareDate=new Date(); compareDate.setDate(compareDate.getDate()-daysOld);
var collected=[];
store.forEachTiddler(function(title,tiddler) {
if (tiddler.isTagged(cme.tag) && tiddler.modified<compareDate)
collected.push(title);
});
if (!collected.length)
displayMessage(daysOld ? cme.noneToDeleteMsg.format([daysOld]) : cme.emptyMsg);
else if (confirm(cme.confirmMsg.format([collected.join("', '")]))) {
for (var i=0;i<collected.length;i++) {
store.removeTiddler(collected[i]);
store.setDirty(true);
displayMessage(cme.deletedMsg.format([collected[i]]));
}
}
if (!store.getTaggedTiddlers(cme.tag).length) // remove Trash if empty
{ story.closeTiddler(cme.tag,true,false); store.removeTiddler(cme.tag); }
else
story.refreshTiddler(cme.tag,false,true); // refresh Trash if visible
return false;
}
}
//}}}
// // hijack delete command
//{{{
config.commands.deleteTiddler.orig_handler=config.commands.deleteTiddler.handler;
config.commands.deleteTiddler.handler=function(event,src,title) {
// BYPASS TRASH: CTRL=normal delete, CTRL+SHIFT=without confirmation
if (event.ctrlKey) {
if (event.shiftKey) { var temp=config.options.chkConfirmDelete; config.options.chkConfirmDelete=false; }
config.commands.deleteTiddler.orig_handler.apply(this,arguments);
if (event.shiftKey) config.options.chkConfirmDelete=temp;
story.refreshTiddler(config.macros.emptyTrash.tag,false,true);
return false;
}
config.commands.deleteTiddler.sendToTrash(title);
return false;
};
config.commands.deleteTiddler.sendToTrash = function(title) {
var cme=config.macros.emptyTrash; // abbrev
if (!store.tiddlerExists(title)) return; // make sure tiddler actually exists
if (!store.tiddlerExists(cme.tag)) // make sure Trash tiddler exists
store.saveTiddler(cme.tag,cme.tag,'<<emptyTrash>>','TrashPlugin',new Date(),[],{});
store.setTiddlerTag(title,1,cme.tag);
store.setTiddlerTag(title,1,'excludeLists');
store.setTiddlerTag(title,1,'excludeMissing');
store.setTiddlerTag(title,1,'excludeSearch');
if (store.getTiddler(title).isTagged('systemConfig'))
store.setTiddlerTag(title,1,'systemConfigDisable');
story.closeTiddler(title,true);
if(config.options.chkAutoSave) saveChanges();
displayMessage(cme.movedMsg.format([title,cme.tag]));
story.refreshTiddler(cme.tag,false,true);
};
//}}}
//{{{
var code = eval("config.macros.emptyTrash.emptyTrash").toString();
var startPosition = code.indexOf("if");
if (startPosition > -1) {
var endPosition = code.indexOf('compareDate', startPosition);
if (endPosition > -1) {
endPosition = code.indexOf(')', endPosition);
if (endPosition > -1) {
newCode = code.substring(0, startPosition) + "if (tiddler.isTagged(cme.tag) && ((daysOld == 0) || tiddler.modified<compareDate))" + code.substring(endPosition + 1);
code = newCode;
}
}
}
if (newCode != null) {
eval("config.macros.emptyTrash.emptyTrash = function emptyTrash" + newCode.substring(newCode.indexOf("(")));
}
//}}}
/***
|Name|TwabBirthdayReminderPlugin|
|License|[[TW Notes License]]|
|Requires|[[ReminderMacrosOverride]]|
!!!!!Code
***/
//{{{
(function () {
var processTwabBirthdayReminders = function processTwabBirthdayReminders(baseDate, leadtime, tags, limit) {
var arr = [];
var tags = config.macros.twab.importTags.split(" ");
var tag = tags[0];
var contacts = store.getTaggedTiddlers(tag);
for (var i = 0; i < contacts.length; i++) {
var contact = store.fetchTiddler(contacts[i].title);
if (contact && contact.text && (contact.text.indexOf('"birthday":"') > -1)) {
var dateHash = {};
var birthday = config.macros.twab.getData(contact.title, "birthday").split("-");
if (birthday.length == 2) {
if (!isNaN(birthday[0]) && !isNaN(birthday[1])) {
dateHash["month"] = parseInt(birthday[0], 10);
dateHash["day"] = parseInt(birthday[1], 10);
}
}
if (birthday.length == 3) {
if (!isNaN(birthday[1]) && !isNaN(birthday[2])) {
dateHash["month"] = parseInt(birthday[1], 10);
dateHash["day"] = parseInt(birthday[2], 10);
if (!isNaN(birthday[0])) {
dateHash["startyear"] = parseInt(birthday[0], 10);
dateHash["startmonth"] = dateHash["month"];
dateHash["startday"] = dateHash["day"];
}
}
}
if (!isNaN(dateHash["month"])) {
dateHash["leadtime"] = [0, 1];
var birthdaylabel = "Birthday : ";
var birthdaybg = "#ffff00";
var birthdaytxt = "#000000";
var birthdaypriority = "10";
if (config.macros.date) {
birthdaylabel = config.macros.date.birthdaylabel !== undefined ? config.macros.date.birthdaylabel : birthdaylabel;
birthdaybg = config.macros.date.birthdaybg !== undefined ? config.macros.date.birthdaybg : birthdaybg;
birthdaytxt = config.macros.date.birthdaytxt !== undefined ? config.macros.date.birthdaytxt : birthdaytxt;
birthdaypriority = config.macros.date.birthdaypriority !== undefined ? config.macros.date.birthdaypriority : birthdaypriority;
}
dateHash["title"] = "@@background-color:" + birthdaybg + ";color:" + birthdaytxt + ";priority:" + birthdaypriority + ";" + birthdaylabel + contact.title + "@@";
if ((limit != null) || (dateHash["leadtime"] == null)) {
if (leadtime == null) {
dateHash["leadtime"] = leadtime;
} else {
dateHash["leadtime"] = [];
dateHash["leadtime"][0] = leadtime[0];
dateHash["leadtime"][1] = leadtime[1];
}
}
if (dateHash["leadtime"] == null) {
dateHash["leadtime"] = config.macros.reminders["defaultLeadTime"];
}
var leadTimeLowerBound = baseDate.addDays(dateHash["leadtime"][0]);
var leadTimeUpperBound = baseDate.addDays(dateHash["leadtime"][1]);
displaystuff= true;
var matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);
while (matchedDate != null) {
var hash = {};
hash["diff"] = matchedDate.getDifferenceInDays(baseDate);
hash["matchedDate"] = new Date(matchedDate.getFullYear(), matchedDate.getMonth(), matchedDate.getDate(), 0, 0);
hash["params"] = cloneParams(dateHash);
hash["tiddler"] = contact.title;
hash["tags"] = ["readOnlyReminder"].concat(contact.tags);
arr.pushUnique(hash);
if (dateHash["recurdays"] != null || (dateHash["year"] == null)) {
leadTimeLowerBound = leadTimeLowerBound.addDays(matchedDate.getDifferenceInDays(leadTimeLowerBound) + 1);
matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);
} else {
matchedDate = null;
}
}
}
}
}
return arr;
};
config.macros.reminder.reminderFunctions[config.macros.reminder.reminderFunctions.length] = processTwabBirthdayReminders;
})();
//}}}
first.name=first.name
last.name=last.name
email=email
other.email=other.email
business.email=business.email
webpage=webpage
phone=phone
mobile=mobile
address=address
city=city
state=state
postal=postal
country=country
company=company
job.title=job.title
business.phone=business.phone
business.mobile=business.mobile
business.fax=business.fax
business.address=business.address
business.city=business.city
business.state=business.state
business.postal=business.postal
business.country=business.country
business.webpage=business.webpage
ssn=ssn
birthday=birthday
anniversary=anniversary
children=children
notes=notes
first.name=Name
last.name=<skip>
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Business Street 2,Business Street 3,Home Street 2,Home Street 3,Other Street,Other Street 2,Other Street 3,Other City,Other State,Other Postal Code,Other Country,Assistant's Phone,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Categories,E-mail Display Name,E-mail 2 Display Name,E-mail 3 Display Name,Gender,Government ID Number,Hobby,Initials,Keywords,Language,Location,Mileage,Office Location,Organizational ID Number,PO Box,Private,Profession,Referred By,Spouse,User 1,User 2,User 3,User 4
first.name,last.name,job.title,webpage,phone,email,company
Princess,Leia,"Leader, Rebels",http://starwars.wikia.com/wiki/Leia_Organa,,leia@alderaan.com
Darth,Vader,"Sith Lord",http://starwars.wikia.com/wiki/Darth_Vader,,darth@deathstar.com
Luke,Skywalker,"Jedi Master",http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com
Luke,Skywalker,"Moisture Farmer",http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com
,,,http://starwars.wikia.com/wiki/501st,,501st@deathstar.com,501st Legion
first.name=First Name
last.name=Last Name
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Business Address PO Box,Categories,Company Yomi,Directory Server,E-mail Type,E-mail Display Name,E-mail 2 Type,E-mail 2 Display Name,E-mail 3 Type,E-mail 3 Display Name,Gender,Given Yomi,Government ID Number,Hobby,Home Address PO Box,Initials,Internet Free Busy,Keywords,Language,Location,Manager's Name,Mileage,Office Location,Organizational ID Number,Other Address PO Box,Priority,Private,Profession,Referred By,Sensitivity,Spouse,Surname Yomi,User 1,User 2
first.name=First Name
last.name=Last Name
email=E-mail Address
other.email=E-mail 2 Address
business.email=E-mail 3 Address
webpage=Web Page
phone=Home Phone
mobile=Mobile Phone
address=Home Street
city=Home City
state=Home State
postal=Home Postal Code
country=Home Country
company=Company
job.title=Job Title
business.phone=Business Phone
business.mobile=Business Phone 2
business.fax=Businss Fax
business.address=Business Street
business.city=Business City
business.state=Business State
business.postal=Business Postal Code
business.country=Business Country
business.webpage=<skip>
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=Children
notes=Notes
unmapped=Title,Middle Name,Suffix,Department,Business Street 2,Business Street 3,Home Street 2,Home Street 3,Other Street,Other Street 2,Other Street 3,Other City,Other State,Other Postal Code,Other Country,Assistant's Phone,Callback,Car Phone,Company Main Phone,Home Fax,Home Phone 2,ISDN,Other Fax,Other Phone,Pager,Primary Phone,Radio Phone,TTY/TDD Phone,Telex,Account,Assistant's Name,Billing Information,Categories,E-mail Display Name,E-mail 2 Display Name,E-mail 3 Display Name,Gender,Government ID Number,Hobby,Initials,Keywords,Language,Location,Mileage,Office Location,Organizational ID Number,PO Box,Private,Profession,Referred By,Spouse,User 1,User 2,User 3,User 4
/***
| Name|TwabPlugin|
| Author|Vincent ~DiBartolo ([[vadibart@gmail.com|mailto:vadibart@gmail.com]])|
| Version|2.2|
| Date|12/08/2008|
| Source|http://www.tiddly-twab.com/#TwabPlugin|
| License|BSD License - http://www.tiddly-twab.com/#TheBSDLicense|
| Requires|~TW2.x, [[DataTiddlerPlugin]], [[FormTiddlerPlugin]], [[InlineJavascriptPlugin]], [[PartTiddlerPlugin]], and any Tiddlers with the [[twab]] tag in the source file |
!Description
Elegant system for keeping your Address Book inside a TiddlyWiki document. Supports import and export of contacts via CSV data. Built-in support for Google, Yahoo, MSN, and Outlook CSV formats. Supports customized formats for those not built-in.
!History
* 08-Dec-08, version 2.2 - use company name if neither first name or last name exists on import, allow for multiple contacts with same title
* 21-Jul-08, version 2.1 - use contact's first and last name in email link if it's present (Thanks to Lyall)
* 20-Feb-08, version 2.0 - import and export Google, Yahoo, MSN, Outlook, or custom CSV formats
* 29-Jun-07, version 1.1 - adding support for mailto:, http:, and map directions
* 20-Jun-07, version 1.0 - preparing for release, changed name to twab
* 19-Jun-07, version 0.4 - modified how new contacts are added
* 21-Nov-06, version 0.3 - changed name from ContactParserMacro to AddressBookMacro
* 10-Oct-06, version 0.2 - converted from regex parsing for title of contact Tiddler to JSON
* 09-Oct-06, version 0.1 - created file
!Example of Adding New Contact
Place the following code in any Tiddler:
{{{
<<twab>>
}}}
Which will result in: <<twab>>
You can add extra parameters to change the button's name as in:
{{{
<<twab press this button>>
}}}
Which will result in: <<twab press this button>>
See [[About:twab:Overview]] for more information.
!Example of Import
Place the following code in any Tiddler:
{{{
<<twab Import AddressBook>>
}}}
Which will result in: <<twab Import AddressBook>>
See [[About:twab:Import]] for more information.
!Example of Export
Place the following code in any Tiddler:
{{{
<<twab Export AddressBook>>
}}}
Which will result in: <<twab Export AddressBook>>
See [[About:twab:Export]] for more information.
! Generate Test Data for Import
Place the following code in any Tiddler:
{{{
<<twab ImportTest>>
}}}
Which will result in: <<twab ImportTest>>
See [[About:twab:Import]] for more information.
!Code
***/
//{{{
version.extensions.TwabMacro = {
major: 2,
minor: 0,
revision: 0,
date: new Date(2008,02,16),
source: "http://www.tiddly-twab.com"
};
config.macros.twab = {};
config.macros.twab.newButtonText = "new contact";
config.macros.twab.importButtonText = "import contacts";
config.macros.twab.exportButtonText = "export contacts";
config.macros.twab.preClean = true;
config.macros.twab.importTiddler = "TwabImport";
config.macros.twab.exportTiddler = "TwabExport";
config.macros.twab.importTags = "AddressBook";
config.macros.twab.exportTags = "AddressBook";
config.macros.twab.fnameField = "first.name";
config.macros.twab.lnameField = "last.name";
config.macros.twab.companyNameField = "company";
config.macros.twab.mapTagPrefix = "format:";
config.macros.twab.skipFlag = "<skip>";
config.macros.twab.unmappedFlag = "unmapped";
config.macros.twab.handler = function (place, macroName, params) {
if( (params.length == 1) && (params[0] == "ImportTest") ){
//if they want to create an import test button
createTiddlyButton(place, "Populate " + config.macros.twab.importTiddler, "", this.testImport);
return;
} else if( (params.length >= 1) && (params[0] == "Import") ){
//any other params are interpreted as tags to be placed on imported tiddlers
if( params.length >= 2 ){
config.macros.twab.importTags = "";
for( var i=1; i<params.length; i++){
config.macros.twab.importTags += params[i] + " ";
}//for
}//if
//if they want to import data
createTiddlyButton(place, config.macros.twab.importButtonText, "", this.importContacts);
} else if( (params.length >= 1) && (params[0] == "Export") ){
//the first tag of the remainder is the one to use for exports
if( params.length >= 2 ){
config.macros.twab.exportTags = params[1];
}//if
//if they want to export data
createTiddlyButton(place, config.macros.twab.exportButtonText, "", this.exportContacts);
} else {
//assume want to create a new contact - any extra params are button name
var buttonText = "";
for( var i=0; i<params.length; i++){
buttonText += " " + params[i] + " ";
}//for
createTiddlyButton(place, ((buttonText == "") ? config.macros.twab.newButtonText : buttonText), "", this.newContact);
}//if-else ifs
}//function handler
//from: http://www.nicknettleton.com/zine/javascript/trim-a-string-in-javascript
config.macros.twab.trim = function(word) { return word.replace(/^\s+|\s+$/g, ''); }
//proxy that retrieves some data and sets null to empty string
config.macros.twab.getData = function(tiddlerName, fieldName){
var data = DataTiddler.getData(tiddlerName, fieldName);
if( data == null ){
data = "";
}//if
return data;
}//function getData
config.macros.twab.testImport = function(e){
var title = config.macros.twab.importTiddler;
var text = "first.name,last.name,job.title,webpage,phone,email\nPrincess,Leia,\"Leader, Rebels\",http://starwars.wikia.com/wiki/Leia_Organa,,leia@alderaan.com\nDarth,Vader,\"Sith Lord\",http://starwars.wikia.com/wiki/Darth_Vader,555-1212,darth@deathstar.com\nLuke,Skywalker,Jedi Master,http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com";
store.saveTiddler(title, title, text, config.options.txtUserName);
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
return true;
}//function import
config.macros.twab.newContact = function(e){
var title = prompt("Please enter contact's name", "");
if( (title == null) || (title == "") ){
return;
}//if
store.saveTiddler(title, title, "<<tiddler ContactsFormTemplate>><data>{}</data>", config.options.txtUserName, new Date(), config.macros.twab.importTags);
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
}//function new
config.macros.twab.importContacts = function(e){
config.macros.twab.deleteAll();
config.macros.twab.parseAll( config.macros.twab.getImportedCSVText() );
alert("Contacts successfully imported.");
return true;
}//function importContacts
config.macros.twab.exportContacts = function(e){
var title = config.macros.twab.exportTiddler;
var mapping = config.macros.twab.getMapping(title, true);
store.saveTiddler(title, title, config.macros.twab.getExportedCSVText(mapping), config.options.txtUserName );
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
return true;
}//function exportContacts
config.macros.twab.deleteAll = function(){
if( !config.macros.twab.preClean ){
return;
}//if
//only remove tiddlers tagged with only first tag if contacts each get more than one
var tags = config.macros.twab.importTags.split(" ");
var tag = tags[0];
if( !confirm("Are you sure you want to clear existing Tiddlers tagged \"" + tag + "\"?") ){
return;
}//if
var contacts = store.getTaggedTiddlers(tag);
for( var i=0; i<contacts.length; i++ ){
store.removeTiddler( contacts[i].title );
}//for
}//function deleteAll
config.macros.twab.getImportedCSVText = function(){
return store.getTiddler(config.macros.twab.importTiddler).text;
}//function getImportedCSVText
config.macros.twab.getExportedCSVText = function(mapping){
var returnStr = "";
//get the mapped header columns
for( var i=0; i<mapping.length; i++ ){
if( mapping[i] && (mapping[i] != config.macros.twab.unmappedFlag) ){
var twabCol = mapping[i];
var mapCol = mapping[twabCol];
returnStr += '"' + mapCol + '",';
}//if
}//for
//get the unmapped header columns
var unmappedStr = mapping[config.macros.twab.unmappedFlag];
if( unmappedStr ){
var unmappedArr = unmappedStr.split(",");
for( var i=0; i<unmappedArr.length; i++ ){
returnStr += '"' + unmappedArr[i] + '",';
}//for
//strip off the last ","
returnStr = returnStr.substring(0, returnStr.length-1);
}//if
returnStr += "\n";
//get all contacts
var tags = config.macros.twab.exportTags.split(" ");
var contacts = store.getTaggedTiddlers( tags[0] );
for( var i=0; i<contacts.length; i++ ){
returnStr += config.macros.twab.exportContact(contacts[i], mapping) + "\n";
}//for
return returnStr;
}//function getExportedCSVText
config.macros.twab.parseAll = function (contactStr){
var rows = contactStr.split("\n");
if( rows.length < 2 ){
alert("Two or more rows must be present to parse contacts.");
return;
}//if
var header = config.macros.twab.parseHeader(rows[0]);
if( header.length == 0 ){
return;
}//if
var contacts = new Array();
for( i=1; i<rows.length; i++){
contacts[ i-1 ] = config.macros.twab.parseContact(header, rows[i]);
}//for
//uncomment this to get contact-by-contact alerts
//config.macros.twab.debugAll(contacts);
config.macros.twab.addAll(contacts);
}//function parseAll
config.macros.twab.parseHeader = function(row){
var mappedHeader = new Array();
//get the raw data
var unmappedHeader = config.macros.twab.parseCSV(row);
//get the appropriate mapping
var mapping = config.macros.twab.getMapping(config.macros.twab.importTiddler, false);
//now convert the unmapped header to the mapped header
for( var i=0; i<unmappedHeader.length; i++ ){
var colName = unmappedHeader[i].replace(/ /g, ".").toLowerCase();
if( mapping[colName] ){
mappedHeader[i] = mapping[colName];
} else {
mappedHeader[i] = config.macros.twab.skipFlag;
}//if
//uncomment this to get field-by-field alerts from the header
//alert("Header field " + i + " is '" + mappedHeader[i] + "'");
}//for
return mappedHeader;
}//function parseHeader
config.macros.twab.getMapping = function(tiddlerName, isExport){
var mapTiddlerName = "";
if( store.getTiddler(tiddlerName) ){
//see if they've declared a mapping (preset or custom) by looking
//at a tag on the tiddler passed in
var tagStr = ""+store.getTiddler(tiddlerName).tags;
var tags = tagStr.split(',');
for( var i=0; i<tags.length; i++ ){
if( tags[i].indexOf( config.macros.twab.mapTagPrefix ) == 0 ){
mapTiddlerName = tags[i].replace(config.macros.twab.mapTagPrefix, "");
}//if
}//for
}//if
//parse the mapping Tiddler
var mapTiddler = config.macros.twab.getMappingTiddler(mapTiddlerName);
if( !mapTiddler ){
alert("Import/Export Format Tiddler " + mapTiddler + " does not exist. Can't proceed.");
return new Array();
}//if
var mapText = ""+store.getTiddler(mapTiddler).text;
var mapArr = mapText.split("\n");
var mapping = new Array();
for( var i=0; i<mapArr.length; i++ ){
var rule = mapArr[i].split("=");
if( !rule || (rule.length < 2) ){
continue;
}//if
//uncomment this to see what mapping is being applied to your import file
//alert("Twab column '" + twabCol + "' is mapped to input tiddler column '" + mapCol + "'");
if( isExport ){
var twabCol = config.macros.twab.trim( rule[0] );
var mapCol = config.macros.twab.trim( rule[1] );
if( mapCol == config.macros.twab.skipFlag ){
continue;
}//inner if
mapping[twabCol] = mapCol;
mapping[i] = twabCol;
} else {
var twabCol = config.macros.twab.trim( rule[0].replace(/ /g, ".").toLowerCase() );
var mapCol = config.macros.twab.trim( rule[1].replace(/ /g, ".").toLowerCase() );
mapping[mapCol] = twabCol;
mapping[i] = mapCol;
}//outer if-else
}//for
return mapping;
}//function getMapping
config.macros.twab.getMappingTiddler = function(name){
var tiddlerName = "TwabDefaultFieldMap";
if( name == "google" ){
tiddlerName = "TwabGoogleFieldMap";
} else if( name == "yahoo" ){
tiddlerName = "TwabYahooFieldMap";
} else if( name == "msn" ){
tiddlerName = "TwabMSNFieldMap";
} else if( name == "outlook" ){
tiddlerName = "TwabOutlookFieldMap";
} else {
//see if a Tiddler by this name exists, if not use the default
if( (name != "default") && store.getTiddler(name) ){
tiddlerName = name;
} else {
tiddlerName = "TwabDefaultFieldMap";
}//inner if-else
}//outer if-else ifs
return tiddlerName;
}//function getMappingTiddler
config.macros.twab.parseCSV = function(row){
var scrubbed = new Array();
var fields = row.split(",");
for( var i=0; i<fields.length; i++){
//if starts with quote but doesn't end with a quote, likely had a comma in the middle
if( (fields[i].charAt(0) == '"') && (fields[i].charAt( fields[i].length-1) != '"') ){
//Hotmail bug: last contact doesn't have ending double-quote
if( i == (fields.length-1) ){
scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");
continue;
}//if
var quoted = fields[i++];
if( !fields[i] ){
continue;
}//if
while( fields[i].charAt( fields[i].length-1 ) != '"' ){
quoted += "," + fields[i++];
}//while
quoted += "," + fields[i];
scrubbed[ scrubbed.length ] = quoted.replace(/"/g, "");
} else {
scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");
}//if-else
}//for
return scrubbed;
}//function parseCSV
config.macros.twab.parseContact = function (header, row){
var returnStr = "";
var fields = config.macros.twab.parseCSV(row);
for( var i=0; i<fields.length; i++ ){
if( header[i] == config.macros.twab.skipFlag ){
continue;
} else if( fields[i] ){
returnStr += "\"" + header[i] + "\":\"" + fields[i] + "\",";
}//if-else
}//for
return returnStr.substr(0, returnStr.length-1);
}//function parseContact
//export a particular contact to a particular mapping
config.macros.twab.exportContact = function(contactTiddler, mapping){
var returnStr = "";
var text = contactTiddler.text;
//have to strip out FormTiddler stuff
text = text.replace(/<<tiddler ContactsFormTemplate>>/g, "");
text = text.replace(/<data>/g, "");
text = text.replace(/<\/data>/g, "");
text = text.replace(/\n/g, "");
//use JSON format to our advantage
var contact = eval( "("+text+")" );
for( var i=0; i<mapping.length; i++){
var twabCol = mapping[i];
if( !(twabCol) ||
!(mapping[twabCol]) ||
(twabCol == config.macros.twab.unmappedFlag) ||
(mapping[twabCol] == config.macros.twab.skipFlag)){
continue;
}//if
//Google hack - on export, "Name" should be "first.name" and "last.name" together.
//Know this because "first.name" is mapped to "Name" and "last.name" is not mapped
if( (twabCol == config.macros.twab.fnameField) && !mapping[config.macros.twab.lnameField] ){
returnStr += '"' + contact[config.macros.twab.fnameField] + " " + contact[config.macros.twab.lnameField] + '",'
} else if( contact[twabCol] ){
returnStr += '"' + contact[twabCol] + '",';
} else {
returnStr += ",";
}//if-else
}//for
//get the unmapped columns
var unmappedStr = mapping[config.macros.twab.unmappedFlag];
if( unmappedStr ){
var unmappedArr = unmappedStr.split(",");
for( var i=0; i<unmappedArr.length; i++ ){
returnStr += ",";
}//for
//strip out the last ","
returnStr = returnStr.substr(0, returnStr.length-1);
}//if
return returnStr.replace(/\n/g, "");
}//function exportContact
//add/overwrite existing contacts with the data parsed out of the import tiddler
config.macros.twab.addAll = function(contacts){
for( var i=0; i<contacts.length; i++ ){
if( !contacts[i] ){
continue;
}//if
//use JSON format to our advantage
var toEval = "({" + contacts[i] + "})";
var contact = eval(toEval);
var title = config.macros.twab.getSaveTitle(contact);
if( title == "" ){
continue;
}//if
//add it now
var text = config.macros.twab.toTiddlyFormat(contacts[i]);
store.saveTiddler(title, title, text, config.options.txtUserName, new Date(), config.macros.twab.importTags);
}//for
}//function addAll
config.macros.twab.getSaveTitle = function(contact){
var title = "";
if( contact[config.macros.twab.fnameField] ){
title += contact[config.macros.twab.fnameField];
}//if
if( contact[config.macros.twab.lnameField] ){
title += contact[config.macros.twab.lnameField];
}//if
if( title == "" ){
title = contact[config.macros.twab.companyNameField];
}//if
if( title == "" ){
alert("Contact missing name field - could not be added: \n" + contacts[i]);
return "";
}//if
if( store.tiddlerExists(title) ){
//try up to 50 times
var seqTitle = "";
var foundOne = false;
for( var i=2; i<51; i++){
seqTitle = title + " (" + i + ")";
if( !store.tiddlerExists(seqTitle) ){
title = seqTitle;
foundOne = true;
break;
}//innermost if
}//inner if
//if got to 50 then there's a problem
if( !foundOne ){
alert("Seriously, you really have that many contacts named '" + title + "'? Wow.");
return "";
}//if
}//if
return title;
}//function getSaveTitle
config.macros.twab.toTiddlyFormat = function(contact){
var tpl = config.macros.twab.getContactTemplate();
return tpl.replace( config.macros.twab.getMacroName(), contact);
}//function toTiddlyFormat
config.macros.twab.getContactTemplate = function(){
var macroName = config.macros.twab.getMacroName();
return "<<tiddler ContactsFormTemplate>>\n<data>{" + macroName + "}</data>";
}//function getContactTemplate
config.macros.twab.getMacroName = function(){
return "#thisContact#";
}//function getMacroName
config.macros.twab.debugAll = function(contacts){
for( var i=0; i<contacts.length; i++ ){
//alert( contacts[i] );
alert( config.macros.twab.toTiddlyFormat(contacts[i]) );
}//for
}//function debugAll
config.macros.twab.populateLinks = function(place){
config.macros.twab.populateEmail(place, "email");
config.macros.twab.populateEmail(place, "other.email");
config.macros.twab.populateEmail(place, "business.email");
config.macros.twab.populateHref(place, "webpage");
config.macros.twab.populateMap(place, "home");
config.macros.twab.populateHref(place, "business.webpage");
config.macros.twab.populateMap(place, "business");
}//function populateLinks
config.macros.twab.populateEmail = function(place, fieldName){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldName);
if( element ){
element.innerHTML = config.macros.twab.formatEmail(tiddlerName, fieldName);
}//if
}//function populateEmail
//Thanks to Udo for the idea for this solution and to Lyall for the code to use display name
config.macros.twab.formatEmail = function(tiddlerName, fieldName){
var returnStr = "";
var mailTo = config.macros.twab.getData(tiddlerName, fieldName);
var displayName = config.macros.twab.getData(tiddlerName, "first.name") + " " + config.macros.twab.getData(tiddlerName, "last.name");
if( mailTo != "" ){
if( displayName != "" ){
mailTo = displayName + "<" + mailTo + ">";
}//if
returnStr = "<a href=\"mailto:" + mailTo + "\" content=\"\">(email)</a>";
}//if
return returnStr;
}//function formatEmail
config.macros.twab.populateHref = function(place, fieldName){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldName);
if( element ){
element.innerHTML = config.macros.twab.formatHref(tiddlerName, fieldName);
}//if
}//function populateHref
//Thanks to Udo for the idea for this solution
config.macros.twab.formatHref = function(tiddlerName, fieldName){
var returnStr = "";
var href = config.macros.twab.getData(tiddlerName, fieldName);
if( href != "" ){
if( href.indexOf("http") != 0 ){
href = "http://" + href;
}//inner if
returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(visit)</a>";
}//if
return returnStr;
}//function formatHref
config.macros.twab.populateMap = function(place, fieldType){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldType + ".map");
if( element ){
element.innerHTML = config.macros.twab.formatMap(tiddlerName, fieldType);
}//if
}//function populateHref
//Thanks to Udo for the idea for this solution
config.macros.twab.formatMap = function(tiddlerName, fieldType){
//hack for lack of planning of home versus business labels
if( fieldType == "business"){
fieldType += ".";
} else if( fieldType == "home" ){
fieldType = "";
}//if-else if
var returnStr = "";
var address = "";
if( DataTiddler.getData(tiddlerName, fieldType+"address") ){
address += DataTiddler.getData(tiddlerName, fieldType+"address") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"city") ){
address += DataTiddler.getData(tiddlerName, fieldType+"city") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"state") ){
address += DataTiddler.getData(tiddlerName, fieldType+"state") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"postal") ){
address += DataTiddler.getData(tiddlerName, fieldType+"postal") + " ";
}//if
if( address == "" ){
return "";
}//if
var href = "http://maps.google.com/maps?ie=UTF8&hl=en&q=" + address + "&f=q&sampleq=1";
returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(map)</a>";
returnStr.replace(/ /g, "\+");
return returnStr;
}//function formatMap
//}}}
/***
|Name|TwabPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[TwabPlugin]]|
!!!!!Code
***/
//{{{
config.macros.twab.handlerOverride_base = config.macros.twab.handler;
config.macros.twab.handler = function(place, macroName, params) {
var type;
var value;
if (params.length >= 1) {
var split = params[0].split(":");
type = split[0].toLowerCase();
value = split[1];
for (var i=2; i < split.length; i++) {
value += ":" + split[i];
}
}
if (type == "tags") {
var buttonText = "";
for (var i = 1; i < params.length; i++) {
buttonText += " " + params[i] + " ";
}
var attribs = {};
attribs[type] = value;
createTiddlyButton(place, (buttonText == "") ? config.macros.twab.newButtonText : buttonText, "", this.newContact, null, null, null, attribs);
} else {
config.macros.twab.handlerOverride_base(place, macroName, params);
}
}
config.macros.twab.newContactOverride_base = config.macros.twab.newContact;
config.macros.twab.newContact = function(e) {
var originalImportTags = config.macros.twab.importTags;
var tags;
if (this.getAttribute) {
tags = this.getAttribute("tags");
}
if (tags) {
config.macros.twab.importTags = config.macros.twab.importTags + " " + tags;
}
config.macros.twab.newContactOverride_base(e);
config.macros.twab.importTags = originalImportTags;
}
config.macros.twab.populateLinks_base = config.macros.twab.populateLinks;
config.macros.twab.populateLinks = function(place) {
config.macros.twab.populateLinks_base(place);
config.macros.twab.populateTel(place, "phone");
config.macros.twab.populateTel(place, "mobile");
config.macros.twab.populateTel(place, "business.phone");
config.macros.twab.populateTel(place, "business.mobile");
config.macros.twab.populateNotes(place, "notes");
}
config.macros.twab.populateTel = function(place, fieldName) {
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldName);
if (element) {
element.innerHTML = config.macros.twab.formatTel(tiddlerName, fieldName);
}
}
config.macros.twab.formatTel = function(tiddlerName, fieldName) {
var returnStr = "";
var telephoneNumber = config.macros.twab.getData(tiddlerName, fieldName);
if (telephoneNumber != "") {
var xLowerIndex = telephoneNumber.lastIndexOf("x");
var xUpperIndex = telephoneNumber.lastIndexOf("X");
var pLowerIndex = telephoneNumber.lastIndexOf("p");
var pUpperIndex = telephoneNumber.lastIndexOf("P");
var commaIndex = telephoneNumber.lastIndexOf(",");
var poundIndex = telephoneNumber.lastIndexOf("#");
var xIndex = (xLowerIndex > xUpperIndex) ? xLowerIndex : xUpperIndex;
xIndex = (xIndex > pLowerIndex) ? xIndex : pLowerIndex;
xIndex = (xIndex > pUpperIndex) ? xIndex : pUpperIndex;
xIndex = (xIndex > commaIndex) ? xIndex : commaIndex;
xIndex = (xIndex > poundIndex) ? xIndex : poundIndex;
if (xIndex > -1) {
var telephoneNumberExtension = telephoneNumber.substring(xIndex + 1);
telephoneNumber = telephoneNumber.substring(0, xIndex);
var isNumber = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
while ((telephoneNumber.length > 0) && !isNumber(telephoneNumber.charAt(telephoneNumber.length - 1))) {
telephoneNumber = telephoneNumber.substring(0, telephoneNumber.length - 1);
}
while ((telephoneNumberExtension.length > 0) && !isNumber(telephoneNumberExtension.charAt(0))) {
telephoneNumberExtension = telephoneNumberExtension.substring(1);
}
if ((telephoneNumber.length > 0) && (telephoneNumberExtension.length > 0)) {
telephoneNumber = telephoneNumber + ";ext=" + telephoneNumberExtension;
}
}
returnStr = "<a href=\"tel:" + telephoneNumber + "\">(tel)</a>";
}
return returnStr;
}
config.macros.twab.populateNotes = function(place, fieldName) {
jQuery(place).find("form").find("span.rolodex").find("textarea").filter(function () {
return jQuery(this).attr("name") === fieldName;
}).each(function () {
var notes = jQuery(this);
var width = notes.width();
notes.closest("span.rolodex").find("input, select, textarea").filter(function () {
return jQuery(this).width() === width;
}).each(function () {
jQuery(this).css("width", width + "px");
});
notes.closest("tr").children("td").first().css("vertical-align", "top");
var notesView = jQuery('<div></div>').insertAfter(notes);
var edit = null;
var ok = null;
edit = function () {
notesView.empty();
notes.show();
createTiddlyButton(jQuery('<div style="float: right;"></div>').appendTo(notesView)[0], "ok", "Close editor", ok);
};
ok = function () {
notesView.empty();
notes.hide();
wikify(notes.val(), jQuery('<div style="width: ' + width + 'px; background-color: #fff; padding: 2px; border: 1px solid #000; overflow-x: auto;"></div>').appendTo(notesView)[0]);
createTiddlyButton(jQuery('<div style="float: right;"></div>').appendTo(notesView)[0], "edit", "Edit notes", edit);
};
ok();
});
}
//}}}
//{{{
if (config.options.txtWebmailURLTemplate === undefined) {
config.options.txtWebmailURLTemplate = "";
}
merge(config.optionsDesc, {
txtWebmailURLTemplate: "The URL template of a webmail server for sending emails: the {name} and {email} placeholders will be replaced accordingly",
});
config.macros.twab.formatEmail = function (tiddlerName, fieldName) {
var cmt = config.macros.twab;
var webmailURLTemplate = config.options.txtWebmailURLTemplate;
var returnStr = "";
var mailTo = cmt.getData(tiddlerName, fieldName);
var displayName = (cmt.getData(tiddlerName, "first.name") + " " + cmt.getData(tiddlerName, "last.name")).trim();
displayName = displayName ? displayName : cmt.getData(tiddlerName, "company");
if (mailTo != "") {
if (webmailURLTemplate) {
returnStr = '<a href="' + webmailURLTemplate.replace(/\{name\}/gi, displayName).replace(/\{email\}/gi, mailTo) + '" content="" target="_blank">(email)</a>';
} else {
if (displayName != "") {
mailTo = displayName + " <" + mailTo + ">";
}
returnStr = '<a href="mailto:' + mailTo + '" content="">(email)</a>';
}
}
return returnStr;
}
//}}}
<part tab1form>
<<formTiddler TwabTabParts/tab1>>
<script>config.macros.twab.populateLinks(place);</script>
</part>
<part tab1>
<html>
<span class="rolodex">
<table>
<tr>
<td align="right"><b>First Name:</b></td>
<td colspan="3"><input name="first.name" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Last Name:</b></td>
<td colspan="3"><input name="last.name" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Email:</b></td>
<td colspan="2"><input name="email" type="text" size="50" style="width:100%" /></td>
<td id="twab.email"></td>
</tr>
<tr>
<td align="right"><b>Alt Email:</b></td>
<td colspan="2"><input name="other.email" type="text" size="50" style="width:100%" /></td>
<td id="twab.other.email"></td>
</tr>
<tr>
<td align="right"><b>Business Email:</b></td>
<td colspan="2"><input name="business.email" type="text" size="50" style="width:100%" /></td>
<td id="twab.business.email"></td>
</tr>
<tr>
<td align="right"><b>Web Site:</b></td>
<td colspan="2"><input name="webpage" type="text" size="50" style="width:100%" /></td>
<td id="twab.webpage"></td>
</tr>
</table>
</span>
</html>
</part>
<part tab2form>
<<formTiddler TwabTabParts/tab2>>
<script>config.macros.twab.populateLinks(place);</script>
</part>
<part tab2>
<html>
<span class="rolodex">
<table>
<tr>
<td align="right"><b>Phone:</b></td>
<td colspan="2"><input name="phone" type="text" size="50" style="width:100%" /></td>
<td id="twab.phone"></td>
</tr>
<tr>
<td align="right"><b>Mobile:</b></td>
<td colspan="2"><input name="mobile" type="text" size="50" style="width:100%" /></td>
<td id="twab.mobile"></td>
</tr>
<tr>
<td align="right" valign="top"><b>Address:</b></td>
<td colspan="3"><textarea name="address" rows="2" cols="40" style="width:100%" ></textarea></td>
</tr>
<tr>
<td align="right"><b>City:</b></td>
<td colspan="3"><input name="city" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>State/Province:</b></td>
<td><input name="state" type="text" size="5" /></td>
<td align="right"><b>ZIP/Postal Code:</b></td>
<td><input name="postal" type="text" size="5" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Country:</b></td>
<td colspan="2"><input name="country" type="text" size="50" style="width:100%" /></td>
<td id="twab.home.map"></td>
</tr>
</table>
</span>
</html>
</part>
<part tab3form>
<<formTiddler TwabTabParts/tab3>>
<script>config.macros.twab.populateLinks(place);</script>
</part>
<part tab3>
<html>
<span class="rolodex">
<table>
<tr>
<td align="right"><b>Company:</b></td>
<td colspan="3"><input name="company" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Job Title:</b></td>
<td colspan="3"><input name="job.title" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Phone:</b></td>
<td colspan="2"><input name="business.phone" type="text" size="50" style="width:100%" /></td>
<td id="twab.business.phone"></td>
</tr>
<tr>
<td align="right"><b>Mobile:</b></td>
<td colspan="2"><input name="business.mobile" type="text" size="50" style="width:100%" /></td>
<td id="twab.business.mobile"></td>
</tr>
<tr>
<td align="right"><b>Fax:</b></td>
<td colspan="3"><input name="business.fax" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right" valign="top"><b>Address:</b></td>
<td colspan="3"><textarea name="business.address" rows="2" cols="40" style="width:100%" ></textarea></td>
</tr>
<tr>
<td align="right"><b>City:</b></td>
<td colspan="3"><input name="business.city" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>State/Province:</b></td>
<td><input name="business.state" type="text" size="5" /></td>
<td align="right"><b>ZIP/Postal Code:</b></td>
<td><input name="business.postal" type="text" size="5" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Country:</b></td>
<td colspan="2"><input name="business.country" type="text" size="50" style="width:100%" /></td>
<td id="twab.business.map"></td>
</tr>
<tr>
<td align="right"><b>Web Site:</b></td>
<td colspan="2"><input name="business.webpage" type="text" size="50" style="width:100%" /></td>
<td id="twab.business.webpage"></td>
</tr>
</table>
</span>
</html>
</part>
<part tab4form>
<<formTiddler TwabTabParts/tab4>>
<script>config.macros.twab.populateLinks(place);</script>
</part>
<part tab4>
<html>
<span class="rolodex">
<table>
<tr>
<td align="right"><b>SSN:</b></td>
<td colspan="3"><input name="ssn" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Birthday:</b></td>
<td colspan="3"><input name="birthday" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Anniversary:</b></td>
<td colspan="3"><input name="anniversary" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Children:</b></td>
<td colspan="3"><input name="children" type="text" size="50" style="width:100%" /></td>
</tr>
<tr>
<td align="right"><b>Notes:</b></td>
<td><textarea name=notes rows="4" cols="40" style="width:100%" ></textarea></td>
</tr>
</table>
</span>
</html>
</part>
first.name=First
last.name=Last
email=Email
other.email=Alternate Email 2
business.email=Alternate Email 1
webpage=Personal Website
phone=Home
mobile=Mobile
address=Home Address
city=Home City
state=Home State
postal=Home ZIP
country=Home Country
company=Company
job.title=Title
business.phone=Work
business.mobile=Other
business.fax=Fax
business.address=Work Address
business.city=Work City
business.state=Work State
business.postal=Work ZIP
business.country=Work Country
business.webpage=Business Website
ssn=<skip>
birthday=Birthday
anniversary=Anniversary
children=<skip>
notes=Comments
unmapped=Middle,Nickname,Category,Distribution Lists,Messenger ID,Pager,Yahoo! Phone,Primary,Custom 1,Custom 2,Custom 3,Custom 4,Messenger ID1,Messenger ID2,Messenger ID3,Messenger ID4,Messenger ID5,Messenger ID6,Messenger ID7,Messenger ID8,Messenger ID9,Skype ID,IRC ID,ICQ ID,Google ID,MSN ID,AIM ID,QQ ID
/***
|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 (store.undo_saveTiddler==undefined) store.undo_saveTiddler=store.saveTiddler;
store.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 (store.undo_removeTiddler==undefined) store.undo_removeTiddler=store.removeTiddler;
store.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);
}
//}}}
/***
|Name|UndoPluginOverride|
|License|[[TW Notes License]]|
|Requires|[[UndoPlugin]]|
!!!!!Code
***/
//{{{
(function () {
var code = eval("store.saveTiddler").toString();
code = code.replace("var tiddler=store.getTiddler(title);", "var titleInstanceofTiddler = title instanceof Tiddler; var tiddler=titleInstanceofTiddler ? title : store.getTiddler(title); title = titleInstanceofTiddler ? title.title : title;");
code = code.replace("this.undo_saveTiddler.apply(this,arguments)", "title = titleInstanceofTiddler ? tiddler : title; return this.undo_saveTiddler.apply(this,arguments)");
code = code.replace("!=text", "!=newBody");
eval("store.saveTiddler = function saveTiddler(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator)" + code.substring(code.indexOf(")") + 1));
})();
//}}}
/***
|Name|UpdateTaggedTiddlersOnSaveChangesPlugin|
|License|[[TW Notes License]]|
!!!!!Code
***/
//{{{
if (config.options.chkUpdateTaggedTiddlersOnSaveChanges === undefined) {
config.options.chkUpdateTaggedTiddlersOnSaveChanges = false;
}
merge(config.optionsDesc, {
chkUpdateTaggedTiddlersOnSaveChanges: "Update tiddlers tagged with the original title after renaming a tiddler"
});
//}}}
//{{{
config.commands.saveTiddler.updateTaggedTiddlers = {
update: function (oldTiddlerTitle, newTiddlerTitle) {
if (oldTiddlerTitle && newTiddlerTitle && (newTiddlerTitle !== oldTiddlerTitle)) {
var taggedTiddlers = store.getTaggedTiddlers(oldTiddlerTitle);
var modifiedDate = new Date();
for (var i = 0; i < taggedTiddlers.length; i++) {
var tagPosition = taggedTiddlers[i].tags.indexOf(oldTiddlerTitle);
if (tagPosition > -1) {
taggedTiddlers[i].tags[tagPosition] = newTiddlerTitle;
taggedTiddlers[i].modifier = config.options.txtUserName;
taggedTiddlers[i].modified = modifiedDate;
}
}
}
},
handler: config.commands.saveTiddler.handler
};
config.commands.saveTiddler.handler = function (event, src, title) {
if (config.options.chkUpdateTaggedTiddlersOnSaveChanges && store.tiddlerExists(title)) {
var tiddlerElem = story.getTiddler(title);
if (tiddlerElem) {
var fields = {};
story.gatherSaveFields(tiddlerElem, fields);
var newTitle = fields.title || title;
config.commands.saveTiddler.updateTaggedTiddlers.update(title, newTitle);
}
}
config.commands.saveTiddler.updateTaggedTiddlers.handler.apply(this, arguments);
};
//}}}
/***
|Name|aes.js|
|Source|https://gist.github.com/chrisveness/b28bd30b2b0c03806b0c|
|Documentation|http://www.movable-type.co.uk/scripts/aes.html|
|License|[[MIT|https://opensource.org/licenses/MIT]]|
|Description|AES implementation in JavaScript|
!!!!!Code
***/
// // ''public—js—aes.js''
//{{{
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* jshint node:true *//* global define */
/* 'use strict'; */
/**
* AES (Rijndael cipher) encryption routines,
*
* Reference implementation of FIPS-197 http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf.
*
* @namespace
*/
/* var */ Aes = {};
/**
* AES Cipher function: encrypt 'input' state with Rijndael algorithm [§5.1];
* applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage.
*
* @param {number[]} input - 16-byte (128-bit) input state array.
* @param {number[][]} w - Key schedule as 2D byte-array (Nr+1 x Nb bytes).
* @returns {number[]} Encrypted output state array.
*/
Aes.cipher = function(input, w) {
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4]
for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i];
state = Aes.addRoundKey(state, w, 0, Nb);
for (var round=1; round<Nr; round++) {
state = Aes.subBytes(state, Nb);
state = Aes.shiftRows(state, Nb);
state = Aes.mixColumns(state, Nb);
state = Aes.addRoundKey(state, w, round, Nb);
}
state = Aes.subBytes(state, Nb);
state = Aes.shiftRows(state, Nb);
state = Aes.addRoundKey(state, w, Nr, Nb);
var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4]
for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)];
return output;
};
/**
* Perform key expansion to generate a key schedule from a cipher key [§5.2].
*
* @param {number[]} key - Cipher key as 16/24/32-byte array.
* @returns {number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes).
*/
Aes.keyExpansion = function(key) {
var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
var Nk = key.length/4; // key length (in words): 4/6/8 for 128/192/256-bit keys
var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
var w = new Array(Nb*(Nr+1));
var temp = new Array(4);
// initialise first Nk words of expanded key with cipher key
for (var i=0; i<Nk; i++) {
var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]];
w[i] = r;
}
// expand the key into the remainder of the schedule
for (var i=Nk; i<(Nb*(Nr+1)); i++) {
w[i] = new Array(4);
for (var t=0; t<4; t++) temp[t] = w[i-1][t];
// each Nk'th word has extra transformation
if (i % Nk == 0) {
temp = Aes.subWord(Aes.rotWord(temp));
for (var t=0; t<4; t++) temp[t] ^= Aes.rCon[i/Nk][t];
}
// 256-bit key has subWord applied every 4th word
else if (Nk > 6 && i%Nk == 4) {
temp = Aes.subWord(temp);
}
// xor w[i] with w[i-1] and w[i-Nk]
for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t];
}
return w;
};
/**
* Apply SBox to state S [§5.1.1]
* @private
*/
Aes.subBytes = function(s, Nb) {
for (var r=0; r<4; r++) {
for (var c=0; c<Nb; c++) s[r][c] = Aes.sBox[s[r][c]];
}
return s;
};
/**
* Shift row r of state S left by r bytes [§5.1.2]
* @private
*/
Aes.shiftRows = function(s, Nb) {
var t = new Array(4);
for (var r=1; r<4; r++) {
for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy
for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back
} // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
return s; // see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
};
/**
* Combine bytes of each col of state S [§5.1.3]
* @private
*/
Aes.mixColumns = function(s, Nb) {
for (var c=0; c<4; c++) {
var a = new Array(4); // 'a' is a copy of the current column from 's'
var b = new Array(4); // 'b' is a•{02} in GF(2^8)
for (var i=0; i<4; i++) {
a[i] = s[i][c];
b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1;
}
// a[n] ^ b[n] is a•{03} in GF(2^8)
s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // {02}•a0 + {03}•a1 + a2 + a3
s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 • {02}•a1 + {03}•a2 + a3
s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + {02}•a2 + {03}•a3
s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // {03}•a0 + a1 + a2 + {02}•a3
}
return s;
};
/**
* Xor Round Key into state S [§5.1.4]
* @private
*/
Aes.addRoundKey = function(state, w, rnd, Nb) {
for (var r=0; r<4; r++) {
for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r];
}
return state;
};
/**
* Apply SBox to 4-byte word w
* @private
*/
Aes.subWord = function(w) {
for (var i=0; i<4; i++) w[i] = Aes.sBox[w[i]];
return w;
};
/**
* Rotate 4-byte word w left by one byte
* @private
*/
Aes.rotWord = function(w) {
var tmp = w[0];
for (var i=0; i<3; i++) w[i] = w[i+1];
w[3] = tmp;
return w;
};
// sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
Aes.sBox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16];
// rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
Aes.rCon = [ [0x00, 0x00, 0x00, 0x00],
[0x01, 0x00, 0x00, 0x00],
[0x02, 0x00, 0x00, 0x00],
[0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00],
[0x10, 0x00, 0x00, 0x00],
[0x20, 0x00, 0x00, 0x00],
[0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00],
[0x1b, 0x00, 0x00, 0x00],
[0x36, 0x00, 0x00, 0x00] ];
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* if (typeof module != 'undefined' && module.exports) module.exports = Aes; // ≡ export default Aes */
//}}}
// // ''public—js—aes-ctr.js''
//{{{
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* AES Counter-mode implementation in JavaScript (c) Chris Veness 2005-2014 / MIT Licence */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* jshint node:true *//* global define, escape, unescape, btoa, atob */
/* 'use strict'; */
/* if (typeof module!='undefined' && module.exports) var Aes = require('./aes'); // CommonJS (Node.js) */
/**
* Aes.Ctr: Counter-mode (CTR) wrapper for AES.
*
* This encrypts a Unicode string to produces a base64 ciphertext using 128/192/256-bit AES,
* and the converse to decrypt an encrypted ciphertext.
*
* See http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
*
* @augments Aes
*/
Aes.Ctr = {};
/**
* Encrypt a text using AES encryption in Counter mode of operation.
*
* Unicode multi-byte character safe
*
* @param {string} plaintext - Source text to be encrypted.
* @param {string} password - The password to use to generate a key for encryption.
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
* @returns {string} Encrypted text.
*
* @example
* var encr = Aes.Ctr.encrypt('big secret', 'pāşšŵōřđ', 256); // 'lwGl66VVwVObKIr6of8HVqJr'
*/
Aes.Ctr.encrypt = function(plaintext, password, nBits) {
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
plaintext = String(plaintext).utf8Encode();
password = String(password).utf8Encode();
// use AES itself to encrypt password to get cipher key (using plain password as source for key
// expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
var nBytes = nBits/8; // no bytes in key (16/24/32)
var pwBytes = new Array(nBytes);
for (var i=0; i<nBytes; i++) { // use 1st 16/24/32 chars of password for key
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}
var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes)); // gives us 16-byte key
key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
// initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
// [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
var counterBlock = new Array(blockSize);
var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970
var nonceMs = nonce%1000;
var nonceSec = Math.floor(nonce/1000);
var nonceRnd = Math.floor(Math.random()*0xffff);
// for debugging: nonce = nonceMs = nonceSec = nonceRnd = 0;
for (var i=0; i<2; i++) counterBlock[i] = (nonceMs >>> i*8) & 0xff;
for (var i=0; i<2; i++) counterBlock[i+2] = (nonceRnd >>> i*8) & 0xff;
for (var i=0; i<4; i++) counterBlock[i+4] = (nonceSec >>> i*8) & 0xff;
// and convert it to a string to go on the front of the ciphertext
var ctrTxt = '';
for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
// generate key schedule - an expansion of the key into distinct Key Rounds for each round
var keySchedule = Aes.keyExpansion(key);
var blockCount = Math.ceil(plaintext.length/blockSize);
var ciphertxt = new Array(blockCount); // ciphertext as array of strings
for (var b=0; b<blockCount; b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
// done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8);
var cipherCntr = Aes.cipher(counterBlock, keySchedule); // -- encrypt counter block --
// block size is reduced on final block
var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1;
var cipherChar = new Array(blockLength);
for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char --
cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i);
cipherChar[i] = String.fromCharCode(cipherChar[i]);
}
ciphertxt[b] = cipherChar.join('');
}
// use Array.join() for better performance than repeated string appends
var ciphertext = ctrTxt + ciphertxt.join('');
ciphertext = ciphertext.base64Encode();
return ciphertext;
};
/**
* Decrypt a text encrypted by AES in counter mode of operation
*
* @param {string} ciphertext - Cipher text to be decrypted.
* @param {string} password - Password to use to generate a key for decryption.
* @param {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
* @returns {string} Decrypted text
*
* @example
* var decr = Aes.Ctr.decrypt('lwGl66VVwVObKIr6of8HVqJr', 'pāşšŵōřđ', 256); // 'big secret'
*/
Aes.Ctr.decrypt = function(ciphertext, password, nBits) {
var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
ciphertext = String(ciphertext).base64Decode();
password = String(password).utf8Encode();
// use AES to encrypt password (mirroring encrypt routine)
var nBytes = nBits/8; // no bytes in key
var pwBytes = new Array(nBytes);
for (var i=0; i<nBytes; i++) {
pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i);
}
var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes));
key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long
// recover nonce from 1st 8 bytes of ciphertext
var counterBlock = new Array(8);
var ctrTxt = ciphertext.slice(0, 8);
for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
// generate key schedule
var keySchedule = Aes.keyExpansion(key);
// separate ciphertext into blocks (skipping past initial 8 bytes)
var nBlocks = Math.ceil((ciphertext.length-8) / blockSize);
var ct = new Array(nBlocks);
for (var b=0; b<nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize);
ciphertext = ct; // ciphertext is now array of block-length strings
// plaintext will get generated block-by-block into array of block-length strings
var plaintxt = new Array(ciphertext.length);
for (var b=0; b<nBlocks; b++) {
// set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff;
for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff;
var cipherCntr = Aes.cipher(counterBlock, keySchedule); // encrypt counter block
var plaintxtByte = new Array(ciphertext[b].length);
for (var i=0; i<ciphertext[b].length; i++) {
// -- xor plaintxt with ciphered counter byte-by-byte --
plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i);
plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]);
}
plaintxt[b] = plaintxtByte.join('');
}
// join array of blocks into single plaintext string
var plaintext = plaintxt.join('');
plaintext = plaintext.utf8Decode(); // decode from UTF8 back to Unicode multi-byte chars
return plaintext;
};
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Extend String object with method to encode multi-byte string to utf8
* - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
* - note utf8Encode is an identity function with 7-bit ascii strings, but not with 8-bit strings;
* - utf8Encode('x') = 'x', but utf8Encode('ça') = 'ça', and utf8Encode('ça') = 'ça'*/
if (typeof String.prototype.utf8Encode == 'undefined') {
String.prototype.utf8Encode = function() {
return unescape( encodeURIComponent( this ) );
};
}
/* Extend String object with method to decode utf8 string to multi-byte */
if (typeof String.prototype.utf8Decode == 'undefined') {
String.prototype.utf8Decode = function() {
try {
return decodeURIComponent( escape( this ) );
} catch (e) {
return this; // invalid UTF-8? return as-is
}
};
}
/* Extend String object with method to encode base64
* - developer.mozilla.org/en-US/docs/Web/API/window.btoa, nodejs.org/api/buffer.html
* - note: btoa & Buffer/binary work on single-byte Unicode (C0/C1), so ok for utf8 strings, not for general Unicode...
* - note: if btoa()/atob() are not available (eg IE9-), try github.com/davidchambers/Base64.js */
if (typeof String.prototype.base64Encode == 'undefined') {
String.prototype.base64Encode = function() {
if (typeof btoa != 'undefined') return btoa(this); // browser
if (typeof Buffer != 'undefined') return new Buffer(this, 'binary').toString('base64'); // Node.js
throw new Error('No Base64 Encode');
};
}
/* Extend String object with method to decode base64 */
if (typeof String.prototype.base64Decode == 'undefined') {
String.prototype.base64Decode = function() {
if (typeof atob != 'undefined') return atob(this); // browser
if (typeof Buffer != 'undefined') return new Buffer(this, 'base64').toString('binary'); // Node.js
throw new Error('No Base64 Decode');
};
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* if (typeof module != 'undefined' && module.exports) module.exports = Aes.Ctr; // ≡ export default Aes.Ctr */
//}}}
//{{{
if ((typeof process !== "undefined") && process.release && (process.release.name === 'node')
&& (typeof module != "undefined") && module.exports) {
module.exports = Aes;
}
//}}}
/***
|Name|colpick.js|
|Source|https://github.com/mrgrain/colpick/blob/master/js/colpick.js|
|Documentation|https://github.com/mrgrain/colpick|
|Version|commit 1c215eebe072c4c96dad92f0a0ae298509932c86 2015-05-27 https://raw.githubusercontent.com/mrgrain/colpick/master/js/colpick.js|
|License|dual licensed under the [[MIT|https://opensource.org/licenses/MIT]] and [[GPLv2|https://opensource.org/licenses/gpl-2.0.php]] licenses|
|Description|colpick - A jQuery Color Picker|
!!!!!Documentation
;Usage examples
;Button
<html><button id="example-button" value="#00FF00">Show Color Picker</button></html>
<script show>
jQuery('#example-button').colpick();
</script>
;Flat mode and hex layout
<html><div id="example-flat"></div></html>
<script show>
jQuery('#example-flat').colpick({
color:'123456',
flat:true,
layout:'hex'
});
</script>
;Text Input and colorScheme 'dark'
<html><input type="text" id="example-text" value="#0000FF"></html>
<script show>
jQuery('#example-text').colpick({
colorScheme:'dark',
onChange:function(hsb,hex,rgb,el,bySetColor) {
jQuery(el).val('#'+hex);
}
});
</script>
;HTML5 Color Input
<html><input type="color" id="example-color" value="#FF0000"></html>
<script show>
jQuery('#example-color').colpick({
onSubmit:function(hsb,hex,rgb,el,bySetColor) {
jQuery(el).val('#'+hex);
jQuery(el).colpickHide();
}
});
</script>
;Polyfill
<html><input type="color" id="example-polyfill"></html>
<script show>
jQuery('#example-polyfill').colpick({
polyfill:true
});
</script>
!!!!!CSS
//{{{
/*
colpick Color Picker / colpick.com
* /
/*Main container* /
.colpick {
position: absolute;
box-sizing:content-box;
width: 346px;
height: 170px;
overflow: hidden;
display: none;
font-family: Arial, Helvetica, sans-serif;
direction:ltr;
background:#ebebeb;
border: 1px solid #bbb;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
/*Prevents selecting text when dragging the selectors* /
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
/*Color selection box with gradients* /
.colpick .colpick_color {
position: absolute;
left: 7px;
top: 7px;
width: 156px;
height: 156px;
overflow: hidden;
outline: 1px solid #aaa;
cursor: crosshair;
}
.colpick .colpick_color_overlay1 {
position: absolute;
left:0;
top:0;
width: 156px;
height: 156px;
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')"; /* IE8 * /
background: -moz-linear-gradient(left, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%); /* FF3.6+ * /
background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ * /
background: -webkit-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ * /
background: -o-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* Opera 11.10+ * /
background: -ms-linear-gradient(left, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%); /* IE10+ * /
background: linear-gradient(to right, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff'); /* IE6 & IE7 * /
}
.colpick .colpick_color_overlay2 {
position: absolute;
left:0;
top:0;
width: 156px;
height: 156px;
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')"; /* IE8 * /
background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 100%); /* FF3.6+ * /
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,0,0,0)), color-stop(100%,rgba(0,0,0,1))); /* Chrome,Safari4+ * /
background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Chrome10+,Safari5.1+ * /
background: -o-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* Opera 11.10+ * /
background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* IE10+ * /
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,1) 100%); /* W3C * /
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#000000',GradientType=0 ); /* IE6-9 * /
}
/*Circular color selector* /
.colpick .colpick_selector_outer {
background:none;
position: absolute;
width: 11px;
height: 11px;
margin: -6px 0 0 -6px;
border: 1px solid black;
border-radius: 50%;
}
.colpick .colpick_selector_inner{
position: absolute;
width: 9px;
height: 9px;
border: 1px solid white;
border-radius: 50%;
}
/*Vertical hue bar* /
.colpick .colpick_hue {
position: absolute;
top: 6px;
left: 175px;
width: 19px;
height: 156px;
border: 1px solid #aaa;
cursor: n-resize;
}
/*Hue bar sliding indicator* /
.colpick .colpick_hue_arrs {
position: absolute;
left: -8px;
width: 35px;
height: 7px;
margin: -7px 0 0 0;
}
.colpick .colpick_hue_larr {
position:absolute;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 7px solid #858585;
}
.colpick .colpick_hue_rarr {
position:absolute;
right:0;
width: 0;
height: 0;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 7px solid #858585;
}
/*New color box* /
.colpick .colpick_new_color {
position: absolute;
left: 207px;
top: 6px;
width: 60px;
height: 27px;
background: #f00;
border: 1px solid #8f8f8f;
}
/*Current color box* /
.colpick .colpick_current_color {
position: absolute;
left: 277px;
top: 6px;
width: 60px;
height: 27px;
background: #f00;
border: 1px solid #8f8f8f;
}
/*Input field containers* /
.colpick .colpick_field, .colpick .colpick_hex_field {
position: absolute;
height: 20px;
width: 60px;
overflow:hidden;
background:#f3f3f3;
color:#b8b8b8;
font-size:12px;
border:1px solid #bdbdbd;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.colpick .colpick_rgb_r {
top: 40px;
left: 207px;
}
.colpick .colpick_rgb_g {
top: 67px;
left: 207px;
}
.colpick .colpick_rgb_b {
top: 94px;
left: 207px;
}
.colpick .colpick_hsb_h {
top: 40px;
left: 277px;
}
.colpick .colpick_hsb_s {
top: 67px;
left: 277px;
}
.colpick .colpick_hsb_b {
top: 94px;
left: 277px;
}
.colpick .colpick_hex_field {
width: 68px;
left: 207px;
top: 121px;
}
/*Text field container on focus* /
.colpick .colpick_focus {
border-color: #999;
}
/*Field label container* /
.colpick .colpick_field_letter {
position: absolute;
width: 12px;
height: 20px;
line-height: 20px;
padding-left: 4px;
background: #efefef;
border-right: 1px solid #bdbdbd;
font-weight: bold;
color:#777;
}
/*Text inputs* /
.colpick .colpick_field input, .colpick .colpick_hex_field input {
position: absolute;
right: 11px;
margin: 0;
padding: 0;
height: 20px;
line-height: 20px;
background: transparent;
border: none;
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
color: #555;
text-align: right;
outline: none;
}
.colpick .colpick_hex_field input {
right: 4px;
}
/*Field up/down arrows* /
.colpick .colpick_field_arrs {
position: absolute;
top: 0;
right: 0;
width: 9px;
height: 21px;
cursor: n-resize;
}
.colpick .colpick_field_uarr {
position: absolute;
top: 5px;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #959595;
}
.colpick .colpick_field_darr {
position: absolute;
bottom:5px;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #959595;
}
/*Submit/Select button* /
.colpick .colpick_submit {
position: absolute;
left: 207px;
top: 149px;
width: 130px;
height: 22px;
line-height:22px;
background: #efefef;
text-align: center;
color: #555;
font-size: 12px;
font-weight:bold;
border: 1px solid #bdbdbd;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.colpick .colpick_submit:hover {
background:#f3f3f3;
border-color:#999;
cursor: pointer;
}
/*full layout with no submit button* /
.colpick.colpick_full_ns .colpick_submit,
.colpick.colpick_full_ns .colpick_current_color{
display:none;
}
.colpick.colpick_full_ns .colpick_new_color {
width: 130px;
height: 25px;
}
.colpick.colpick_full_ns .colpick_rgb_r,
.colpick.colpick_full_ns .colpick_hsb_h {
top: 42px;
}
.colpick.colpick_full_ns .colpick_rgb_g,
.colpick.colpick_full_ns .colpick_hsb_s {
top: 73px;
}
.colpick.colpick_full_ns .colpick_rgb_b,
.colpick.colpick_full_ns .colpick_hsb_b {
top: 104px;
}
.colpick.colpick_full_ns .colpick_hex_field {
top: 135px;
}
/*rgbhex layout* /
.colpick.colpick_rgbhex .colpick_hsb_h,
.colpick.colpick_rgbhex .colpick_hsb_s,
.colpick.colpick_rgbhex .colpick_hsb_b {
display:none;
}
.colpick.colpick_rgbhex {
width:282px;
}
.colpick.colpick_rgbhex .colpick_field,
.colpick.colpick_rgbhex .colpick_submit {
width:68px;
}
.colpick.colpick_rgbhex .colpick_new_color {
width:34px;
border-right:none;
}
.colpick.colpick_rgbhex .colpick_current_color {
width:34px;
left:240px;
border-left:none;
}
/*rgbhex layout, no submit button* /
.colpick.colpick_rgbhex_ns .colpick_submit,
.colpick.colpick_rgbhex_ns .colpick_current_color{
display:none;
}
.colpick.colpick_rgbhex_ns .colpick_new_color{
width:68px;
border: 1px solid #8f8f8f;
}
.colpick.colpick_rgbhex_ns .colpick_rgb_r {
top: 42px;
}
.colpick.colpick_rgbhex_ns .colpick_rgb_g {
top: 73px;
}
.colpick.colpick_rgbhex_ns .colpick_rgb_b {
top: 104px;
}
.colpick.colpick_rgbhex_ns .colpick_hex_field {
top: 135px;
}
/*hex layout* /
.colpick.colpick_hex .colpick_hsb_h,
.colpick.colpick_hex .colpick_hsb_s,
.colpick.colpick_hex .colpick_hsb_b,
.colpick.colpick_hex .colpick_rgb_r,
.colpick.colpick_hex .colpick_rgb_g,
.colpick.colpick_hex .colpick_rgb_b {
display:none;
}
.colpick.colpick_hex {
width:206px;
height:201px;
}
.colpick.colpick_hex .colpick_hex_field {
width:72px;
height:25px;
top:168px;
left:80px;
}
.colpick.colpick_hex .colpick_hex_field div,
.colpick.colpick_hex .colpick_hex_field input {
height: 25px;
line-height: 25px;
}
.colpick.colpick_hex .colpick_new_color {
left:9px;
top:168px;
width:30px;
border-right:none;
}
.colpick.colpick_hex .colpick_current_color {
left:39px;
top:168px;
width:30px;
border-left:none;
}
.colpick.colpick_hex .colpick_submit {
left:164px;
top: 168px;
width:30px;
height:25px;
line-height: 25px;
}
/*hex layout, no submit button* /
.colpick.colpick_hex_ns .colpick_submit,
.colpick.colpick_hex_ns .colpick_current_color {
display:none;
}
.colpick.colpick_hex_ns .colpick_hex_field {
width:80px;
}
.colpick.colpick_hex_ns .colpick_new_color{
width:60px;
border: 1px solid #8f8f8f;
}
/*Dark color scheme* /
.colpick.colpick_dark {
background: #161616;
border-color: #2a2a2a;
}
.colpick.colpick_dark .colpick_color {
outline-color: #333;
}
.colpick.colpick_dark .colpick_hue {
border-color: #555;
}
.colpick.colpick_dark .colpick_field,
.colpick.colpick_dark .colpick_hex_field {
background: #101010;
border-color: #2d2d2d;
}
.colpick.colpick_dark .colpick_field_letter {
background: #131313;
border-color: #2d2d2d;
color: #696969;
}
.colpick.colpick_dark .colpick_field input,
.colpick.colpick_dark .colpick_hex_field input {
color: #7a7a7a;
}
.colpick.colpick_dark .colpick_field_uarr {
border-bottom-color:#696969;
}
.colpick.colpick_dark .colpick_field_darr {
border-top-color:#696969;
}
.colpick.colpick_dark .colpick_focus {
border-color:#444;
}
.colpick.colpick_dark .colpick_submit {
background: #131313;
border-color:#2d2d2d;
color:#7a7a7a;
}
.colpick.colpick_dark .colpick_submit:hover {
background-color:#101010;
border-color:#444;
}
//}}}
!!!!!Code
***/
//{{{
/*!
* colpick Color Picker
* https://github.com/mrgrain/colpick
*
* Copyright 2013, 2015 Moritz Kornher, Jose Vargas, Stefan Petre
* Released under the MIT license and GPLv2 license
* https://github.com/mrgrain/colpick/blob/master/LICENSE
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node/CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var colpick = function () {
var
tpl = '<div class="colpick"><div class="colpick_color"><div class="colpick_color_overlay1"><div class="colpick_color_overlay2"><div class="colpick_selector_outer"><div class="colpick_selector_inner"></div></div></div></div></div><div class="colpick_hue"><div class="colpick_hue_arrs"><div class="colpick_hue_larr"></div><div class="colpick_hue_rarr"></div></div></div><div class="colpick_new_color"></div><div class="colpick_current_color"></div><div class="colpick_hex_field"><div class="colpick_field_letter">#</div><input type="text" maxlength="6" size="6" /></div><div class="colpick_rgb_r colpick_field"><div class="colpick_field_letter">R</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_rgb_g colpick_field"><div class="colpick_field_letter">G</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_rgb_b colpick_field"><div class="colpick_field_letter">B</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_h colpick_field"><div class="colpick_field_letter">H</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_s colpick_field"><div class="colpick_field_letter">S</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_hsb_b colpick_field"><div class="colpick_field_letter">B</div><input type="text" maxlength="3" size="3" /><div class="colpick_field_arrs"><div class="colpick_field_uarr"></div><div class="colpick_field_darr"></div></div></div><div class="colpick_submit"></div></div>',
defaults = {
showEvent: 'click',
onShow: function () {
},
onBeforeShow: function () {
},
onHide: function () {
},
onChange: function () {
},
onSubmit: function () {
},
colorScheme: 'light',
color: 'auto',
livePreview: true,
flat: false,
layout: 'full',
submit: 1,
submitText: 'OK',
height: 156,
polyfill: false,
styles: false
},
//Fill the inputs of the plugin
fillRGBFields = function (hsb, cal) {
var rgb = hsbToRgb(hsb);
$(cal).data('colpick').fields
.eq(1).val(rgb.r).end()
.eq(2).val(rgb.g).end()
.eq(3).val(rgb.b).end();
},
fillHSBFields = function (hsb, cal) {
$(cal).data('colpick').fields
.eq(4).val(Math.round(hsb.h)).end()
.eq(5).val(Math.round(hsb.s)).end()
.eq(6).val(Math.round(hsb.b)).end();
},
fillHexFields = function (hsb, cal) {
$(cal).data('colpick').fields.eq(0).val(hsbToHex(hsb));
},
//Set the round selector position
setSelector = function (hsb, cal) {
$(cal).data('colpick').selector.css('backgroundColor', '#' + hsbToHex({h: hsb.h, s: 100, b: 100}));
$(cal).data('colpick').selectorIndic.css({
left: parseInt($(cal).data('colpick').height * hsb.s / 100, 10),
top: parseInt($(cal).data('colpick').height * (100 - hsb.b) / 100, 10)
});
},
//Set the hue selector position
setHue = function (hsb, cal) {
$(cal).data('colpick').hue.css('top', parseInt($(cal).data('colpick').height - $(cal).data('colpick').height * hsb.h / 360, 10));
},
//Set current and new colors
setCurrentColor = function (hsb, cal) {
$(cal).data('colpick').currentColor.css('backgroundColor', '#' + hsbToHex(hsb));
},
setNewColor = function (hsb, cal) {
$(cal).data('colpick').newColor.css('backgroundColor', '#' + hsbToHex(hsb));
},
//Called when the new color is changed
change = function () {
var cal = $(this).parent().parent(), col;
if (this.parentNode.className.indexOf('_hex') > 0) {
cal.data('colpick').color = col = hexToHsb(fixHex(this.value));
fillRGBFields(col, cal.get(0));
fillHSBFields(col, cal.get(0));
} else if (this.parentNode.className.indexOf('_hsb') > 0) {
cal.data('colpick').color = col = fixHSB({
h: parseInt(cal.data('colpick').fields.eq(4).val(), 10),
s: parseInt(cal.data('colpick').fields.eq(5).val(), 10),
b: parseInt(cal.data('colpick').fields.eq(6).val(), 10)
});
fillRGBFields(col, cal.get(0));
fillHexFields(col, cal.get(0));
} else {
cal.data('colpick').color = col = rgbToHsb(fixRGB({
r: parseInt(cal.data('colpick').fields.eq(1).val(), 10),
g: parseInt(cal.data('colpick').fields.eq(2).val(), 10),
b: parseInt(cal.data('colpick').fields.eq(3).val(), 10)
}));
fillHexFields(col, cal.get(0));
fillHSBFields(col, cal.get(0));
}
setSelector(col, cal.get(0));
setHue(col, cal.get(0));
setNewColor(col, cal.get(0));
cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 0]);
},
//Change style on blur and on focus of inputs
blur = function () {
$(this).parent().removeClass('colpick_focus');
},
focus = function () {
$(this).parent().parent().data('colpick').fields.parent().removeClass('colpick_focus');
$(this).parent().addClass('colpick_focus');
},
//Increment/decrement arrows functions
downIncrement = function (ev) {
ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
var field = $(this).parent().find('input').focus();
var current = {
el: $(this).parent().addClass('colpick_slider'),
max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255),
y: ev.pageY,
field: field,
val: parseInt(field.val(), 10),
preview: $(this).parent().parent().data('colpick').livePreview
};
$(document).mouseup(current, upIncrement);
$(document).mousemove(current, moveIncrement);
},
moveIncrement = function (ev) {
ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val - ev.pageY + ev.data.y, 10))));
if (ev.data.preview) {
change.apply(ev.data.field.get(0), [true]);
}
return false;
},
upIncrement = function (ev) {
change.apply(ev.data.field.get(0), [true]);
ev.data.el.removeClass('colpick_slider').find('input').focus();
$(document).off('mouseup', upIncrement);
$(document).off('mousemove', moveIncrement);
return false;
},
//Hue slider functions
downHue = function (ev) {
ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
var current = {
cal: $(this).parent(),
y: $(this).offset().top
};
$(document).on('mouseup touchend', current, upHue);
$(document).on('mousemove touchmove', current, moveHue);
var pageY = ((ev.type == 'touchstart') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY );
change.apply(
current.cal.data('colpick')
.fields.eq(4).val(parseInt(360 * (current.cal.data('colpick').height - (pageY - current.y)) / current.cal.data('colpick').height, 10))
.get(0),
[current.cal.data('colpick').livePreview]
);
return false;
},
moveHue = function (ev) {
var pageY = ((ev.type == 'touchmove') ? ev.originalEvent.changedTouches[0].pageY : ev.pageY );
change.apply(
ev.data.cal.data('colpick')
.fields.eq(4).val(parseInt(360 * (ev.data.cal.data('colpick').height - Math.max(0, Math.min(ev.data.cal.data('colpick').height, (pageY - ev.data.y)))) / ev.data.cal.data('colpick').height, 10))
.get(0),
[ev.data.preview]
);
return false;
},
upHue = function (ev) {
fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
$(document).off('mouseup touchend', upHue);
$(document).off('mousemove touchmove', moveHue);
return false;
},
//Color selector functions
downSelector = function (ev) {
ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
var current = {
cal: $(this).parent(),
pos: $(this).offset()
};
current.preview = current.cal.data('colpick').livePreview;
$(document).on('mouseup touchend', current, upSelector);
$(document).on('mousemove touchmove', current, moveSelector);
var pageX, pageY;
if (ev.type == 'touchstart') {
pageX = ev.originalEvent.changedTouches[0].pageX;
pageY = ev.originalEvent.changedTouches[0].pageY;
} else {
pageX = ev.pageX;
pageY = ev.pageY;
}
change.apply(
current.cal.data('colpick').fields
.eq(6).val(parseInt(100 * (current.cal.data('colpick').height - (pageY - current.pos.top)) / current.cal.data('colpick').height, 10)).end()
.eq(5).val(parseInt(100 * (pageX - current.pos.left) / current.cal.data('colpick').height, 10))
.get(0),
[current.preview]
);
return false;
},
moveSelector = function (ev) {
var pageX, pageY;
if (ev.type == 'touchmove') {
pageX = ev.originalEvent.changedTouches[0].pageX;
pageY = ev.originalEvent.changedTouches[0].pageY;
} else {
pageX = ev.pageX;
pageY = ev.pageY;
}
change.apply(
ev.data.cal.data('colpick').fields
.eq(6).val(parseInt(100 * (ev.data.cal.data('colpick').height - Math.max(0, Math.min(ev.data.cal.data('colpick').height, (pageY - ev.data.pos.top)))) / ev.data.cal.data('colpick').height, 10)).end()
.eq(5).val(parseInt(100 * (Math.max(0, Math.min(ev.data.cal.data('colpick').height, (pageX - ev.data.pos.left)))) / ev.data.cal.data('colpick').height, 10))
.get(0),
[ev.data.preview]
);
return false;
},
upSelector = function (ev) {
fillRGBFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
fillHexFields(ev.data.cal.data('colpick').color, ev.data.cal.get(0));
$(document).off('mouseup touchend', upSelector);
$(document).off('mousemove touchmove', moveSelector);
return false;
},
//Submit button
clickSubmit = function () {
var cal = $(this).parent();
var col = cal.data('colpick').color;
cal.data('colpick').origColor = col;
setCurrentColor(col, cal.get(0));
cal.data('colpick').onSubmit(col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el);
},
//Show/hide the color picker
show = function (ev) {
if (ev) {
// Prevent the trigger of any direct parent
ev.stopPropagation();
}
var cal = $('#' + $(this).data('colpickId'));
if (ev && !cal.data('colpick').polyfill) {
ev.preventDefault();
}
cal.data('colpick').onBeforeShow.apply(this, [cal.get(0)]);
var pos = $(this).offset();
var top = pos.top + this.offsetHeight;
var left = pos.left;
var viewPort = getViewport();
var calW = cal.width();
if (left + calW > viewPort.l + viewPort.w) {
left -= calW;
}
cal.css({left: left + 'px', top: top + 'px'});
if (cal.data('colpick').onShow.apply(this, [cal.get(0)]) != false) {
cal.show();
}
//Hide when user clicks outside
$('html').mousedown({cal: cal}, hide);
cal.mousedown(function (ev) {
ev.stopPropagation();
})
},
hide = function (ev) {
var cal = $('#' + $(this).data('colpickId'));
if (ev) {
cal = ev.data.cal;
}
if (cal.data('colpick').onHide.apply(this, [cal.get(0)]) != false) {
cal.hide();
}
$('html').off('mousedown', hide);
},
getViewport = function () {
var m = document.compatMode == 'CSS1Compat';
return {
l: window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
w: window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth)
};
},
//Fix the values if the user enters a negative or high value
fixHSB = function (hsb) {
return {
h: Math.min(360, Math.max(0, hsb.h)),
s: Math.min(100, Math.max(0, hsb.s)),
b: Math.min(100, Math.max(0, hsb.b))
};
},
fixRGB = function (rgb) {
return {
r: Math.min(255, Math.max(0, rgb.r)),
g: Math.min(255, Math.max(0, rgb.g)),
b: Math.min(255, Math.max(0, rgb.b))
};
},
fixHex = function (hex) {
var len = 6 - hex.length;
if (len == 3) {
var e = [];
for (var j = 0; j < len; j++) {
e.push(hex[j]);
e.push(hex[j]);
}
hex = e.join('');
} else {
if (len > 0) {
var o = [];
for (var i = 0; i < len; i++) {
o.push('0');
}
o.push(hex);
hex = o.join('');
}
}
return hex;
},
restoreOriginal = function () {
var cal = $(this).parent();
var col = cal.data('colpick').origColor;
cal.data('colpick').color = col;
fillRGBFields(col, cal.get(0));
fillHexFields(col, cal.get(0));
fillHSBFields(col, cal.get(0));
setSelector(col, cal.get(0));
setHue(col, cal.get(0));
setNewColor(col, cal.get(0));
};
return {
init: function (opt) {
opt = $.extend({}, defaults, opt || {});
//Set color
if (opt.color === 'auto') {
} else if (typeof opt.color == 'string') {
opt.color = hexToHsb(opt.color);
} else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) {
opt.color = rgbToHsb(opt.color);
} else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) {
opt.color = fixHSB(opt.color);
} else {
return this;
}
//For each selected DOM element
return this.each(function () {
//If the element does not have an ID
if (!$(this).data('colpickId')) {
var options = $.extend({}, opt);
//Color
if (opt.color === 'auto') {
options.color = $(this).val() ? hexToHsb($(this).val()) : {h: 0, s: 0, b: 0};
}
options.origColor = options.color;
//Polyfill
if (typeof opt.polyfill == 'function') {
options.polyfill = opt.polyfill(this);
}
if (options.polyfill && $(this).is('input') && this.type === "color") {
return;
}
//Generate and assign a random ID
var id = 'collorpicker_' + parseInt(Math.random() * 1000);
$(this).data('colpickId', id);
//Set the tpl's ID and get the HTML
var cal = $(tpl).attr('id', id);
//Add class according to layout
cal.addClass('colpick_' + options.layout + (options.submit ? '' : ' colpick_' + options.layout + '_ns'));
//Add class if the color scheme is not default
if (options.colorScheme != 'light') {
cal.addClass('colpick_' + options.colorScheme);
}
//Setup submit button
cal.find('div.colpick_submit').html(options.submitText).click(clickSubmit);
//Setup input fields
options.fields = cal.find('input').change(change).blur(blur).focus(focus);
cal.find('div.colpick_field_arrs').mousedown(downIncrement).end().find('div.colpick_current_color').click(restoreOriginal);
//Setup hue selector
options.selector = cal.find('div.colpick_color').on('mousedown touchstart', downSelector);
options.selectorIndic = options.selector.find('div.colpick_selector_outer');
//Store parts of the plugin
options.el = this;
options.hue = cal.find('div.colpick_hue_arrs');
var huebar = options.hue.parent();
//Paint the hue bar
var UA = navigator.userAgent.toLowerCase();
var isIE = navigator.appName === 'Microsoft Internet Explorer';
var IEver = isIE ? parseFloat(UA.match(/msie ([0-9]*[\.0-9]+)/)[1]) : 0;
var ngIE = ( isIE && IEver < 10 );
var stops = ['#ff0000', '#ff0080', '#ff00ff', '#8000ff', '#0000ff', '#0080ff', '#00ffff', '#00ff80', '#00ff00', '#80ff00', '#ffff00', '#ff8000', '#ff0000'];
if (ngIE) {
var i, div;
for (i = 0; i <= 11; i++) {
div = $('<div></div>').attr('style', 'height:8.333333%; filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=' + stops[i] + ', endColorstr=' + stops[i + 1] + '); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=' + stops[i] + ', endColorstr=' + stops[i + 1] + ')";');
huebar.append(div);
}
} else {
var stopList = stops.join(',');
huebar.attr('style', 'background:-webkit-linear-gradient(top,' + stopList + '); background: -o-linear-gradient(top,' + stopList + '); background: -ms-linear-gradient(top,' + stopList + '); background:-moz-linear-gradient(top,' + stopList + '); -webkit-linear-gradient(top,' + stopList + '); background:linear-gradient(to bottom,' + stopList + '); ');
}
cal.find('div.colpick_hue').on('mousedown touchstart', downHue);
options.newColor = cal.find('div.colpick_new_color');
options.currentColor = cal.find('div.colpick_current_color');
//Store options and fill with default color
cal.data('colpick', options);
fillRGBFields(options.color, cal.get(0));
fillHSBFields(options.color, cal.get(0));
fillHexFields(options.color, cal.get(0));
setHue(options.color, cal.get(0));
setSelector(options.color, cal.get(0));
setCurrentColor(options.color, cal.get(0));
setNewColor(options.color, cal.get(0));
//Append to body if flat=false, else show in place
if (options.flat) {
cal.appendTo(options.appendTo || this).show();
cal.css(options.styles || {
position: 'relative',
display: 'block'
});
} else {
cal.appendTo(options.appendTo || document.body);
$(this).on(options.showEvent, show);
cal.css(options.styles || {
position: 'absolute'
});
}
}
});
},
//Shows the picker
showPicker: function () {
return this.each(function () {
if ($(this).data('colpickId')) {
show.apply(this);
}
});
},
//Hides the picker
hidePicker: function () {
return this.each(function () {
if ($(this).data('colpickId')) {
hide.apply(this);
}
});
},
//Sets a color as new and current (default)
setColor: function (col, setCurrent) {
setCurrent = (typeof setCurrent === "undefined") ? 1 : setCurrent;
if (typeof col == 'string') {
col = hexToHsb(col);
} else if (col.r != undefined && col.g != undefined && col.b != undefined) {
col = rgbToHsb(col);
} else if (col.h != undefined && col.s != undefined && col.b != undefined) {
col = fixHSB(col);
} else {
return this;
}
return this.each(function () {
if ($(this).data('colpickId')) {
var cal = $('#' + $(this).data('colpickId'));
cal.data('colpick').color = col;
cal.data('colpick').origColor = col;
fillRGBFields(col, cal.get(0));
fillHSBFields(col, cal.get(0));
fillHexFields(col, cal.get(0));
setHue(col, cal.get(0));
setSelector(col, cal.get(0));
setNewColor(col, cal.get(0));
cal.data('colpick').onChange.apply(cal.parent(), [col, hsbToHex(col), hsbToRgb(col), cal.data('colpick').el, 1]);
if (setCurrent) {
setCurrentColor(col, cal.get(0));
}
}
});
},
destroy: function () {
$('#' + $(this).data('colpickId')).remove();
}
};
}();
//Color space conversions
var hexToRgb = function (hex) {
hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)};
};
var hexToHsb = function (hex) {
return rgbToHsb(hexToRgb(hex));
};
var rgbToHsb = function (rgb) {
var hsb = {h: 0, s: 0, b: 0};
var min = Math.min(rgb.r, rgb.g, rgb.b);
var max = Math.max(rgb.r, rgb.g, rgb.b);
var delta = max - min;
hsb.b = max;
hsb.s = max != 0 ? 255 * delta / max : 0;
if (hsb.s != 0) {
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
else hsb.h = 4 + (rgb.r - rgb.g) / delta;
} else hsb.h = -1;
hsb.h *= 60;
if (hsb.h < 0) hsb.h += 360;
hsb.s *= 100 / 255;
hsb.b *= 100 / 255;
return hsb;
};
var hsbToRgb = function (hsb) {
var rgb = {};
var h = hsb.h;
var s = hsb.s * 255 / 100;
var v = hsb.b * 255 / 100;
if (s == 0) {
rgb.r = rgb.g = rgb.b = v;
} else {
var t1 = v;
var t2 = (255 - s) * v / 255;
var t3 = (t1 - t2) * (h % 60) / 60;
if (h == 360) h = 0;
if (h < 60) {
rgb.r = t1;
rgb.b = t2;
rgb.g = t2 + t3
}
else if (h < 120) {
rgb.g = t1;
rgb.b = t2;
rgb.r = t1 - t3
}
else if (h < 180) {
rgb.g = t1;
rgb.r = t2;
rgb.b = t2 + t3
}
else if (h < 240) {
rgb.b = t1;
rgb.r = t2;
rgb.g = t1 - t3
}
else if (h < 300) {
rgb.b = t1;
rgb.g = t2;
rgb.r = t2 + t3
}
else if (h < 360) {
rgb.r = t1;
rgb.g = t2;
rgb.b = t1 - t3
}
else {
rgb.r = 0;
rgb.g = 0;
rgb.b = 0
}
}
return {r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b)};
};
var rgbToHex = function (rgb) {
var hex = [
rgb.r.toString(16),
rgb.g.toString(16),
rgb.b.toString(16)
];
$.each(hex, function (nr, val) {
if (val.length == 1) {
hex[nr] = '0' + val;
}
});
return hex.join('');
};
var hsbToHex = function (hsb) {
return rgbToHex(hsbToRgb(hsb));
};
$.fn.extend({
colpick: colpick.init,
colpickHide: colpick.hidePicker,
colpickShow: colpick.showPicker,
colpickSetColor: colpick.setColor,
colpickDestroy: colpick.destroy
});
$.extend({
colpick: {
rgbToHex: rgbToHex,
rgbToHsb: rgbToHsb,
hsbToHex: hsbToHex,
hsbToRgb: hsbToRgb,
hexToHsb: hexToHsb,
hexToRgb: hexToRgb
}
});
}));
//}}}
//{{{
(function() {
var css = store.getTiddlerText("colpick.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}"));
setStylesheet(css, "colpick.js-stylesheet");
})();
//}}}
// // http://d3js.org
//{{{
// https://d3js.org Version 5.5.0. Copyright 2018 Mike Bostock.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.d3 = global.d3 || {})));
}(this, (function (exports) { 'use strict';
var version = "5.5.0";
function ascending(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function bisector(compare) {
if (compare.length === 1) compare = ascendingComparator(compare);
return {
left: function(a, x, lo, hi) {
if (lo == null) lo = 0;
if (hi == null) hi = a.length;
while (lo < hi) {
var mid = lo + hi >>> 1;
if (compare(a[mid], x) < 0) lo = mid + 1;
else hi = mid;
}
return lo;
},
right: function(a, x, lo, hi) {
if (lo == null) lo = 0;
if (hi == null) hi = a.length;
while (lo < hi) {
var mid = lo + hi >>> 1;
if (compare(a[mid], x) > 0) hi = mid;
else lo = mid + 1;
}
return lo;
}
};
}
function ascendingComparator(f) {
return function(d, x) {
return ascending(f(d), x);
};
}
var ascendingBisect = bisector(ascending);
var bisectRight = ascendingBisect.right;
var bisectLeft = ascendingBisect.left;
function pairs(array, f) {
if (f == null) f = pair;
var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
while (i < n) pairs[i] = f(p, p = array[++i]);
return pairs;
}
function pair(a, b) {
return [a, b];
}
function cross(values0, values1, reduce) {
var n0 = values0.length,
n1 = values1.length,
values = new Array(n0 * n1),
i0,
i1,
i,
value0;
if (reduce == null) reduce = pair;
for (i0 = i = 0; i0 < n0; ++i0) {
for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {
values[i] = reduce(value0, values1[i1]);
}
}
return values;
}
function descending(a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
function number(x) {
return x === null ? NaN : +x;
}
function variance(values, valueof) {
var n = values.length,
m = 0,
i = -1,
mean = 0,
value,
delta,
sum = 0;
if (valueof == null) {
while (++i < n) {
if (!isNaN(value = number(values[i]))) {
delta = value - mean;
mean += delta / ++m;
sum += delta * (value - mean);
}
}
}
else {
while (++i < n) {
if (!isNaN(value = number(valueof(values[i], i, values)))) {
delta = value - mean;
mean += delta / ++m;
sum += delta * (value - mean);
}
}
}
if (m > 1) return sum / (m - 1);
}
function deviation(array, f) {
var v = variance(array, f);
return v ? Math.sqrt(v) : v;
}
function extent(values, valueof) {
var n = values.length,
i = -1,
value,
min,
max;
if (valueof == null) {
while (++i < n) { // Find the first comparable value.
if ((value = values[i]) != null && value >= value) {
min = max = value;
while (++i < n) { // Compare the remaining values.
if ((value = values[i]) != null) {
if (min > value) min = value;
if (max < value) max = value;
}
}
}
}
}
else {
while (++i < n) { // Find the first comparable value.
if ((value = valueof(values[i], i, values)) != null && value >= value) {
min = max = value;
while (++i < n) { // Compare the remaining values.
if ((value = valueof(values[i], i, values)) != null) {
if (min > value) min = value;
if (max < value) max = value;
}
}
}
}
}
return [min, max];
}
var array = Array.prototype;
var slice = array.slice;
var map = array.map;
function constant(x) {
return function() {
return x;
};
}
function identity(x) {
return x;
}
function sequence(start, stop, step) {
start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
var i = -1,
n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
range = new Array(n);
while (++i < n) {
range[i] = start + i * step;
}
return range;
}
var e10 = Math.sqrt(50),
e5 = Math.sqrt(10),
e2 = Math.sqrt(2);
function ticks(start, stop, count) {
var reverse,
i = -1,
n,
ticks,
step;
stop = +stop, start = +start, count = +count;
if (start === stop && count > 0) return [start];
if (reverse = stop < start) n = start, start = stop, stop = n;
if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
if (step > 0) {
start = Math.ceil(start / step);
stop = Math.floor(stop / step);
ticks = new Array(n = Math.ceil(stop - start + 1));
while (++i < n) ticks[i] = (start + i) * step;
} else {
start = Math.floor(start * step);
stop = Math.ceil(stop * step);
ticks = new Array(n = Math.ceil(start - stop + 1));
while (++i < n) ticks[i] = (start - i) / step;
}
if (reverse) ticks.reverse();
return ticks;
}
function tickIncrement(start, stop, count) {
var step = (stop - start) / Math.max(0, count),
power = Math.floor(Math.log(step) / Math.LN10),
error = step / Math.pow(10, power);
return power >= 0
? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
: -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
}
function tickStep(start, stop, count) {
var step0 = Math.abs(stop - start) / Math.max(0, count),
step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
error = step0 / step1;
if (error >= e10) step1 *= 10;
else if (error >= e5) step1 *= 5;
else if (error >= e2) step1 *= 2;
return stop < start ? -step1 : step1;
}
function thresholdSturges(values) {
return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
}
function histogram() {
var value = identity,
domain = extent,
threshold = thresholdSturges;
function histogram(data) {
var i,
n = data.length,
x,
values = new Array(n);
for (i = 0; i < n; ++i) {
values[i] = value(data[i], i, data);
}
var xz = domain(values),
x0 = xz[0],
x1 = xz[1],
tz = threshold(values, x0, x1);
// Convert number of thresholds into uniform thresholds.
if (!Array.isArray(tz)) {
tz = tickStep(x0, x1, tz);
tz = sequence(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive
}
// Remove any thresholds outside the domain.
var m = tz.length;
while (tz[0] <= x0) tz.shift(), --m;
while (tz[m - 1] > x1) tz.pop(), --m;
var bins = new Array(m + 1),
bin;
// Initialize bins.
for (i = 0; i <= m; ++i) {
bin = bins[i] = [];
bin.x0 = i > 0 ? tz[i - 1] : x0;
bin.x1 = i < m ? tz[i] : x1;
}
// Assign data to bins by value, ignoring any outside the domain.
for (i = 0; i < n; ++i) {
x = values[i];
if (x0 <= x && x <= x1) {
bins[bisectRight(tz, x, 0, m)].push(data[i]);
}
}
return bins;
}
histogram.value = function(_) {
return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value;
};
histogram.domain = function(_) {
return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain;
};
histogram.thresholds = function(_) {
return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold;
};
return histogram;
}
function threshold(values, p, valueof) {
if (valueof == null) valueof = number;
if (!(n = values.length)) return;
if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
if (p >= 1) return +valueof(values[n - 1], n - 1, values);
var n,
i = (n - 1) * p,
i0 = Math.floor(i),
value0 = +valueof(values[i0], i0, values),
value1 = +valueof(values[i0 + 1], i0 + 1, values);
return value0 + (value1 - value0) * (i - i0);
}
function freedmanDiaconis(values, min, max) {
values = map.call(values, number).sort(ascending);
return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3)));
}
function scott(values, min, max) {
return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
}
function max(values, valueof) {
var n = values.length,
i = -1,
value,
max;
if (valueof == null) {
while (++i < n) { // Find the first comparable value.
if ((value = values[i]) != null && value >= value) {
max = value;
while (++i < n) { // Compare the remaining values.
if ((value = values[i]) != null && value > max) {
max = value;
}
}
}
}
}
else {
while (++i < n) { // Find the first comparable value.
if ((value = valueof(values[i], i, values)) != null && value >= value) {
max = value;
while (++i < n) { // Compare the remaining values.
if ((value = valueof(values[i], i, values)) != null && value > max) {
max = value;
}
}
}
}
}
return max;
}
function mean(values, valueof) {
var n = values.length,
m = n,
i = -1,
value,
sum = 0;
if (valueof == null) {
while (++i < n) {
if (!isNaN(value = number(values[i]))) sum += value;
else --m;
}
}
else {
while (++i < n) {
if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value;
else --m;
}
}
if (m) return sum / m;
}
function median(values, valueof) {
var n = values.length,
i = -1,
value,
numbers = [];
if (valueof == null) {
while (++i < n) {
if (!isNaN(value = number(values[i]))) {
numbers.push(value);
}
}
}
else {
while (++i < n) {
if (!isNaN(value = number(valueof(values[i], i, values)))) {
numbers.push(value);
}
}
}
return threshold(numbers.sort(ascending), 0.5);
}
function merge(arrays) {
var n = arrays.length,
m,
i = -1,
j = 0,
merged,
array;
while (++i < n) j += arrays[i].length;
merged = new Array(j);
while (--n >= 0) {
array = arrays[n];
m = array.length;
while (--m >= 0) {
merged[--j] = array[m];
}
}
return merged;
}
function min(values, valueof) {
var n = values.length,
i = -1,
value,
min;
if (valueof == null) {
while (++i < n) { // Find the first comparable value.
if ((value = values[i]) != null && value >= value) {
min = value;
while (++i < n) { // Compare the remaining values.
if ((value = values[i]) != null && min > value) {
min = value;
}
}
}
}
}
else {
while (++i < n) { // Find the first comparable value.
if ((value = valueof(values[i], i, values)) != null && value >= value) {
min = value;
while (++i < n) { // Compare the remaining values.
if ((value = valueof(values[i], i, values)) != null && min > value) {
min = value;
}
}
}
}
}
return min;
}
function permute(array, indexes) {
var i = indexes.length, permutes = new Array(i);
while (i--) permutes[i] = array[indexes[i]];
return permutes;
}
function scan(values, compare) {
if (!(n = values.length)) return;
var n,
i = 0,
j = 0,
xi,
xj = values[j];
if (compare == null) compare = ascending;
while (++i < n) {
if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {
xj = xi, j = i;
}
}
if (compare(xj, xj) === 0) return j;
}
function shuffle(array, i0, i1) {
var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
t,
i;
while (m) {
i = Math.random() * m-- | 0;
t = array[m + i0];
array[m + i0] = array[i + i0];
array[i + i0] = t;
}
return array;
}
function sum(values, valueof) {
var n = values.length,
i = -1,
value,
sum = 0;
if (valueof == null) {
while (++i < n) {
if (value = +values[i]) sum += value; // Note: zero and null are equivalent.
}
}
else {
while (++i < n) {
if (value = +valueof(values[i], i, values)) sum += value;
}
}
return sum;
}
function transpose(matrix) {
if (!(n = matrix.length)) return [];
for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
row[j] = matrix[j][i];
}
}
return transpose;
}
function length(d) {
return d.length;
}
function zip() {
return transpose(arguments);
}
var slice$1 = Array.prototype.slice;
function identity$1(x) {
return x;
}
var top = 1,
right = 2,
bottom = 3,
left = 4,
epsilon = 1e-6;
function translateX(x) {
return "translate(" + (x + 0.5) + ",0)";
}
function translateY(y) {
return "translate(0," + (y + 0.5) + ")";
}
function number$1(scale) {
return function(d) {
return +scale(d);
};
}
function center(scale) {
var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.
if (scale.round()) offset = Math.round(offset);
return function(d) {
return +scale(d) + offset;
};
}
function entering() {
return !this.__axis;
}
function axis(orient, scale) {
var tickArguments = [],
tickValues = null,
tickFormat = null,
tickSizeInner = 6,
tickSizeOuter = 6,
tickPadding = 3,
k = orient === top || orient === left ? -1 : 1,
x = orient === left || orient === right ? "x" : "y",
transform = orient === top || orient === bottom ? translateX : translateY;
function axis(context) {
var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$1) : tickFormat,
spacing = Math.max(tickSizeInner, 0) + tickPadding,
range = scale.range(),
range0 = +range[0] + 0.5,
range1 = +range[range.length - 1] + 0.5,
position = (scale.bandwidth ? center : number$1)(scale.copy()),
selection = context.selection ? context.selection() : context,
path = selection.selectAll(".domain").data([null]),
tick = selection.selectAll(".tick").data(values, scale).order(),
tickExit = tick.exit(),
tickEnter = tick.enter().append("g").attr("class", "tick"),
line = tick.select("line"),
text = tick.select("text");
path = path.merge(path.enter().insert("path", ".tick")
.attr("class", "domain")
.attr("stroke", "#000"));
tick = tick.merge(tickEnter);
line = line.merge(tickEnter.append("line")
.attr("stroke", "#000")
.attr(x + "2", k * tickSizeInner));
text = text.merge(tickEnter.append("text")
.attr("fill", "#000")
.attr(x, k * spacing)
.attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));
if (context !== selection) {
path = path.transition(context);
tick = tick.transition(context);
line = line.transition(context);
text = text.transition(context);
tickExit = tickExit.transition(context)
.attr("opacity", epsilon)
.attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); });
tickEnter
.attr("opacity", epsilon)
.attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });
}
tickExit.remove();
path
.attr("d", orient === left || orient == right
? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter
: "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter);
tick
.attr("opacity", 1)
.attr("transform", function(d) { return transform(position(d)); });
line
.attr(x + "2", k * tickSizeInner);
text
.attr(x, k * spacing)
.text(format);
selection.filter(entering)
.attr("fill", "none")
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");
selection
.each(function() { this.__axis = position; });
}
axis.scale = function(_) {
return arguments.length ? (scale = _, axis) : scale;
};
axis.ticks = function() {
return tickArguments = slice$1.call(arguments), axis;
};
axis.tickArguments = function(_) {
return arguments.length ? (tickArguments = _ == null ? [] : slice$1.call(_), axis) : tickArguments.slice();
};
axis.tickValues = function(_) {
return arguments.length ? (tickValues = _ == null ? null : slice$1.call(_), axis) : tickValues && tickValues.slice();
};
axis.tickFormat = function(_) {
return arguments.length ? (tickFormat = _, axis) : tickFormat;
};
axis.tickSize = function(_) {
return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
};
axis.tickSizeInner = function(_) {
return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
};
axis.tickSizeOuter = function(_) {
return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
};
axis.tickPadding = function(_) {
return arguments.length ? (tickPadding = +_, axis) : tickPadding;
};
return axis;
}
function axisTop(scale) {
return axis(top, scale);
}
function axisRight(scale) {
return axis(right, scale);
}
function axisBottom(scale) {
return axis(bottom, scale);
}
function axisLeft(scale) {
return axis(left, scale);
}
var noop = {value: function() {}};
function dispatch() {
for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t);
_[t] = [];
}
return new Dispatch(_);
}
function Dispatch(_) {
this._ = _;
}
function parseTypenames(typenames, types) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name = "", i = t.indexOf(".");
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
return {type: t, name: name};
});
}
Dispatch.prototype = dispatch.prototype = {
constructor: Dispatch,
on: function(typename, callback) {
var _ = this._,
T = parseTypenames(typename + "", _),
t,
i = -1,
n = T.length;
// If no callback was specified, return the callback of the given type and name.
if (arguments.length < 2) {
while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
return;
}
// If a type was specified, set the callback for the given type and name.
// Otherwise, if a null callback was specified, remove callbacks of the given name.
if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
while (++i < n) {
if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);
else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
}
return this;
},
copy: function() {
var copy = {}, _ = this._;
for (var t in _) copy[t] = _[t].slice();
return new Dispatch(copy);
},
call: function(type, that) {
if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
},
apply: function(type, that, args) {
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
}
};
function get(type, name) {
for (var i = 0, n = type.length, c; i < n; ++i) {
if ((c = type[i]).name === name) {
return c.value;
}
}
}
function set(type, name, callback) {
for (var i = 0, n = type.length; i < n; ++i) {
if (type[i].name === name) {
type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
break;
}
}
if (callback != null) type.push({name: name, value: callback});
return type;
}
var xhtml = "http://www.w3.org/1999/xhtml";
var namespaces = {
svg: "http://www.w3.org/2000/svg",
xhtml: xhtml,
xlink: "http://www.w3.org/1999/xlink",
xml: "http://www.w3.org/XML/1998/namespace",
xmlns: "http://www.w3.org/2000/xmlns/"
};
function namespace(name) {
var prefix = name += "", i = prefix.indexOf(":");
if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
}
function creatorInherit(name) {
return function() {
var document = this.ownerDocument,
uri = this.namespaceURI;
return uri === xhtml && document.documentElement.namespaceURI === xhtml
? document.createElement(name)
: document.createElementNS(uri, name);
};
}
function creatorFixed(fullname) {
return function() {
return this.ownerDocument.createElementNS(fullname.space, fullname.local);
};
}
function creator(name) {
var fullname = namespace(name);
return (fullname.local
? creatorFixed
: creatorInherit)(fullname);
}
function none() {}
function selector(selector) {
return selector == null ? none : function() {
return this.querySelector(selector);
};
}
function selection_select(select) {
if (typeof select !== "function") select = selector(select);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i] = subnode;
}
}
}
return new Selection(subgroups, this._parents);
}
function empty() {
return [];
}
function selectorAll(selector) {
return selector == null ? empty : function() {
return this.querySelectorAll(selector);
};
}
function selection_selectAll(select) {
if (typeof select !== "function") select = selectorAll(select);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
subgroups.push(select.call(node, node.__data__, i, group));
parents.push(node);
}
}
}
return new Selection(subgroups, parents);
}
var matcher = function(selector) {
return function() {
return this.matches(selector);
};
};
if (typeof document !== "undefined") {
var element = document.documentElement;
if (!element.matches) {
var vendorMatches = element.webkitMatchesSelector
|| element.msMatchesSelector
|| element.mozMatchesSelector
|| element.oMatchesSelector;
matcher = function(selector) {
return function() {
return vendorMatches.call(this, selector);
};
};
}
}
var matcher$1 = matcher;
function selection_filter(match) {
if (typeof match !== "function") match = matcher$1(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
subgroup.push(node);
}
}
}
return new Selection(subgroups, this._parents);
}
function sparse(update) {
return new Array(update.length);
}
function selection_enter() {
return new Selection(this._enter || this._groups.map(sparse), this._parents);
}
function EnterNode(parent, datum) {
this.ownerDocument = parent.ownerDocument;
this.namespaceURI = parent.namespaceURI;
this._next = null;
this._parent = parent;
this.__data__ = datum;
}
EnterNode.prototype = {
constructor: EnterNode,
appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
querySelector: function(selector) { return this._parent.querySelector(selector); },
querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
};
function constant$1(x) {
return function() {
return x;
};
}
var keyPrefix = "$"; // Protect against keys like “__proto__”.
function bindIndex(parent, group, enter, update, exit, data) {
var i = 0,
node,
groupLength = group.length,
dataLength = data.length;
// Put any non-null nodes that fit into update.
// Put any null nodes into enter.
// Put any remaining data into enter.
for (; i < dataLength; ++i) {
if (node = group[i]) {
node.__data__ = data[i];
update[i] = node;
} else {
enter[i] = new EnterNode(parent, data[i]);
}
}
// Put any non-null nodes that don’t fit into exit.
for (; i < groupLength; ++i) {
if (node = group[i]) {
exit[i] = node;
}
}
}
function bindKey(parent, group, enter, update, exit, data, key) {
var i,
node,
nodeByKeyValue = {},
groupLength = group.length,
dataLength = data.length,
keyValues = new Array(groupLength),
keyValue;
// Compute the key for each node.
// If multiple nodes have the same key, the duplicates are added to exit.
for (i = 0; i < groupLength; ++i) {
if (node = group[i]) {
keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
if (keyValue in nodeByKeyValue) {
exit[i] = node;
} else {
nodeByKeyValue[keyValue] = node;
}
}
}
// Compute the key for each datum.
// If there a node associated with this key, join and add it to update.
// If there is not (or the key is a duplicate), add it to enter.
for (i = 0; i < dataLength; ++i) {
keyValue = keyPrefix + key.call(parent, data[i], i, data);
if (node = nodeByKeyValue[keyValue]) {
update[i] = node;
node.__data__ = data[i];
nodeByKeyValue[keyValue] = null;
} else {
enter[i] = new EnterNode(parent, data[i]);
}
}
// Add any remaining nodes that were not bound to data to exit.
for (i = 0; i < groupLength; ++i) {
if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
exit[i] = node;
}
}
}
function selection_data(value, key) {
if (!value) {
data = new Array(this.size()), j = -1;
this.each(function(d) { data[++j] = d; });
return data;
}
var bind = key ? bindKey : bindIndex,
parents = this._parents,
groups = this._groups;
if (typeof value !== "function") value = constant$1(value);
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
var parent = parents[j],
group = groups[j],
groupLength = group.length,
data = value.call(parent, parent && parent.__data__, j, parents),
dataLength = data.length,
enterGroup = enter[j] = new Array(dataLength),
updateGroup = update[j] = new Array(dataLength),
exitGroup = exit[j] = new Array(groupLength);
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
// Now connect the enter nodes to their following update node, such that
// appendChild can insert the materialized enter node before this node,
// rather than at the end of the parent node.
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
if (previous = enterGroup[i0]) {
if (i0 >= i1) i1 = i0 + 1;
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
previous._next = next || null;
}
}
}
update = new Selection(update, parents);
update._enter = enter;
update._exit = exit;
return update;
}
function selection_exit() {
return new Selection(this._exit || this._groups.map(sparse), this._parents);
}
function selection_merge(selection$$1) {
for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group0[i] || group1[i]) {
merge[i] = node;
}
}
}
for (; j < m0; ++j) {
merges[j] = groups0[j];
}
return new Selection(merges, this._parents);
}
function selection_order() {
for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
if (node = group[i]) {
if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
next = node;
}
}
}
return this;
}
function selection_sort(compare) {
if (!compare) compare = ascending$1;
function compareNode(a, b) {
return a && b ? compare(a.__data__, b.__data__) : !a - !b;
}
for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group[i]) {
sortgroup[i] = node;
}
}
sortgroup.sort(compareNode);
}
return new Selection(sortgroups, this._parents).order();
}
function ascending$1(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function selection_call() {
var callback = arguments[0];
arguments[0] = this;
callback.apply(null, arguments);
return this;
}
function selection_nodes() {
var nodes = new Array(this.size()), i = -1;
this.each(function() { nodes[++i] = this; });
return nodes;
}
function selection_node() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
var node = group[i];
if (node) return node;
}
}
return null;
}
function selection_size() {
var size = 0;
this.each(function() { ++size; });
return size;
}
function selection_empty() {
return !this.node();
}
function selection_each(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) callback.call(node, node.__data__, i, group);
}
}
return this;
}
function attrRemove(name) {
return function() {
this.removeAttribute(name);
};
}
function attrRemoveNS(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant(name, value) {
return function() {
this.setAttribute(name, value);
};
}
function attrConstantNS(fullname, value) {
return function() {
this.setAttributeNS(fullname.space, fullname.local, value);
};
}
function attrFunction(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttribute(name);
else this.setAttribute(name, v);
};
}
function attrFunctionNS(fullname, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
else this.setAttributeNS(fullname.space, fullname.local, v);
};
}
function selection_attr(name, value) {
var fullname = namespace(name);
if (arguments.length < 2) {
var node = this.node();
return fullname.local
? node.getAttributeNS(fullname.space, fullname.local)
: node.getAttribute(fullname);
}
return this.each((value == null
? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
? (fullname.local ? attrFunctionNS : attrFunction)
: (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
}
function defaultView(node) {
return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
|| (node.document && node) // node is a Window
|| node.defaultView; // node is a Document
}
function styleRemove(name) {
return function() {
this.style.removeProperty(name);
};
}
function styleConstant(name, value, priority) {
return function() {
this.style.setProperty(name, value, priority);
};
}
function styleFunction(name, value, priority) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.style.removeProperty(name);
else this.style.setProperty(name, v, priority);
};
}
function selection_style(name, value, priority) {
return arguments.length > 1
? this.each((value == null
? styleRemove : typeof value === "function"
? styleFunction
: styleConstant)(name, value, priority == null ? "" : priority))
: styleValue(this.node(), name);
}
function styleValue(node, name) {
return node.style.getPropertyValue(name)
|| defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
}
function propertyRemove(name) {
return function() {
delete this[name];
};
}
function propertyConstant(name, value) {
return function() {
this[name] = value;
};
}
function propertyFunction(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) delete this[name];
else this[name] = v;
};
}
function selection_property(name, value) {
return arguments.length > 1
? this.each((value == null
? propertyRemove : typeof value === "function"
? propertyFunction
: propertyConstant)(name, value))
: this.node()[name];
}
function classArray(string) {
return string.trim().split(/^|\s+/);
}
function classList(node) {
return node.classList || new ClassList(node);
}
function ClassList(node) {
this._node = node;
this._names = classArray(node.getAttribute("class") || "");
}
ClassList.prototype = {
add: function(name) {
var i = this._names.indexOf(name);
if (i < 0) {
this._names.push(name);
this._node.setAttribute("class", this._names.join(" "));
}
},
remove: function(name) {
var i = this._names.indexOf(name);
if (i >= 0) {
this._names.splice(i, 1);
this._node.setAttribute("class", this._names.join(" "));
}
},
contains: function(name) {
return this._names.indexOf(name) >= 0;
}
};
function classedAdd(node, names) {
var list = classList(node), i = -1, n = names.length;
while (++i < n) list.add(names[i]);
}
function classedRemove(node, names) {
var list = classList(node), i = -1, n = names.length;
while (++i < n) list.remove(names[i]);
}
function classedTrue(names) {
return function() {
classedAdd(this, names);
};
}
function classedFalse(names) {
return function() {
classedRemove(this, names);
};
}
function classedFunction(names, value) {
return function() {
(value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
};
}
function selection_classed(name, value) {
var names = classArray(name + "");
if (arguments.length < 2) {
var list = classList(this.node()), i = -1, n = names.length;
while (++i < n) if (!list.contains(names[i])) return false;
return true;
}
return this.each((typeof value === "function"
? classedFunction : value
? classedTrue
: classedFalse)(names, value));
}
function textRemove() {
this.textContent = "";
}
function textConstant(value) {
return function() {
this.textContent = value;
};
}
function textFunction(value) {
return function() {
var v = value.apply(this, arguments);
this.textContent = v == null ? "" : v;
};
}
function selection_text(value) {
return arguments.length
? this.each(value == null
? textRemove : (typeof value === "function"
? textFunction
: textConstant)(value))
: this.node().textContent;
}
function htmlRemove() {
this.innerHTML = "";
}
function htmlConstant(value) {
return function() {
this.innerHTML = value;
};
}
function htmlFunction(value) {
return function() {
var v = value.apply(this, arguments);
this.innerHTML = v == null ? "" : v;
};
}
function selection_html(value) {
return arguments.length
? this.each(value == null
? htmlRemove : (typeof value === "function"
? htmlFunction
: htmlConstant)(value))
: this.node().innerHTML;
}
function raise() {
if (this.nextSibling) this.parentNode.appendChild(this);
}
function selection_raise() {
return this.each(raise);
}
function lower() {
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
function selection_lower() {
return this.each(lower);
}
function selection_append(name) {
var create = typeof name === "function" ? name : creator(name);
return this.select(function() {
return this.appendChild(create.apply(this, arguments));
});
}
function constantNull() {
return null;
}
function selection_insert(name, before) {
var create = typeof name === "function" ? name : creator(name),
select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
return this.select(function() {
return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
});
}
function remove() {
var parent = this.parentNode;
if (parent) parent.removeChild(this);
}
function selection_remove() {
return this.each(remove);
}
function selection_cloneShallow() {
return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);
}
function selection_cloneDeep() {
return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);
}
function selection_clone(deep) {
return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
}
function selection_datum(value) {
return arguments.length
? this.property("__data__", value)
: this.node().__data__;
}
var filterEvents = {};
exports.event = null;
if (typeof document !== "undefined") {
var element$1 = document.documentElement;
if (!("onmouseenter" in element$1)) {
filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
}
}
function filterContextListener(listener, index, group) {
listener = contextListener(listener, index, group);
return function(event) {
var related = event.relatedTarget;
if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
listener.call(this, event);
}
};
}
function contextListener(listener, index, group) {
return function(event1) {
var event0 = exports.event; // Events can be reentrant (e.g., focus).
exports.event = event1;
try {
listener.call(this, this.__data__, index, group);
} finally {
exports.event = event0;
}
};
}
function parseTypenames$1(typenames) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name = "", i = t.indexOf(".");
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
return {type: t, name: name};
});
}
function onRemove(typename) {
return function() {
var on = this.__on;
if (!on) return;
for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.capture);
} else {
on[++i] = o;
}
}
if (++i) on.length = i;
else delete this.__on;
};
}
function onAdd(typename, value, capture) {
var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
return function(d, i, group) {
var on = this.__on, o, listener = wrap(value, i, group);
if (on) for (var j = 0, m = on.length; j < m; ++j) {
if ((o = on[j]).type === typename.type && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.capture);
this.addEventListener(o.type, o.listener = listener, o.capture = capture);
o.value = value;
return;
}
}
this.addEventListener(typename.type, listener, capture);
o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
if (!on) this.__on = [o];
else on.push(o);
};
}
function selection_on(typename, value, capture) {
var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t;
if (arguments.length < 2) {
var on = this.node().__on;
if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
for (i = 0, o = on[j]; i < n; ++i) {
if ((t = typenames[i]).type === o.type && t.name === o.name) {
return o.value;
}
}
}
return;
}
on = value ? onAdd : onRemove;
if (capture == null) capture = false;
for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
return this;
}
function customEvent(event1, listener, that, args) {
var event0 = exports.event;
event1.sourceEvent = exports.event;
exports.event = event1;
try {
return listener.apply(that, args);
} finally {
exports.event = event0;
}
}
function dispatchEvent(node, type, params) {
var window = defaultView(node),
event = window.CustomEvent;
if (typeof event === "function") {
event = new event(type, params);
} else {
event = window.document.createEvent("Event");
if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
else event.initEvent(type, false, false);
}
node.dispatchEvent(event);
}
function dispatchConstant(type, params) {
return function() {
return dispatchEvent(this, type, params);
};
}
function dispatchFunction(type, params) {
return function() {
return dispatchEvent(this, type, params.apply(this, arguments));
};
}
function selection_dispatch(type, params) {
return this.each((typeof params === "function"
? dispatchFunction
: dispatchConstant)(type, params));
}
var root = [null];
function Selection(groups, parents) {
this._groups = groups;
this._parents = parents;
}
function selection() {
return new Selection([[document.documentElement]], root);
}
Selection.prototype = selection.prototype = {
constructor: Selection,
select: selection_select,
selectAll: selection_selectAll,
filter: selection_filter,
data: selection_data,
enter: selection_enter,
exit: selection_exit,
merge: selection_merge,
order: selection_order,
sort: selection_sort,
call: selection_call,
nodes: selection_nodes,
node: selection_node,
size: selection_size,
empty: selection_empty,
each: selection_each,
attr: selection_attr,
style: selection_style,
property: selection_property,
classed: selection_classed,
text: selection_text,
html: selection_html,
raise: selection_raise,
lower: selection_lower,
append: selection_append,
insert: selection_insert,
remove: selection_remove,
clone: selection_clone,
datum: selection_datum,
on: selection_on,
dispatch: selection_dispatch
};
function select(selector) {
return typeof selector === "string"
? new Selection([[document.querySelector(selector)]], [document.documentElement])
: new Selection([[selector]], root);
}
function create(name) {
return select(creator(name).call(document.documentElement));
}
var nextId = 0;
function local() {
return new Local;
}
function Local() {
this._ = "@" + (++nextId).toString(36);
}
Local.prototype = local.prototype = {
constructor: Local,
get: function(node) {
var id = this._;
while (!(id in node)) if (!(node = node.parentNode)) return;
return node[id];
},
set: function(node, value) {
return node[this._] = value;
},
remove: function(node) {
return this._ in node && delete node[this._];
},
toString: function() {
return this._;
}
};
function sourceEvent() {
var current = exports.event, source;
while (source = current.sourceEvent) current = source;
return current;
}
function point(node, event) {
var svg = node.ownerSVGElement || node;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
point.x = event.clientX, point.y = event.clientY;
point = point.matrixTransform(node.getScreenCTM().inverse());
return [point.x, point.y];
}
var rect = node.getBoundingClientRect();
return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
}
function mouse(node) {
var event = sourceEvent();
if (event.changedTouches) event = event.changedTouches[0];
return point(node, event);
}
function selectAll(selector) {
return typeof selector === "string"
? new Selection([document.querySelectorAll(selector)], [document.documentElement])
: new Selection([selector == null ? [] : selector], root);
}
function touch(node, touches, identifier) {
if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;
for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
if ((touch = touches[i]).identifier === identifier) {
return point(node, touch);
}
}
return null;
}
function touches(node, touches) {
if (touches == null) touches = sourceEvent().touches;
for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {
points[i] = point(node, touches[i]);
}
return points;
}
function nopropagation() {
exports.event.stopImmediatePropagation();
}
function noevent() {
exports.event.preventDefault();
exports.event.stopImmediatePropagation();
}
function dragDisable(view) {
var root = view.document.documentElement,
selection$$1 = select(view).on("dragstart.drag", noevent, true);
if ("onselectstart" in root) {
selection$$1.on("selectstart.drag", noevent, true);
} else {
root.__noselect = root.style.MozUserSelect;
root.style.MozUserSelect = "none";
}
}
function yesdrag(view, noclick) {
var root = view.document.documentElement,
selection$$1 = select(view).on("dragstart.drag", null);
if (noclick) {
selection$$1.on("click.drag", noevent, true);
setTimeout(function() { selection$$1.on("click.drag", null); }, 0);
}
if ("onselectstart" in root) {
selection$$1.on("selectstart.drag", null);
} else {
root.style.MozUserSelect = root.__noselect;
delete root.__noselect;
}
}
function constant$2(x) {
return function() {
return x;
};
}
function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
this.target = target;
this.type = type;
this.subject = subject;
this.identifier = id;
this.active = active;
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this._ = dispatch;
}
DragEvent.prototype.on = function() {
var value = this._.on.apply(this._, arguments);
return value === this._ ? this : value;
};
// Ignore right-click, since that should open the context menu.
function defaultFilter() {
return !exports.event.button;
}
function defaultContainer() {
return this.parentNode;
}
function defaultSubject(d) {
return d == null ? {x: exports.event.x, y: exports.event.y} : d;
}
function defaultTouchable() {
return "ontouchstart" in this;
}
function drag() {
var filter = defaultFilter,
container = defaultContainer,
subject = defaultSubject,
touchable = defaultTouchable,
gestures = {},
listeners = dispatch("start", "drag", "end"),
active = 0,
mousedownx,
mousedowny,
mousemoving,
touchending,
clickDistance2 = 0;
function drag(selection$$1) {
selection$$1
.on("mousedown.drag", mousedowned)
.filter(touchable)
.on("touchstart.drag", touchstarted)
.on("touchmove.drag", touchmoved)
.on("touchend.drag touchcancel.drag", touchended)
.style("touch-action", "none")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}
function mousedowned() {
if (touchending || !filter.apply(this, arguments)) return;
var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments);
if (!gesture) return;
select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
dragDisable(exports.event.view);
nopropagation();
mousemoving = false;
mousedownx = exports.event.clientX;
mousedowny = exports.event.clientY;
gesture("start");
}
function mousemoved() {
noevent();
if (!mousemoving) {
var dx = exports.event.clientX - mousedownx, dy = exports.event.clientY - mousedowny;
mousemoving = dx * dx + dy * dy > clickDistance2;
}
gestures.mouse("drag");
}
function mouseupped() {
select(exports.event.view).on("mousemove.drag mouseup.drag", null);
yesdrag(exports.event.view, mousemoving);
noevent();
gestures.mouse("end");
}
function touchstarted() {
if (!filter.apply(this, arguments)) return;
var touches$$1 = exports.event.changedTouches,
c = container.apply(this, arguments),
n = touches$$1.length, i, gesture;
for (i = 0; i < n; ++i) {
if (gesture = beforestart(touches$$1[i].identifier, c, touch, this, arguments)) {
nopropagation();
gesture("start");
}
}
}
function touchmoved() {
var touches$$1 = exports.event.changedTouches,
n = touches$$1.length, i, gesture;
for (i = 0; i < n; ++i) {
if (gesture = gestures[touches$$1[i].identifier]) {
noevent();
gesture("drag");
}
}
}
function touchended() {
var touches$$1 = exports.event.changedTouches,
n = touches$$1.length, i, gesture;
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
for (i = 0; i < n; ++i) {
if (gesture = gestures[touches$$1[i].identifier]) {
nopropagation();
gesture("end");
}
}
}
function beforestart(id, container, point$$1, that, args) {
var p = point$$1(container, id), s, dx, dy,
sublisteners = listeners.copy();
if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {
if ((exports.event.subject = s = subject.apply(that, args)) == null) return false;
dx = s.x - p[0] || 0;
dy = s.y - p[1] || 0;
return true;
})) return;
return function gesture(type) {
var p0 = p, n;
switch (type) {
case "start": gestures[id] = gesture, n = active++; break;
case "end": delete gestures[id], --active; // nobreak
case "drag": p = point$$1(container, id), n = active; break;
}
customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);
};
}
drag.filter = function(_) {
return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter;
};
drag.container = function(_) {
return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container;
};
drag.subject = function(_) {
return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject;
};
drag.touchable = function(_) {
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable;
};
drag.on = function() {
var value = listeners.on.apply(listeners, arguments);
return value === listeners ? drag : value;
};
drag.clickDistance = function(_) {
return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
};
return drag;
}
function define(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
}
function extend(parent, definition) {
var prototype = Object.create(parent.prototype);
for (var key in definition) prototype[key] = definition[key];
return prototype;
}
function Color() {}
var darker = 0.7;
var brighter = 1 / darker;
var reI = "\\s*([+-]?\\d+)\\s*",
reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
reHex3 = /^#([0-9a-f]{3})$/,
reHex6 = /^#([0-9a-f]{6})$/,
reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
var named = {
aliceblue: 0xf0f8ff,
antiquewhite: 0xfaebd7,
aqua: 0x00ffff,
aquamarine: 0x7fffd4,
azure: 0xf0ffff,
beige: 0xf5f5dc,
bisque: 0xffe4c4,
black: 0x000000,
blanchedalmond: 0xffebcd,
blue: 0x0000ff,
blueviolet: 0x8a2be2,
brown: 0xa52a2a,
burlywood: 0xdeb887,
cadetblue: 0x5f9ea0,
chartreuse: 0x7fff00,
chocolate: 0xd2691e,
coral: 0xff7f50,
cornflowerblue: 0x6495ed,
cornsilk: 0xfff8dc,
crimson: 0xdc143c,
cyan: 0x00ffff,
darkblue: 0x00008b,
darkcyan: 0x008b8b,
darkgoldenrod: 0xb8860b,
darkgray: 0xa9a9a9,
darkgreen: 0x006400,
darkgrey: 0xa9a9a9,
darkkhaki: 0xbdb76b,
darkmagenta: 0x8b008b,
darkolivegreen: 0x556b2f,
darkorange: 0xff8c00,
darkorchid: 0x9932cc,
darkred: 0x8b0000,
darksalmon: 0xe9967a,
darkseagreen: 0x8fbc8f,
darkslateblue: 0x483d8b,
darkslategray: 0x2f4f4f,
darkslategrey: 0x2f4f4f,
darkturquoise: 0x00ced1,
darkviolet: 0x9400d3,
deeppink: 0xff1493,
deepskyblue: 0x00bfff,
dimgray: 0x696969,
dimgrey: 0x696969,
dodgerblue: 0x1e90ff,
firebrick: 0xb22222,
floralwhite: 0xfffaf0,
forestgreen: 0x228b22,
fuchsia: 0xff00ff,
gainsboro: 0xdcdcdc,
ghostwhite: 0xf8f8ff,
gold: 0xffd700,
goldenrod: 0xdaa520,
gray: 0x808080,
green: 0x008000,
greenyellow: 0xadff2f,
grey: 0x808080,
honeydew: 0xf0fff0,
hotpink: 0xff69b4,
indianred: 0xcd5c5c,
indigo: 0x4b0082,
ivory: 0xfffff0,
khaki: 0xf0e68c,
lavender: 0xe6e6fa,
lavenderblush: 0xfff0f5,
lawngreen: 0x7cfc00,
lemonchiffon: 0xfffacd,
lightblue: 0xadd8e6,
lightcoral: 0xf08080,
lightcyan: 0xe0ffff,
lightgoldenrodyellow: 0xfafad2,
lightgray: 0xd3d3d3,
lightgreen: 0x90ee90,
lightgrey: 0xd3d3d3,
lightpink: 0xffb6c1,
lightsalmon: 0xffa07a,
lightseagreen: 0x20b2aa,
lightskyblue: 0x87cefa,
lightslategray: 0x778899,
lightslategrey: 0x778899,
lightsteelblue: 0xb0c4de,
lightyellow: 0xffffe0,
lime: 0x00ff00,
limegreen: 0x32cd32,
linen: 0xfaf0e6,
magenta: 0xff00ff,
maroon: 0x800000,
mediumaquamarine: 0x66cdaa,
mediumblue: 0x0000cd,
mediumorchid: 0xba55d3,
mediumpurple: 0x9370db,
mediumseagreen: 0x3cb371,
mediumslateblue: 0x7b68ee,
mediumspringgreen: 0x00fa9a,
mediumturquoise: 0x48d1cc,
mediumvioletred: 0xc71585,
midnightblue: 0x191970,
mintcream: 0xf5fffa,
mistyrose: 0xffe4e1,
moccasin: 0xffe4b5,
navajowhite: 0xffdead,
navy: 0x000080,
oldlace: 0xfdf5e6,
olive: 0x808000,
olivedrab: 0x6b8e23,
orange: 0xffa500,
orangered: 0xff4500,
orchid: 0xda70d6,
palegoldenrod: 0xeee8aa,
palegreen: 0x98fb98,
paleturquoise: 0xafeeee,
palevioletred: 0xdb7093,
papayawhip: 0xffefd5,
peachpuff: 0xffdab9,
peru: 0xcd853f,
pink: 0xffc0cb,
plum: 0xdda0dd,
powderblue: 0xb0e0e6,
purple: 0x800080,
rebeccapurple: 0x663399,
red: 0xff0000,
rosybrown: 0xbc8f8f,
royalblue: 0x4169e1,
saddlebrown: 0x8b4513,
salmon: 0xfa8072,
sandybrown: 0xf4a460,
seagreen: 0x2e8b57,
seashell: 0xfff5ee,
sienna: 0xa0522d,
silver: 0xc0c0c0,
skyblue: 0x87ceeb,
slateblue: 0x6a5acd,
slategray: 0x708090,
slategrey: 0x708090,
snow: 0xfffafa,
springgreen: 0x00ff7f,
steelblue: 0x4682b4,
tan: 0xd2b48c,
teal: 0x008080,
thistle: 0xd8bfd8,
tomato: 0xff6347,
turquoise: 0x40e0d0,
violet: 0xee82ee,
wheat: 0xf5deb3,
white: 0xffffff,
whitesmoke: 0xf5f5f5,
yellow: 0xffff00,
yellowgreen: 0x9acd32
};
define(Color, color, {
displayable: function() {
return this.rgb().displayable();
},
hex: function() {
return this.rgb().hex();
},
toString: function() {
return this.rgb() + "";
}
});
function color(format) {
var m;
format = (format + "").trim().toLowerCase();
return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00
: (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
: (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
: (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
: (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
: (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
: (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
: (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
: named.hasOwnProperty(format) ? rgbn(named[format])
: format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
: null;
}
function rgbn(n) {
return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
}
function rgba(r, g, b, a) {
if (a <= 0) r = g = b = NaN;
return new Rgb(r, g, b, a);
}
function rgbConvert(o) {
if (!(o instanceof Color)) o = color(o);
if (!o) return new Rgb;
o = o.rgb();
return new Rgb(o.r, o.g, o.b, o.opacity);
}
function rgb(r, g, b, opacity) {
return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
}
function Rgb(r, g, b, opacity) {
this.r = +r;
this.g = +g;
this.b = +b;
this.opacity = +opacity;
}
define(Rgb, rgb, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
rgb: function() {
return this;
},
displayable: function() {
return (0 <= this.r && this.r <= 255)
&& (0 <= this.g && this.g <= 255)
&& (0 <= this.b && this.b <= 255)
&& (0 <= this.opacity && this.opacity <= 1);
},
hex: function() {
return "#" + hex(this.r) + hex(this.g) + hex(this.b);
},
toString: function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
}
}));
function hex(value) {
value = Math.max(0, Math.min(255, Math.round(value) || 0));
return (value < 16 ? "0" : "") + value.toString(16);
}
function hsla(h, s, l, a) {
if (a <= 0) h = s = l = NaN;
else if (l <= 0 || l >= 1) h = s = NaN;
else if (s <= 0) h = NaN;
return new Hsl(h, s, l, a);
}
function hslConvert(o) {
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Color)) o = color(o);
if (!o) return new Hsl;
if (o instanceof Hsl) return o;
o = o.rgb();
var r = o.r / 255,
g = o.g / 255,
b = o.b / 255,
min = Math.min(r, g, b),
max = Math.max(r, g, b),
h = NaN,
s = max - min,
l = (max + min) / 2;
if (s) {
if (r === max) h = (g - b) / s + (g < b) * 6;
else if (g === max) h = (b - r) / s + 2;
else h = (r - g) / s + 4;
s /= l < 0.5 ? max + min : 2 - max - min;
h *= 60;
} else {
s = l > 0 && l < 1 ? 0 : h;
}
return new Hsl(h, s, l, o.opacity);
}
function hsl(h, s, l, opacity) {
return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
}
function Hsl(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define(Hsl, hsl, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = this.h % 360 + (this.h < 0) * 360,
s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
l = this.l,
m2 = l + (l < 0.5 ? l : 1 - l) * s,
m1 = 2 * l - m2;
return new Rgb(
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
hsl2rgb(h, m1, m2),
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
this.opacity
);
},
displayable: function() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s))
&& (0 <= this.l && this.l <= 1)
&& (0 <= this.opacity && this.opacity <= 1);
}
}));
/* From FvD 13.37, CSS Color Module Level 3 */
function hsl2rgb(h, m1, m2) {
return (h < 60 ? m1 + (m2 - m1) * h / 60
: h < 180 ? m2
: h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
: m1) * 255;
}
var deg2rad = Math.PI / 180;
var rad2deg = 180 / Math.PI;
// https://beta.observablehq.com/@mbostock/lab-and-rgb
var K = 18,
Xn = 0.96422,
Yn = 1,
Zn = 0.82521,
t0 = 4 / 29,
t1 = 6 / 29,
t2 = 3 * t1 * t1,
t3 = t1 * t1 * t1;
function labConvert(o) {
if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
if (o instanceof Hcl) {
if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
var h = o.h * deg2rad;
return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
}
if (!(o instanceof Rgb)) o = rgbConvert(o);
var r = rgb2lrgb(o.r),
g = rgb2lrgb(o.g),
b = rgb2lrgb(o.b),
y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;
if (r === g && g === b) x = z = y; else {
x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
}
return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
}
function gray(l, opacity) {
return new Lab(l, 0, 0, opacity == null ? 1 : opacity);
}
function lab(l, a, b, opacity) {
return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
}
function Lab(l, a, b, opacity) {
this.l = +l;
this.a = +a;
this.b = +b;
this.opacity = +opacity;
}
define(Lab, lab, extend(Color, {
brighter: function(k) {
return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
darker: function(k) {
return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
rgb: function() {
var y = (this.l + 16) / 116,
x = isNaN(this.a) ? y : y + this.a / 500,
z = isNaN(this.b) ? y : y - this.b / 200;
x = Xn * lab2xyz(x);
y = Yn * lab2xyz(y);
z = Zn * lab2xyz(z);
return new Rgb(
lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),
lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),
lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),
this.opacity
);
}
}));
function xyz2lab(t) {
return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
}
function lab2xyz(t) {
return t > t1 ? t * t * t : t2 * (t - t0);
}
function lrgb2rgb(x) {
return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
}
function rgb2lrgb(x) {
return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}
function hclConvert(o) {
if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
if (!(o instanceof Lab)) o = labConvert(o);
if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);
var h = Math.atan2(o.b, o.a) * rad2deg;
return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
}
function lch(l, c, h, opacity) {
return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
}
function hcl(h, c, l, opacity) {
return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
}
function Hcl(h, c, l, opacity) {
this.h = +h;
this.c = +c;
this.l = +l;
this.opacity = +opacity;
}
define(Hcl, hcl, extend(Color, {
brighter: function(k) {
return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
},
darker: function(k) {
return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
},
rgb: function() {
return labConvert(this).rgb();
}
}));
var A = -0.14861,
B = +1.78277,
C = -0.29227,
D = -0.90649,
E = +1.97294,
ED = E * D,
EB = E * B,
BC_DA = B * C - D * A;
function cubehelixConvert(o) {
if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Rgb)) o = rgbConvert(o);
var r = o.r / 255,
g = o.g / 255,
b = o.b / 255,
l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
bl = b - l,
k = (E * (g - l) - C * bl) / D,
s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
}
function cubehelix(h, s, l, opacity) {
return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
}
function Cubehelix(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define(Cubehelix, cubehelix, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
l = +this.l,
a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
cosh = Math.cos(h),
sinh = Math.sin(h);
return new Rgb(
255 * (l + a * (A * cosh + B * sinh)),
255 * (l + a * (C * cosh + D * sinh)),
255 * (l + a * (E * cosh)),
this.opacity
);
}
}));
function basis(t1, v0, v1, v2, v3) {
var t2 = t1 * t1, t3 = t2 * t1;
return ((1 - 3 * t1 + 3 * t2 - t3) * v0
+ (4 - 6 * t2 + 3 * t3) * v1
+ (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2
+ t3 * v3) / 6;
}
function basis$1(values) {
var n = values.length - 1;
return function(t) {
var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),
v1 = values[i],
v2 = values[i + 1],
v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
return basis((t - i / n) * n, v0, v1, v2, v3);
};
}
function basisClosed(values) {
var n = values.length;
return function(t) {
var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),
v0 = values[(i + n - 1) % n],
v1 = values[i % n],
v2 = values[(i + 1) % n],
v3 = values[(i + 2) % n];
return basis((t - i / n) * n, v0, v1, v2, v3);
};
}
function constant$3(x) {
return function() {
return x;
};
}
function linear(a, d) {
return function(t) {
return a + t * d;
};
}
function exponential(a, b, y) {
return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
return Math.pow(a + t * b, y);
};
}
function hue(a, b) {
var d = b - a;
return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a);
}
function gamma(y) {
return (y = +y) === 1 ? nogamma : function(a, b) {
return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a);
};
}
function nogamma(a, b) {
var d = b - a;
return d ? linear(a, d) : constant$3(isNaN(a) ? b : a);
}
var interpolateRgb = (function rgbGamma(y) {
var color$$1 = gamma(y);
function rgb$$1(start, end) {
var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r),
g = color$$1(start.g, end.g),
b = color$$1(start.b, end.b),
opacity = nogamma(start.opacity, end.opacity);
return function(t) {
start.r = r(t);
start.g = g(t);
start.b = b(t);
start.opacity = opacity(t);
return start + "";
};
}
rgb$$1.gamma = rgbGamma;
return rgb$$1;
})(1);
function rgbSpline(spline) {
return function(colors) {
var n = colors.length,
r = new Array(n),
g = new Array(n),
b = new Array(n),
i, color$$1;
for (i = 0; i < n; ++i) {
color$$1 = rgb(colors[i]);
r[i] = color$$1.r || 0;
g[i] = color$$1.g || 0;
b[i] = color$$1.b || 0;
}
r = spline(r);
g = spline(g);
b = spline(b);
color$$1.opacity = 1;
return function(t) {
color$$1.r = r(t);
color$$1.g = g(t);
color$$1.b = b(t);
return color$$1 + "";
};
};
}
var rgbBasis = rgbSpline(basis$1);
var rgbBasisClosed = rgbSpline(basisClosed);
function array$1(a, b) {
var nb = b ? b.length : 0,
na = a ? Math.min(nb, a.length) : 0,
x = new Array(na),
c = new Array(nb),
i;
for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
for (; i < nb; ++i) c[i] = b[i];
return function(t) {
for (i = 0; i < na; ++i) c[i] = x[i](t);
return c;
};
}
function date(a, b) {
var d = new Date;
return a = +a, b -= a, function(t) {
return d.setTime(a + b * t), d;
};
}
function reinterpolate(a, b) {
return a = +a, b -= a, function(t) {
return a + b * t;
};
}
function object(a, b) {
var i = {},
c = {},
k;
if (a === null || typeof a !== "object") a = {};
if (b === null || typeof b !== "object") b = {};
for (k in b) {
if (k in a) {
i[k] = interpolateValue(a[k], b[k]);
} else {
c[k] = b[k];
}
}
return function(t) {
for (k in i) c[k] = i[k](t);
return c;
};
}
var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
reB = new RegExp(reA.source, "g");
function zero(b) {
return function() {
return b;
};
}
function one(b) {
return function(t) {
return b(t) + "";
};
}
function interpolateString(a, b) {
var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
am, // current match in a
bm, // current match in b
bs, // string preceding current number in b, if any
i = -1, // index in s
s = [], // string constants and placeholders
q = []; // number interpolators
// Coerce inputs to strings.
a = a + "", b = b + "";
// Interpolate pairs of numbers in a & b.
while ((am = reA.exec(a))
&& (bm = reB.exec(b))) {
if ((bs = bm.index) > bi) { // a string precedes the next number in b
bs = b.slice(bi, bs);
if (s[i]) s[i] += bs; // coalesce with previous string
else s[++i] = bs;
}
if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
if (s[i]) s[i] += bm; // coalesce with previous string
else s[++i] = bm;
} else { // interpolate non-matching numbers
s[++i] = null;
q.push({i: i, x: reinterpolate(am, bm)});
}
bi = reB.lastIndex;
}
// Add remains of b.
if (bi < b.length) {
bs = b.slice(bi);
if (s[i]) s[i] += bs; // coalesce with previous string
else s[++i] = bs;
}
// Special optimization for only a single match.
// Otherwise, interpolate each of the numbers and rejoin the string.
return s.length < 2 ? (q[0]
? one(q[0].x)
: zero(b))
: (b = q.length, function(t) {
for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
return s.join("");
});
}
function interpolateValue(a, b) {
var t = typeof b, c;
return b == null || t === "boolean" ? constant$3(b)
: (t === "number" ? reinterpolate
: t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString)
: b instanceof color ? interpolateRgb
: b instanceof Date ? date
: Array.isArray(b) ? array$1
: typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
: reinterpolate)(a, b);
}
function interpolateRound(a, b) {
return a = +a, b -= a, function(t) {
return Math.round(a + b * t);
};
}
var degrees = 180 / Math.PI;
var identity$2 = {
translateX: 0,
translateY: 0,
rotate: 0,
skewX: 0,
scaleX: 1,
scaleY: 1
};
function decompose(a, b, c, d, e, f) {
var scaleX, scaleY, skewX;
if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
return {
translateX: e,
translateY: f,
rotate: Math.atan2(b, a) * degrees,
skewX: Math.atan(skewX) * degrees,
scaleX: scaleX,
scaleY: scaleY
};
}
var cssNode,
cssRoot,
cssView,
svgNode;
function parseCss(value) {
if (value === "none") return identity$2;
if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
cssNode.style.transform = value;
value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
cssRoot.removeChild(cssNode);
value = value.slice(7, -1).split(",");
return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
}
function parseSvg(value) {
if (value == null) return identity$2;
if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
svgNode.setAttribute("transform", value);
if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2;
value = value.matrix;
return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
}
function interpolateTransform(parse, pxComma, pxParen, degParen) {
function pop(s) {
return s.length ? s.pop() + " " : "";
}
function translate(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i = s.push("translate(", null, pxComma, null, pxParen);
q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
} else if (xb || yb) {
s.push("translate(" + xb + pxComma + yb + pxParen);
}
}
function rotate(a, b, s, q) {
if (a !== b) {
if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: reinterpolate(a, b)});
} else if (b) {
s.push(pop(s) + "rotate(" + b + degParen);
}
}
function skewX(a, b, s, q) {
if (a !== b) {
q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: reinterpolate(a, b)});
} else if (b) {
s.push(pop(s) + "skewX(" + b + degParen);
}
}
function scale(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i = s.push(pop(s) + "scale(", null, ",", null, ")");
q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
} else if (xb !== 1 || yb !== 1) {
s.push(pop(s) + "scale(" + xb + "," + yb + ")");
}
}
return function(a, b) {
var s = [], // string constants and placeholders
q = []; // number interpolators
a = parse(a), b = parse(b);
translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
rotate(a.rotate, b.rotate, s, q);
skewX(a.skewX, b.skewX, s, q);
scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
a = b = null; // gc
return function(t) {
var i = -1, n = q.length, o;
while (++i < n) s[(o = q[i]).i] = o.x(t);
return s.join("");
};
};
}
var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
var rho = Math.SQRT2,
rho2 = 2,
rho4 = 4,
epsilon2 = 1e-12;
function cosh(x) {
return ((x = Math.exp(x)) + 1 / x) / 2;
}
function sinh(x) {
return ((x = Math.exp(x)) - 1 / x) / 2;
}
function tanh(x) {
return ((x = Math.exp(2 * x)) - 1) / (x + 1);
}
// p0 = [ux0, uy0, w0]
// p1 = [ux1, uy1, w1]
function interpolateZoom(p0, p1) {
var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
dx = ux1 - ux0,
dy = uy1 - uy0,
d2 = dx * dx + dy * dy,
i,
S;
// Special case for u0 ≅ u1.
if (d2 < epsilon2) {
S = Math.log(w1 / w0) / rho;
i = function(t) {
return [
ux0 + t * dx,
uy0 + t * dy,
w0 * Math.exp(rho * t * S)
];
};
}
// General case.
else {
var d1 = Math.sqrt(d2),
b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
S = (r1 - r0) / rho;
i = function(t) {
var s = t * S,
coshr0 = cosh(r0),
u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
return [
ux0 + u * dx,
uy0 + u * dy,
w0 * coshr0 / cosh(rho * s + r0)
];
};
}
i.duration = S * 1000;
return i;
}
function hsl$1(hue$$1) {
return function(start, end) {
var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h),
s = nogamma(start.s, end.s),
l = nogamma(start.l, end.l),
opacity = nogamma(start.opacity, end.opacity);
return function(t) {
start.h = h(t);
start.s = s(t);
start.l = l(t);
start.opacity = opacity(t);
return start + "";
};
}
}
var hsl$2 = hsl$1(hue);
var hslLong = hsl$1(nogamma);
function lab$1(start, end) {
var l = nogamma((start = lab(start)).l, (end = lab(end)).l),
a = nogamma(start.a, end.a),
b = nogamma(start.b, end.b),
opacity = nogamma(start.opacity, end.opacity);
return function(t) {
start.l = l(t);
start.a = a(t);
start.b = b(t);
start.opacity = opacity(t);
return start + "";
};
}
function hcl$1(hue$$1) {
return function(start, end) {
var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h),
c = nogamma(start.c, end.c),
l = nogamma(start.l, end.l),
opacity = nogamma(start.opacity, end.opacity);
return function(t) {
start.h = h(t);
start.c = c(t);
start.l = l(t);
start.opacity = opacity(t);
return start + "";
};
}
}
var hcl$2 = hcl$1(hue);
var hclLong = hcl$1(nogamma);
function cubehelix$1(hue$$1) {
return (function cubehelixGamma(y) {
y = +y;
function cubehelix$$1(start, end) {
var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h),
s = nogamma(start.s, end.s),
l = nogamma(start.l, end.l),
opacity = nogamma(start.opacity, end.opacity);
return function(t) {
start.h = h(t);
start.s = s(t);
start.l = l(Math.pow(t, y));
start.opacity = opacity(t);
return start + "";
};
}
cubehelix$$1.gamma = cubehelixGamma;
return cubehelix$$1;
})(1);
}
var cubehelix$2 = cubehelix$1(hue);
var cubehelixLong = cubehelix$1(nogamma);
function piecewise(interpolate, values) {
var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);
while (i < n) I[i] = interpolate(v, v = values[++i]);
return function(t) {
var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));
return I[i](t - i);
};
}
function quantize(interpolator, n) {
var samples = new Array(n);
for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
return samples;
}
var frame = 0, // is an animation frame pending?
timeout = 0, // is a timeout pending?
interval = 0, // are any timers active?
pokeDelay = 1000, // how frequently we check for clock skew
taskHead,
taskTail,
clockLast = 0,
clockNow = 0,
clockSkew = 0,
clock = typeof performance === "object" && performance.now ? performance : Date,
setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };
function now() {
return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
}
function clearNow() {
clockNow = 0;
}
function Timer() {
this._call =
this._time =
this._next = null;
}
Timer.prototype = timer.prototype = {
constructor: Timer,
restart: function(callback, delay, time) {
if (typeof callback !== "function") throw new TypeError("callback is not a function");
time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
if (!this._next && taskTail !== this) {
if (taskTail) taskTail._next = this;
else taskHead = this;
taskTail = this;
}
this._call = callback;
this._time = time;
sleep();
},
stop: function() {
if (this._call) {
this._call = null;
this._time = Infinity;
sleep();
}
}
};
function timer(callback, delay, time) {
var t = new Timer;
t.restart(callback, delay, time);
return t;
}
function timerFlush() {
now(); // Get the current time, if not already set.
++frame; // Pretend we’ve set an alarm, if we haven’t already.
var t = taskHead, e;
while (t) {
if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
t = t._next;
}
--frame;
}
function wake() {
clockNow = (clockLast = clock.now()) + clockSkew;
frame = timeout = 0;
try {
timerFlush();
} finally {
frame = 0;
nap();
clockNow = 0;
}
}
function poke() {
var now = clock.now(), delay = now - clockLast;
if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
}
function nap() {
var t0, t1 = taskHead, t2, time = Infinity;
while (t1) {
if (t1._call) {
if (time > t1._time) time = t1._time;
t0 = t1, t1 = t1._next;
} else {
t2 = t1._next, t1._next = null;
t1 = t0 ? t0._next = t2 : taskHead = t2;
}
}
taskTail = t0;
sleep(time);
}
function sleep(time) {
if (frame) return; // Soonest alarm already set, or will be.
if (timeout) timeout = clearTimeout(timeout);
var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
if (delay > 24) {
if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
if (interval) interval = clearInterval(interval);
} else {
if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
frame = 1, setFrame(wake);
}
}
function timeout$1(callback, delay, time) {
var t = new Timer;
delay = delay == null ? 0 : +delay;
t.restart(function(elapsed) {
t.stop();
callback(elapsed + delay);
}, delay, time);
return t;
}
function interval$1(callback, delay, time) {
var t = new Timer, total = delay;
if (delay == null) return t.restart(callback, delay, time), t;
delay = +delay, time = time == null ? now() : +time;
t.restart(function tick(elapsed) {
elapsed += total;
t.restart(tick, total += delay, time);
callback(elapsed);
}, delay, time);
return t;
}
var emptyOn = dispatch("start", "end", "interrupt");
var emptyTween = [];
var CREATED = 0;
var SCHEDULED = 1;
var STARTING = 2;
var STARTED = 3;
var RUNNING = 4;
var ENDING = 5;
var ENDED = 6;
function schedule(node, name, id, index, group, timing) {
var schedules = node.__transition;
if (!schedules) node.__transition = {};
else if (id in schedules) return;
create$1(node, id, {
name: name,
index: index, // For context during callback.
group: group, // For context during callback.
on: emptyOn,
tween: emptyTween,
time: timing.time,
delay: timing.delay,
duration: timing.duration,
ease: timing.ease,
timer: null,
state: CREATED
});
}
function init(node, id) {
var schedule = get$1(node, id);
if (schedule.state > CREATED) throw new Error("too late; already scheduled");
return schedule;
}
function set$1(node, id) {
var schedule = get$1(node, id);
if (schedule.state > STARTING) throw new Error("too late; already started");
return schedule;
}
function get$1(node, id) {
var schedule = node.__transition;
if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
return schedule;
}
function create$1(node, id, self) {
var schedules = node.__transition,
tween;
// Initialize the self timer when the transition is created.
// Note the actual delay is not known until the first callback!
schedules[id] = self;
self.timer = timer(schedule, 0, self.time);
function schedule(elapsed) {
self.state = SCHEDULED;
self.timer.restart(start, self.delay, self.time);
// If the elapsed delay is less than our first sleep, start immediately.
if (self.delay <= elapsed) start(elapsed - self.delay);
}
function start(elapsed) {
var i, j, n, o;
// If the state is not SCHEDULED, then we previously errored on start.
if (self.state !== SCHEDULED) return stop();
for (i in schedules) {
o = schedules[i];
if (o.name !== self.name) continue;
// While this element already has a starting transition during this frame,
// defer starting an interrupting transition until that transition has a
// chance to tick (and possibly end); see d3/d3-transition#54!
if (o.state === STARTED) return timeout$1(start);
// Interrupt the active transition, if any.
// Dispatch the interrupt event.
if (o.state === RUNNING) {
o.state = ENDED;
o.timer.stop();
o.on.call("interrupt", node, node.__data__, o.index, o.group);
delete schedules[i];
}
// Cancel any pre-empted transitions. No interrupt event is dispatched
// because the cancelled transitions never started. Note that this also
// removes this transition from the pending list!
else if (+i < id) {
o.state = ENDED;
o.timer.stop();
delete schedules[i];
}
}
// Defer the first tick to end of the current frame; see d3/d3#1576.
// Note the transition may be canceled after start and before the first tick!
// Note this must be scheduled before the start event; see d3/d3-transition#16!
// Assuming this is successful, subsequent callbacks go straight to tick.
timeout$1(function() {
if (self.state === STARTED) {
self.state = RUNNING;
self.timer.restart(tick, self.delay, self.time);
tick(elapsed);
}
});
// Dispatch the start event.
// Note this must be done before the tween are initialized.
self.state = STARTING;
self.on.call("start", node, node.__data__, self.index, self.group);
if (self.state !== STARTING) return; // interrupted
self.state = STARTED;
// Initialize the tween, deleting null tween.
tween = new Array(n = self.tween.length);
for (i = 0, j = -1; i < n; ++i) {
if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
tween[++j] = o;
}
}
tween.length = j + 1;
}
function tick(elapsed) {
var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
i = -1,
n = tween.length;
while (++i < n) {
tween[i].call(null, t);
}
// Dispatch the end event.
if (self.state === ENDING) {
self.on.call("end", node, node.__data__, self.index, self.group);
stop();
}
}
function stop() {
self.state = ENDED;
self.timer.stop();
delete schedules[id];
for (var i in schedules) return; // eslint-disable-line no-unused-vars
delete node.__transition;
}
}
function interrupt(node, name) {
var schedules = node.__transition,
schedule$$1,
active,
empty = true,
i;
if (!schedules) return;
name = name == null ? null : name + "";
for (i in schedules) {
if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; }
active = schedule$$1.state > STARTING && schedule$$1.state < ENDING;
schedule$$1.state = ENDED;
schedule$$1.timer.stop();
if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group);
delete schedules[i];
}
if (empty) delete node.__transition;
}
function selection_interrupt(name) {
return this.each(function() {
interrupt(this, name);
});
}
function tweenRemove(id, name) {
var tween0, tween1;
return function() {
var schedule$$1 = set$1(this, id),
tween = schedule$$1.tween;
// If this node shared tween with the previous node,
// just assign the updated shared tween and we’re done!
// Otherwise, copy-on-write.
if (tween !== tween0) {
tween1 = tween0 = tween;
for (var i = 0, n = tween1.length; i < n; ++i) {
if (tween1[i].name === name) {
tween1 = tween1.slice();
tween1.splice(i, 1);
break;
}
}
}
schedule$$1.tween = tween1;
};
}
function tweenFunction(id, name, value) {
var tween0, tween1;
if (typeof value !== "function") throw new Error;
return function() {
var schedule$$1 = set$1(this, id),
tween = schedule$$1.tween;
// If this node shared tween with the previous node,
// just assign the updated shared tween and we’re done!
// Otherwise, copy-on-write.
if (tween !== tween0) {
tween1 = (tween0 = tween).slice();
for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
if (tween1[i].name === name) {
tween1[i] = t;
break;
}
}
if (i === n) tween1.push(t);
}
schedule$$1.tween = tween1;
};
}
function transition_tween(name, value) {
var id = this._id;
name += "";
if (arguments.length < 2) {
var tween = get$1(this.node(), id).tween;
for (var i = 0, n = tween.length, t; i < n; ++i) {
if ((t = tween[i]).name === name) {
return t.value;
}
}
return null;
}
return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
}
function tweenValue(transition, name, value) {
var id = transition._id;
transition.each(function() {
var schedule$$1 = set$1(this, id);
(schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments);
});
return function(node) {
return get$1(node, id).value[name];
};
}
function interpolate(a, b) {
var c;
return (typeof b === "number" ? reinterpolate
: b instanceof color ? interpolateRgb
: (c = color(b)) ? (b = c, interpolateRgb)
: interpolateString)(a, b);
}
function attrRemove$1(name) {
return function() {
this.removeAttribute(name);
};
}
function attrRemoveNS$1(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant$1(name, interpolate$$1, value1) {
var value00,
interpolate0;
return function() {
var value0 = this.getAttribute(name);
return value0 === value1 ? null
: value0 === value00 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value1);
};
}
function attrConstantNS$1(fullname, interpolate$$1, value1) {
var value00,
interpolate0;
return function() {
var value0 = this.getAttributeNS(fullname.space, fullname.local);
return value0 === value1 ? null
: value0 === value00 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value1);
};
}
function attrFunction$1(name, interpolate$$1, value) {
var value00,
value10,
interpolate0;
return function() {
var value0, value1 = value(this);
if (value1 == null) return void this.removeAttribute(name);
value0 = this.getAttribute(name);
return value0 === value1 ? null
: value0 === value00 && value1 === value10 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
};
}
function attrFunctionNS$1(fullname, interpolate$$1, value) {
var value00,
value10,
interpolate0;
return function() {
var value0, value1 = value(this);
if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
value0 = this.getAttributeNS(fullname.space, fullname.local);
return value0 === value1 ? null
: value0 === value00 && value1 === value10 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
};
}
function transition_attr(name, value) {
var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate;
return this.attrTween(name, typeof value === "function"
? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
: value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
: (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + ""));
}
function attrTweenNS(fullname, value) {
function tween() {
var node = this, i = value.apply(node, arguments);
return i && function(t) {
node.setAttributeNS(fullname.space, fullname.local, i(t));
};
}
tween._value = value;
return tween;
}
function attrTween(name, value) {
function tween() {
var node = this, i = value.apply(node, arguments);
return i && function(t) {
node.setAttribute(name, i(t));
};
}
tween._value = value;
return tween;
}
function transition_attrTween(name, value) {
var key = "attr." + name;
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error;
var fullname = namespace(name);
return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
}
function delayFunction(id, value) {
return function() {
init(this, id).delay = +value.apply(this, arguments);
};
}
function delayConstant(id, value) {
return value = +value, function() {
init(this, id).delay = value;
};
}
function transition_delay(value) {
var id = this._id;
return arguments.length
? this.each((typeof value === "function"
? delayFunction
: delayConstant)(id, value))
: get$1(this.node(), id).delay;
}
function durationFunction(id, value) {
return function() {
set$1(this, id).duration = +value.apply(this, arguments);
};
}
function durationConstant(id, value) {
return value = +value, function() {
set$1(this, id).duration = value;
};
}
function transition_duration(value) {
var id = this._id;
return arguments.length
? this.each((typeof value === "function"
? durationFunction
: durationConstant)(id, value))
: get$1(this.node(), id).duration;
}
function easeConstant(id, value) {
if (typeof value !== "function") throw new Error;
return function() {
set$1(this, id).ease = value;
};
}
function transition_ease(value) {
var id = this._id;
return arguments.length
? this.each(easeConstant(id, value))
: get$1(this.node(), id).ease;
}
function transition_filter(match) {
if (typeof match !== "function") match = matcher$1(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
subgroup.push(node);
}
}
}
return new Transition(subgroups, this._parents, this._name, this._id);
}
function transition_merge(transition$$1) {
if (transition$$1._id !== this._id) throw new Error;
for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group0[i] || group1[i]) {
merge[i] = node;
}
}
}
for (; j < m0; ++j) {
merges[j] = groups0[j];
}
return new Transition(merges, this._parents, this._name, this._id);
}
function start(name) {
return (name + "").trim().split(/^|\s+/).every(function(t) {
var i = t.indexOf(".");
if (i >= 0) t = t.slice(0, i);
return !t || t === "start";
});
}
function onFunction(id, name, listener) {
var on0, on1, sit = start(name) ? init : set$1;
return function() {
var schedule$$1 = sit(this, id),
on = schedule$$1.on;
// If this node shared a dispatch with the previous node,
// just assign the updated shared dispatch and we’re done!
// Otherwise, copy-on-write.
if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
schedule$$1.on = on1;
};
}
function transition_on(name, listener) {
var id = this._id;
return arguments.length < 2
? get$1(this.node(), id).on.on(name)
: this.each(onFunction(id, name, listener));
}
function removeFunction(id) {
return function() {
var parent = this.parentNode;
for (var i in this.__transition) if (+i !== id) return;
if (parent) parent.removeChild(this);
};
}
function transition_remove() {
return this.on("end.remove", removeFunction(this._id));
}
function transition_select(select$$1) {
var name = this._name,
id = this._id;
if (typeof select$$1 !== "function") select$$1 = selector(select$$1);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i] = subnode;
schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
}
}
}
return new Transition(subgroups, this._parents, name, id);
}
function transition_selectAll(select$$1) {
var name = this._name,
id = this._id;
if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
if (child = children[k]) {
schedule(child, name, id, k, children, inherit);
}
}
subgroups.push(children);
parents.push(node);
}
}
}
return new Transition(subgroups, parents, name, id);
}
var Selection$1 = selection.prototype.constructor;
function transition_selection() {
return new Selection$1(this._groups, this._parents);
}
function styleRemove$1(name, interpolate$$1) {
var value00,
value10,
interpolate0;
return function() {
var value0 = styleValue(this, name),
value1 = (this.style.removeProperty(name), styleValue(this, name));
return value0 === value1 ? null
: value0 === value00 && value1 === value10 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
};
}
function styleRemoveEnd(name) {
return function() {
this.style.removeProperty(name);
};
}
function styleConstant$1(name, interpolate$$1, value1) {
var value00,
interpolate0;
return function() {
var value0 = styleValue(this, name);
return value0 === value1 ? null
: value0 === value00 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value1);
};
}
function styleFunction$1(name, interpolate$$1, value) {
var value00,
value10,
interpolate0;
return function() {
var value0 = styleValue(this, name),
value1 = value(this);
if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name));
return value0 === value1 ? null
: value0 === value00 && value1 === value10 ? interpolate0
: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
};
}
function transition_style(name, value, priority) {
var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
return value == null ? this
.styleTween(name, styleRemove$1(name, i))
.on("end.style." + name, styleRemoveEnd(name))
: this.styleTween(name, typeof value === "function"
? styleFunction$1(name, i, tweenValue(this, "style." + name, value))
: styleConstant$1(name, i, value + ""), priority);
}
function styleTween(name, value, priority) {
function tween() {
var node = this, i = value.apply(node, arguments);
return i && function(t) {
node.style.setProperty(name, i(t), priority);
};
}
tween._value = value;
return tween;
}
function transition_styleTween(name, value, priority) {
var key = "style." + (name += "");
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error;
return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
}
function textConstant$1(value) {
return function() {
this.textContent = value;
};
}
function textFunction$1(value) {
return function() {
var value1 = value(this);
this.textContent = value1 == null ? "" : value1;
};
}
function transition_text(value) {
return this.tween("text", typeof value === "function"
? textFunction$1(tweenValue(this, "text", value))
: textConstant$1(value == null ? "" : value + ""));
}
function transition_transition() {
var name = this._name,
id0 = this._id,
id1 = newId();
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
var inherit = get$1(node, id0);
schedule(node, name, id1, i, group, {
time: inherit.time + inherit.delay + inherit.duration,
delay: 0,
duration: inherit.duration,
ease: inherit.ease
});
}
}
}
return new Transition(groups, this._parents, name, id1);
}
var id = 0;
function Transition(groups, parents, name, id) {
this._groups = groups;
this._parents = parents;
this._name = name;
this._id = id;
}
function transition(name) {
return selection().transition(name);
}
function newId() {
return ++id;
}
var selection_prototype = selection.prototype;
Transition.prototype = transition.prototype = {
constructor: Transition,
select: transition_select,
selectAll: transition_selectAll,
filter: transition_filter,
merge: transition_merge,
selection: transition_selection,
transition: transition_transition,
call: selection_prototype.call,
nodes: selection_prototype.nodes,
node: selection_prototype.node,
size: selection_prototype.size,
empty: selection_prototype.empty,
each: selection_prototype.each,
on: transition_on,
attr: transition_attr,
attrTween: transition_attrTween,
style: transition_style,
styleTween: transition_styleTween,
text: transition_text,
remove: transition_remove,
tween: transition_tween,
delay: transition_delay,
duration: transition_duration,
ease: transition_ease
};
function linear$1(t) {
return +t;
}
function quadIn(t) {
return t * t;
}
function quadOut(t) {
return t * (2 - t);
}
function quadInOut(t) {
return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
}
function cubicIn(t) {
return t * t * t;
}
function cubicOut(t) {
return --t * t * t + 1;
}
function cubicInOut(t) {
return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
}
var exponent = 3;
var polyIn = (function custom(e) {
e = +e;
function polyIn(t) {
return Math.pow(t, e);
}
polyIn.exponent = custom;
return polyIn;
})(exponent);
var polyOut = (function custom(e) {
e = +e;
function polyOut(t) {
return 1 - Math.pow(1 - t, e);
}
polyOut.exponent = custom;
return polyOut;
})(exponent);
var polyInOut = (function custom(e) {
e = +e;
function polyInOut(t) {
return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;
}
polyInOut.exponent = custom;
return polyInOut;
})(exponent);
var pi = Math.PI,
halfPi = pi / 2;
function sinIn(t) {
return 1 - Math.cos(t * halfPi);
}
function sinOut(t) {
return Math.sin(t * halfPi);
}
function sinInOut(t) {
return (1 - Math.cos(pi * t)) / 2;
}
function expIn(t) {
return Math.pow(2, 10 * t - 10);
}
function expOut(t) {
return 1 - Math.pow(2, -10 * t);
}
function expInOut(t) {
return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;
}
function circleIn(t) {
return 1 - Math.sqrt(1 - t * t);
}
function circleOut(t) {
return Math.sqrt(1 - --t * t);
}
function circleInOut(t) {
return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;
}
var b1 = 4 / 11,
b2 = 6 / 11,
b3 = 8 / 11,
b4 = 3 / 4,
b5 = 9 / 11,
b6 = 10 / 11,
b7 = 15 / 16,
b8 = 21 / 22,
b9 = 63 / 64,
b0 = 1 / b1 / b1;
function bounceIn(t) {
return 1 - bounceOut(1 - t);
}
function bounceOut(t) {
return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;
}
function bounceInOut(t) {
return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;
}
var overshoot = 1.70158;
var backIn = (function custom(s) {
s = +s;
function backIn(t) {
return t * t * ((s + 1) * t - s);
}
backIn.overshoot = custom;
return backIn;
})(overshoot);
var backOut = (function custom(s) {
s = +s;
function backOut(t) {
return --t * t * ((s + 1) * t + s) + 1;
}
backOut.overshoot = custom;
return backOut;
})(overshoot);
var backInOut = (function custom(s) {
s = +s;
function backInOut(t) {
return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;
}
backInOut.overshoot = custom;
return backInOut;
})(overshoot);
var tau = 2 * Math.PI,
amplitude = 1,
period = 0.3;
var elasticIn = (function custom(a, p) {
var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
function elasticIn(t) {
return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);
}
elasticIn.amplitude = function(a) { return custom(a, p * tau); };
elasticIn.period = function(p) { return custom(a, p); };
return elasticIn;
})(amplitude, period);
var elasticOut = (function custom(a, p) {
var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
function elasticOut(t) {
return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);
}
elasticOut.amplitude = function(a) { return custom(a, p * tau); };
elasticOut.period = function(p) { return custom(a, p); };
return elasticOut;
})(amplitude, period);
var elasticInOut = (function custom(a, p) {
var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
function elasticInOut(t) {
return ((t = t * 2 - 1) < 0
? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)
: 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;
}
elasticInOut.amplitude = function(a) { return custom(a, p * tau); };
elasticInOut.period = function(p) { return custom(a, p); };
return elasticInOut;
})(amplitude, period);
var defaultTiming = {
time: null, // Set on use.
delay: 0,
duration: 250,
ease: cubicInOut
};
function inherit(node, id) {
var timing;
while (!(timing = node.__transition) || !(timing = timing[id])) {
if (!(node = node.parentNode)) {
return defaultTiming.time = now(), defaultTiming;
}
}
return timing;
}
function selection_transition(name) {
var id,
timing;
if (name instanceof Transition) {
id = name._id, name = name._name;
} else {
id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
}
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
schedule(node, name, id, i, group, timing || inherit(node, id));
}
}
}
return new Transition(groups, this._parents, name, id);
}
selection.prototype.interrupt = selection_interrupt;
selection.prototype.transition = selection_transition;
var root$1 = [null];
function active(node, name) {
var schedules = node.__transition,
schedule$$1,
i;
if (schedules) {
name = name == null ? null : name + "";
for (i in schedules) {
if ((schedule$$1 = schedules[i]).state > SCHEDULED && schedule$$1.name === name) {
return new Transition([[node]], root$1, name, +i);
}
}
}
return null;
}
function constant$4(x) {
return function() {
return x;
};
}
function BrushEvent(target, type, selection) {
this.target = target;
this.type = type;
this.selection = selection;
}
function nopropagation$1() {
exports.event.stopImmediatePropagation();
}
function noevent$1() {
exports.event.preventDefault();
exports.event.stopImmediatePropagation();
}
var MODE_DRAG = {name: "drag"},
MODE_SPACE = {name: "space"},
MODE_HANDLE = {name: "handle"},
MODE_CENTER = {name: "center"};
var X = {
name: "x",
handles: ["e", "w"].map(type),
input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },
output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
};
var Y = {
name: "y",
handles: ["n", "s"].map(type),
input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },
output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
};
var XY = {
name: "xy",
handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type),
input: function(xy) { return xy; },
output: function(xy) { return xy; }
};
var cursors = {
overlay: "crosshair",
selection: "move",
n: "ns-resize",
e: "ew-resize",
s: "ns-resize",
w: "ew-resize",
nw: "nwse-resize",
ne: "nesw-resize",
se: "nwse-resize",
sw: "nesw-resize"
};
var flipX = {
e: "w",
w: "e",
nw: "ne",
ne: "nw",
se: "sw",
sw: "se"
};
var flipY = {
n: "s",
s: "n",
nw: "sw",
ne: "se",
se: "ne",
sw: "nw"
};
var signsX = {
overlay: +1,
selection: +1,
n: null,
e: +1,
s: null,
w: -1,
nw: -1,
ne: +1,
se: +1,
sw: -1
};
var signsY = {
overlay: +1,
selection: +1,
n: -1,
e: null,
s: +1,
w: null,
nw: -1,
ne: -1,
se: +1,
sw: +1
};
function type(t) {
return {type: t};
}
// Ignore right-click, since that should open the context menu.
function defaultFilter$1() {
return !exports.event.button;
}
function defaultExtent() {
var svg = this.ownerSVGElement || this;
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
}
// Like d3.local, but with the name “__brush” rather than auto-generated.
function local$1(node) {
while (!node.__brush) if (!(node = node.parentNode)) return;
return node.__brush;
}
function empty$1(extent) {
return extent[0][0] === extent[1][0]
|| extent[0][1] === extent[1][1];
}
function brushSelection(node) {
var state = node.__brush;
return state ? state.dim.output(state.selection) : null;
}
function brushX() {
return brush$1(X);
}
function brushY() {
return brush$1(Y);
}
function brush() {
return brush$1(XY);
}
function brush$1(dim) {
var extent = defaultExtent,
filter = defaultFilter$1,
listeners = dispatch(brush, "start", "brush", "end"),
handleSize = 6,
touchending;
function brush(group) {
var overlay = group
.property("__brush", initialize)
.selectAll(".overlay")
.data([type("overlay")]);
overlay.enter().append("rect")
.attr("class", "overlay")
.attr("pointer-events", "all")
.attr("cursor", cursors.overlay)
.merge(overlay)
.each(function() {
var extent = local$1(this).extent;
select(this)
.attr("x", extent[0][0])
.attr("y", extent[0][1])
.attr("width", extent[1][0] - extent[0][0])
.attr("height", extent[1][1] - extent[0][1]);
});
group.selectAll(".selection")
.data([type("selection")])
.enter().append("rect")
.attr("class", "selection")
.attr("cursor", cursors.selection)
.attr("fill", "#777")
.attr("fill-opacity", 0.3)
.attr("stroke", "#fff")
.attr("shape-rendering", "crispEdges");
var handle = group.selectAll(".handle")
.data(dim.handles, function(d) { return d.type; });
handle.exit().remove();
handle.enter().append("rect")
.attr("class", function(d) { return "handle handle--" + d.type; })
.attr("cursor", function(d) { return cursors[d.type]; });
group
.each(redraw)
.attr("fill", "none")
.attr("pointer-events", "all")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
.on("mousedown.brush touchstart.brush", started);
}
brush.move = function(group, selection$$1) {
if (group.selection) {
group
.on("start.brush", function() { emitter(this, arguments).beforestart().start(); })
.on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); })
.tween("brush", function() {
var that = this,
state = that.__brush,
emit = emitter(that, arguments),
selection0 = state.selection,
selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(this, arguments) : selection$$1, state.extent),
i = interpolateValue(selection0, selection1);
function tween(t) {
state.selection = t === 1 && empty$1(selection1) ? null : i(t);
redraw.call(that);
emit.brush();
}
return selection0 && selection1 ? tween : tween(1);
});
} else {
group
.each(function() {
var that = this,
args = arguments,
state = that.__brush,
selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(that, args) : selection$$1, state.extent),
emit = emitter(that, args).beforestart();
interrupt(that);
state.selection = selection1 == null || empty$1(selection1) ? null : selection1;
redraw.call(that);
emit.start().brush().end();
});
}
};
function redraw() {
var group = select(this),
selection$$1 = local$1(this).selection;
if (selection$$1) {
group.selectAll(".selection")
.style("display", null)
.attr("x", selection$$1[0][0])
.attr("y", selection$$1[0][1])
.attr("width", selection$$1[1][0] - selection$$1[0][0])
.attr("height", selection$$1[1][1] - selection$$1[0][1]);
group.selectAll(".handle")
.style("display", null)
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection$$1[1][0] - handleSize / 2 : selection$$1[0][0] - handleSize / 2; })
.attr("y", function(d) { return d.type[0] === "s" ? selection$$1[1][1] - handleSize / 2 : selection$$1[0][1] - handleSize / 2; })
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection$$1[1][0] - selection$$1[0][0] + handleSize : handleSize; })
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection$$1[1][1] - selection$$1[0][1] + handleSize : handleSize; });
}
else {
group.selectAll(".selection,.handle")
.style("display", "none")
.attr("x", null)
.attr("y", null)
.attr("width", null)
.attr("height", null);
}
}
function emitter(that, args) {
return that.__brush.emitter || new Emitter(that, args);
}
function Emitter(that, args) {
this.that = that;
this.args = args;
this.state = that.__brush;
this.active = 0;
}
Emitter.prototype = {
beforestart: function() {
if (++this.active === 1) this.state.emitter = this, this.starting = true;
return this;
},
start: function() {
if (this.starting) this.starting = false, this.emit("start");
return this;
},
brush: function() {
this.emit("brush");
return this;
},
end: function() {
if (--this.active === 0) delete this.state.emitter, this.emit("end");
return this;
},
emit: function(type) {
customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);
}
};
function started() {
if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$1(); }
else if (touchending) return;
if (!filter.apply(this, arguments)) return;
var that = this,
type = exports.event.target.__data__.type,
mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE),
signX = dim === Y ? null : signsX[type],
signY = dim === X ? null : signsY[type],
state = local$1(that),
extent = state.extent,
selection$$1 = state.selection,
W = extent[0][0], w0, w1,
N = extent[0][1], n0, n1,
E = extent[1][0], e0, e1,
S = extent[1][1], s0, s1,
dx,
dy,
moving,
shifting = signX && signY && exports.event.shiftKey,
lockX,
lockY,
point0 = mouse(that),
point$$1 = point0,
emit = emitter(that, arguments).beforestart();
if (type === "overlay") {
state.selection = selection$$1 = [
[w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
[e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
];
} else {
w0 = selection$$1[0][0];
n0 = selection$$1[0][1];
e0 = selection$$1[1][0];
s0 = selection$$1[1][1];
}
w1 = w0;
n1 = n0;
e1 = e0;
s1 = s0;
var group = select(that)
.attr("pointer-events", "none");
var overlay = group.selectAll(".overlay")
.attr("cursor", cursors[type]);
if (exports.event.touches) {
group
.on("touchmove.brush", moved, true)
.on("touchend.brush touchcancel.brush", ended, true);
} else {
var view = select(exports.event.view)
.on("keydown.brush", keydowned, true)
.on("keyup.brush", keyupped, true)
.on("mousemove.brush", moved, true)
.on("mouseup.brush", ended, true);
dragDisable(exports.event.view);
}
nopropagation$1();
interrupt(that);
redraw.call(that);
emit.start();
function moved() {
var point1 = mouse(that);
if (shifting && !lockX && !lockY) {
if (Math.abs(point1[0] - point$$1[0]) > Math.abs(point1[1] - point$$1[1])) lockY = true;
else lockX = true;
}
point$$1 = point1;
moving = true;
noevent$1();
move();
}
function move() {
var t;
dx = point$$1[0] - point0[0];
dy = point$$1[1] - point0[1];
switch (mode) {
case MODE_SPACE:
case MODE_DRAG: {
if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
break;
}
case MODE_HANDLE: {
if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
break;
}
case MODE_CENTER: {
if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));
if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));
break;
}
}
if (e1 < w1) {
signX *= -1;
t = w0, w0 = e0, e0 = t;
t = w1, w1 = e1, e1 = t;
if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
}
if (s1 < n1) {
signY *= -1;
t = n0, n0 = s0, s0 = t;
t = n1, n1 = s1, s1 = t;
if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
}
if (state.selection) selection$$1 = state.selection; // May be set by brush.move!
if (lockX) w1 = selection$$1[0][0], e1 = selection$$1[1][0];
if (lockY) n1 = selection$$1[0][1], s1 = selection$$1[1][1];
if (selection$$1[0][0] !== w1
|| selection$$1[0][1] !== n1
|| selection$$1[1][0] !== e1
|| selection$$1[1][1] !== s1) {
state.selection = [[w1, n1], [e1, s1]];
redraw.call(that);
emit.brush();
}
}
function ended() {
nopropagation$1();
if (exports.event.touches) {
if (exports.event.touches.length) return;
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
group.on("touchmove.brush touchend.brush touchcancel.brush", null);
} else {
yesdrag(exports.event.view, moving);
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
}
group.attr("pointer-events", "all");
overlay.attr("cursor", cursors.overlay);
if (state.selection) selection$$1 = state.selection; // May be set by brush.move (on start)!
if (empty$1(selection$$1)) state.selection = null, redraw.call(that);
emit.end();
}
function keydowned() {
switch (exports.event.keyCode) {
case 16: { // SHIFT
shifting = signX && signY;
break;
}
case 18: { // ALT
if (mode === MODE_HANDLE) {
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
mode = MODE_CENTER;
move();
}
break;
}
case 32: { // SPACE; takes priority over ALT
if (mode === MODE_HANDLE || mode === MODE_CENTER) {
if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
mode = MODE_SPACE;
overlay.attr("cursor", cursors.selection);
move();
}
break;
}
default: return;
}
noevent$1();
}
function keyupped() {
switch (exports.event.keyCode) {
case 16: { // SHIFT
if (shifting) {
lockX = lockY = shifting = false;
move();
}
break;
}
case 18: { // ALT
if (mode === MODE_CENTER) {
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
mode = MODE_HANDLE;
move();
}
break;
}
case 32: { // SPACE
if (mode === MODE_SPACE) {
if (exports.event.altKey) {
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
mode = MODE_CENTER;
} else {
if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
mode = MODE_HANDLE;
}
overlay.attr("cursor", cursors[type]);
move();
}
break;
}
default: return;
}
noevent$1();
}
}
function initialize() {
var state = this.__brush || {selection: null};
state.extent = extent.apply(this, arguments);
state.dim = dim;
return state;
}
brush.extent = function(_) {
return arguments.length ? (extent = typeof _ === "function" ? _ : constant$4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;
};
brush.filter = function(_) {
return arguments.length ? (filter = typeof _ === "function" ? _ : constant$4(!!_), brush) : filter;
};
brush.handleSize = function(_) {
return arguments.length ? (handleSize = +_, brush) : handleSize;
};
brush.on = function() {
var value = listeners.on.apply(listeners, arguments);
return value === listeners ? brush : value;
};
return brush;
}
var cos = Math.cos;
var sin = Math.sin;
var pi$1 = Math.PI;
var halfPi$1 = pi$1 / 2;
var tau$1 = pi$1 * 2;
var max$1 = Math.max;
function compareValue(compare) {
return function(a, b) {
return compare(
a.source.value + a.target.value,
b.source.value + b.target.value
);
};
}
function chord() {
var padAngle = 0,
sortGroups = null,
sortSubgroups = null,
sortChords = null;
function chord(matrix) {
var n = matrix.length,
groupSums = [],
groupIndex = sequence(n),
subgroupIndex = [],
chords = [],
groups = chords.groups = new Array(n),
subgroups = new Array(n * n),
k,
x,
x0,
dx,
i,
j;
// Compute the sum.
k = 0, i = -1; while (++i < n) {
x = 0, j = -1; while (++j < n) {
x += matrix[i][j];
}
groupSums.push(x);
subgroupIndex.push(sequence(n));
k += x;
}
// Sort groups…
if (sortGroups) groupIndex.sort(function(a, b) {
return sortGroups(groupSums[a], groupSums[b]);
});
// Sort subgroups…
if (sortSubgroups) subgroupIndex.forEach(function(d, i) {
d.sort(function(a, b) {
return sortSubgroups(matrix[i][a], matrix[i][b]);
});
});
// Convert the sum to scaling factor for [0, 2pi].
// TODO Allow start and end angle to be specified?
// TODO Allow padding to be specified as percentage?
k = max$1(0, tau$1 - padAngle * n) / k;
dx = k ? padAngle : tau$1 / n;
// Compute the start and end angle for each group and subgroup.
// Note: Opera has a bug reordering object literal properties!
x = 0, i = -1; while (++i < n) {
x0 = x, j = -1; while (++j < n) {
var di = groupIndex[i],
dj = subgroupIndex[di][j],
v = matrix[di][dj],
a0 = x,
a1 = x += v * k;
subgroups[dj * n + di] = {
index: di,
subindex: dj,
startAngle: a0,
endAngle: a1,
value: v
};
}
groups[di] = {
index: di,
startAngle: x0,
endAngle: x,
value: groupSums[di]
};
x += dx;
}
// Generate chords for each (non-empty) subgroup-subgroup link.
i = -1; while (++i < n) {
j = i - 1; while (++j < n) {
var source = subgroups[j * n + i],
target = subgroups[i * n + j];
if (source.value || target.value) {
chords.push(source.value < target.value
? {source: target, target: source}
: {source: source, target: target});
}
}
}
return sortChords ? chords.sort(sortChords) : chords;
}
chord.padAngle = function(_) {
return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle;
};
chord.sortGroups = function(_) {
return arguments.length ? (sortGroups = _, chord) : sortGroups;
};
chord.sortSubgroups = function(_) {
return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;
};
chord.sortChords = function(_) {
return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;
};
return chord;
}
var slice$2 = Array.prototype.slice;
function constant$5(x) {
return function() {
return x;
};
}
var pi$2 = Math.PI,
tau$2 = 2 * pi$2,
epsilon$1 = 1e-6,
tauEpsilon = tau$2 - epsilon$1;
function Path() {
this._x0 = this._y0 = // start of current subpath
this._x1 = this._y1 = null; // end of current subpath
this._ = "";
}
function path() {
return new Path;
}
Path.prototype = path.prototype = {
constructor: Path,
moveTo: function(x, y) {
this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
},
closePath: function() {
if (this._x1 !== null) {
this._x1 = this._x0, this._y1 = this._y0;
this._ += "Z";
}
},
lineTo: function(x, y) {
this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
},
quadraticCurveTo: function(x1, y1, x, y) {
this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
},
bezierCurveTo: function(x1, y1, x2, y2, x, y) {
this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
},
arcTo: function(x1, y1, x2, y2, r) {
x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
var x0 = this._x1,
y0 = this._y1,
x21 = x2 - x1,
y21 = y2 - y1,
x01 = x0 - x1,
y01 = y0 - y1,
l01_2 = x01 * x01 + y01 * y01;
// Is the radius negative? Error.
if (r < 0) throw new Error("negative radius: " + r);
// Is this path empty? Move to (x1,y1).
if (this._x1 === null) {
this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
}
// Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
else if (!(l01_2 > epsilon$1)) {}
// Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
// Equivalently, is (x1,y1) coincident with (x2,y2)?
// Or, is the radius zero? Line to (x1,y1).
else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) {
this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
}
// Otherwise, draw an arc!
else {
var x20 = x2 - x0,
y20 = y2 - y0,
l21_2 = x21 * x21 + y21 * y21,
l20_2 = x20 * x20 + y20 * y20,
l21 = Math.sqrt(l21_2),
l01 = Math.sqrt(l01_2),
l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
t01 = l / l01,
t21 = l / l21;
// If the start tangent is not coincident with (x0,y0), line to.
if (Math.abs(t01 - 1) > epsilon$1) {
this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
}
this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
}
},
arc: function(x, y, r, a0, a1, ccw) {
x = +x, y = +y, r = +r;
var dx = r * Math.cos(a0),
dy = r * Math.sin(a0),
x0 = x + dx,
y0 = y + dy,
cw = 1 ^ ccw,
da = ccw ? a0 - a1 : a1 - a0;
// Is the radius negative? Error.
if (r < 0) throw new Error("negative radius: " + r);
// Is this path empty? Move to (x0,y0).
if (this._x1 === null) {
this._ += "M" + x0 + "," + y0;
}
// Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) {
this._ += "L" + x0 + "," + y0;
}
// Is this arc empty? We’re done.
if (!r) return;
// Does the angle go the wrong way? Flip the direction.
if (da < 0) da = da % tau$2 + tau$2;
// Is this a complete circle? Draw two arcs to complete the circle.
if (da > tauEpsilon) {
this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
}
// Is this arc non-empty? Draw an arc!
else if (da > epsilon$1) {
this._ += "A" + r + "," + r + ",0," + (+(da >= pi$2)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
}
},
rect: function(x, y, w, h) {
this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z";
},
toString: function() {
return this._;
}
};
function defaultSource(d) {
return d.source;
}
function defaultTarget(d) {
return d.target;
}
function defaultRadius(d) {
return d.radius;
}
function defaultStartAngle(d) {
return d.startAngle;
}
function defaultEndAngle(d) {
return d.endAngle;
}
function ribbon() {
var source = defaultSource,
target = defaultTarget,
radius = defaultRadius,
startAngle = defaultStartAngle,
endAngle = defaultEndAngle,
context = null;
function ribbon() {
var buffer,
argv = slice$2.call(arguments),
s = source.apply(this, argv),
t = target.apply(this, argv),
sr = +radius.apply(this, (argv[0] = s, argv)),
sa0 = startAngle.apply(this, argv) - halfPi$1,
sa1 = endAngle.apply(this, argv) - halfPi$1,
sx0 = sr * cos(sa0),
sy0 = sr * sin(sa0),
tr = +radius.apply(this, (argv[0] = t, argv)),
ta0 = startAngle.apply(this, argv) - halfPi$1,
ta1 = endAngle.apply(this, argv) - halfPi$1;
if (!context) context = buffer = path();
context.moveTo(sx0, sy0);
context.arc(0, 0, sr, sa0, sa1);
if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?
context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0));
context.arc(0, 0, tr, ta0, ta1);
}
context.quadraticCurveTo(0, 0, sx0, sy0);
context.closePath();
if (buffer) return context = null, buffer + "" || null;
}
ribbon.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant$5(+_), ribbon) : radius;
};
ribbon.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : startAngle;
};
ribbon.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : endAngle;
};
ribbon.source = function(_) {
return arguments.length ? (source = _, ribbon) : source;
};
ribbon.target = function(_) {
return arguments.length ? (target = _, ribbon) : target;
};
ribbon.context = function(_) {
return arguments.length ? (context = _ == null ? null : _, ribbon) : context;
};
return ribbon;
}
var prefix = "$";
function Map() {}
Map.prototype = map$1.prototype = {
constructor: Map,
has: function(key) {
return (prefix + key) in this;
},
get: function(key) {
return this[prefix + key];
},
set: function(key, value) {
this[prefix + key] = value;
return this;
},
remove: function(key) {
var property = prefix + key;
return property in this && delete this[property];
},
clear: function() {
for (var property in this) if (property[0] === prefix) delete this[property];
},
keys: function() {
var keys = [];
for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
return keys;
},
values: function() {
var values = [];
for (var property in this) if (property[0] === prefix) values.push(this[property]);
return values;
},
entries: function() {
var entries = [];
for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
return entries;
},
size: function() {
var size = 0;
for (var property in this) if (property[0] === prefix) ++size;
return size;
},
empty: function() {
for (var property in this) if (property[0] === prefix) return false;
return true;
},
each: function(f) {
for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
}
};
function map$1(object, f) {
var map = new Map;
// Copy constructor.
if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
// Index array by numeric index or specified key function.
else if (Array.isArray(object)) {
var i = -1,
n = object.length,
o;
if (f == null) while (++i < n) map.set(i, object[i]);
else while (++i < n) map.set(f(o = object[i], i, object), o);
}
// Convert object to map.
else if (object) for (var key in object) map.set(key, object[key]);
return map;
}
function nest() {
var keys = [],
sortKeys = [],
sortValues,
rollup,
nest;
function apply(array, depth, createResult, setResult) {
if (depth >= keys.length) {
if (sortValues != null) array.sort(sortValues);
return rollup != null ? rollup(array) : array;
}
var i = -1,
n = array.length,
key = keys[depth++],
keyValue,
value,
valuesByKey = map$1(),
values,
result = createResult();
while (++i < n) {
if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
values.push(value);
} else {
valuesByKey.set(keyValue, [value]);
}
}
valuesByKey.each(function(values, key) {
setResult(result, key, apply(values, depth, createResult, setResult));
});
return result;
}
function entries(map, depth) {
if (++depth > keys.length) return map;
var array, sortKey = sortKeys[depth - 1];
if (rollup != null && depth >= keys.length) array = map.entries();
else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
}
return nest = {
object: function(array) { return apply(array, 0, createObject, setObject); },
map: function(array) { return apply(array, 0, createMap, setMap); },
entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
key: function(d) { keys.push(d); return nest; },
sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
sortValues: function(order) { sortValues = order; return nest; },
rollup: function(f) { rollup = f; return nest; }
};
}
function createObject() {
return {};
}
function setObject(object, key, value) {
object[key] = value;
}
function createMap() {
return map$1();
}
function setMap(map, key, value) {
map.set(key, value);
}
function Set() {}
var proto = map$1.prototype;
Set.prototype = set$2.prototype = {
constructor: Set,
has: proto.has,
add: function(value) {
value += "";
this[prefix + value] = value;
return this;
},
remove: proto.remove,
clear: proto.clear,
values: proto.keys,
size: proto.size,
empty: proto.empty,
each: proto.each
};
function set$2(object, f) {
var set = new Set;
// Copy constructor.
if (object instanceof Set) object.each(function(value) { set.add(value); });
// Otherwise, assume it’s an array.
else if (object) {
var i = -1, n = object.length;
if (f == null) while (++i < n) set.add(object[i]);
else while (++i < n) set.add(f(object[i], i, object));
}
return set;
}
function keys(map) {
var keys = [];
for (var key in map) keys.push(key);
return keys;
}
function values(map) {
var values = [];
for (var key in map) values.push(map[key]);
return values;
}
function entries(map) {
var entries = [];
for (var key in map) entries.push({key: key, value: map[key]});
return entries;
}
var array$2 = Array.prototype;
var slice$3 = array$2.slice;
function ascending$2(a, b) {
return a - b;
}
function area(ring) {
var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
return area;
}
function constant$6(x) {
return function() {
return x;
};
}
function contains(ring, hole) {
var i = -1, n = hole.length, c;
while (++i < n) if (c = ringContains(ring, hole[i])) return c;
return 0;
}
function ringContains(ring, point) {
var x = point[0], y = point[1], contains = -1;
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];
if (segmentContains(pi, pj, point)) return 0;
if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains;
}
return contains;
}
function segmentContains(a, b, c) {
var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);
}
function collinear(a, b, c) {
return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);
}
function within(p, q, r) {
return p <= q && q <= r || r <= q && q <= p;
}
function noop$1() {}
var cases = [
[],
[[[1.0, 1.5], [0.5, 1.0]]],
[[[1.5, 1.0], [1.0, 1.5]]],
[[[1.5, 1.0], [0.5, 1.0]]],
[[[1.0, 0.5], [1.5, 1.0]]],
[[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]],
[[[1.0, 0.5], [1.0, 1.5]]],
[[[1.0, 0.5], [0.5, 1.0]]],
[[[0.5, 1.0], [1.0, 0.5]]],
[[[1.0, 1.5], [1.0, 0.5]]],
[[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]],
[[[1.5, 1.0], [1.0, 0.5]]],
[[[0.5, 1.0], [1.5, 1.0]]],
[[[1.0, 1.5], [1.5, 1.0]]],
[[[0.5, 1.0], [1.0, 1.5]]],
[]
];
function contours() {
var dx = 1,
dy = 1,
threshold$$1 = thresholdSturges,
smooth = smoothLinear;
function contours(values) {
var tz = threshold$$1(values);
// Convert number of thresholds into uniform thresholds.
if (!Array.isArray(tz)) {
var domain = extent(values), start = domain[0], stop = domain[1];
tz = tickStep(start, stop, tz);
tz = sequence(Math.floor(start / tz) * tz, Math.floor(stop / tz) * tz, tz);
} else {
tz = tz.slice().sort(ascending$2);
}
return tz.map(function(value) {
return contour(values, value);
});
}
// Accumulate, smooth contour rings, assign holes to exterior rings.
// Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
function contour(values, value) {
var polygons = [],
holes = [];
isorings(values, value, function(ring) {
smooth(ring, values, value);
if (area(ring) > 0) polygons.push([ring]);
else holes.push(ring);
});
holes.forEach(function(hole) {
for (var i = 0, n = polygons.length, polygon; i < n; ++i) {
if (contains((polygon = polygons[i])[0], hole) !== -1) {
polygon.push(hole);
return;
}
}
});
return {
type: "MultiPolygon",
value: value,
coordinates: polygons
};
}
// Marching squares with isolines stitched into rings.
// Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
function isorings(values, value, callback) {
var fragmentByStart = new Array,
fragmentByEnd = new Array,
x, y, t0, t1, t2, t3;
// Special case for the first row (y = -1, t2 = t3 = 0).
x = y = -1;
t1 = values[0] >= value;
cases[t1 << 1].forEach(stitch);
while (++x < dx - 1) {
t0 = t1, t1 = values[x + 1] >= value;
cases[t0 | t1 << 1].forEach(stitch);
}
cases[t1 << 0].forEach(stitch);
// General case for the intermediate rows.
while (++y < dy - 1) {
x = -1;
t1 = values[y * dx + dx] >= value;
t2 = values[y * dx] >= value;
cases[t1 << 1 | t2 << 2].forEach(stitch);
while (++x < dx - 1) {
t0 = t1, t1 = values[y * dx + dx + x + 1] >= value;
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);
}
cases[t1 | t2 << 3].forEach(stitch);
}
// Special case for the last row (y = dy - 1, t0 = t1 = 0).
x = -1;
t2 = values[y * dx] >= value;
cases[t2 << 2].forEach(stitch);
while (++x < dx - 1) {
t3 = t2, t2 = values[y * dx + x + 1] >= value;
cases[t2 << 2 | t3 << 3].forEach(stitch);
}
cases[t2 << 3].forEach(stitch);
function stitch(line) {
var start = [line[0][0] + x, line[0][1] + y],
end = [line[1][0] + x, line[1][1] + y],
startIndex = index(start),
endIndex = index(end),
f, g;
if (f = fragmentByEnd[startIndex]) {
if (g = fragmentByStart[endIndex]) {
delete fragmentByEnd[f.end];
delete fragmentByStart[g.start];
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)};
}
} else {
delete fragmentByEnd[f.end];
f.ring.push(end);
fragmentByEnd[f.end = endIndex] = f;
}
} else if (f = fragmentByStart[endIndex]) {
if (g = fragmentByEnd[startIndex]) {
delete fragmentByStart[f.start];
delete fragmentByEnd[g.end];
if (f === g) {
f.ring.push(end);
callback(f.ring);
} else {
fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)};
}
} else {
delete fragmentByStart[f.start];
f.ring.unshift(start);
fragmentByStart[f.start = startIndex] = f;
}
} else {
fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]};
}
}
}
function index(point) {
return point[0] * 2 + point[1] * (dx + 1) * 4;
}
function smoothLinear(ring, values, value) {
ring.forEach(function(point) {
var x = point[0],
y = point[1],
xt = x | 0,
yt = y | 0,
v0,
v1 = values[yt * dx + xt];
if (x > 0 && x < dx && xt === x) {
v0 = values[yt * dx + xt - 1];
point[0] = x + (value - v0) / (v1 - v0) - 0.5;
}
if (y > 0 && y < dy && yt === y) {
v0 = values[(yt - 1) * dx + xt];
point[1] = y + (value - v0) / (v1 - v0) - 0.5;
}
});
}
contours.contour = contour;
contours.size = function(_) {
if (!arguments.length) return [dx, dy];
var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);
if (!(_0 > 0) || !(_1 > 0)) throw new Error("invalid size");
return dx = _0, dy = _1, contours;
};
contours.thresholds = function(_) {
return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), contours) : threshold$$1;
};
contours.smooth = function(_) {
return arguments.length ? (smooth = _ ? smoothLinear : noop$1, contours) : smooth === smoothLinear;
};
return contours;
}
// TODO Optimize edge cases.
// TODO Optimize index calculation.
// TODO Optimize arguments.
function blurX(source, target, r) {
var n = source.width,
m = source.height,
w = (r << 1) + 1;
for (var j = 0; j < m; ++j) {
for (var i = 0, sr = 0; i < n + r; ++i) {
if (i < n) {
sr += source.data[i + j * n];
}
if (i >= r) {
if (i >= w) {
sr -= source.data[i - w + j * n];
}
target.data[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w);
}
}
}
}
// TODO Optimize edge cases.
// TODO Optimize index calculation.
// TODO Optimize arguments.
function blurY(source, target, r) {
var n = source.width,
m = source.height,
w = (r << 1) + 1;
for (var i = 0; i < n; ++i) {
for (var j = 0, sr = 0; j < m + r; ++j) {
if (j < m) {
sr += source.data[i + j * n];
}
if (j >= r) {
if (j >= w) {
sr -= source.data[i + (j - w) * n];
}
target.data[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w);
}
}
}
}
function defaultX(d) {
return d[0];
}
function defaultY(d) {
return d[1];
}
function density() {
var x = defaultX,
y = defaultY,
dx = 960,
dy = 500,
r = 20, // blur radius
k = 2, // log2(grid cell size)
o = r * 3, // grid offset, to pad for blur
n = (dx + o * 2) >> k, // grid width
m = (dy + o * 2) >> k, // grid height
threshold$$1 = constant$6(20);
function density(data) {
var values0 = new Float32Array(n * m),
values1 = new Float32Array(n * m);
data.forEach(function(d, i, data) {
var xi = (x(d, i, data) + o) >> k,
yi = (y(d, i, data) + o) >> k;
if (xi >= 0 && xi < n && yi >= 0 && yi < m) {
++values0[xi + yi * n];
}
});
// TODO Optimize.
blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
var tz = threshold$$1(values0);
// Convert number of thresholds into uniform thresholds.
if (!Array.isArray(tz)) {
var stop = max(values0);
tz = tickStep(0, stop, tz);
tz = sequence(0, Math.floor(stop / tz) * tz, tz);
tz.shift();
}
return contours()
.thresholds(tz)
.size([n, m])
(values0)
.map(transform);
}
function transform(geometry) {
geometry.value *= Math.pow(2, -2 * k); // Density in points per square pixel.
geometry.coordinates.forEach(transformPolygon);
return geometry;
}
function transformPolygon(coordinates) {
coordinates.forEach(transformRing);
}
function transformRing(coordinates) {
coordinates.forEach(transformPoint);
}
// TODO Optimize.
function transformPoint(coordinates) {
coordinates[0] = coordinates[0] * Math.pow(2, k) - o;
coordinates[1] = coordinates[1] * Math.pow(2, k) - o;
}
function resize() {
o = r * 3;
n = (dx + o * 2) >> k;
m = (dy + o * 2) >> k;
return density;
}
density.x = function(_) {
return arguments.length ? (x = typeof _ === "function" ? _ : constant$6(+_), density) : x;
};
density.y = function(_) {
return arguments.length ? (y = typeof _ === "function" ? _ : constant$6(+_), density) : y;
};
density.size = function(_) {
if (!arguments.length) return [dx, dy];
var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);
if (!(_0 >= 0) && !(_0 >= 0)) throw new Error("invalid size");
return dx = _0, dy = _1, resize();
};
density.cellSize = function(_) {
if (!arguments.length) return 1 << k;
if (!((_ = +_) >= 1)) throw new Error("invalid cell size");
return k = Math.floor(Math.log(_) / Math.LN2), resize();
};
density.thresholds = function(_) {
return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), density) : threshold$$1;
};
density.bandwidth = function(_) {
if (!arguments.length) return Math.sqrt(r * (r + 1));
if (!((_ = +_) >= 0)) throw new Error("invalid bandwidth");
return r = Math.round((Math.sqrt(4 * _ * _ + 1) - 1) / 2), resize();
};
return density;
}
var EOL = {},
EOF = {},
QUOTE = 34,
NEWLINE = 10,
RETURN = 13;
function objectConverter(columns) {
return new Function("d", "return {" + columns.map(function(name, i) {
return JSON.stringify(name) + ": d[" + i + "]";
}).join(",") + "}");
}
function customConverter(columns, f) {
var object = objectConverter(columns);
return function(row, i) {
return f(object(row), i, columns);
};
}
// Compute unique columns in order of discovery.
function inferColumns(rows) {
var columnSet = Object.create(null),
columns = [];
rows.forEach(function(row) {
for (var column in row) {
if (!(column in columnSet)) {
columns.push(columnSet[column] = column);
}
}
});
return columns;
}
function dsvFormat(delimiter) {
var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
DELIMITER = delimiter.charCodeAt(0);
function parse(text, f) {
var convert, columns, rows = parseRows(text, function(row, i) {
if (convert) return convert(row, i - 1);
columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
});
rows.columns = columns || [];
return rows;
}
function parseRows(text, f) {
var rows = [], // output rows
N = text.length,
I = 0, // current character index
n = 0, // current line number
t, // current token
eof = N <= 0, // current token followed by EOF?
eol = false; // current token followed by EOL?
// Strip the trailing newline.
if (text.charCodeAt(N - 1) === NEWLINE) --N;
if (text.charCodeAt(N - 1) === RETURN) --N;
function token() {
if (eof) return EOF;
if (eol) return eol = false, EOL;
// Unescape quotes.
var i, j = I, c;
if (text.charCodeAt(j) === QUOTE) {
while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
if ((i = I) >= N) eof = true;
else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;
else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
return text.slice(j + 1, i - 1).replace(/""/g, "\"");
}
// Find next delimiter or newline.
while (I < N) {
if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;
else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
else if (c !== DELIMITER) continue;
return text.slice(j, i);
}
// Return last token before EOF.
return eof = true, text.slice(j, N);
}
while ((t = token()) !== EOF) {
var row = [];
while (t !== EOL && t !== EOF) row.push(t), t = token();
if (f && (row = f(row, n++)) == null) continue;
rows.push(row);
}
return rows;
}
function format(rows, columns) {
if (columns == null) columns = inferColumns(rows);
return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
return columns.map(function(column) {
return formatValue(row[column]);
}).join(delimiter);
})).join("\n");
}
function formatRows(rows) {
return rows.map(formatRow).join("\n");
}
function formatRow(row) {
return row.map(formatValue).join(delimiter);
}
function formatValue(text) {
return text == null ? ""
: reFormat.test(text += "") ? "\"" + text.replace(/"/g, "\"\"") + "\""
: text;
}
return {
parse: parse,
parseRows: parseRows,
format: format,
formatRows: formatRows
};
}
var csv = dsvFormat(",");
var csvParse = csv.parse;
var csvParseRows = csv.parseRows;
var csvFormat = csv.format;
var csvFormatRows = csv.formatRows;
var tsv = dsvFormat("\t");
var tsvParse = tsv.parse;
var tsvParseRows = tsv.parseRows;
var tsvFormat = tsv.format;
var tsvFormatRows = tsv.formatRows;
function responseBlob(response) {
if (!response.ok) throw new Error(response.status + " " + response.statusText);
return response.blob();
}
function blob(input, init) {
return fetch(input, init).then(responseBlob);
}
function responseArrayBuffer(response) {
if (!response.ok) throw new Error(response.status + " " + response.statusText);
return response.arrayBuffer();
}
function buffer(input, init) {
return fetch(input, init).then(responseArrayBuffer);
}
function responseText(response) {
if (!response.ok) throw new Error(response.status + " " + response.statusText);
return response.text();
}
function text(input, init) {
return fetch(input, init).then(responseText);
}
function dsvParse(parse) {
return function(input, init, row) {
if (arguments.length === 2 && typeof init === "function") row = init, init = undefined;
return text(input, init).then(function(response) {
return parse(response, row);
});
};
}
function dsv(delimiter, input, init, row) {
if (arguments.length === 3 && typeof init === "function") row = init, init = undefined;
var format = dsvFormat(delimiter);
return text(input, init).then(function(response) {
return format.parse(response, row);
});
}
var csv$1 = dsvParse(csvParse);
var tsv$1 = dsvParse(tsvParse);
function image(input, init) {
return new Promise(function(resolve, reject) {
var image = new Image;
for (var key in init) image[key] = init[key];
image.onerror = reject;
image.onload = function() { resolve(image); };
image.src = input;
});
}
function responseJson(response) {
if (!response.ok) throw new Error(response.status + " " + response.statusText);
return response.json();
}
function json(input, init) {
return fetch(input, init).then(responseJson);
}
function parser(type) {
return function(input, init) {
return text(input, init).then(function(text$$1) {
return (new DOMParser).parseFromString(text$$1, type);
});
};
}
var xml = parser("application/xml");
var html = parser("text/html");
var svg = parser("image/svg+xml");
function center$1(x, y) {
var nodes;
if (x == null) x = 0;
if (y == null) y = 0;
function force() {
var i,
n = nodes.length,
node,
sx = 0,
sy = 0;
for (i = 0; i < n; ++i) {
node = nodes[i], sx += node.x, sy += node.y;
}
for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {
node = nodes[i], node.x -= sx, node.y -= sy;
}
}
force.initialize = function(_) {
nodes = _;
};
force.x = function(_) {
return arguments.length ? (x = +_, force) : x;
};
force.y = function(_) {
return arguments.length ? (y = +_, force) : y;
};
return force;
}
function constant$7(x) {
return function() {
return x;
};
}
function jiggle() {
return (Math.random() - 0.5) * 1e-6;
}
function tree_add(d) {
var x = +this._x.call(null, d),
y = +this._y.call(null, d);
return add(this.cover(x, y), x, y, d);
}
function add(tree, x, y, d) {
if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
var parent,
node = tree._root,
leaf = {data: d},
x0 = tree._x0,
y0 = tree._y0,
x1 = tree._x1,
y1 = tree._y1,
xm,
ym,
xp,
yp,
right,
bottom,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return tree._root = leaf, tree;
// Find the existing leaf for the new point, or add it.
while (node.length) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
}
// Is the new point is exactly coincident with the existing point?
xp = +tree._x.call(null, node.data);
yp = +tree._y.call(null, node.data);
if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
// Otherwise, split the leaf node until the old and new point are separated.
do {
parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
} while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
return parent[j] = node, parent[i] = leaf, tree;
}
function addAll(data) {
var d, i, n = data.length,
x,
y,
xz = new Array(n),
yz = new Array(n),
x0 = Infinity,
y0 = Infinity,
x1 = -Infinity,
y1 = -Infinity;
// Compute the points and their extent.
for (i = 0; i < n; ++i) {
if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
xz[i] = x;
yz[i] = y;
if (x < x0) x0 = x;
if (x > x1) x1 = x;
if (y < y0) y0 = y;
if (y > y1) y1 = y;
}
// If there were no (valid) points, inherit the existing extent.
if (x1 < x0) x0 = this._x0, x1 = this._x1;
if (y1 < y0) y0 = this._y0, y1 = this._y1;
// Expand the tree to cover the new points.
this.cover(x0, y0).cover(x1, y1);
// Add the new points.
for (i = 0; i < n; ++i) {
add(this, xz[i], yz[i], data[i]);
}
return this;
}
function tree_cover(x, y) {
if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
var x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1;
// If the quadtree has no extent, initialize them.
// Integer extent are necessary so that if we later double the extent,
// the existing quadrant boundaries don’t change due to floating point error!
if (isNaN(x0)) {
x1 = (x0 = Math.floor(x)) + 1;
y1 = (y0 = Math.floor(y)) + 1;
}
// Otherwise, double repeatedly to cover.
else if (x0 > x || x > x1 || y0 > y || y > y1) {
var z = x1 - x0,
node = this._root,
parent,
i;
switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {
case 0: {
do parent = new Array(4), parent[i] = node, node = parent;
while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);
break;
}
case 1: {
do parent = new Array(4), parent[i] = node, node = parent;
while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);
break;
}
case 2: {
do parent = new Array(4), parent[i] = node, node = parent;
while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);
break;
}
case 3: {
do parent = new Array(4), parent[i] = node, node = parent;
while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);
break;
}
}
if (this._root && this._root.length) this._root = node;
}
// If the quadtree covers the point already, just return.
else return this;
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
return this;
}
function tree_data() {
var data = [];
this.visit(function(node) {
if (!node.length) do data.push(node.data); while (node = node.next)
});
return data;
}
function tree_extent(_) {
return arguments.length
? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
: isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
}
function Quad(node, x0, y0, x1, y1) {
this.node = node;
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
}
function tree_find(x, y, radius) {
var data,
x0 = this._x0,
y0 = this._y0,
x1,
y1,
x2,
y2,
x3 = this._x1,
y3 = this._y1,
quads = [],
node = this._root,
q,
i;
if (node) quads.push(new Quad(node, x0, y0, x3, y3));
if (radius == null) radius = Infinity;
else {
x0 = x - radius, y0 = y - radius;
x3 = x + radius, y3 = y + radius;
radius *= radius;
}
while (q = quads.pop()) {
// Stop searching if this quadrant can’t contain a closer node.
if (!(node = q.node)
|| (x1 = q.x0) > x3
|| (y1 = q.y0) > y3
|| (x2 = q.x1) < x0
|| (y2 = q.y1) < y0) continue;
// Bisect the current quadrant.
if (node.length) {
var xm = (x1 + x2) / 2,
ym = (y1 + y2) / 2;
quads.push(
new Quad(node[3], xm, ym, x2, y2),
new Quad(node[2], x1, ym, xm, y2),
new Quad(node[1], xm, y1, x2, ym),
new Quad(node[0], x1, y1, xm, ym)
);
// Visit the closest quadrant first.
if (i = (y >= ym) << 1 | (x >= xm)) {
q = quads[quads.length - 1];
quads[quads.length - 1] = quads[quads.length - 1 - i];
quads[quads.length - 1 - i] = q;
}
}
// Visit this point. (Visiting coincident points isn’t necessary!)
else {
var dx = x - +this._x.call(null, node.data),
dy = y - +this._y.call(null, node.data),
d2 = dx * dx + dy * dy;
if (d2 < radius) {
var d = Math.sqrt(radius = d2);
x0 = x - d, y0 = y - d;
x3 = x + d, y3 = y + d;
data = node.data;
}
}
}
return data;
}
function tree_remove(d) {
if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
var parent,
node = this._root,
retainer,
previous,
next,
x0 = this._x0,
y0 = this._y0,
x1 = this._x1,
y1 = this._y1,
x,
y,
xm,
ym,
right,
bottom,
i,
j;
// If the tree is empty, initialize the root as a leaf.
if (!node) return this;
// Find the leaf node for the point.
// While descending, also retain the deepest parent with a non-removed sibling.
if (node.length) while (true) {
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
if (!node.length) break;
if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
}
// Find the point to remove.
while (node.data !== d) if (!(previous = node, node = node.next)) return this;
if (next = node.next) delete node.next;
// If there are multiple coincident points, remove just the point.
if (previous) return next ? previous.next = next : delete previous.next, this;
// If this is the root point, remove it.
if (!parent) return this._root = next, this;
// Remove this leaf.
next ? parent[i] = next : delete parent[i];
// If the parent now contains exactly one leaf, collapse superfluous parents.
if ((node = parent[0] || parent[1] || parent[2] || parent[3])
&& node === (parent[3] || parent[2] || parent[1] || parent[0])
&& !node.length) {
if (retainer) retainer[j] = node;
else this._root = node;
}
return this;
}
function removeAll(data) {
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
return this;
}
function tree_root() {
return this._root;
}
function tree_size() {
var size = 0;
this.visit(function(node) {
if (!node.length) do ++size; while (node = node.next)
});
return size;
}
function tree_visit(callback) {
var quads = [], q, node = this._root, child, x0, y0, x1, y1;
if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
while (q = quads.pop()) {
if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
}
}
return this;
}
function tree_visitAfter(callback) {
var quads = [], next = [], q;
if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
while (q = quads.pop()) {
var node = q.node;
if (node.length) {
var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
}
next.push(q);
}
while (q = next.pop()) {
callback(q.node, q.x0, q.y0, q.x1, q.y1);
}
return this;
}
function defaultX$1(d) {
return d[0];
}
function tree_x(_) {
return arguments.length ? (this._x = _, this) : this._x;
}
function defaultY$1(d) {
return d[1];
}
function tree_y(_) {
return arguments.length ? (this._y = _, this) : this._y;
}
function quadtree(nodes, x, y) {
var tree = new Quadtree(x == null ? defaultX$1 : x, y == null ? defaultY$1 : y, NaN, NaN, NaN, NaN);
return nodes == null ? tree : tree.addAll(nodes);
}
function Quadtree(x, y, x0, y0, x1, y1) {
this._x = x;
this._y = y;
this._x0 = x0;
this._y0 = y0;
this._x1 = x1;
this._y1 = y1;
this._root = undefined;
}
function leaf_copy(leaf) {
var copy = {data: leaf.data}, next = copy;
while (leaf = leaf.next) next = next.next = {data: leaf.data};
return copy;
}
var treeProto = quadtree.prototype = Quadtree.prototype;
treeProto.copy = function() {
var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
node = this._root,
nodes,
child;
if (!node) return copy;
if (!node.length) return copy._root = leaf_copy(node), copy;
nodes = [{source: node, target: copy._root = new Array(4)}];
while (node = nodes.pop()) {
for (var i = 0; i < 4; ++i) {
if (child = node.source[i]) {
if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
else node.target[i] = leaf_copy(child);
}
}
}
return copy;
};
treeProto.add = tree_add;
treeProto.addAll = addAll;
treeProto.cover = tree_cover;
treeProto.data = tree_data;
treeProto.extent = tree_extent;
treeProto.find = tree_find;
treeProto.remove = tree_remove;
treeProto.removeAll = removeAll;
treeProto.root = tree_root;
treeProto.size = tree_size;
treeProto.visit = tree_visit;
treeProto.visitAfter = tree_visitAfter;
treeProto.x = tree_x;
treeProto.y = tree_y;
function x(d) {
return d.x + d.vx;
}
function y(d) {
return d.y + d.vy;
}
function collide(radius) {
var nodes,
radii,
strength = 1,
iterations = 1;
if (typeof radius !== "function") radius = constant$7(radius == null ? 1 : +radius);
function force() {
var i, n = nodes.length,
tree,
node,
xi,
yi,
ri,
ri2;
for (var k = 0; k < iterations; ++k) {
tree = quadtree(nodes, x, y).visitAfter(prepare);
for (i = 0; i < n; ++i) {
node = nodes[i];
ri = radii[node.index], ri2 = ri * ri;
xi = node.x + node.vx;
yi = node.y + node.vy;
tree.visit(apply);
}
}
function apply(quad, x0, y0, x1, y1) {
var data = quad.data, rj = quad.r, r = ri + rj;
if (data) {
if (data.index > node.index) {
var x = xi - data.x - data.vx,
y = yi - data.y - data.vy,
l = x * x + y * y;
if (l < r * r) {
if (x === 0) x = jiggle(), l += x * x;
if (y === 0) y = jiggle(), l += y * y;
l = (r - (l = Math.sqrt(l))) / l * strength;
node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
node.vy += (y *= l) * r;
data.vx -= x * (r = 1 - r);
data.vy -= y * r;
}
}
return;
}
return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
}
}
function prepare(quad) {
if (quad.data) return quad.r = radii[quad.data.index];
for (var i = quad.r = 0; i < 4; ++i) {
if (quad[i] && quad[i].r > quad.r) {
quad.r = quad[i].r;
}
}
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length, node;
radii = new Array(n);
for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
}
force.initialize = function(_) {
nodes = _;
initialize();
};
force.iterations = function(_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
force.strength = function(_) {
return arguments.length ? (strength = +_, force) : strength;
};
force.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius;
};
return force;
}
function index(d) {
return d.index;
}
function find(nodeById, nodeId) {
var node = nodeById.get(nodeId);
if (!node) throw new Error("missing: " + nodeId);
return node;
}
function link(links) {
var id = index,
strength = defaultStrength,
strengths,
distance = constant$7(30),
distances,
nodes,
count,
bias,
iterations = 1;
if (links == null) links = [];
function defaultStrength(link) {
return 1 / Math.min(count[link.source.index], count[link.target.index]);
}
function force(alpha) {
for (var k = 0, n = links.length; k < iterations; ++k) {
for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {
link = links[i], source = link.source, target = link.target;
x = target.x + target.vx - source.x - source.vx || jiggle();
y = target.y + target.vy - source.y - source.vy || jiggle();
l = Math.sqrt(x * x + y * y);
l = (l - distances[i]) / l * alpha * strengths[i];
x *= l, y *= l;
target.vx -= x * (b = bias[i]);
target.vy -= y * b;
source.vx += x * (b = 1 - b);
source.vy += y * b;
}
}
}
function initialize() {
if (!nodes) return;
var i,
n = nodes.length,
m = links.length,
nodeById = map$1(nodes, id),
link;
for (i = 0, count = new Array(n); i < m; ++i) {
link = links[i], link.index = i;
if (typeof link.source !== "object") link.source = find(nodeById, link.source);
if (typeof link.target !== "object") link.target = find(nodeById, link.target);
count[link.source.index] = (count[link.source.index] || 0) + 1;
count[link.target.index] = (count[link.target.index] || 0) + 1;
}
for (i = 0, bias = new Array(m); i < m; ++i) {
link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
}
strengths = new Array(m), initializeStrength();
distances = new Array(m), initializeDistance();
}
function initializeStrength() {
if (!nodes) return;
for (var i = 0, n = links.length; i < n; ++i) {
strengths[i] = +strength(links[i], i, links);
}
}
function initializeDistance() {
if (!nodes) return;
for (var i = 0, n = links.length; i < n; ++i) {
distances[i] = +distance(links[i], i, links);
}
}
force.initialize = function(_) {
nodes = _;
initialize();
};
force.links = function(_) {
return arguments.length ? (links = _, initialize(), force) : links;
};
force.id = function(_) {
return arguments.length ? (id = _, force) : id;
};
force.iterations = function(_) {
return arguments.length ? (iterations = +_, force) : iterations;
};
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initializeStrength(), force) : strength;
};
force.distance = function(_) {
return arguments.length ? (distance = typeof _ === "function" ? _ : constant$7(+_), initializeDistance(), force) : distance;
};
return force;
}
function x$1(d) {
return d.x;
}
function y$1(d) {
return d.y;
}
var initialRadius = 10,
initialAngle = Math.PI * (3 - Math.sqrt(5));
function simulation(nodes) {
var simulation,
alpha = 1,
alphaMin = 0.001,
alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
alphaTarget = 0,
velocityDecay = 0.6,
forces = map$1(),
stepper = timer(step),
event = dispatch("tick", "end");
if (nodes == null) nodes = [];
function step() {
tick();
event.call("tick", simulation);
if (alpha < alphaMin) {
stepper.stop();
event.call("end", simulation);
}
}
function tick() {
var i, n = nodes.length, node;
alpha += (alphaTarget - alpha) * alphaDecay;
forces.each(function(force) {
force(alpha);
});
for (i = 0; i < n; ++i) {
node = nodes[i];
if (node.fx == null) node.x += node.vx *= velocityDecay;
else node.x = node.fx, node.vx = 0;
if (node.fy == null) node.y += node.vy *= velocityDecay;
else node.y = node.fy, node.vy = 0;
}
}
function initializeNodes() {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.index = i;
if (isNaN(node.x) || isNaN(node.y)) {
var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
node.x = radius * Math.cos(angle);
node.y = radius * Math.sin(angle);
}
if (isNaN(node.vx) || isNaN(node.vy)) {
node.vx = node.vy = 0;
}
}
}
function initializeForce(force) {
if (force.initialize) force.initialize(nodes);
return force;
}
initializeNodes();
return simulation = {
tick: tick,
restart: function() {
return stepper.restart(step), simulation;
},
stop: function() {
return stepper.stop(), simulation;
},
nodes: function(_) {
return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
},
alpha: function(_) {
return arguments.length ? (alpha = +_, simulation) : alpha;
},
alphaMin: function(_) {
return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
},
alphaDecay: function(_) {
return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
},
alphaTarget: function(_) {
return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
},
velocityDecay: function(_) {
return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
},
force: function(name, _) {
return arguments.length > 1 ? (_ == null ? forces.remove(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name);
},
find: function(x, y, radius) {
var i = 0,
n = nodes.length,
dx,
dy,
d2,
node,
closest;
if (radius == null) radius = Infinity;
else radius *= radius;
for (i = 0; i < n; ++i) {
node = nodes[i];
dx = x - node.x;
dy = y - node.y;
d2 = dx * dx + dy * dy;
if (d2 < radius) closest = node, radius = d2;
}
return closest;
},
on: function(name, _) {
return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
}
};
}
function manyBody() {
var nodes,
node,
alpha,
strength = constant$7(-30),
strengths,
distanceMin2 = 1,
distanceMax2 = Infinity,
theta2 = 0.81;
function force(_) {
var i, n = nodes.length, tree = quadtree(nodes, x$1, y$1).visitAfter(accumulate);
for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length, node;
strengths = new Array(n);
for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
}
function accumulate(quad) {
var strength = 0, q, c, weight = 0, x, y, i;
// For internal nodes, accumulate forces from child quadrants.
if (quad.length) {
for (x = y = i = 0; i < 4; ++i) {
if ((q = quad[i]) && (c = Math.abs(q.value))) {
strength += q.value, weight += c, x += c * q.x, y += c * q.y;
}
}
quad.x = x / weight;
quad.y = y / weight;
}
// For leaf nodes, accumulate forces from coincident quadrants.
else {
q = quad;
q.x = q.data.x;
q.y = q.data.y;
do strength += strengths[q.data.index];
while (q = q.next);
}
quad.value = strength;
}
function apply(quad, x1, _, x2) {
if (!quad.value) return true;
var x = quad.x - node.x,
y = quad.y - node.y,
w = x2 - x1,
l = x * x + y * y;
// Apply the Barnes-Hut approximation if possible.
// Limit forces for very close nodes; randomize direction if coincident.
if (w * w / theta2 < l) {
if (l < distanceMax2) {
if (x === 0) x = jiggle(), l += x * x;
if (y === 0) y = jiggle(), l += y * y;
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
node.vx += x * quad.value * alpha / l;
node.vy += y * quad.value * alpha / l;
}
return true;
}
// Otherwise, process points directly.
else if (quad.length || l >= distanceMax2) return;
// Limit forces for very close nodes; randomize direction if coincident.
if (quad.data !== node || quad.next) {
if (x === 0) x = jiggle(), l += x * x;
if (y === 0) y = jiggle(), l += y * y;
if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
}
do if (quad.data !== node) {
w = strengths[quad.data.index] * alpha / l;
node.vx += x * w;
node.vy += y * w;
} while (quad = quad.next);
}
force.initialize = function(_) {
nodes = _;
initialize();
};
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
};
force.distanceMin = function(_) {
return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
};
force.distanceMax = function(_) {
return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
};
force.theta = function(_) {
return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
};
return force;
}
function radial(radius, x, y) {
var nodes,
strength = constant$7(0.1),
strengths,
radiuses;
if (typeof radius !== "function") radius = constant$7(+radius);
if (x == null) x = 0;
if (y == null) y = 0;
function force(alpha) {
for (var i = 0, n = nodes.length; i < n; ++i) {
var node = nodes[i],
dx = node.x - x || 1e-6,
dy = node.y - y || 1e-6,
r = Math.sqrt(dx * dx + dy * dy),
k = (radiuses[i] - r) * strengths[i] * alpha / r;
node.vx += dx * k;
node.vy += dy * k;
}
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length;
strengths = new Array(n);
radiuses = new Array(n);
for (i = 0; i < n; ++i) {
radiuses[i] = +radius(nodes[i], i, nodes);
strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);
}
}
force.initialize = function(_) {
nodes = _, initialize();
};
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
};
force.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius;
};
force.x = function(_) {
return arguments.length ? (x = +_, force) : x;
};
force.y = function(_) {
return arguments.length ? (y = +_, force) : y;
};
return force;
}
function x$2(x) {
var strength = constant$7(0.1),
nodes,
strengths,
xz;
if (typeof x !== "function") x = constant$7(x == null ? 0 : +x);
function force(alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
}
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length;
strengths = new Array(n);
xz = new Array(n);
for (i = 0; i < n; ++i) {
strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
}
}
force.initialize = function(_) {
nodes = _;
initialize();
};
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
};
force.x = function(_) {
return arguments.length ? (x = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : x;
};
return force;
}
function y$2(y) {
var strength = constant$7(0.1),
nodes,
strengths,
yz;
if (typeof y !== "function") y = constant$7(y == null ? 0 : +y);
function force(alpha) {
for (var i = 0, n = nodes.length, node; i < n; ++i) {
node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
}
}
function initialize() {
if (!nodes) return;
var i, n = nodes.length;
strengths = new Array(n);
yz = new Array(n);
for (i = 0; i < n; ++i) {
strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
}
}
force.initialize = function(_) {
nodes = _;
initialize();
};
force.strength = function(_) {
return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
};
force.y = function(_) {
return arguments.length ? (y = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : y;
};
return force;
}
// Computes the decimal coefficient and exponent of the specified number x with
// significant digits p, where x is positive and p is in [1, 21] or undefined.
// For example, formatDecimal(1.23) returns ["123", 0].
function formatDecimal(x, p) {
if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
var i, coefficient = x.slice(0, i);
// The string returned by toExponential either has the form \d\.\d+e[-+]\d+
// (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
return [
coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
+x.slice(i + 1)
];
}
function exponent$1(x) {
return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
}
function formatGroup(grouping, thousands) {
return function(value, width) {
var i = value.length,
t = [],
j = 0,
g = grouping[0],
length = 0;
while (i > 0 && g > 0) {
if (length + g + 1 > width) g = Math.max(1, width - length);
t.push(value.substring(i -= g, i + g));
if ((length += g + 1) > width) break;
g = grouping[j = (j + 1) % grouping.length];
}
return t.reverse().join(thousands);
};
}
function formatNumerals(numerals) {
return function(value) {
return value.replace(/[0-9]/g, function(i) {
return numerals[+i];
});
};
}
// [[fill]align][sign][symbol][0][width][,][.precision][~][type]
var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
function formatSpecifier(specifier) {
return new FormatSpecifier(specifier);
}
formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
function FormatSpecifier(specifier) {
if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
var match;
this.fill = match[1] || " ";
this.align = match[2] || ">";
this.sign = match[3] || "-";
this.symbol = match[4] || "";
this.zero = !!match[5];
this.width = match[6] && +match[6];
this.comma = !!match[7];
this.precision = match[8] && +match[8].slice(1);
this.trim = !!match[9];
this.type = match[10] || "";
}
FormatSpecifier.prototype.toString = function() {
return this.fill
+ this.align
+ this.sign
+ this.symbol
+ (this.zero ? "0" : "")
+ (this.width == null ? "" : Math.max(1, this.width | 0))
+ (this.comma ? "," : "")
+ (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
+ (this.trim ? "~" : "")
+ this.type;
};
// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
function formatTrim(s) {
out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
switch (s[i]) {
case ".": i0 = i1 = i; break;
case "0": if (i0 === 0) i0 = i; i1 = i; break;
default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;
}
}
return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
}
var prefixExponent;
function formatPrefixAuto(x, p) {
var d = formatDecimal(x, p);
if (!d) return x + "";
var coefficient = d[0],
exponent = d[1],
i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
n = coefficient.length;
return i === n ? coefficient
: i > n ? coefficient + new Array(i - n + 1).join("0")
: i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
: "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
}
function formatRounded(x, p) {
var d = formatDecimal(x, p);
if (!d) return x + "";
var coefficient = d[0],
exponent = d[1];
return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
: coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
: coefficient + new Array(exponent - coefficient.length + 2).join("0");
}
var formatTypes = {
"%": function(x, p) { return (x * 100).toFixed(p); },
"b": function(x) { return Math.round(x).toString(2); },
"c": function(x) { return x + ""; },
"d": function(x) { return Math.round(x).toString(10); },
"e": function(x, p) { return x.toExponential(p); },
"f": function(x, p) { return x.toFixed(p); },
"g": function(x, p) { return x.toPrecision(p); },
"o": function(x) { return Math.round(x).toString(8); },
"p": function(x, p) { return formatRounded(x * 100, p); },
"r": formatRounded,
"s": formatPrefixAuto,
"X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
"x": function(x) { return Math.round(x).toString(16); }
};
function identity$3(x) {
return x;
}
var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];
function formatLocale(locale) {
var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3,
currency = locale.currency,
decimal = locale.decimal,
numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3,
percent = locale.percent || "%";
function newFormat(specifier) {
specifier = formatSpecifier(specifier);
var fill = specifier.fill,
align = specifier.align,
sign = specifier.sign,
symbol = specifier.symbol,
zero = specifier.zero,
width = specifier.width,
comma = specifier.comma,
precision = specifier.precision,
trim = specifier.trim,
type = specifier.type;
// The "n" type is an alias for ",g".
if (type === "n") comma = true, type = "g";
// The "" type, and any invalid type, is an alias for ".12~g".
else if (!formatTypes[type]) precision == null && (precision = 12), trim = true, type = "g";
// If zero fill is specified, padding goes after sign and before digits.
if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
// Compute the prefix and suffix.
// For SI-prefix, the suffix is lazily computed.
var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
// What format function should we use?
// Is this an integer type?
// Can this type generate exponential notation?
var formatType = formatTypes[type],
maybeSuffix = /[defgprs%]/.test(type);
// Set the default precision if not specified,
// or clamp the specified precision to the supported range.
// For significant precision, it must be in [1, 21].
// For fixed precision, it must be in [0, 20].
precision = precision == null ? 6
: /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
: Math.max(0, Math.min(20, precision));
function format(value) {
var valuePrefix = prefix,
valueSuffix = suffix,
i, n, c;
if (type === "c") {
valueSuffix = formatType(value) + valueSuffix;
value = "";
} else {
value = +value;
// Perform the initial formatting.
var valueNegative = value < 0;
value = formatType(Math.abs(value), precision);
// Trim insignificant zeros.
if (trim) value = formatTrim(value);
// If a negative value rounds to zero during formatting, treat as positive.
if (valueNegative && +value === 0) valueNegative = false;
// Compute the prefix and suffix.
valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
// Break the formatted value into the integer “value” part that can be
// grouped, and fractional or exponential “suffix” part that is not.
if (maybeSuffix) {
i = -1, n = value.length;
while (++i < n) {
if (c = value.charCodeAt(i), 48 > c || c > 57) {
valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
value = value.slice(0, i);
break;
}
}
}
}
// If the fill character is not "0", grouping is applied before padding.
if (comma && !zero) value = group(value, Infinity);
// Compute the padding.
var length = valuePrefix.length + value.length + valueSuffix.length,
padding = length < width ? new Array(width - length + 1).join(fill) : "";
// If the fill character is "0", grouping is applied after padding.
if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
// Reconstruct the final output based on the desired alignment.
switch (align) {
case "<": value = valuePrefix + value + valueSuffix + padding; break;
case "=": value = valuePrefix + padding + value + valueSuffix; break;
case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
default: value = padding + valuePrefix + value + valueSuffix; break;
}
return numerals(value);
}
format.toString = function() {
return specifier + "";
};
return format;
}
function formatPrefix(specifier, value) {
var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3,
k = Math.pow(10, -e),
prefix = prefixes[8 + e / 3];
return function(value) {
return f(k * value) + prefix;
};
}
return {
format: newFormat,
formatPrefix: formatPrefix
};
}
var locale;
defaultLocale({
decimal: ".",
thousands: ",",
grouping: [3],
currency: ["$", ""]
});
function defaultLocale(definition) {
locale = formatLocale(definition);
exports.format = locale.format;
exports.formatPrefix = locale.formatPrefix;
return locale;
}
function precisionFixed(step) {
return Math.max(0, -exponent$1(Math.abs(step)));
}
function precisionPrefix(step, value) {
return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step)));
}
function precisionRound(step, max) {
step = Math.abs(step), max = Math.abs(max) - step;
return Math.max(0, exponent$1(max) - exponent$1(step)) + 1;
}
// Adds floating point numbers with twice the normal precision.
// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
// 305–363 (1997).
// Code adapted from GeographicLib by Charles F. F. Karney,
// http://geographiclib.sourceforge.net/
function adder() {
return new Adder;
}
function Adder() {
this.reset();
}
Adder.prototype = {
constructor: Adder,
reset: function() {
this.s = // rounded value
this.t = 0; // exact error
},
add: function(y) {
add$1(temp, y, this.t);
add$1(this, temp.s, this.s);
if (this.s) this.t += temp.t;
else this.s = temp.t;
},
valueOf: function() {
return this.s;
}
};
var temp = new Adder;
function add$1(adder, a, b) {
var x = adder.s = a + b,
bv = x - a,
av = x - bv;
adder.t = (a - av) + (b - bv);
}
var epsilon$2 = 1e-6;
var epsilon2$1 = 1e-12;
var pi$3 = Math.PI;
var halfPi$2 = pi$3 / 2;
var quarterPi = pi$3 / 4;
var tau$3 = pi$3 * 2;
var degrees$1 = 180 / pi$3;
var radians = pi$3 / 180;
var abs = Math.abs;
var atan = Math.atan;
var atan2 = Math.atan2;
var cos$1 = Math.cos;
var ceil = Math.ceil;
var exp = Math.exp;
var log = Math.log;
var pow = Math.pow;
var sin$1 = Math.sin;
var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
var sqrt = Math.sqrt;
var tan = Math.tan;
function acos(x) {
return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x);
}
function asin(x) {
return x > 1 ? halfPi$2 : x < -1 ? -halfPi$2 : Math.asin(x);
}
function haversin(x) {
return (x = sin$1(x / 2)) * x;
}
function noop$2() {}
function streamGeometry(geometry, stream) {
if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
streamGeometryType[geometry.type](geometry, stream);
}
}
var streamObjectType = {
Feature: function(object, stream) {
streamGeometry(object.geometry, stream);
},
FeatureCollection: function(object, stream) {
var features = object.features, i = -1, n = features.length;
while (++i < n) streamGeometry(features[i].geometry, stream);
}
};
var streamGeometryType = {
Sphere: function(object, stream) {
stream.sphere();
},
Point: function(object, stream) {
object = object.coordinates;
stream.point(object[0], object[1], object[2]);
},
MultiPoint: function(object, stream) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
},
LineString: function(object, stream) {
streamLine(object.coordinates, stream, 0);
},
MultiLineString: function(object, stream) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) streamLine(coordinates[i], stream, 0);
},
Polygon: function(object, stream) {
streamPolygon(object.coordinates, stream);
},
MultiPolygon: function(object, stream) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) streamPolygon(coordinates[i], stream);
},
GeometryCollection: function(object, stream) {
var geometries = object.geometries, i = -1, n = geometries.length;
while (++i < n) streamGeometry(geometries[i], stream);
}
};
function streamLine(coordinates, stream, closed) {
var i = -1, n = coordinates.length - closed, coordinate;
stream.lineStart();
while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
stream.lineEnd();
}
function streamPolygon(coordinates, stream) {
var i = -1, n = coordinates.length;
stream.polygonStart();
while (++i < n) streamLine(coordinates[i], stream, 1);
stream.polygonEnd();
}
function geoStream(object, stream) {
if (object && streamObjectType.hasOwnProperty(object.type)) {
streamObjectType[object.type](object, stream);
} else {
streamGeometry(object, stream);
}
}
var areaRingSum = adder();
var areaSum = adder(),
lambda00,
phi00,
lambda0,
cosPhi0,
sinPhi0;
var areaStream = {
point: noop$2,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: function() {
areaRingSum.reset();
areaStream.lineStart = areaRingStart;
areaStream.lineEnd = areaRingEnd;
},
polygonEnd: function() {
var areaRing = +areaRingSum;
areaSum.add(areaRing < 0 ? tau$3 + areaRing : areaRing);
this.lineStart = this.lineEnd = this.point = noop$2;
},
sphere: function() {
areaSum.add(tau$3);
}
};
function areaRingStart() {
areaStream.point = areaPointFirst;
}
function areaRingEnd() {
areaPoint(lambda00, phi00);
}
function areaPointFirst(lambda, phi) {
areaStream.point = areaPoint;
lambda00 = lambda, phi00 = phi;
lambda *= radians, phi *= radians;
lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi);
}
function areaPoint(lambda, phi) {
lambda *= radians, phi *= radians;
phi = phi / 2 + quarterPi; // half the angular distance from south pole
// Spherical excess E for a spherical triangle with vertices: south pole,
// previous point, current point. Uses a formula derived from Cagnoli’s
// theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
var dLambda = lambda - lambda0,
sdLambda = dLambda >= 0 ? 1 : -1,
adLambda = sdLambda * dLambda,
cosPhi = cos$1(phi),
sinPhi = sin$1(phi),
k = sinPhi0 * sinPhi,
u = cosPhi0 * cosPhi + k * cos$1(adLambda),
v = k * sdLambda * sin$1(adLambda);
areaRingSum.add(atan2(v, u));
// Advance the previous points.
lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
}
function area$1(object) {
areaSum.reset();
geoStream(object, areaStream);
return areaSum * 2;
}
function spherical(cartesian) {
return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
}
function cartesian(spherical) {
var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi);
return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)];
}
function cartesianDot(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
function cartesianCross(a, b) {
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
}
// TODO return a
function cartesianAddInPlace(a, b) {
a[0] += b[0], a[1] += b[1], a[2] += b[2];
}
function cartesianScale(vector, k) {
return [vector[0] * k, vector[1] * k, vector[2] * k];
}
// TODO return d
function cartesianNormalizeInPlace(d) {
var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
d[0] /= l, d[1] /= l, d[2] /= l;
}
var lambda0$1, phi0, lambda1, phi1, // bounds
lambda2, // previous lambda-coordinate
lambda00$1, phi00$1, // first point
p0, // previous 3D point
deltaSum = adder(),
ranges,
range;
var boundsStream = {
point: boundsPoint,
lineStart: boundsLineStart,
lineEnd: boundsLineEnd,
polygonStart: function() {
boundsStream.point = boundsRingPoint;
boundsStream.lineStart = boundsRingStart;
boundsStream.lineEnd = boundsRingEnd;
deltaSum.reset();
areaStream.polygonStart();
},
polygonEnd: function() {
areaStream.polygonEnd();
boundsStream.point = boundsPoint;
boundsStream.lineStart = boundsLineStart;
boundsStream.lineEnd = boundsLineEnd;
if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
else if (deltaSum > epsilon$2) phi1 = 90;
else if (deltaSum < -epsilon$2) phi0 = -90;
range[0] = lambda0$1, range[1] = lambda1;
}
};
function boundsPoint(lambda, phi) {
ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
}
function linePoint(lambda, phi) {
var p = cartesian([lambda * radians, phi * radians]);
if (p0) {
var normal = cartesianCross(p0, p),
equatorial = [normal[1], -normal[0], 0],
inflection = cartesianCross(equatorial, normal);
cartesianNormalizeInPlace(inflection);
inflection = spherical(inflection);
var delta = lambda - lambda2,
sign$$1 = delta > 0 ? 1 : -1,
lambdai = inflection[0] * degrees$1 * sign$$1,
phii,
antimeridian = abs(delta) > 180;
if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
phii = inflection[1] * degrees$1;
if (phii > phi1) phi1 = phii;
} else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
phii = -inflection[1] * degrees$1;
if (phii < phi0) phi0 = phii;
} else {
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
}
if (antimeridian) {
if (lambda < lambda2) {
if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
} else {
if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
}
} else {
if (lambda1 >= lambda0$1) {
if (lambda < lambda0$1) lambda0$1 = lambda;
if (lambda > lambda1) lambda1 = lambda;
} else {
if (lambda > lambda2) {
if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
} else {
if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
}
}
}
} else {
ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
}
if (phi < phi0) phi0 = phi;
if (phi > phi1) phi1 = phi;
p0 = p, lambda2 = lambda;
}
function boundsLineStart() {
boundsStream.point = linePoint;
}
function boundsLineEnd() {
range[0] = lambda0$1, range[1] = lambda1;
boundsStream.point = boundsPoint;
p0 = null;
}
function boundsRingPoint(lambda, phi) {
if (p0) {
var delta = lambda - lambda2;
deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
} else {
lambda00$1 = lambda, phi00$1 = phi;
}
areaStream.point(lambda, phi);
linePoint(lambda, phi);
}
function boundsRingStart() {
areaStream.lineStart();
}
function boundsRingEnd() {
boundsRingPoint(lambda00$1, phi00$1);
areaStream.lineEnd();
if (abs(deltaSum) > epsilon$2) lambda0$1 = -(lambda1 = 180);
range[0] = lambda0$1, range[1] = lambda1;
p0 = null;
}
// Finds the left-right distance between two longitudes.
// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
// the distance between ±180° to be 360°.
function angle(lambda0, lambda1) {
return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
}
function rangeCompare(a, b) {
return a[0] - b[0];
}
function rangeContains(range, x) {
return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
}
function bounds(feature) {
var i, n, a, b, merged, deltaMax, delta;
phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
ranges = [];
geoStream(feature, boundsStream);
// First, sort ranges by their minimum longitudes.
if (n = ranges.length) {
ranges.sort(rangeCompare);
// Then, merge any ranges that overlap.
for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
b = ranges[i];
if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
} else {
merged.push(a = b);
}
}
// Finally, find the largest gap between the merged ranges.
// The final bounding box will be the inverse of this gap.
for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
b = merged[i];
if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
}
}
ranges = range = null;
return lambda0$1 === Infinity || phi0 === Infinity
? [[NaN, NaN], [NaN, NaN]]
: [[lambda0$1, phi0], [lambda1, phi1]];
}
var W0, W1,
X0, Y0, Z0,
X1, Y1, Z1,
X2, Y2, Z2,
lambda00$2, phi00$2, // first point
x0, y0, z0; // previous point
var centroidStream = {
sphere: noop$2,
point: centroidPoint,
lineStart: centroidLineStart,
lineEnd: centroidLineEnd,
polygonStart: function() {
centroidStream.lineStart = centroidRingStart;
centroidStream.lineEnd = centroidRingEnd;
},
polygonEnd: function() {
centroidStream.lineStart = centroidLineStart;
centroidStream.lineEnd = centroidLineEnd;
}
};
// Arithmetic mean of Cartesian vectors.
function centroidPoint(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi);
centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi));
}
function centroidPointCartesian(x, y, z) {
++W0;
X0 += (x - X0) / W0;
Y0 += (y - Y0) / W0;
Z0 += (z - Z0) / W0;
}
function centroidLineStart() {
centroidStream.point = centroidLinePointFirst;
}
function centroidLinePointFirst(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi);
x0 = cosPhi * cos$1(lambda);
y0 = cosPhi * sin$1(lambda);
z0 = sin$1(phi);
centroidStream.point = centroidLinePoint;
centroidPointCartesian(x0, y0, z0);
}
function centroidLinePoint(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi),
x = cosPhi * cos$1(lambda),
y = cosPhi * sin$1(lambda),
z = sin$1(phi),
w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
W1 += w;
X1 += w * (x0 + (x0 = x));
Y1 += w * (y0 + (y0 = y));
Z1 += w * (z0 + (z0 = z));
centroidPointCartesian(x0, y0, z0);
}
function centroidLineEnd() {
centroidStream.point = centroidPoint;
}
// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
// J. Applied Mechanics 42, 239 (1975).
function centroidRingStart() {
centroidStream.point = centroidRingPointFirst;
}
function centroidRingEnd() {
centroidRingPoint(lambda00$2, phi00$2);
centroidStream.point = centroidPoint;
}
function centroidRingPointFirst(lambda, phi) {
lambda00$2 = lambda, phi00$2 = phi;
lambda *= radians, phi *= radians;
centroidStream.point = centroidRingPoint;
var cosPhi = cos$1(phi);
x0 = cosPhi * cos$1(lambda);
y0 = cosPhi * sin$1(lambda);
z0 = sin$1(phi);
centroidPointCartesian(x0, y0, z0);
}
function centroidRingPoint(lambda, phi) {
lambda *= radians, phi *= radians;
var cosPhi = cos$1(phi),
x = cosPhi * cos$1(lambda),
y = cosPhi * sin$1(lambda),
z = sin$1(phi),
cx = y0 * z - z0 * y,
cy = z0 * x - x0 * z,
cz = x0 * y - y0 * x,
m = sqrt(cx * cx + cy * cy + cz * cz),
w = asin(m), // line weight = angle
v = m && -w / m; // area weight multiplier
X2 += v * cx;
Y2 += v * cy;
Z2 += v * cz;
W1 += w;
X1 += w * (x0 + (x0 = x));
Y1 += w * (y0 + (y0 = y));
Z1 += w * (z0 + (z0 = z));
centroidPointCartesian(x0, y0, z0);
}
function centroid(object) {
W0 = W1 =
X0 = Y0 = Z0 =
X1 = Y1 = Z1 =
X2 = Y2 = Z2 = 0;
geoStream(object, centroidStream);
var x = X2,
y = Y2,
z = Z2,
m = x * x + y * y + z * z;
// If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
if (m < epsilon2$1) {
x = X1, y = Y1, z = Z1;
// If the feature has zero length, fall back to arithmetic mean of point vectors.
if (W1 < epsilon$2) x = X0, y = Y0, z = Z0;
m = x * x + y * y + z * z;
// If the feature still has an undefined ccentroid, then return.
if (m < epsilon2$1) return [NaN, NaN];
}
return [atan2(y, x) * degrees$1, asin(z / sqrt(m)) * degrees$1];
}
function constant$8(x) {
return function() {
return x;
};
}
function compose(a, b) {
function compose(x, y) {
return x = a(x, y), b(x[0], x[1]);
}
if (a.invert && b.invert) compose.invert = function(x, y) {
return x = b.invert(x, y), x && a.invert(x[0], x[1]);
};
return compose;
}
function rotationIdentity(lambda, phi) {
return [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
}
rotationIdentity.invert = rotationIdentity;
function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
return (deltaLambda %= tau$3) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
: rotationLambda(deltaLambda))
: (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
: rotationIdentity);
}
function forwardRotationLambda(deltaLambda) {
return function(lambda, phi) {
return lambda += deltaLambda, [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
};
}
function rotationLambda(deltaLambda) {
var rotation = forwardRotationLambda(deltaLambda);
rotation.invert = forwardRotationLambda(-deltaLambda);
return rotation;
}
function rotationPhiGamma(deltaPhi, deltaGamma) {
var cosDeltaPhi = cos$1(deltaPhi),
sinDeltaPhi = sin$1(deltaPhi),
cosDeltaGamma = cos$1(deltaGamma),
sinDeltaGamma = sin$1(deltaGamma);
function rotation(lambda, phi) {
var cosPhi = cos$1(phi),
x = cos$1(lambda) * cosPhi,
y = sin$1(lambda) * cosPhi,
z = sin$1(phi),
k = z * cosDeltaPhi + x * sinDeltaPhi;
return [
atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
asin(k * cosDeltaGamma + y * sinDeltaGamma)
];
}
rotation.invert = function(lambda, phi) {
var cosPhi = cos$1(phi),
x = cos$1(lambda) * cosPhi,
y = sin$1(lambda) * cosPhi,
z = sin$1(phi),
k = z * cosDeltaGamma - y * sinDeltaGamma;
return [
atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
asin(k * cosDeltaPhi - x * sinDeltaPhi)
];
};
return rotation;
}
function rotation(rotate) {
rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
function forward(coordinates) {
coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
}
forward.invert = function(coordinates) {
coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
};
return forward;
}
// Generates a circle centered at [0°, 0°], with a given radius and precision.
function circleStream(stream, radius, delta, direction, t0, t1) {
if (!delta) return;
var cosRadius = cos$1(radius),
sinRadius = sin$1(radius),
step = direction * delta;
if (t0 == null) {
t0 = radius + direction * tau$3;
t1 = radius - step / 2;
} else {
t0 = circleRadius(cosRadius, t0);
t1 = circleRadius(cosRadius, t1);
if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$3;
}
for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]);
stream.point(point[0], point[1]);
}
}
// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
function circleRadius(cosRadius, point) {
point = cartesian(point), point[0] -= cosRadius;
cartesianNormalizeInPlace(point);
var radius = acos(-point[1]);
return ((-point[2] < 0 ? -radius : radius) + tau$3 - epsilon$2) % tau$3;
}
function circle() {
var center = constant$8([0, 0]),
radius = constant$8(90),
precision = constant$8(6),
ring,
rotate,
stream = {point: point};
function point(x, y) {
ring.push(x = rotate(x, y));
x[0] *= degrees$1, x[1] *= degrees$1;
}
function circle() {
var c = center.apply(this, arguments),
r = radius.apply(this, arguments) * radians,
p = precision.apply(this, arguments) * radians;
ring = [];
rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert;
circleStream(stream, r, p, 1);
c = {type: "Polygon", coordinates: [ring]};
ring = rotate = null;
return c;
}
circle.center = function(_) {
return arguments.length ? (center = typeof _ === "function" ? _ : constant$8([+_[0], +_[1]]), circle) : center;
};
circle.radius = function(_) {
return arguments.length ? (radius = typeof _ === "function" ? _ : constant$8(+_), circle) : radius;
};
circle.precision = function(_) {
return arguments.length ? (precision = typeof _ === "function" ? _ : constant$8(+_), circle) : precision;
};
return circle;
}
function clipBuffer() {
var lines = [],
line;
return {
point: function(x, y) {
line.push([x, y]);
},
lineStart: function() {
lines.push(line = []);
},
lineEnd: noop$2,
rejoin: function() {
if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
},
result: function() {
var result = lines;
lines = [];
line = null;
return result;
}
};
}
function pointEqual(a, b) {
return abs(a[0] - b[0]) < epsilon$2 && abs(a[1] - b[1]) < epsilon$2;
}
function Intersection(point, points, other, entry) {
this.x = point;
this.z = points;
this.o = other; // another intersection
this.e = entry; // is an entry?
this.v = false; // visited
this.n = this.p = null; // next & previous
}
// A generalized polygon clipping algorithm: given a polygon that has been cut
// into its visible line segments, and rejoins the segments by interpolating
// along the clip edge.
function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) {
var subject = [],
clip = [],
i,
n;
segments.forEach(function(segment) {
if ((n = segment.length - 1) <= 0) return;
var n, p0 = segment[0], p1 = segment[n], x;
// If the first and last points of a segment are coincident, then treat as a
// closed ring. TODO if all rings are closed, then the winding order of the
// exterior ring should be checked.
if (pointEqual(p0, p1)) {
stream.lineStart();
for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
stream.lineEnd();
return;
}
subject.push(x = new Intersection(p0, segment, null, true));
clip.push(x.o = new Intersection(p0, null, x, false));
subject.push(x = new Intersection(p1, segment, null, false));
clip.push(x.o = new Intersection(p1, null, x, true));
});
if (!subject.length) return;
clip.sort(compareIntersection);
link$1(subject);
link$1(clip);
for (i = 0, n = clip.length; i < n; ++i) {
clip[i].e = startInside = !startInside;
}
var start = subject[0],
points,
point;
while (1) {
// Find first unvisited intersection.
var current = start,
isSubject = true;
while (current.v) if ((current = current.n) === start) return;
points = current.z;
stream.lineStart();
do {
current.v = current.o.v = true;
if (current.e) {
if (isSubject) {
for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
} else {
interpolate(current.x, current.n.x, 1, stream);
}
current = current.n;
} else {
if (isSubject) {
points = current.p.z;
for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
} else {
interpolate(current.x, current.p.x, -1, stream);
}
current = current.p;
}
current = current.o;
points = current.z;
isSubject = !isSubject;
} while (!current.v);
stream.lineEnd();
}
}
function link$1(array) {
if (!(n = array.length)) return;
var n,
i = 0,
a = array[0],
b;
while (++i < n) {
a.n = b = array[i];
b.p = a;
a = b;
}
a.n = b = array[0];
b.p = a;
}
var sum$1 = adder();
function polygonContains(polygon, point) {
var lambda = point[0],
phi = point[1],
sinPhi = sin$1(phi),
normal = [sin$1(lambda), -cos$1(lambda), 0],
angle = 0,
winding = 0;
sum$1.reset();
if (sinPhi === 1) phi = halfPi$2 + epsilon$2;
else if (sinPhi === -1) phi = -halfPi$2 - epsilon$2;
for (var i = 0, n = polygon.length; i < n; ++i) {
if (!(m = (ring = polygon[i]).length)) continue;
var ring,
m,
point0 = ring[m - 1],
lambda0 = point0[0],
phi0 = point0[1] / 2 + quarterPi,
sinPhi0 = sin$1(phi0),
cosPhi0 = cos$1(phi0);
for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
var point1 = ring[j],
lambda1 = point1[0],
phi1 = point1[1] / 2 + quarterPi,
sinPhi1 = sin$1(phi1),
cosPhi1 = cos$1(phi1),
delta = lambda1 - lambda0,
sign$$1 = delta >= 0 ? 1 : -1,
absDelta = sign$$1 * delta,
antimeridian = absDelta > pi$3,
k = sinPhi0 * sinPhi1;
sum$1.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta)));
angle += antimeridian ? delta + sign$$1 * tau$3 : delta;
// Are the longitudes either side of the point’s meridian (lambda),
// and are the latitudes smaller than the parallel (phi)?
if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
var arc = cartesianCross(cartesian(point0), cartesian(point1));
cartesianNormalizeInPlace(arc);
var intersection = cartesianCross(normal, arc);
cartesianNormalizeInPlace(intersection);
var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
winding += antimeridian ^ delta >= 0 ? 1 : -1;
}
}
}
}
// First, determine whether the South pole is inside or outside:
//
// It is inside if:
// * the polygon winds around it in a clockwise direction.
// * the polygon does not (cumulatively) wind around it, but has a negative
// (counter-clockwise) area.
//
// Second, count the (signed) number of times a segment crosses a lambda
// from the point to the South pole. If it is zero, then the point is the
// same side as the South pole.
return (angle < -epsilon$2 || angle < epsilon$2 && sum$1 < -epsilon$2) ^ (winding & 1);
}
function clip(pointVisible, clipLine, interpolate, start) {
return function(sink) {
var line = clipLine(sink),
ringBuffer = clipBuffer(),
ringSink = clipLine(ringBuffer),
polygonStarted = false,
polygon,
segments,
ring;
var clip = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: function() {
clip.point = pointRing;
clip.lineStart = ringStart;
clip.lineEnd = ringEnd;
segments = [];
polygon = [];
},
polygonEnd: function() {
clip.point = point;
clip.lineStart = lineStart;
clip.lineEnd = lineEnd;
segments = merge(segments);
var startInside = polygonContains(polygon, start);
if (segments.length) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
} else if (startInside) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
sink.lineStart();
interpolate(null, null, 1, sink);
sink.lineEnd();
}
if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
segments = polygon = null;
},
sphere: function() {
sink.polygonStart();
sink.lineStart();
interpolate(null, null, 1, sink);
sink.lineEnd();
sink.polygonEnd();
}
};
function point(lambda, phi) {
if (pointVisible(lambda, phi)) sink.point(lambda, phi);
}
function pointLine(lambda, phi) {
line.point(lambda, phi);
}
function lineStart() {
clip.point = pointLine;
line.lineStart();
}
function lineEnd() {
clip.point = point;
line.lineEnd();
}
function pointRing(lambda, phi) {
ring.push([lambda, phi]);
ringSink.point(lambda, phi);
}
function ringStart() {
ringSink.lineStart();
ring = [];
}
function ringEnd() {
pointRing(ring[0][0], ring[0][1]);
ringSink.lineEnd();
var clean = ringSink.clean(),
ringSegments = ringBuffer.result(),
i, n = ringSegments.length, m,
segment,
point;
ring.pop();
polygon.push(ring);
ring = null;
if (!n) return;
// No intersections.
if (clean & 1) {
segment = ringSegments[0];
if ((m = segment.length - 1) > 0) {
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
sink.lineStart();
for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
sink.lineEnd();
}
return;
}
// Rejoin connected segments.
// TODO reuse ringBuffer.rejoin()?
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
segments.push(ringSegments.filter(validSegment));
}
return clip;
};
}
function validSegment(segment) {
return segment.length > 1;
}
// Intersections are sorted along the clip edge. For both antimeridian cutting
// and circle clipping, the same comparison is used.
function compareIntersection(a, b) {
return ((a = a.x)[0] < 0 ? a[1] - halfPi$2 - epsilon$2 : halfPi$2 - a[1])
- ((b = b.x)[0] < 0 ? b[1] - halfPi$2 - epsilon$2 : halfPi$2 - b[1]);
}
var clipAntimeridian = clip(
function() { return true; },
clipAntimeridianLine,
clipAntimeridianInterpolate,
[-pi$3, -halfPi$2]
);
// Takes a line and cuts into visible segments. Return values: 0 - there were
// intersections or the line was empty; 1 - no intersections; 2 - there were
// intersections, and the first and last segments should be rejoined.
function clipAntimeridianLine(stream) {
var lambda0 = NaN,
phi0 = NaN,
sign0 = NaN,
clean; // no intersections
return {
lineStart: function() {
stream.lineStart();
clean = 1;
},
point: function(lambda1, phi1) {
var sign1 = lambda1 > 0 ? pi$3 : -pi$3,
delta = abs(lambda1 - lambda0);
if (abs(delta - pi$3) < epsilon$2) { // line crosses a pole
stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$2 : -halfPi$2);
stream.point(sign0, phi0);
stream.lineEnd();
stream.lineStart();
stream.point(sign1, phi0);
stream.point(lambda1, phi0);
clean = 0;
} else if (sign0 !== sign1 && delta >= pi$3) { // line crosses antimeridian
if (abs(lambda0 - sign0) < epsilon$2) lambda0 -= sign0 * epsilon$2; // handle degeneracies
if (abs(lambda1 - sign1) < epsilon$2) lambda1 -= sign1 * epsilon$2;
phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
stream.point(sign0, phi0);
stream.lineEnd();
stream.lineStart();
stream.point(sign1, phi0);
clean = 0;
}
stream.point(lambda0 = lambda1, phi0 = phi1);
sign0 = sign1;
},
lineEnd: function() {
stream.lineEnd();
lambda0 = phi0 = NaN;
},
clean: function() {
return 2 - clean; // if intersections, rejoin first and last segments
}
};
}
function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
var cosPhi0,
cosPhi1,
sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
return abs(sinLambda0Lambda1) > epsilon$2
? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1)
- sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0))
/ (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
: (phi0 + phi1) / 2;
}
function clipAntimeridianInterpolate(from, to, direction, stream) {
var phi;
if (from == null) {
phi = direction * halfPi$2;
stream.point(-pi$3, phi);
stream.point(0, phi);
stream.point(pi$3, phi);
stream.point(pi$3, 0);
stream.point(pi$3, -phi);
stream.point(0, -phi);
stream.point(-pi$3, -phi);
stream.point(-pi$3, 0);
stream.point(-pi$3, phi);
} else if (abs(from[0] - to[0]) > epsilon$2) {
var lambda = from[0] < to[0] ? pi$3 : -pi$3;
phi = direction * lambda / 2;
stream.point(-lambda, phi);
stream.point(0, phi);
stream.point(lambda, phi);
} else {
stream.point(to[0], to[1]);
}
}
function clipCircle(radius) {
var cr = cos$1(radius),
delta = 6 * radians,
smallRadius = cr > 0,
notHemisphere = abs(cr) > epsilon$2; // TODO optimise for this common case
function interpolate(from, to, direction, stream) {
circleStream(stream, radius, delta, direction, from, to);
}
function visible(lambda, phi) {
return cos$1(lambda) * cos$1(phi) > cr;
}
// Takes a line and cuts into visible segments. Return values used for polygon
// clipping: 0 - there were intersections or the line was empty; 1 - no
// intersections 2 - there were intersections, and the first and last segments
// should be rejoined.
function clipLine(stream) {
var point0, // previous point
c0, // code for previous point
v0, // visibility of previous point
v00, // visibility of first point
clean; // no intersections
return {
lineStart: function() {
v00 = v0 = false;
clean = 1;
},
point: function(lambda, phi) {
var point1 = [lambda, phi],
point2,
v = visible(lambda, phi),
c = smallRadius
? v ? 0 : code(lambda, phi)
: v ? code(lambda + (lambda < 0 ? pi$3 : -pi$3), phi) : 0;
if (!point0 && (v00 = v0 = v)) stream.lineStart();
// Handle degeneracies.
// TODO ignore if not clipping polygons.
if (v !== v0) {
point2 = intersect(point0, point1);
if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) {
point1[0] += epsilon$2;
point1[1] += epsilon$2;
v = visible(point1[0], point1[1]);
}
}
if (v !== v0) {
clean = 0;
if (v) {
// outside going in
stream.lineStart();
point2 = intersect(point1, point0);
stream.point(point2[0], point2[1]);
} else {
// inside going out
point2 = intersect(point0, point1);
stream.point(point2[0], point2[1]);
stream.lineEnd();
}
point0 = point2;
} else if (notHemisphere && point0 && smallRadius ^ v) {
var t;
// If the codes for two points are different, or are both zero,
// and there this segment intersects with the small circle.
if (!(c & c0) && (t = intersect(point1, point0, true))) {
clean = 0;
if (smallRadius) {
stream.lineStart();
stream.point(t[0][0], t[0][1]);
stream.point(t[1][0], t[1][1]);
stream.lineEnd();
} else {
stream.point(t[1][0], t[1][1]);
stream.lineEnd();
stream.lineStart();
stream.point(t[0][0], t[0][1]);
}
}
}
if (v && (!point0 || !pointEqual(point0, point1))) {
stream.point(point1[0], point1[1]);
}
point0 = point1, v0 = v, c0 = c;
},
lineEnd: function() {
if (v0) stream.lineEnd();
point0 = null;
},
// Rejoin first and last segments if there were intersections and the first
// and last points were visible.
clean: function() {
return clean | ((v00 && v0) << 1);
}
};
}
// Intersects the great circle between a and b with the clip circle.
function intersect(a, b, two) {
var pa = cartesian(a),
pb = cartesian(b);
// We have two planes, n1.p = d1 and n2.p = d2.
// Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
var n1 = [1, 0, 0], // normal
n2 = cartesianCross(pa, pb),
n2n2 = cartesianDot(n2, n2),
n1n2 = n2[0], // cartesianDot(n1, n2),
determinant = n2n2 - n1n2 * n1n2;
// Two polar points.
if (!determinant) return !two && a;
var c1 = cr * n2n2 / determinant,
c2 = -cr * n1n2 / determinant,
n1xn2 = cartesianCross(n1, n2),
A = cartesianScale(n1, c1),
B = cartesianScale(n2, c2);
cartesianAddInPlace(A, B);
// Solve |p(t)|^2 = 1.
var u = n1xn2,
w = cartesianDot(A, u),
uu = cartesianDot(u, u),
t2 = w * w - uu * (cartesianDot(A, A) - 1);
if (t2 < 0) return;
var t = sqrt(t2),
q = cartesianScale(u, (-w - t) / uu);
cartesianAddInPlace(q, A);
q = spherical(q);
if (!two) return q;
// Two intersection points.
var lambda0 = a[0],
lambda1 = b[0],
phi0 = a[1],
phi1 = b[1],
z;
if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
var delta = lambda1 - lambda0,
polar = abs(delta - pi$3) < epsilon$2,
meridian = polar || delta < epsilon$2;
if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
// Check that the first point is between a and b.
if (meridian
? polar
? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$2 ? phi0 : phi1)
: phi0 <= q[1] && q[1] <= phi1
: delta > pi$3 ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
var q1 = cartesianScale(u, (-w + t) / uu);
cartesianAddInPlace(q1, A);
return [q, spherical(q1)];
}
}
// Generates a 4-bit vector representing the location of a point relative to
// the small circle's bounding box.
function code(lambda, phi) {
var r = smallRadius ? radius : pi$3 - radius,
code = 0;
if (lambda < -r) code |= 1; // left
else if (lambda > r) code |= 2; // right
if (phi < -r) code |= 4; // below
else if (phi > r) code |= 8; // above
return code;
}
return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$3, radius - pi$3]);
}
function clipLine(a, b, x0, y0, x1, y1) {
var ax = a[0],
ay = a[1],
bx = b[0],
by = b[1],
t0 = 0,
t1 = 1,
dx = bx - ax,
dy = by - ay,
r;
r = x0 - ax;
if (!dx && r > 0) return;
r /= dx;
if (dx < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dx > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
r = x1 - ax;
if (!dx && r < 0) return;
r /= dx;
if (dx < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dx > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
r = y0 - ay;
if (!dy && r > 0) return;
r /= dy;
if (dy < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dy > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
r = y1 - ay;
if (!dy && r < 0) return;
r /= dy;
if (dy < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dy > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
return true;
}
var clipMax = 1e9, clipMin = -clipMax;
// TODO Use d3-polygon’s polygonContains here for the ring check?
// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
function clipRectangle(x0, y0, x1, y1) {
function visible(x, y) {
return x0 <= x && x <= x1 && y0 <= y && y <= y1;
}
function interpolate(from, to, direction, stream) {
var a = 0, a1 = 0;
if (from == null
|| (a = corner(from, direction)) !== (a1 = corner(to, direction))
|| comparePoint(from, to) < 0 ^ direction > 0) {
do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
while ((a = (a + direction + 4) % 4) !== a1);
} else {
stream.point(to[0], to[1]);
}
}
function corner(p, direction) {
return abs(p[0] - x0) < epsilon$2 ? direction > 0 ? 0 : 3
: abs(p[0] - x1) < epsilon$2 ? direction > 0 ? 2 : 1
: abs(p[1] - y0) < epsilon$2 ? direction > 0 ? 1 : 0
: direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
}
function compareIntersection(a, b) {
return comparePoint(a.x, b.x);
}
function comparePoint(a, b) {
var ca = corner(a, 1),
cb = corner(b, 1);
return ca !== cb ? ca - cb
: ca === 0 ? b[1] - a[1]
: ca === 1 ? a[0] - b[0]
: ca === 2 ? a[1] - b[1]
: b[0] - a[0];
}
return function(stream) {
var activeStream = stream,
bufferStream = clipBuffer(),
segments,
polygon,
ring,
x__, y__, v__, // first point
x_, y_, v_, // previous point
first,
clean;
var clipStream = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: polygonStart,
polygonEnd: polygonEnd
};
function point(x, y) {
if (visible(x, y)) activeStream.point(x, y);
}
function polygonInside() {
var winding = 0;
for (var i = 0, n = polygon.length; i < n; ++i) {
for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
}
}
return winding;
}
// Buffer geometry within a polygon and then clip it en masse.
function polygonStart() {
activeStream = bufferStream, segments = [], polygon = [], clean = true;
}
function polygonEnd() {
var startInside = polygonInside(),
cleanInside = clean && startInside,
visible = (segments = merge(segments)).length;
if (cleanInside || visible) {
stream.polygonStart();
if (cleanInside) {
stream.lineStart();
interpolate(null, null, 1, stream);
stream.lineEnd();
}
if (visible) {
clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
}
stream.polygonEnd();
}
activeStream = stream, segments = polygon = ring = null;
}
function lineStart() {
clipStream.point = linePoint;
if (polygon) polygon.push(ring = []);
first = true;
v_ = false;
x_ = y_ = NaN;
}
// TODO rather than special-case polygons, simply handle them separately.
// Ideally, coincident intersection points should be jittered to avoid
// clipping issues.
function lineEnd() {
if (segments) {
linePoint(x__, y__);
if (v__ && v_) bufferStream.rejoin();
segments.push(bufferStream.result());
}
clipStream.point = point;
if (v_) activeStream.lineEnd();
}
function linePoint(x, y) {
var v = visible(x, y);
if (polygon) ring.push([x, y]);
if (first) {
x__ = x, y__ = y, v__ = v;
first = false;
if (v) {
activeStream.lineStart();
activeStream.point(x, y);
}
} else {
if (v && v_) activeStream.point(x, y);
else {
var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
if (clipLine(a, b, x0, y0, x1, y1)) {
if (!v_) {
activeStream.lineStart();
activeStream.point(a[0], a[1]);
}
activeStream.point(b[0], b[1]);
if (!v) activeStream.lineEnd();
clean = false;
} else if (v) {
activeStream.lineStart();
activeStream.point(x, y);
clean = false;
}
}
}
x_ = x, y_ = y, v_ = v;
}
return clipStream;
};
}
function extent$1() {
var x0 = 0,
y0 = 0,
x1 = 960,
y1 = 500,
cache,
cacheStream,
clip;
return clip = {
stream: function(stream) {
return cache && cacheStream === stream ? cache : cache = clipRectangle(x0, y0, x1, y1)(cacheStream = stream);
},
extent: function(_) {
return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];
}
};
}
var lengthSum = adder(),
lambda0$2,
sinPhi0$1,
cosPhi0$1;
var lengthStream = {
sphere: noop$2,
point: noop$2,
lineStart: lengthLineStart,
lineEnd: noop$2,
polygonStart: noop$2,
polygonEnd: noop$2
};
function lengthLineStart() {
lengthStream.point = lengthPointFirst;
lengthStream.lineEnd = lengthLineEnd;
}
function lengthLineEnd() {
lengthStream.point = lengthStream.lineEnd = noop$2;
}
function lengthPointFirst(lambda, phi) {
lambda *= radians, phi *= radians;
lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi);
lengthStream.point = lengthPoint;
}
function lengthPoint(lambda, phi) {
lambda *= radians, phi *= radians;
var sinPhi = sin$1(phi),
cosPhi = cos$1(phi),
delta = abs(lambda - lambda0$2),
cosDelta = cos$1(delta),
sinDelta = sin$1(delta),
x = cosPhi * sinDelta,
y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
lengthSum.add(atan2(sqrt(x * x + y * y), z));
lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
}
function length$1(object) {
lengthSum.reset();
geoStream(object, lengthStream);
return +lengthSum;
}
var coordinates = [null, null],
object$1 = {type: "LineString", coordinates: coordinates};
function distance(a, b) {
coordinates[0] = a;
coordinates[1] = b;
return length$1(object$1);
}
var containsObjectType = {
Feature: function(object, point) {
return containsGeometry(object.geometry, point);
},
FeatureCollection: function(object, point) {
var features = object.features, i = -1, n = features.length;
while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;
return false;
}
};
var containsGeometryType = {
Sphere: function() {
return true;
},
Point: function(object, point) {
return containsPoint(object.coordinates, point);
},
MultiPoint: function(object, point) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) if (containsPoint(coordinates[i], point)) return true;
return false;
},
LineString: function(object, point) {
return containsLine(object.coordinates, point);
},
MultiLineString: function(object, point) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) if (containsLine(coordinates[i], point)) return true;
return false;
},
Polygon: function(object, point) {
return containsPolygon(object.coordinates, point);
},
MultiPolygon: function(object, point) {
var coordinates = object.coordinates, i = -1, n = coordinates.length;
while (++i < n) if (containsPolygon(coordinates[i], point)) return true;
return false;
},
GeometryCollection: function(object, point) {
var geometries = object.geometries, i = -1, n = geometries.length;
while (++i < n) if (containsGeometry(geometries[i], point)) return true;
return false;
}
};
function containsGeometry(geometry, point) {
return geometry && containsGeometryType.hasOwnProperty(geometry.type)
? containsGeometryType[geometry.type](geometry, point)
: false;
}
function containsPoint(coordinates, point) {
return distance(coordinates, point) === 0;
}
function containsLine(coordinates, point) {
var ab = distance(coordinates[0], coordinates[1]),
ao = distance(coordinates[0], point),
ob = distance(point, coordinates[1]);
return ao + ob <= ab + epsilon$2;
}
function containsPolygon(coordinates, point) {
return !!polygonContains(coordinates.map(ringRadians), pointRadians(point));
}
function ringRadians(ring) {
return ring = ring.map(pointRadians), ring.pop(), ring;
}
function pointRadians(point) {
return [point[0] * radians, point[1] * radians];
}
function contains$1(object, point) {
return (object && containsObjectType.hasOwnProperty(object.type)
? containsObjectType[object.type]
: containsGeometry)(object, point);
}
function graticuleX(y0, y1, dy) {
var y = sequence(y0, y1 - epsilon$2, dy).concat(y1);
return function(x) { return y.map(function(y) { return [x, y]; }); };
}
function graticuleY(x0, x1, dx) {
var x = sequence(x0, x1 - epsilon$2, dx).concat(x1);
return function(y) { return x.map(function(x) { return [x, y]; }); };
}
function graticule() {
var x1, x0, X1, X0,
y1, y0, Y1, Y0,
dx = 10, dy = dx, DX = 90, DY = 360,
x, y, X, Y,
precision = 2.5;
function graticule() {
return {type: "MultiLineString", coordinates: lines()};
}
function lines() {
return sequence(ceil(X0 / DX) * DX, X1, DX).map(X)
.concat(sequence(ceil(Y0 / DY) * DY, Y1, DY).map(Y))
.concat(sequence(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$2; }).map(x))
.concat(sequence(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$2; }).map(y));
}
graticule.lines = function() {
return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; });
};
graticule.outline = function() {
return {
type: "Polygon",
coordinates: [
X(X0).concat(
Y(Y1).slice(1),
X(X1).reverse().slice(1),
Y(Y0).reverse().slice(1))
]
};
};
graticule.extent = function(_) {
if (!arguments.length) return graticule.extentMinor();
return graticule.extentMajor(_).extentMinor(_);
};
graticule.extentMajor = function(_) {
if (!arguments.length) return [[X0, Y0], [X1, Y1]];
X0 = +_[0][0], X1 = +_[1][0];
Y0 = +_[0][1], Y1 = +_[1][1];
if (X0 > X1) _ = X0, X0 = X1, X1 = _;
if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
return graticule.precision(precision);
};
graticule.extentMinor = function(_) {
if (!arguments.length) return [[x0, y0], [x1, y1]];
x0 = +_[0][0], x1 = +_[1][0];
y0 = +_[0][1], y1 = +_[1][1];
if (x0 > x1) _ = x0, x0 = x1, x1 = _;
if (y0 > y1) _ = y0, y0 = y1, y1 = _;
return graticule.precision(precision);
};
graticule.step = function(_) {
if (!arguments.length) return graticule.stepMinor();
return graticule.stepMajor(_).stepMinor(_);
};
graticule.stepMajor = function(_) {
if (!arguments.length) return [DX, DY];
DX = +_[0], DY = +_[1];
return graticule;
};
graticule.stepMinor = function(_) {
if (!arguments.length) return [dx, dy];
dx = +_[0], dy = +_[1];
return graticule;
};
graticule.precision = function(_) {
if (!arguments.length) return precision;
precision = +_;
x = graticuleX(y0, y1, 90);
y = graticuleY(x0, x1, precision);
X = graticuleX(Y0, Y1, 90);
Y = graticuleY(X0, X1, precision);
return graticule;
};
return graticule
.extentMajor([[-180, -90 + epsilon$2], [180, 90 - epsilon$2]])
.extentMinor([[-180, -80 - epsilon$2], [180, 80 + epsilon$2]]);
}
function graticule10() {
return graticule()();
}
function interpolate$1(a, b) {
var x0 = a[0] * radians,
y0 = a[1] * radians,
x1 = b[0] * radians,
y1 = b[1] * radians,
cy0 = cos$1(y0),
sy0 = sin$1(y0),
cy1 = cos$1(y1),
sy1 = sin$1(y1),
kx0 = cy0 * cos$1(x0),
ky0 = cy0 * sin$1(x0),
kx1 = cy1 * cos$1(x1),
ky1 = cy1 * sin$1(x1),
d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))),
k = sin$1(d);
var interpolate = d ? function(t) {
var B = sin$1(t *= d) / k,
A = sin$1(d - t) / k,
x = A * kx0 + B * kx1,
y = A * ky0 + B * ky1,
z = A * sy0 + B * sy1;
return [
atan2(y, x) * degrees$1,
atan2(z, sqrt(x * x + y * y)) * degrees$1
];
} : function() {
return [x0 * degrees$1, y0 * degrees$1];
};
interpolate.distance = d;
return interpolate;
}
function identity$4(x) {
return x;
}
var areaSum$1 = adder(),
areaRingSum$1 = adder(),
x00,
y00,
x0$1,
y0$1;
var areaStream$1 = {
point: noop$2,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: function() {
areaStream$1.lineStart = areaRingStart$1;
areaStream$1.lineEnd = areaRingEnd$1;
},
polygonEnd: function() {
areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2;
areaSum$1.add(abs(areaRingSum$1));
areaRingSum$1.reset();
},
result: function() {
var area = areaSum$1 / 2;
areaSum$1.reset();
return area;
}
};
function areaRingStart$1() {
areaStream$1.point = areaPointFirst$1;
}
function areaPointFirst$1(x, y) {
areaStream$1.point = areaPoint$1;
x00 = x0$1 = x, y00 = y0$1 = y;
}
function areaPoint$1(x, y) {
areaRingSum$1.add(y0$1 * x - x0$1 * y);
x0$1 = x, y0$1 = y;
}
function areaRingEnd$1() {
areaPoint$1(x00, y00);
}
var x0$2 = Infinity,
y0$2 = x0$2,
x1 = -x0$2,
y1 = x1;
var boundsStream$1 = {
point: boundsPoint$1,
lineStart: noop$2,
lineEnd: noop$2,
polygonStart: noop$2,
polygonEnd: noop$2,
result: function() {
var bounds = [[x0$2, y0$2], [x1, y1]];
x1 = y1 = -(y0$2 = x0$2 = Infinity);
return bounds;
}
};
function boundsPoint$1(x, y) {
if (x < x0$2) x0$2 = x;
if (x > x1) x1 = x;
if (y < y0$2) y0$2 = y;
if (y > y1) y1 = y;
}
// TODO Enforce positive area for exterior, negative area for interior?
var X0$1 = 0,
Y0$1 = 0,
Z0$1 = 0,
X1$1 = 0,
Y1$1 = 0,
Z1$1 = 0,
X2$1 = 0,
Y2$1 = 0,
Z2$1 = 0,
x00$1,
y00$1,
x0$3,
y0$3;
var centroidStream$1 = {
point: centroidPoint$1,
lineStart: centroidLineStart$1,
lineEnd: centroidLineEnd$1,
polygonStart: function() {
centroidStream$1.lineStart = centroidRingStart$1;
centroidStream$1.lineEnd = centroidRingEnd$1;
},
polygonEnd: function() {
centroidStream$1.point = centroidPoint$1;
centroidStream$1.lineStart = centroidLineStart$1;
centroidStream$1.lineEnd = centroidLineEnd$1;
},
result: function() {
var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
: Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
: Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
: [NaN, NaN];
X0$1 = Y0$1 = Z0$1 =
X1$1 = Y1$1 = Z1$1 =
X2$1 = Y2$1 = Z2$1 = 0;
return centroid;
}
};
function centroidPoint$1(x, y) {
X0$1 += x;
Y0$1 += y;
++Z0$1;
}
function centroidLineStart$1() {
centroidStream$1.point = centroidPointFirstLine;
}
function centroidPointFirstLine(x, y) {
centroidStream$1.point = centroidPointLine;
centroidPoint$1(x0$3 = x, y0$3 = y);
}
function centroidPointLine(x, y) {
var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy);
X1$1 += z * (x0$3 + x) / 2;
Y1$1 += z * (y0$3 + y) / 2;
Z1$1 += z;
centroidPoint$1(x0$3 = x, y0$3 = y);
}
function centroidLineEnd$1() {
centroidStream$1.point = centroidPoint$1;
}
function centroidRingStart$1() {
centroidStream$1.point = centroidPointFirstRing;
}
function centroidRingEnd$1() {
centroidPointRing(x00$1, y00$1);
}
function centroidPointFirstRing(x, y) {
centroidStream$1.point = centroidPointRing;
centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
}
function centroidPointRing(x, y) {
var dx = x - x0$3,
dy = y - y0$3,
z = sqrt(dx * dx + dy * dy);
X1$1 += z * (x0$3 + x) / 2;
Y1$1 += z * (y0$3 + y) / 2;
Z1$1 += z;
z = y0$3 * x - x0$3 * y;
X2$1 += z * (x0$3 + x);
Y2$1 += z * (y0$3 + y);
Z2$1 += z * 3;
centroidPoint$1(x0$3 = x, y0$3 = y);
}
function PathContext(context) {
this._context = context;
}
PathContext.prototype = {
_radius: 4.5,
pointRadius: function(_) {
return this._radius = _, this;
},
polygonStart: function() {
this._line = 0;
},
polygonEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line === 0) this._context.closePath();
this._point = NaN;
},
point: function(x, y) {
switch (this._point) {
case 0: {
this._context.moveTo(x, y);
this._point = 1;
break;
}
case 1: {
this._context.lineTo(x, y);
break;
}
default: {
this._context.moveTo(x + this._radius, y);
this._context.arc(x, y, this._radius, 0, tau$3);
break;
}
}
},
result: noop$2
};
var lengthSum$1 = adder(),
lengthRing,
x00$2,
y00$2,
x0$4,
y0$4;
var lengthStream$1 = {
point: noop$2,
lineStart: function() {
lengthStream$1.point = lengthPointFirst$1;
},
lineEnd: function() {
if (lengthRing) lengthPoint$1(x00$2, y00$2);
lengthStream$1.point = noop$2;
},
polygonStart: function() {
lengthRing = true;
},
polygonEnd: function() {
lengthRing = null;
},
result: function() {
var length = +lengthSum$1;
lengthSum$1.reset();
return length;
}
};
function lengthPointFirst$1(x, y) {
lengthStream$1.point = lengthPoint$1;
x00$2 = x0$4 = x, y00$2 = y0$4 = y;
}
function lengthPoint$1(x, y) {
x0$4 -= x, y0$4 -= y;
lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4));
x0$4 = x, y0$4 = y;
}
function PathString() {
this._string = [];
}
PathString.prototype = {
_radius: 4.5,
_circle: circle$1(4.5),
pointRadius: function(_) {
if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
return this;
},
polygonStart: function() {
this._line = 0;
},
polygonEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line === 0) this._string.push("Z");
this._point = NaN;
},
point: function(x, y) {
switch (this._point) {
case 0: {
this._string.push("M", x, ",", y);
this._point = 1;
break;
}
case 1: {
this._string.push("L", x, ",", y);
break;
}
default: {
if (this._circle == null) this._circle = circle$1(this._radius);
this._string.push("M", x, ",", y, this._circle);
break;
}
}
},
result: function() {
if (this._string.length) {
var result = this._string.join("");
this._string = [];
return result;
} else {
return null;
}
}
};
function circle$1(radius) {
return "m0," + radius
+ "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
+ "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
+ "z";
}
function index$1(projection, context) {
var pointRadius = 4.5,
projectionStream,
contextStream;
function path(object) {
if (object) {
if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
geoStream(object, projectionStream(contextStream));
}
return contextStream.result();
}
path.area = function(object) {
geoStream(object, projectionStream(areaStream$1));
return areaStream$1.result();
};
path.measure = function(object) {
geoStream(object, projectionStream(lengthStream$1));
return lengthStream$1.result();
};
path.bounds = function(object) {
geoStream(object, projectionStream(boundsStream$1));
return boundsStream$1.result();
};
path.centroid = function(object) {
geoStream(object, projectionStream(centroidStream$1));
return centroidStream$1.result();
};
path.projection = function(_) {
return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
};
path.context = function(_) {
if (!arguments.length) return context;
contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _);
if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
return path;
};
path.pointRadius = function(_) {
if (!arguments.length) return pointRadius;
pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
return path;
};
return path.projection(projection).context(context);
}
function transform(methods) {
return {
stream: transformer(methods)
};
}
function transformer(methods) {
return function(stream) {
var s = new TransformStream;
for (var key in methods) s[key] = methods[key];
s.stream = stream;
return s;
};
}
function TransformStream() {}
TransformStream.prototype = {
constructor: TransformStream,
point: function(x, y) { this.stream.point(x, y); },
sphere: function() { this.stream.sphere(); },
lineStart: function() { this.stream.lineStart(); },
lineEnd: function() { this.stream.lineEnd(); },
polygonStart: function() { this.stream.polygonStart(); },
polygonEnd: function() { this.stream.polygonEnd(); }
};
function fit(projection, fitBounds, object) {
var clip = projection.clipExtent && projection.clipExtent();
projection.scale(150).translate([0, 0]);
if (clip != null) projection.clipExtent(null);
geoStream(object, projection.stream(boundsStream$1));
fitBounds(boundsStream$1.result());
if (clip != null) projection.clipExtent(clip);
return projection;
}
function fitExtent(projection, extent, object) {
return fit(projection, function(b) {
var w = extent[1][0] - extent[0][0],
h = extent[1][1] - extent[0][1],
k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
projection.scale(150 * k).translate([x, y]);
}, object);
}
function fitSize(projection, size, object) {
return fitExtent(projection, [[0, 0], size], object);
}
function fitWidth(projection, width, object) {
return fit(projection, function(b) {
var w = +width,
k = w / (b[1][0] - b[0][0]),
x = (w - k * (b[1][0] + b[0][0])) / 2,
y = -k * b[0][1];
projection.scale(150 * k).translate([x, y]);
}, object);
}
function fitHeight(projection, height, object) {
return fit(projection, function(b) {
var h = +height,
k = h / (b[1][1] - b[0][1]),
x = -k * b[0][0],
y = (h - k * (b[1][1] + b[0][1])) / 2;
projection.scale(150 * k).translate([x, y]);
}, object);
}
var maxDepth = 16, // maximum depth of subdivision
cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)
function resample(project, delta2) {
return +delta2 ? resample$1(project, delta2) : resampleNone(project);
}
function resampleNone(project) {
return transformer({
point: function(x, y) {
x = project(x, y);
this.stream.point(x[0], x[1]);
}
});
}
function resample$1(project, delta2) {
function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
var dx = x1 - x0,
dy = y1 - y0,
d2 = dx * dx + dy * dy;
if (d2 > 4 * delta2 && depth--) {
var a = a0 + a1,
b = b0 + b1,
c = c0 + c1,
m = sqrt(a * a + b * b + c * c),
phi2 = asin(c /= m),
lambda2 = abs(abs(c) - 1) < epsilon$2 || abs(lambda0 - lambda1) < epsilon$2 ? (lambda0 + lambda1) / 2 : atan2(b, a),
p = project(lambda2, phi2),
x2 = p[0],
y2 = p[1],
dx2 = x2 - x0,
dy2 = y2 - y0,
dz = dy * dx2 - dx * dy2;
if (dz * dz / d2 > delta2 // perpendicular projected distance
|| abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
|| a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
stream.point(x2, y2);
resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
}
}
}
return function(stream) {
var lambda00, x00, y00, a00, b00, c00, // first point
lambda0, x0, y0, a0, b0, c0; // previous point
var resampleStream = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
};
function point(x, y) {
x = project(x, y);
stream.point(x[0], x[1]);
}
function lineStart() {
x0 = NaN;
resampleStream.point = linePoint;
stream.lineStart();
}
function linePoint(lambda, phi) {
var c = cartesian([lambda, phi]), p = project(lambda, phi);
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
stream.point(x0, y0);
}
function lineEnd() {
resampleStream.point = point;
stream.lineEnd();
}
function ringStart() {
lineStart();
resampleStream.point = ringPoint;
resampleStream.lineEnd = ringEnd;
}
function ringPoint(lambda, phi) {
linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
resampleStream.point = linePoint;
}
function ringEnd() {
resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
resampleStream.lineEnd = lineEnd;
lineEnd();
}
return resampleStream;
};
}
var transformRadians = transformer({
point: function(x, y) {
this.stream.point(x * radians, y * radians);
}
});
function transformRotate(rotate) {
return transformer({
point: function(x, y) {
var r = rotate(x, y);
return this.stream.point(r[0], r[1]);
}
});
}
function scaleTranslate(k, dx, dy) {
function transform$$1(x, y) {
return [dx + k * x, dy - k * y];
}
transform$$1.invert = function(x, y) {
return [(x - dx) / k, (dy - y) / k];
};
return transform$$1;
}
function scaleTranslateRotate(k, dx, dy, alpha) {
var cosAlpha = cos$1(alpha),
sinAlpha = sin$1(alpha),
a = cosAlpha * k,
b = sinAlpha * k,
ai = cosAlpha / k,
bi = sinAlpha / k,
ci = (sinAlpha * dy - cosAlpha * dx) / k,
fi = (sinAlpha * dx + cosAlpha * dy) / k;
function transform$$1(x, y) {
return [a * x - b * y + dx, dy - b * x - a * y];
}
transform$$1.invert = function(x, y) {
return [ai * x - bi * y + ci, fi - bi * x - ai * y];
};
return transform$$1;
}
function projection(project) {
return projectionMutator(function() { return project; })();
}
function projectionMutator(projectAt) {
var project,
k = 150, // scale
x = 480, y = 250, // translate
lambda = 0, phi = 0, // center
deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate
alpha = 0, // post-rotate
theta = null, preclip = clipAntimeridian, // pre-clip angle
x0 = null, y0, x1, y1, postclip = identity$4, // post-clip extent
delta2 = 0.5, // precision
projectResample,
projectTransform,
projectRotateTransform,
cache,
cacheStream;
function projection(point) {
return projectRotateTransform(point[0] * radians, point[1] * radians);
}
function invert(point) {
point = projectRotateTransform.invert(point[0], point[1]);
return point && [point[0] * degrees$1, point[1] * degrees$1];
}
projection.stream = function(stream) {
return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
};
projection.preclip = function(_) {
return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
};
projection.postclip = function(_) {
return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
};
projection.clipAngle = function(_) {
return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
};
projection.clipExtent = function(_) {
return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
};
projection.scale = function(_) {
return arguments.length ? (k = +_, recenter()) : k;
};
projection.translate = function(_) {
return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
};
projection.center = function(_) {
return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
};
projection.rotate = function(_) {
return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
};
projection.angle = function(_) {
return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1;
};
projection.precision = function(_) {
return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
};
projection.fitExtent = function(extent, object) {
return fitExtent(projection, extent, object);
};
projection.fitSize = function(size, object) {
return fitSize(projection, size, object);
};
projection.fitWidth = function(width, object) {
return fitWidth(projection, width, object);
};
projection.fitHeight = function(height, object) {
return fitHeight(projection, height, object);
};
function recenter() {
var center = scaleTranslateRotate(k, 0, 0, alpha).apply(null, project(lambda, phi)),
transform$$1 = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], alpha);
rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
projectTransform = compose(project, transform$$1);
projectRotateTransform = compose(rotate, projectTransform);
projectResample = resample(projectTransform, delta2);
return reset();
}
function reset() {
cache = cacheStream = null;
return projection;
}
return function() {
project = projectAt.apply(this, arguments);
projection.invert = project.invert && invert;
return recenter();
};
}
function conicProjection(projectAt) {
var phi0 = 0,
phi1 = pi$3 / 3,
m = projectionMutator(projectAt),
p = m(phi0, phi1);
p.parallels = function(_) {
return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1];
};
return p;
}
function cylindricalEqualAreaRaw(phi0) {
var cosPhi0 = cos$1(phi0);
function forward(lambda, phi) {
return [lambda * cosPhi0, sin$1(phi) / cosPhi0];
}
forward.invert = function(x, y) {
return [x / cosPhi0, asin(y * cosPhi0)];
};
return forward;
}
function conicEqualAreaRaw(y0, y1) {
var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2;
// Are the parallels symmetrical around the Equator?
if (abs(n) < epsilon$2) return cylindricalEqualAreaRaw(y0);
var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n;
function project(x, y) {
var r = sqrt(c - 2 * n * sin$1(y)) / n;
return [r * sin$1(x *= n), r0 - r * cos$1(x)];
}
project.invert = function(x, y) {
var r0y = r0 - y;
return [atan2(x, abs(r0y)) / n * sign(r0y), asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
};
return project;
}
function conicEqualArea() {
return conicProjection(conicEqualAreaRaw)
.scale(155.424)
.center([0, 33.6442]);
}
function albers() {
return conicEqualArea()
.parallels([29.5, 45.5])
.scale(1070)
.translate([480, 250])
.rotate([96, 0])
.center([-0.6, 38.7]);
}
// The projections must have mutually exclusive clip regions on the sphere,
// as this will avoid emitting interleaving lines and polygons.
function multiplex(streams) {
var n = streams.length;
return {
point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },
sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },
lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },
lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },
polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },
polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }
};
}
// A composite projection for the United States, configured by default for
// 960×500. The projection also works quite well at 960×600 if you change the
// scale to 1285 and adjust the translate accordingly. The set of standard
// parallels for each region comes from USGS, which is published here:
// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
function albersUsa() {
var cache,
cacheStream,
lower48 = albers(), lower48Point,
alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338
hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007
point, pointStream = {point: function(x, y) { point = [x, y]; }};
function albersUsa(coordinates) {
var x = coordinates[0], y = coordinates[1];
return point = null, (lower48Point.point(x, y), point)
|| (alaskaPoint.point(x, y), point)
|| (hawaiiPoint.point(x, y), point);
}
albersUsa.invert = function(coordinates) {
var k = lower48.scale(),
t = lower48.translate(),
x = (coordinates[0] - t[0]) / k,
y = (coordinates[1] - t[1]) / k;
return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
: y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
: lower48).invert(coordinates);
};
albersUsa.stream = function(stream) {
return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
};
albersUsa.precision = function(_) {
if (!arguments.length) return lower48.precision();
lower48.precision(_), alaska.precision(_), hawaii.precision(_);
return reset();
};
albersUsa.scale = function(_) {
if (!arguments.length) return lower48.scale();
lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
return albersUsa.translate(lower48.translate());
};
albersUsa.translate = function(_) {
if (!arguments.length) return lower48.translate();
var k = lower48.scale(), x = +_[0], y = +_[1];
lower48Point = lower48
.translate(_)
.clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
.stream(pointStream);
alaskaPoint = alaska
.translate([x - 0.307 * k, y + 0.201 * k])
.clipExtent([[x - 0.425 * k + epsilon$2, y + 0.120 * k + epsilon$2], [x - 0.214 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
.stream(pointStream);
hawaiiPoint = hawaii
.translate([x - 0.205 * k, y + 0.212 * k])
.clipExtent([[x - 0.214 * k + epsilon$2, y + 0.166 * k + epsilon$2], [x - 0.115 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
.stream(pointStream);
return reset();
};
albersUsa.fitExtent = function(extent, object) {
return fitExtent(albersUsa, extent, object);
};
albersUsa.fitSize = function(size, object) {
return fitSize(albersUsa, size, object);
};
albersUsa.fitWidth = function(width, object) {
return fitWidth(albersUsa, width, object);
};
albersUsa.fitHeight = function(height, object) {
return fitHeight(albersUsa, height, object);
};
function reset() {
cache = cacheStream = null;
return albersUsa;
}
return albersUsa.scale(1070);
}
function azimuthalRaw(scale) {
return function(x, y) {
var cx = cos$1(x),
cy = cos$1(y),
k = scale(cx * cy);
return [
k * cy * sin$1(x),
k * sin$1(y)
];
}
}
function azimuthalInvert(angle) {
return function(x, y) {
var z = sqrt(x * x + y * y),
c = angle(z),
sc = sin$1(c),
cc = cos$1(c);
return [
atan2(x * sc, z * cc),
asin(z && y * sc / z)
];
}
}
var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
return sqrt(2 / (1 + cxcy));
});
azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
return 2 * asin(z / 2);
});
function azimuthalEqualArea() {
return projection(azimuthalEqualAreaRaw)
.scale(124.75)
.clipAngle(180 - 1e-3);
}
var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
return (c = acos(c)) && c / sin$1(c);
});
azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
return z;
});
function azimuthalEquidistant() {
return projection(azimuthalEquidistantRaw)
.scale(79.4188)
.clipAngle(180 - 1e-3);
}
function mercatorRaw(lambda, phi) {
return [lambda, log(tan((halfPi$2 + phi) / 2))];
}
mercatorRaw.invert = function(x, y) {
return [x, 2 * atan(exp(y)) - halfPi$2];
};
function mercator() {
return mercatorProjection(mercatorRaw)
.scale(961 / tau$3);
}
function mercatorProjection(project) {
var m = projection(project),
center = m.center,
scale = m.scale,
translate = m.translate,
clipExtent = m.clipExtent,
x0 = null, y0, x1, y1; // clip extent
m.scale = function(_) {
return arguments.length ? (scale(_), reclip()) : scale();
};
m.translate = function(_) {
return arguments.length ? (translate(_), reclip()) : translate();
};
m.center = function(_) {
return arguments.length ? (center(_), reclip()) : center();
};
m.clipExtent = function(_) {
return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
};
function reclip() {
var k = pi$3 * scale(),
t = m(rotation(m.rotate()).invert([0, 0]));
return clipExtent(x0 == null
? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
: [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
}
return reclip();
}
function tany(y) {
return tan((halfPi$2 + y) / 2);
}
function conicConformalRaw(y0, y1) {
var cy0 = cos$1(y0),
n = y0 === y1 ? sin$1(y0) : log(cy0 / cos$1(y1)) / log(tany(y1) / tany(y0)),
f = cy0 * pow(tany(y0), n) / n;
if (!n) return mercatorRaw;
function project(x, y) {
if (f > 0) { if (y < -halfPi$2 + epsilon$2) y = -halfPi$2 + epsilon$2; }
else { if (y > halfPi$2 - epsilon$2) y = halfPi$2 - epsilon$2; }
var r = f / pow(tany(y), n);
return [r * sin$1(n * x), f - r * cos$1(n * x)];
}
project.invert = function(x, y) {
var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy);
return [atan2(x, abs(fy)) / n * sign(fy), 2 * atan(pow(f / r, 1 / n)) - halfPi$2];
};
return project;
}
function conicConformal() {
return conicProjection(conicConformalRaw)
.scale(109.5)
.parallels([30, 30]);
}
function equirectangularRaw(lambda, phi) {
return [lambda, phi];
}
equirectangularRaw.invert = equirectangularRaw;
function equirectangular() {
return projection(equirectangularRaw)
.scale(152.63);
}
function conicEquidistantRaw(y0, y1) {
var cy0 = cos$1(y0),
n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
g = cy0 / n + y0;
if (abs(n) < epsilon$2) return equirectangularRaw;
function project(x, y) {
var gy = g - y, nx = n * x;
return [gy * sin$1(nx), g - gy * cos$1(nx)];
}
project.invert = function(x, y) {
var gy = g - y;
return [atan2(x, abs(gy)) / n * sign(gy), g - sign(n) * sqrt(x * x + gy * gy)];
};
return project;
}
function conicEquidistant() {
return conicProjection(conicEquidistantRaw)
.scale(131.154)
.center([0, 13.9389]);
}
function gnomonicRaw(x, y) {
var cy = cos$1(y), k = cos$1(x) * cy;
return [cy * sin$1(x) / k, sin$1(y) / k];
}
gnomonicRaw.invert = azimuthalInvert(atan);
function gnomonic() {
return projection(gnomonicRaw)
.scale(144.049)
.clipAngle(60);
}
function scaleTranslate$1(kx, ky, tx, ty) {
return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? identity$4 : transformer({
point: function(x, y) {
this.stream.point(x * kx + tx, y * ky + ty);
}
});
}
function identity$5() {
var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform$$1 = identity$4, // scale, translate and reflect
x0 = null, y0, x1, y1, // clip extent
postclip = identity$4,
cache,
cacheStream,
projection;
function reset() {
cache = cacheStream = null;
return projection;
}
return projection = {
stream: function(stream) {
return cache && cacheStream === stream ? cache : cache = transform$$1(postclip(cacheStream = stream));
},
postclip: function(_) {
return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
},
clipExtent: function(_) {
return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
},
scale: function(_) {
return arguments.length ? (transform$$1 = scaleTranslate$1((k = +_) * sx, k * sy, tx, ty), reset()) : k;
},
translate: function(_) {
return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];
},
reflectX: function(_) {
return arguments.length ? (transform$$1 = scaleTranslate$1(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0;
},
reflectY: function(_) {
return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0;
},
fitExtent: function(extent, object) {
return fitExtent(projection, extent, object);
},
fitSize: function(size, object) {
return fitSize(projection, size, object);
},
fitWidth: function(width, object) {
return fitWidth(projection, width, object);
},
fitHeight: function(height, object) {
return fitHeight(projection, height, object);
}
};
}
function naturalEarth1Raw(lambda, phi) {
var phi2 = phi * phi, phi4 = phi2 * phi2;
return [
lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))),
phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))
];
}
naturalEarth1Raw.invert = function(x, y) {
var phi = y, i = 25, delta;
do {
var phi2 = phi * phi, phi4 = phi2 * phi2;
phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) /
(1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));
} while (abs(delta) > epsilon$2 && --i > 0);
return [
x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))),
phi
];
};
function naturalEarth1() {
return projection(naturalEarth1Raw)
.scale(175.295);
}
function orthographicRaw(x, y) {
return [cos$1(y) * sin$1(x), sin$1(y)];
}
orthographicRaw.invert = azimuthalInvert(asin);
function orthographic() {
return projection(orthographicRaw)
.scale(249.5)
.clipAngle(90 + epsilon$2);
}
function stereographicRaw(x, y) {
var cy = cos$1(y), k = 1 + cos$1(x) * cy;
return [cy * sin$1(x) / k, sin$1(y) / k];
}
stereographicRaw.invert = azimuthalInvert(function(z) {
return 2 * atan(z);
});
function stereographic() {
return projection(stereographicRaw)
.scale(250)
.clipAngle(142);
}
function transverseMercatorRaw(lambda, phi) {
return [log(tan((halfPi$2 + phi) / 2)), -lambda];
}
transverseMercatorRaw.invert = function(x, y) {
return [-y, 2 * atan(exp(x)) - halfPi$2];
};
function transverseMercator() {
var m = mercatorProjection(transverseMercatorRaw),
center = m.center,
rotate = m.rotate;
m.center = function(_) {
return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
};
m.rotate = function(_) {
return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
};
return rotate([0, 0, 90])
.scale(159.155);
}
function defaultSeparation(a, b) {
return a.parent === b.parent ? 1 : 2;
}
function meanX(children) {
return children.reduce(meanXReduce, 0) / children.length;
}
function meanXReduce(x, c) {
return x + c.x;
}
function maxY(children) {
return 1 + children.reduce(maxYReduce, 0);
}
function maxYReduce(y, c) {
return Math.max(y, c.y);
}
function leafLeft(node) {
var children;
while (children = node.children) node = children[0];
return node;
}
function leafRight(node) {
var children;
while (children = node.children) node = children[children.length - 1];
return node;
}
function cluster() {
var separation = defaultSeparation,
dx = 1,
dy = 1,
nodeSize = false;
function cluster(root) {
var previousNode,
x = 0;
// First walk, computing the initial x & y values.
root.eachAfter(function(node) {
var children = node.children;
if (children) {
node.x = meanX(children);
node.y = maxY(children);
} else {
node.x = previousNode ? x += separation(node, previousNode) : 0;
node.y = 0;
previousNode = node;
}
});
var left = leafLeft(root),
right = leafRight(root),
x0 = left.x - separation(left, right) / 2,
x1 = right.x + separation(right, left) / 2;
// Second walk, normalizing x & y to the desired size.
return root.eachAfter(nodeSize ? function(node) {
node.x = (node.x - root.x) * dx;
node.y = (root.y - node.y) * dy;
} : function(node) {
node.x = (node.x - x0) / (x1 - x0) * dx;
node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;
});
}
cluster.separation = function(x) {
return arguments.length ? (separation = x, cluster) : separation;
};
cluster.size = function(x) {
return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);
};
cluster.nodeSize = function(x) {
return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);
};
return cluster;
}
function count(node) {
var sum = 0,
children = node.children,
i = children && children.length;
if (!i) sum = 1;
else while (--i >= 0) sum += children[i].value;
node.value = sum;
}
function node_count() {
return this.eachAfter(count);
}
function node_each(callback) {
var node = this, current, next = [node], children, i, n;
do {
current = next.reverse(), next = [];
while (node = current.pop()) {
callback(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
next.push(children[i]);
}
}
} while (next.length);
return this;
}
function node_eachBefore(callback) {
var node = this, nodes = [node], children, i;
while (node = nodes.pop()) {
callback(node), children = node.children;
if (children) for (i = children.length - 1; i >= 0; --i) {
nodes.push(children[i]);
}
}
return this;
}
function node_eachAfter(callback) {
var node = this, nodes = [node], next = [], children, i, n;
while (node = nodes.pop()) {
next.push(node), children = node.children;
if (children) for (i = 0, n = children.length; i < n; ++i) {
nodes.push(children[i]);
}
}
while (node = next.pop()) {
callback(node);
}
return this;
}
function node_sum(value) {
return this.eachAfter(function(node) {
var sum = +value(node.data) || 0,
children = node.children,
i = children && children.length;
while (--i >= 0) sum += children[i].value;
node.value = sum;
});
}
function node_sort(compare) {
return this.eachBefore(function(node) {
if (node.children) {
node.children.sort(compare);
}
});
}
function node_path(end) {
var start = this,
ancestor = leastCommonAncestor(start, end),
nodes = [start];
while (start !== ancestor) {
start = start.parent;
nodes.push(start);
}
var k = nodes.length;
while (end !== ancestor) {
nodes.splice(k, 0, end);
end = end.parent;
}
return nodes;
}
function leastCommonAncestor(a, b) {
if (a === b) return a;
var aNodes = a.ancestors(),
bNodes = b.ancestors(),
c = null;
a = aNodes.pop();
b = bNodes.pop();
while (a === b) {
c = a;
a = aNodes.pop();
b = bNodes.pop();
}
return c;
}
function node_ancestors() {
var node = this, nodes = [node];
while (node = node.parent) {
nodes.push(node);
}
return nodes;
}
function node_descendants() {
var nodes = [];
this.each(function(node) {
nodes.push(node);
});
return nodes;
}
function node_leaves() {
var leaves = [];
this.eachBefore(function(node) {
if (!node.children) {
leaves.push(node);
}
});
return leaves;
}
function node_links() {
var root = this, links = [];
root.each(function(node) {
if (node !== root) { // Don’t include the root’s parent, if any.
links.push({source: node.parent, target: node});
}
});
return links;
}
function hierarchy(data, children) {
var root = new Node(data),
valued = +data.value && (root.value = data.value),
node,
nodes = [root],
child,
childs,
i,
n;
if (children == null) children = defaultChildren;
while (node = nodes.pop()) {
if (valued) node.value = +node.data.value;
if ((childs = children(node.data)) && (n = childs.length)) {
node.children = new Array(n);
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new Node(childs[i]));
child.parent = node;
child.depth = node.depth + 1;
}
}
}
return root.eachBefore(computeHeight);
}
function node_copy() {
return hierarchy(this).eachBefore(copyData);
}
function defaultChildren(d) {
return d.children;
}
function copyData(node) {
node.data = node.data.data;
}
function computeHeight(node) {
var height = 0;
do node.height = height;
while ((node = node.parent) && (node.height < ++height));
}
function Node(data) {
this.data = data;
this.depth =
this.height = 0;
this.parent = null;
}
Node.prototype = hierarchy.prototype = {
constructor: Node,
count: node_count,
each: node_each,
eachAfter: node_eachAfter,
eachBefore: node_eachBefore,
sum: node_sum,
sort: node_sort,
path: node_path,
ancestors: node_ancestors,
descendants: node_descendants,
leaves: node_leaves,
links: node_links,
copy: node_copy
};
var slice$4 = Array.prototype.slice;
function shuffle$1(array) {
var m = array.length,
t,
i;
while (m) {
i = Math.random() * m-- | 0;
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
function enclose(circles) {
var i = 0, n = (circles = shuffle$1(slice$4.call(circles))).length, B = [], p, e;
while (i < n) {
p = circles[i];
if (e && enclosesWeak(e, p)) ++i;
else e = encloseBasis(B = extendBasis(B, p)), i = 0;
}
return e;
}
function extendBasis(B, p) {
var i, j;
if (enclosesWeakAll(p, B)) return [p];
// If we get here then B must have at least one element.
for (i = 0; i < B.length; ++i) {
if (enclosesNot(p, B[i])
&& enclosesWeakAll(encloseBasis2(B[i], p), B)) {
return [B[i], p];
}
}
// If we get here then B must have at least two elements.
for (i = 0; i < B.length - 1; ++i) {
for (j = i + 1; j < B.length; ++j) {
if (enclosesNot(encloseBasis2(B[i], B[j]), p)
&& enclosesNot(encloseBasis2(B[i], p), B[j])
&& enclosesNot(encloseBasis2(B[j], p), B[i])
&& enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {
return [B[i], B[j], p];
}
}
}
// If we get here then something is very wrong.
throw new Error;
}
function enclosesNot(a, b) {
var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y;
return dr < 0 || dr * dr < dx * dx + dy * dy;
}
function enclosesWeak(a, b) {
var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y;
return dr > 0 && dr * dr > dx * dx + dy * dy;
}
function enclosesWeakAll(a, B) {
for (var i = 0; i < B.length; ++i) {
if (!enclosesWeak(a, B[i])) {
return false;
}
}
return true;
}
function encloseBasis(B) {
switch (B.length) {
case 1: return encloseBasis1(B[0]);
case 2: return encloseBasis2(B[0], B[1]);
case 3: return encloseBasis3(B[0], B[1], B[2]);
}
}
function encloseBasis1(a) {
return {
x: a.x,
y: a.y,
r: a.r
};
}
function encloseBasis2(a, b) {
var x1 = a.x, y1 = a.y, r1 = a.r,
x2 = b.x, y2 = b.y, r2 = b.r,
x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,
l = Math.sqrt(x21 * x21 + y21 * y21);
return {
x: (x1 + x2 + x21 / l * r21) / 2,
y: (y1 + y2 + y21 / l * r21) / 2,
r: (l + r1 + r2) / 2
};
}
function encloseBasis3(a, b, c) {
var x1 = a.x, y1 = a.y, r1 = a.r,
x2 = b.x, y2 = b.y, r2 = b.r,
x3 = c.x, y3 = c.y, r3 = c.r,
a2 = x1 - x2,
a3 = x1 - x3,
b2 = y1 - y2,
b3 = y1 - y3,
c2 = r2 - r1,
c3 = r3 - r1,
d1 = x1 * x1 + y1 * y1 - r1 * r1,
d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,
d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,
ab = a3 * b2 - a2 * b3,
xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,
xb = (b3 * c2 - b2 * c3) / ab,
ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,
yb = (a2 * c3 - a3 * c2) / ab,
A = xb * xb + yb * yb - 1,
B = 2 * (r1 + xa * xb + ya * yb),
C = xa * xa + ya * ya - r1 * r1,
r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);
return {
x: x1 + xa + xb * r,
y: y1 + ya + yb * r,
r: r
};
}
function place(b, a, c) {
var dx = b.x - a.x, x, a2,
dy = b.y - a.y, y, b2,
d2 = dx * dx + dy * dy;
if (d2) {
a2 = a.r + c.r, a2 *= a2;
b2 = b.r + c.r, b2 *= b2;
if (a2 > b2) {
x = (d2 + b2 - a2) / (2 * d2);
y = Math.sqrt(Math.max(0, b2 / d2 - x * x));
c.x = b.x - x * dx - y * dy;
c.y = b.y - x * dy + y * dx;
} else {
x = (d2 + a2 - b2) / (2 * d2);
y = Math.sqrt(Math.max(0, a2 / d2 - x * x));
c.x = a.x + x * dx - y * dy;
c.y = a.y + x * dy + y * dx;
}
} else {
c.x = a.x + c.r;
c.y = a.y;
}
}
function intersects(a, b) {
var dr = a.r + b.r - 1e-6, dx = b.x - a.x, dy = b.y - a.y;
return dr > 0 && dr * dr > dx * dx + dy * dy;
}
function score(node) {
var a = node._,
b = node.next._,
ab = a.r + b.r,
dx = (a.x * b.r + b.x * a.r) / ab,
dy = (a.y * b.r + b.y * a.r) / ab;
return dx * dx + dy * dy;
}
function Node$1(circle) {
this._ = circle;
this.next = null;
this.previous = null;
}
function packEnclose(circles) {
if (!(n = circles.length)) return 0;
var a, b, c, n, aa, ca, i, j, k, sj, sk;
// Place the first circle.
a = circles[0], a.x = 0, a.y = 0;
if (!(n > 1)) return a.r;
// Place the second circle.
b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
if (!(n > 2)) return a.r + b.r;
// Place the third circle.
place(b, a, c = circles[2]);
// Initialize the front-chain using the first three circles a, b and c.
a = new Node$1(a), b = new Node$1(b), c = new Node$1(c);
a.next = c.previous = b;
b.next = a.previous = c;
c.next = b.previous = a;
// Attempt to place each remaining circle…
pack: for (i = 3; i < n; ++i) {
place(a._, b._, c = circles[i]), c = new Node$1(c);
// Find the closest intersecting circle on the front-chain, if any.
// “Closeness” is determined by linear distance along the front-chain.
// “Ahead” or “behind” is likewise determined by linear distance.
j = b.next, k = a.previous, sj = b._.r, sk = a._.r;
do {
if (sj <= sk) {
if (intersects(j._, c._)) {
b = j, a.next = b, b.previous = a, --i;
continue pack;
}
sj += j._.r, j = j.next;
} else {
if (intersects(k._, c._)) {
a = k, a.next = b, b.previous = a, --i;
continue pack;
}
sk += k._.r, k = k.previous;
}
} while (j !== k.next);
// Success! Insert the new circle c between a and b.
c.previous = a, c.next = b, a.next = b.previous = b = c;
// Compute the new closest circle pair to the centroid.
aa = score(a);
while ((c = c.next) !== b) {
if ((ca = score(c)) < aa) {
a = c, aa = ca;
}
}
b = a.next;
}
// Compute the enclosing circle of the front chain.
a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a);
// Translate the circles to put the enclosing circle around the origin.
for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
return c.r;
}
function siblings(circles) {
packEnclose(circles);
return circles;
}
function optional(f) {
return f == null ? null : required(f);
}
function required(f) {
if (typeof f !== "function") throw new Error;
return f;
}
function constantZero() {
return 0;
}
function constant$9(x) {
return function() {
return x;
};
}
function defaultRadius$1(d) {
return Math.sqrt(d.value);
}
function index$2() {
var radius = null,
dx = 1,
dy = 1,
padding = constantZero;
function pack(root) {
root.x = dx / 2, root.y = dy / 2;
if (radius) {
root.eachBefore(radiusLeaf(radius))
.eachAfter(packChildren(padding, 0.5))
.eachBefore(translateChild(1));
} else {
root.eachBefore(radiusLeaf(defaultRadius$1))
.eachAfter(packChildren(constantZero, 1))
.eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))
.eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
}
return root;
}
pack.radius = function(x) {
return arguments.length ? (radius = optional(x), pack) : radius;
};
pack.size = function(x) {
return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];
};
pack.padding = function(x) {
return arguments.length ? (padding = typeof x === "function" ? x : constant$9(+x), pack) : padding;
};
return pack;
}
function radiusLeaf(radius) {
return function(node) {
if (!node.children) {
node.r = Math.max(0, +radius(node) || 0);
}
};
}
function packChildren(padding, k) {
return function(node) {
if (children = node.children) {
var children,
i,
n = children.length,
r = padding(node) * k || 0,
e;
if (r) for (i = 0; i < n; ++i) children[i].r += r;
e = packEnclose(children);
if (r) for (i = 0; i < n; ++i) children[i].r -= r;
node.r = e + r;
}
};
}
function translateChild(k) {
return function(node) {
var parent = node.parent;
node.r *= k;
if (parent) {
node.x = parent.x + k * node.x;
node.y = parent.y + k * node.y;
}
};
}
function roundNode(node) {
node.x0 = Math.round(node.x0);
node.y0 = Math.round(node.y0);
node.x1 = Math.round(node.x1);
node.y1 = Math.round(node.y1);
}
function treemapDice(parent, x0, y0, x1, y1) {
var nodes = parent.children,
node,
i = -1,
n = nodes.length,
k = parent.value && (x1 - x0) / parent.value;
while (++i < n) {
node = nodes[i], node.y0 = y0, node.y1 = y1;
node.x0 = x0, node.x1 = x0 += node.value * k;
}
}
function partition() {
var dx = 1,
dy = 1,
padding = 0,
round = false;
function partition(root) {
var n = root.height + 1;
root.x0 =
root.y0 = padding;
root.x1 = dx;
root.y1 = dy / n;
root.eachBefore(positionNode(dy, n));
if (round) root.eachBefore(roundNode);
return root;
}
function positionNode(dy, n) {
return function(node) {
if (node.children) {
treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);
}
var x0 = node.x0,
y0 = node.y0,
x1 = node.x1 - padding,
y1 = node.y1 - padding;
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
node.x0 = x0;
node.y0 = y0;
node.x1 = x1;
node.y1 = y1;
};
}
partition.round = function(x) {
return arguments.length ? (round = !!x, partition) : round;
};
partition.size = function(x) {
return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];
};
partition.padding = function(x) {
return arguments.length ? (padding = +x, partition) : padding;
};
return partition;
}
var keyPrefix$1 = "$", // Protect against keys like “__proto__”.
preroot = {depth: -1},
ambiguous = {};
function defaultId(d) {
return d.id;
}
function defaultParentId(d) {
return d.parentId;
}
function stratify() {
var id = defaultId,
parentId = defaultParentId;
function stratify(data) {
var d,
i,
n = data.length,
root,
parent,
node,
nodes = new Array(n),
nodeId,
nodeKey,
nodeByKey = {};
for (i = 0; i < n; ++i) {
d = data[i], node = nodes[i] = new Node(d);
if ((nodeId = id(d, i, data)) != null && (nodeId += "")) {
nodeKey = keyPrefix$1 + (node.id = nodeId);
nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;
}
}
for (i = 0; i < n; ++i) {
node = nodes[i], nodeId = parentId(data[i], i, data);
if (nodeId == null || !(nodeId += "")) {
if (root) throw new Error("multiple roots");
root = node;
} else {
parent = nodeByKey[keyPrefix$1 + nodeId];
if (!parent) throw new Error("missing: " + nodeId);
if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
if (parent.children) parent.children.push(node);
else parent.children = [node];
node.parent = parent;
}
}
if (!root) throw new Error("no root");
root.parent = preroot;
root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
root.parent = null;
if (n > 0) throw new Error("cycle");
return root;
}
stratify.id = function(x) {
return arguments.length ? (id = required(x), stratify) : id;
};
stratify.parentId = function(x) {
return arguments.length ? (parentId = required(x), stratify) : parentId;
};
return stratify;
}
function defaultSeparation$1(a, b) {
return a.parent === b.parent ? 1 : 2;
}
// function radialSeparation(a, b) {
// return (a.parent === b.parent ? 1 : 2) / a.depth;
// }
// This function is used to traverse the left contour of a subtree (or
// subforest). It returns the successor of v on this contour. This successor is
// either given by the leftmost child of v or by the thread of v. The function
// returns null if and only if v is on the highest level of its subtree.
function nextLeft(v) {
var children = v.children;
return children ? children[0] : v.t;
}
// This function works analogously to nextLeft.
function nextRight(v) {
var children = v.children;
return children ? children[children.length - 1] : v.t;
}
// Shifts the current subtree rooted at w+. This is done by increasing
// prelim(w+) and mod(w+) by shift.
function moveSubtree(wm, wp, shift) {
var change = shift / (wp.i - wm.i);
wp.c -= change;
wp.s += shift;
wm.c += change;
wp.z += shift;
wp.m += shift;
}
// All other shifts, applied to the smaller subtrees between w- and w+, are
// performed by this function. To prepare the shifts, we have to adjust
// change(w+), shift(w+), and change(w-).
function executeShifts(v) {
var shift = 0,
change = 0,
children = v.children,
i = children.length,
w;
while (--i >= 0) {
w = children[i];
w.z += shift;
w.m += shift;
shift += w.s + (change += w.c);
}
}
// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
// returns the specified (default) ancestor.
function nextAncestor(vim, v, ancestor) {
return vim.a.parent === v.parent ? vim.a : ancestor;
}
function TreeNode(node, i) {
this._ = node;
this.parent = null;
this.children = null;
this.A = null; // default ancestor
this.a = this; // ancestor
this.z = 0; // prelim
this.m = 0; // mod
this.c = 0; // change
this.s = 0; // shift
this.t = null; // thread
this.i = i; // number
}
TreeNode.prototype = Object.create(Node.prototype);
function treeRoot(root) {
var tree = new TreeNode(root, 0),
node,
nodes = [tree],
child,
children,
i,
n;
while (node = nodes.pop()) {
if (children = node._.children) {
node.children = new Array(n = children.length);
for (i = n - 1; i >= 0; --i) {
nodes.push(child = node.children[i] = new TreeNode(children[i], i));
child.parent = node;
}
}
}
(tree.parent = new TreeNode(null, 0)).children = [tree];
return tree;
}
// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
function tree() {
var separation = defaultSeparation$1,
dx = 1,
dy = 1,
nodeSize = null;
function tree(root) {
var t = treeRoot(root);
// Compute the layout using Buchheim et al.’s algorithm.
t.eachAfter(firstWalk), t.parent.m = -t.z;
t.eachBefore(secondWalk);
// If a fixed node size is specified, scale x and y.
if (nodeSize) root.eachBefore(sizeNode);
// If a fixed tree size is specified, scale x and y based on the extent.
// Compute the left-most, right-most, and depth-most nodes for extents.
else {
var left = root,
right = root,
bottom = root;
root.eachBefore(function(node) {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
if (node.depth > bottom.depth) bottom = node;
});
var s = left === right ? 1 : separation(left, right) / 2,
tx = s - left.x,
kx = dx / (right.x + s + tx),
ky = dy / (bottom.depth || 1);
root.eachBefore(function(node) {
node.x = (node.x + tx) * kx;
node.y = node.depth * ky;
});
}
return root;
}
// Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
// applied recursively to the children of v, as well as the function
// APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
// node v is placed to the midpoint of its outermost children.
function firstWalk(v) {
var children = v.children,
siblings = v.parent.children,
w = v.i ? siblings[v.i - 1] : null;
if (children) {
executeShifts(v);
var midpoint = (children[0].z + children[children.length - 1].z) / 2;
if (w) {
v.z = w.z + separation(v._, w._);
v.m = v.z - midpoint;
} else {
v.z = midpoint;
}
} else if (w) {
v.z = w.z + separation(v._, w._);
}
v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
}
// Computes all real x-coordinates by summing up the modifiers recursively.
function secondWalk(v) {
v._.x = v.z + v.parent.m;
v.m += v.parent.m;
}
// The core of the algorithm. Here, a new subtree is combined with the
// previous subtrees. Threads are used to traverse the inside and outside
// contours of the left and right subtree up to the highest common level. The
// vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
// superscript o means outside and i means inside, the subscript - means left
// subtree and + means right subtree. For summing up the modifiers along the
// contour, we use respective variables si+, si-, so-, and so+. Whenever two
// nodes of the inside contours conflict, we compute the left one of the
// greatest uncommon ancestors using the function ANCESTOR and call MOVE
// SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
// Finally, we add a new thread (if necessary).
function apportion(v, w, ancestor) {
if (w) {
var vip = v,
vop = v,
vim = w,
vom = vip.parent.children[0],
sip = vip.m,
sop = vop.m,
sim = vim.m,
som = vom.m,
shift;
while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
vom = nextLeft(vom);
vop = nextRight(vop);
vop.a = v;
shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
if (shift > 0) {
moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
sip += shift;
sop += shift;
}
sim += vim.m;
sip += vip.m;
som += vom.m;
sop += vop.m;
}
if (vim && !nextRight(vop)) {
vop.t = vim;
vop.m += sim - sop;
}
if (vip && !nextLeft(vom)) {
vom.t = vip;
vom.m += sip - som;
ancestor = v;
}
}
return ancestor;
}
function sizeNode(node) {
node.x *= dx;
node.y = node.depth * dy;
}
tree.separation = function(x) {
return arguments.length ? (separation = x, tree) : separation;
};
tree.size = function(x) {
return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);
};
tree.nodeSize = function(x) {
return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);
};
return tree;
}
function treemapSlice(parent, x0, y0, x1, y1) {
var nodes = parent.children,
node,
i = -1,
n = nodes.length,
k = parent.value && (y1 - y0) / parent.value;
while (++i < n) {
node = nodes[i], node.x0 = x0, node.x1 = x1;
node.y0 = y0, node.y1 = y0 += node.value * k;
}
}
var phi = (1 + Math.sqrt(5)) / 2;
function squarifyRatio(ratio, parent, x0, y0, x1, y1) {
var rows = [],
nodes = parent.children,
row,
nodeValue,
i0 = 0,
i1 = 0,
n = nodes.length,
dx, dy,
value = parent.value,
sumValue,
minValue,
maxValue,
newRatio,
minRatio,
alpha,
beta;
while (i0 < n) {
dx = x1 - x0, dy = y1 - y0;
// Find the next non-empty node.
do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);
minValue = maxValue = sumValue;
alpha = Math.max(dy / dx, dx / dy) / (value * ratio);
beta = sumValue * sumValue * alpha;
minRatio = Math.max(maxValue / beta, beta / minValue);
// Keep adding nodes while the aspect ratio maintains or improves.
for (; i1 < n; ++i1) {
sumValue += nodeValue = nodes[i1].value;
if (nodeValue < minValue) minValue = nodeValue;
if (nodeValue > maxValue) maxValue = nodeValue;
beta = sumValue * sumValue * alpha;
newRatio = Math.max(maxValue / beta, beta / minValue);
if (newRatio > minRatio) { sumValue -= nodeValue; break; }
minRatio = newRatio;
}
// Position and record the row orientation.
rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});
if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);
else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);
value -= sumValue, i0 = i1;
}
return rows;
}
var squarify = (function custom(ratio) {
function squarify(parent, x0, y0, x1, y1) {
squarifyRatio(ratio, parent, x0, y0, x1, y1);
}
squarify.ratio = function(x) {
return custom((x = +x) > 1 ? x : 1);
};
return squarify;
})(phi);
function index$3() {
var tile = squarify,
round = false,
dx = 1,
dy = 1,
paddingStack = [0],
paddingInner = constantZero,
paddingTop = constantZero,
paddingRight = constantZero,
paddingBottom = constantZero,
paddingLeft = constantZero;
function treemap(root) {
root.x0 =
root.y0 = 0;
root.x1 = dx;
root.y1 = dy;
root.eachBefore(positionNode);
paddingStack = [0];
if (round) root.eachBefore(roundNode);
return root;
}
function positionNode(node) {
var p = paddingStack[node.depth],
x0 = node.x0 + p,
y0 = node.y0 + p,
x1 = node.x1 - p,
y1 = node.y1 - p;
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
node.x0 = x0;
node.y0 = y0;
node.x1 = x1;
node.y1 = y1;
if (node.children) {
p = paddingStack[node.depth + 1] = paddingInner(node) / 2;
x0 += paddingLeft(node) - p;
y0 += paddingTop(node) - p;
x1 -= paddingRight(node) - p;
y1 -= paddingBottom(node) - p;
if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
tile(node, x0, y0, x1, y1);
}
}
treemap.round = function(x) {
return arguments.length ? (round = !!x, treemap) : round;
};
treemap.size = function(x) {
return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];
};
treemap.tile = function(x) {
return arguments.length ? (tile = required(x), treemap) : tile;
};
treemap.padding = function(x) {
return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();
};
treemap.paddingInner = function(x) {
return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$9(+x), treemap) : paddingInner;
};
treemap.paddingOuter = function(x) {
return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();
};
treemap.paddingTop = function(x) {
return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$9(+x), treemap) : paddingTop;
};
treemap.paddingRight = function(x) {
return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$9(+x), treemap) : paddingRight;
};
treemap.paddingBottom = function(x) {
return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$9(+x), treemap) : paddingBottom;
};
treemap.paddingLeft = function(x) {
return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$9(+x), treemap) : paddingLeft;
};
return treemap;
}
function binary(parent, x0, y0, x1, y1) {
var nodes = parent.children,
i, n = nodes.length,
sum, sums = new Array(n + 1);
for (sums[0] = sum = i = 0; i < n; ++i) {
sums[i + 1] = sum += nodes[i].value;
}
partition(0, n, parent.value, x0, y0, x1, y1);
function partition(i, j, value, x0, y0, x1, y1) {
if (i >= j - 1) {
var node = nodes[i];
node.x0 = x0, node.y0 = y0;
node.x1 = x1, node.y1 = y1;
return;
}
var valueOffset = sums[i],
valueTarget = (value / 2) + valueOffset,
k = i + 1,
hi = j - 1;
while (k < hi) {
var mid = k + hi >>> 1;
if (sums[mid] < valueTarget) k = mid + 1;
else hi = mid;
}
if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k;
var valueLeft = sums[k] - valueOffset,
valueRight = value - valueLeft;
if ((x1 - x0) > (y1 - y0)) {
var xk = (x0 * valueRight + x1 * valueLeft) / value;
partition(i, k, valueLeft, x0, y0, xk, y1);
partition(k, j, valueRight, xk, y0, x1, y1);
} else {
var yk = (y0 * valueRight + y1 * valueLeft) / value;
partition(i, k, valueLeft, x0, y0, x1, yk);
partition(k, j, valueRight, x0, yk, x1, y1);
}
}
}
function sliceDice(parent, x0, y0, x1, y1) {
(parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1);
}
var resquarify = (function custom(ratio) {
function resquarify(parent, x0, y0, x1, y1) {
if ((rows = parent._squarify) && (rows.ratio === ratio)) {
var rows,
row,
nodes,
i,
j = -1,
n,
m = rows.length,
value = parent.value;
while (++j < m) {
row = rows[j], nodes = row.children;
for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;
if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);
else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);
value -= row.value;
}
} else {
parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1);
rows.ratio = ratio;
}
}
resquarify.ratio = function(x) {
return custom((x = +x) > 1 ? x : 1);
};
return resquarify;
})(phi);
function area$2(polygon) {
var i = -1,
n = polygon.length,
a,
b = polygon[n - 1],
area = 0;
while (++i < n) {
a = b;
b = polygon[i];
area += a[1] * b[0] - a[0] * b[1];
}
return area / 2;
}
function centroid$1(polygon) {
var i = -1,
n = polygon.length,
x = 0,
y = 0,
a,
b = polygon[n - 1],
c,
k = 0;
while (++i < n) {
a = b;
b = polygon[i];
k += c = a[0] * b[1] - b[0] * a[1];
x += (a[0] + b[0]) * c;
y += (a[1] + b[1]) * c;
}
return k *= 3, [x / k, y / k];
}
// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
// the 3D cross product in a quadrant I Cartesian coordinate system (+x is
// right, +y is up). Returns a positive value if ABC is counter-clockwise,
// negative if clockwise, and zero if the points are collinear.
function cross$1(a, b, c) {
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
}
function lexicographicOrder(a, b) {
return a[0] - b[0] || a[1] - b[1];
}
// Computes the upper convex hull per the monotone chain algorithm.
// Assumes points.length >= 3, is sorted by x, unique in y.
// Returns an array of indices into points in left-to-right order.
function computeUpperHullIndexes(points) {
var n = points.length,
indexes = [0, 1],
size = 2;
for (var i = 2; i < n; ++i) {
while (size > 1 && cross$1(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
indexes[size++] = i;
}
return indexes.slice(0, size); // remove popped points
}
function hull(points) {
if ((n = points.length) < 3) return null;
var i,
n,
sortedPoints = new Array(n),
flippedPoints = new Array(n);
for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
sortedPoints.sort(lexicographicOrder);
for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
var upperIndexes = computeUpperHullIndexes(sortedPoints),
lowerIndexes = computeUpperHullIndexes(flippedPoints);
// Construct the hull polygon, removing possible duplicate endpoints.
var skipLeft = lowerIndexes[0] === upperIndexes[0],
skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
hull = [];
// Add upper hull in right-to-l order.
// Then add lower hull in left-to-right order.
for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
return hull;
}
function contains$2(polygon, point) {
var n = polygon.length,
p = polygon[n - 1],
x = point[0], y = point[1],
x0 = p[0], y0 = p[1],
x1, y1,
inside = false;
for (var i = 0; i < n; ++i) {
p = polygon[i], x1 = p[0], y1 = p[1];
if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;
x0 = x1, y0 = y1;
}
return inside;
}
function length$2(polygon) {
var i = -1,
n = polygon.length,
b = polygon[n - 1],
xa,
ya,
xb = b[0],
yb = b[1],
perimeter = 0;
while (++i < n) {
xa = xb;
ya = yb;
b = polygon[i];
xb = b[0];
yb = b[1];
xa -= xb;
ya -= yb;
perimeter += Math.sqrt(xa * xa + ya * ya);
}
return perimeter;
}
function defaultSource$1() {
return Math.random();
}
var uniform = (function sourceRandomUniform(source) {
function randomUniform(min, max) {
min = min == null ? 0 : +min;
max = max == null ? 1 : +max;
if (arguments.length === 1) max = min, min = 0;
else max -= min;
return function() {
return source() * max + min;
};
}
randomUniform.source = sourceRandomUniform;
return randomUniform;
})(defaultSource$1);
var normal = (function sourceRandomNormal(source) {
function randomNormal(mu, sigma) {
var x, r;
mu = mu == null ? 0 : +mu;
sigma = sigma == null ? 1 : +sigma;
return function() {
var y;
// If available, use the second previously-generated uniform random.
if (x != null) y = x, x = null;
// Otherwise, generate a new x and y.
else do {
x = source() * 2 - 1;
y = source() * 2 - 1;
r = x * x + y * y;
} while (!r || r > 1);
return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);
};
}
randomNormal.source = sourceRandomNormal;
return randomNormal;
})(defaultSource$1);
var logNormal = (function sourceRandomLogNormal(source) {
function randomLogNormal() {
var randomNormal = normal.source(source).apply(this, arguments);
return function() {
return Math.exp(randomNormal());
};
}
randomLogNormal.source = sourceRandomLogNormal;
return randomLogNormal;
})(defaultSource$1);
var irwinHall = (function sourceRandomIrwinHall(source) {
function randomIrwinHall(n) {
return function() {
for (var sum = 0, i = 0; i < n; ++i) sum += source();
return sum;
};
}
randomIrwinHall.source = sourceRandomIrwinHall;
return randomIrwinHall;
})(defaultSource$1);
var bates = (function sourceRandomBates(source) {
function randomBates(n) {
var randomIrwinHall = irwinHall.source(source)(n);
return function() {
return randomIrwinHall() / n;
};
}
randomBates.source = sourceRandomBates;
return randomBates;
})(defaultSource$1);
var exponential$1 = (function sourceRandomExponential(source) {
function randomExponential(lambda) {
return function() {
return -Math.log(1 - source()) / lambda;
};
}
randomExponential.source = sourceRandomExponential;
return randomExponential;
})(defaultSource$1);
var array$3 = Array.prototype;
var map$2 = array$3.map;
var slice$5 = array$3.slice;
var implicit = {name: "implicit"};
function ordinal(range) {
var index = map$1(),
domain = [],
unknown = implicit;
range = range == null ? [] : slice$5.call(range);
function scale(d) {
var key = d + "", i = index.get(key);
if (!i) {
if (unknown !== implicit) return unknown;
index.set(key, i = domain.push(d));
}
return range[(i - 1) % range.length];
}
scale.domain = function(_) {
if (!arguments.length) return domain.slice();
domain = [], index = map$1();
var i = -1, n = _.length, d, key;
while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
return scale;
};
scale.range = function(_) {
return arguments.length ? (range = slice$5.call(_), scale) : range.slice();
};
scale.unknown = function(_) {
return arguments.length ? (unknown = _, scale) : unknown;
};
scale.copy = function() {
return ordinal()
.domain(domain)
.range(range)
.unknown(unknown);
};
return scale;
}
function band() {
var scale = ordinal().unknown(undefined),
domain = scale.domain,
ordinalRange = scale.range,
range$$1 = [0, 1],
step,
bandwidth,
round = false,
paddingInner = 0,
paddingOuter = 0,
align = 0.5;
delete scale.unknown;
function rescale() {
var n = domain().length,
reverse = range$$1[1] < range$$1[0],
start = range$$1[reverse - 0],
stop = range$$1[1 - reverse];
step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);
if (round) step = Math.floor(step);
start += (stop - start - step * (n - paddingInner)) * align;
bandwidth = step * (1 - paddingInner);
if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);
var values = sequence(n).map(function(i) { return start + step * i; });
return ordinalRange(reverse ? values.reverse() : values);
}
scale.domain = function(_) {
return arguments.length ? (domain(_), rescale()) : domain();
};
scale.range = function(_) {
return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice();
};
scale.rangeRound = function(_) {
return range$$1 = [+_[0], +_[1]], round = true, rescale();
};
scale.bandwidth = function() {
return bandwidth;
};
scale.step = function() {
return step;
};
scale.round = function(_) {
return arguments.length ? (round = !!_, rescale()) : round;
};
scale.padding = function(_) {
return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
};
scale.paddingInner = function(_) {
return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
};
scale.paddingOuter = function(_) {
return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;
};
scale.align = function(_) {
return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;
};
scale.copy = function() {
return band()
.domain(domain())
.range(range$$1)
.round(round)
.paddingInner(paddingInner)
.paddingOuter(paddingOuter)
.align(align);
};
return rescale();
}
function pointish(scale) {
var copy = scale.copy;
scale.padding = scale.paddingOuter;
delete scale.paddingInner;
delete scale.paddingOuter;
scale.copy = function() {
return pointish(copy());
};
return scale;
}
function point$1() {
return pointish(band().paddingInner(1));
}
function constant$10(x) {
return function() {
return x;
};
}
function number$2(x) {
return +x;
}
var unit = [0, 1];
function deinterpolateLinear(a, b) {
return (b -= (a = +a))
? function(x) { return (x - a) / b; }
: constant$10(b);
}
function deinterpolateClamp(deinterpolate) {
return function(a, b) {
var d = deinterpolate(a = +a, b = +b);
return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
};
}
function reinterpolateClamp(reinterpolate$$1) {
return function(a, b) {
var r = reinterpolate$$1(a = +a, b = +b);
return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
};
}
function bimap(domain, range, deinterpolate, reinterpolate$$1) {
var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate$$1(r1, r0);
else d0 = deinterpolate(d0, d1), r0 = reinterpolate$$1(r0, r1);
return function(x) { return r0(d0(x)); };
}
function polymap(domain, range, deinterpolate, reinterpolate$$1) {
var j = Math.min(domain.length, range.length) - 1,
d = new Array(j),
r = new Array(j),
i = -1;
// Reverse descending domains.
if (domain[j] < domain[0]) {
domain = domain.slice().reverse();
range = range.slice().reverse();
}
while (++i < j) {
d[i] = deinterpolate(domain[i], domain[i + 1]);
r[i] = reinterpolate$$1(range[i], range[i + 1]);
}
return function(x) {
var i = bisectRight(domain, x, 1, j) - 1;
return r[i](d[i](x));
};
}
function copy(source, target) {
return target
.domain(source.domain())
.range(source.range())
.interpolate(source.interpolate())
.clamp(source.clamp());
}
// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
function continuous(deinterpolate, reinterpolate$$1) {
var domain = unit,
range = unit,
interpolate$$1 = interpolateValue,
clamp = false,
piecewise$$1,
output,
input;
function rescale() {
piecewise$$1 = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
output = input = null;
return scale;
}
function scale(x) {
return (output || (output = piecewise$$1(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x);
}
scale.invert = function(y) {
return (input || (input = piecewise$$1(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate$$1) : reinterpolate$$1)))(+y);
};
scale.domain = function(_) {
return arguments.length ? (domain = map$2.call(_, number$2), rescale()) : domain.slice();
};
scale.range = function(_) {
return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice();
};
scale.rangeRound = function(_) {
return range = slice$5.call(_), interpolate$$1 = interpolateRound, rescale();
};
scale.clamp = function(_) {
return arguments.length ? (clamp = !!_, rescale()) : clamp;
};
scale.interpolate = function(_) {
return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1;
};
return rescale();
}
function tickFormat(domain, count, specifier) {
var start = domain[0],
stop = domain[domain.length - 1],
step = tickStep(start, stop, count == null ? 10 : count),
precision;
specifier = formatSpecifier(specifier == null ? ",f" : specifier);
switch (specifier.type) {
case "s": {
var value = Math.max(Math.abs(start), Math.abs(stop));
if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
return exports.formatPrefix(specifier, value);
}
case "":
case "e":
case "g":
case "p":
case "r": {
if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
break;
}
case "f":
case "%": {
if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
break;
}
}
return exports.format(specifier);
}
function linearish(scale) {
var domain = scale.domain;
scale.ticks = function(count) {
var d = domain();
return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
};
scale.tickFormat = function(count, specifier) {
return tickFormat(domain(), count, specifier);
};
scale.nice = function(count) {
if (count == null) count = 10;
var d = domain(),
i0 = 0,
i1 = d.length - 1,
start = d[i0],
stop = d[i1],
step;
if (stop < start) {
step = start, start = stop, stop = step;
step = i0, i0 = i1, i1 = step;
}
step = tickIncrement(start, stop, count);
if (step > 0) {
start = Math.floor(start / step) * step;
stop = Math.ceil(stop / step) * step;
step = tickIncrement(start, stop, count);
} else if (step < 0) {
start = Math.ceil(start * step) / step;
stop = Math.floor(stop * step) / step;
step = tickIncrement(start, stop, count);
}
if (step > 0) {
d[i0] = Math.floor(start / step) * step;
d[i1] = Math.ceil(stop / step) * step;
domain(d);
} else if (step < 0) {
d[i0] = Math.ceil(start * step) / step;
d[i1] = Math.floor(stop * step) / step;
domain(d);
}
return scale;
};
return scale;
}
function linear$2() {
var scale = continuous(deinterpolateLinear, reinterpolate);
scale.copy = function() {
return copy(scale, linear$2());
};
return linearish(scale);
}
function identity$6() {
var domain = [0, 1];
function scale(x) {
return +x;
}
scale.invert = scale;
scale.domain = scale.range = function(_) {
return arguments.length ? (domain = map$2.call(_, number$2), scale) : domain.slice();
};
scale.copy = function() {
return identity$6().domain(domain);
};
return linearish(scale);
}
function nice(domain, interval) {
domain = domain.slice();
var i0 = 0,
i1 = domain.length - 1,
x0 = domain[i0],
x1 = domain[i1],
t;
if (x1 < x0) {
t = i0, i0 = i1, i1 = t;
t = x0, x0 = x1, x1 = t;
}
domain[i0] = interval.floor(x0);
domain[i1] = interval.ceil(x1);
return domain;
}
function deinterpolate(a, b) {
return (b = Math.log(b / a))
? function(x) { return Math.log(x / a) / b; }
: constant$10(b);
}
function reinterpolate$1(a, b) {
return a < 0
? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }
: function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };
}
function pow10(x) {
return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x;
}
function powp(base) {
return base === 10 ? pow10
: base === Math.E ? Math.exp
: function(x) { return Math.pow(base, x); };
}
function logp(base) {
return base === Math.E ? Math.log
: base === 10 && Math.log10
|| base === 2 && Math.log2
|| (base = Math.log(base), function(x) { return Math.log(x) / base; });
}
function reflect(f) {
return function(x) {
return -f(-x);
};
}
function log$1() {
var scale = continuous(deinterpolate, reinterpolate$1).domain([1, 10]),
domain = scale.domain,
base = 10,
logs = logp(10),
pows = powp(10);
function rescale() {
logs = logp(base), pows = powp(base);
if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);
return scale;
}
scale.base = function(_) {
return arguments.length ? (base = +_, rescale()) : base;
};
scale.domain = function(_) {
return arguments.length ? (domain(_), rescale()) : domain();
};
scale.ticks = function(count) {
var d = domain(),
u = d[0],
v = d[d.length - 1],
r;
if (r = v < u) i = u, u = v, v = i;
var i = logs(u),
j = logs(v),
p,
k,
t,
n = count == null ? 10 : +count,
z = [];
if (!(base % 1) && j - i < n) {
i = Math.round(i) - 1, j = Math.round(j) + 1;
if (u > 0) for (; i < j; ++i) {
for (k = 1, p = pows(i); k < base; ++k) {
t = p * k;
if (t < u) continue;
if (t > v) break;
z.push(t);
}
} else for (; i < j; ++i) {
for (k = base - 1, p = pows(i); k >= 1; --k) {
t = p * k;
if (t < u) continue;
if (t > v) break;
z.push(t);
}
}
} else {
z = ticks(i, j, Math.min(j - i, n)).map(pows);
}
return r ? z.reverse() : z;
};
scale.tickFormat = function(count, specifier) {
if (specifier == null) specifier = base === 10 ? ".0e" : ",";
if (typeof specifier !== "function") specifier = exports.format(specifier);
if (count === Infinity) return specifier;
if (count == null) count = 10;
var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
return function(d) {
var i = d / pows(Math.round(logs(d)));
if (i * base < base - 0.5) i *= base;
return i <= k ? specifier(d) : "";
};
};
scale.nice = function() {
return domain(nice(domain(), {
floor: function(x) { return pows(Math.floor(logs(x))); },
ceil: function(x) { return pows(Math.ceil(logs(x))); }
}));
};
scale.copy = function() {
return copy(scale, log$1().base(base));
};
return scale;
}
function raise$1(x, exponent) {
return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
}
function pow$1() {
var exponent = 1,
scale = continuous(deinterpolate, reinterpolate),
domain = scale.domain;
function deinterpolate(a, b) {
return (b = raise$1(b, exponent) - (a = raise$1(a, exponent)))
? function(x) { return (raise$1(x, exponent) - a) / b; }
: constant$10(b);
}
function reinterpolate(a, b) {
b = raise$1(b, exponent) - (a = raise$1(a, exponent));
return function(t) { return raise$1(a + b * t, 1 / exponent); };
}
scale.exponent = function(_) {
return arguments.length ? (exponent = +_, domain(domain())) : exponent;
};
scale.copy = function() {
return copy(scale, pow$1().exponent(exponent));
};
return linearish(scale);
}
function sqrt$1() {
return pow$1().exponent(0.5);
}
function quantile$$1() {
var domain = [],
range = [],
thresholds = [];
function rescale() {
var i = 0, n = Math.max(1, range.length);
thresholds = new Array(n - 1);
while (++i < n) thresholds[i - 1] = threshold(domain, i / n);
return scale;
}
function scale(x) {
if (!isNaN(x = +x)) return range[bisectRight(thresholds, x)];
}
scale.invertExtent = function(y) {
var i = range.indexOf(y);
return i < 0 ? [NaN, NaN] : [
i > 0 ? thresholds[i - 1] : domain[0],
i < thresholds.length ? thresholds[i] : domain[domain.length - 1]
];
};
scale.domain = function(_) {
if (!arguments.length) return domain.slice();
domain = [];
for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);
domain.sort(ascending);
return rescale();
};
scale.range = function(_) {
return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice();
};
scale.quantiles = function() {
return thresholds.slice();
};
scale.copy = function() {
return quantile$$1()
.domain(domain)
.range(range);
};
return scale;
}
function quantize$1() {
var x0 = 0,
x1 = 1,
n = 1,
domain = [0.5],
range = [0, 1];
function scale(x) {
if (x <= x) return range[bisectRight(domain, x, 0, n)];
}
function rescale() {
var i = -1;
domain = new Array(n);
while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
return scale;
}
scale.domain = function(_) {
return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
};
scale.range = function(_) {
return arguments.length ? (n = (range = slice$5.call(_)).length - 1, rescale()) : range.slice();
};
scale.invertExtent = function(y) {
var i = range.indexOf(y);
return i < 0 ? [NaN, NaN]
: i < 1 ? [x0, domain[0]]
: i >= n ? [domain[n - 1], x1]
: [domain[i - 1], domain[i]];
};
scale.copy = function() {
return quantize$1()
.domain([x0, x1])
.range(range);
};
return linearish(scale);
}
function threshold$1() {
var domain = [0.5],
range = [0, 1],
n = 1;
function scale(x) {
if (x <= x) return range[bisectRight(domain, x, 0, n)];
}
scale.domain = function(_) {
return arguments.length ? (domain = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();
};
scale.range = function(_) {
return arguments.length ? (range = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();
};
scale.invertExtent = function(y) {
var i = range.indexOf(y);
return [domain[i - 1], domain[i]];
};
scale.copy = function() {
return threshold$1()
.domain(domain)
.range(range);
};
return scale;
}
var t0$1 = new Date,
t1$1 = new Date;
function newInterval(floori, offseti, count, field) {
function interval(date) {
return floori(date = new Date(+date)), date;
}
interval.floor = interval;
interval.ceil = function(date) {
return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
};
interval.round = function(date) {
var d0 = interval(date),
d1 = interval.ceil(date);
return date - d0 < d1 - date ? d0 : d1;
};
interval.offset = function(date, step) {
return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
};
interval.range = function(start, stop, step) {
var range = [], previous;
start = interval.ceil(start);
step = step == null ? 1 : Math.floor(step);
if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
do range.push(previous = new Date(+start)), offseti(start, step), floori(start);
while (previous < start && start < stop);
return range;
};
interval.filter = function(test) {
return newInterval(function(date) {
if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
}, function(date, step) {
if (date >= date) {
if (step < 0) while (++step <= 0) {
while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
} else while (--step >= 0) {
while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
}
}
});
};
if (count) {
interval.count = function(start, end) {
t0$1.setTime(+start), t1$1.setTime(+end);
floori(t0$1), floori(t1$1);
return Math.floor(count(t0$1, t1$1));
};
interval.every = function(step) {
step = Math.floor(step);
return !isFinite(step) || !(step > 0) ? null
: !(step > 1) ? interval
: interval.filter(field
? function(d) { return field(d) % step === 0; }
: function(d) { return interval.count(0, d) % step === 0; });
};
}
return interval;
}
var millisecond = newInterval(function() {
// noop
}, function(date, step) {
date.setTime(+date + step);
}, function(start, end) {
return end - start;
});
// An optimized implementation for this simple case.
millisecond.every = function(k) {
k = Math.floor(k);
if (!isFinite(k) || !(k > 0)) return null;
if (!(k > 1)) return millisecond;
return newInterval(function(date) {
date.setTime(Math.floor(date / k) * k);
}, function(date, step) {
date.setTime(+date + step * k);
}, function(start, end) {
return (end - start) / k;
});
};
var milliseconds = millisecond.range;
var durationSecond = 1e3;
var durationMinute = 6e4;
var durationHour = 36e5;
var durationDay = 864e5;
var durationWeek = 6048e5;
var second = newInterval(function(date) {
date.setTime(Math.floor(date / durationSecond) * durationSecond);
}, function(date, step) {
date.setTime(+date + step * durationSecond);
}, function(start, end) {
return (end - start) / durationSecond;
}, function(date) {
return date.getUTCSeconds();
});
var seconds = second.range;
var minute = newInterval(function(date) {
date.setTime(Math.floor(date / durationMinute) * durationMinute);
}, function(date, step) {
date.setTime(+date + step * durationMinute);
}, function(start, end) {
return (end - start) / durationMinute;
}, function(date) {
return date.getMinutes();
});
var minutes = minute.range;
var hour = newInterval(function(date) {
var offset = date.getTimezoneOffset() * durationMinute % durationHour;
if (offset < 0) offset += durationHour;
date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset);
}, function(date, step) {
date.setTime(+date + step * durationHour);
}, function(start, end) {
return (end - start) / durationHour;
}, function(date) {
return date.getHours();
});
var hours = hour.range;
var day = newInterval(function(date) {
date.setHours(0, 0, 0, 0);
}, function(date, step) {
date.setDate(date.getDate() + step);
}, function(start, end) {
return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
}, function(date) {
return date.getDate() - 1;
});
var days = day.range;
function weekday(i) {
return newInterval(function(date) {
date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
date.setHours(0, 0, 0, 0);
}, function(date, step) {
date.setDate(date.getDate() + step * 7);
}, function(start, end) {
return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
});
}
var sunday = weekday(0);
var monday = weekday(1);
var tuesday = weekday(2);
var wednesday = weekday(3);
var thursday = weekday(4);
var friday = weekday(5);
var saturday = weekday(6);
var sundays = sunday.range;
var mondays = monday.range;
var tuesdays = tuesday.range;
var wednesdays = wednesday.range;
var thursdays = thursday.range;
var fridays = friday.range;
var saturdays = saturday.range;
var month = newInterval(function(date) {
date.setDate(1);
date.setHours(0, 0, 0, 0);
}, function(date, step) {
date.setMonth(date.getMonth() + step);
}, function(start, end) {
return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
}, function(date) {
return date.getMonth();
});
var months = month.range;
var year = newInterval(function(date) {
date.setMonth(0, 1);
date.setHours(0, 0, 0, 0);
}, function(date, step) {
date.setFullYear(date.getFullYear() + step);
}, function(start, end) {
return end.getFullYear() - start.getFullYear();
}, function(date) {
return date.getFullYear();
});
// An optimized implementation for this simple case.
year.every = function(k) {
return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
date.setFullYear(Math.floor(date.getFullYear() / k) * k);
date.setMonth(0, 1);
date.setHours(0, 0, 0, 0);
}, function(date, step) {
date.setFullYear(date.getFullYear() + step * k);
});
};
var years = year.range;
var utcMinute = newInterval(function(date) {
date.setUTCSeconds(0, 0);
}, function(date, step) {
date.setTime(+date + step * durationMinute);
}, function(start, end) {
return (end - start) / durationMinute;
}, function(date) {
return date.getUTCMinutes();
});
var utcMinutes = utcMinute.range;
var utcHour = newInterval(function(date) {
date.setUTCMinutes(0, 0, 0);
}, function(date, step) {
date.setTime(+date + step * durationHour);
}, function(start, end) {
return (end - start) / durationHour;
}, function(date) {
return date.getUTCHours();
});
var utcHours = utcHour.range;
var utcDay = newInterval(function(date) {
date.setUTCHours(0, 0, 0, 0);
}, function(date, step) {
date.setUTCDate(date.getUTCDate() + step);
}, function(start, end) {
return (end - start) / durationDay;
}, function(date) {
return date.getUTCDate() - 1;
});
var utcDays = utcDay.range;
function utcWeekday(i) {
return newInterval(function(date) {
date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
date.setUTCHours(0, 0, 0, 0);
}, function(date, step) {
date.setUTCDate(date.getUTCDate() + step * 7);
}, function(start, end) {
return (end - start) / durationWeek;
});
}
var utcSunday = utcWeekday(0);
var utcMonday = utcWeekday(1);
var utcTuesday = utcWeekday(2);
var utcWednesday = utcWeekday(3);
var utcThursday = utcWeekday(4);
var utcFriday = utcWeekday(5);
var utcSaturday = utcWeekday(6);
var utcSundays = utcSunday.range;
var utcMondays = utcMonday.range;
var utcTuesdays = utcTuesday.range;
var utcWednesdays = utcWednesday.range;
var utcThursdays = utcThursday.range;
var utcFridays = utcFriday.range;
var utcSaturdays = utcSaturday.range;
var utcMonth = newInterval(function(date) {
date.setUTCDate(1);
date.setUTCHours(0, 0, 0, 0);
}, function(date, step) {
date.setUTCMonth(date.getUTCMonth() + step);
}, function(start, end) {
return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
}, function(date) {
return date.getUTCMonth();
});
var utcMonths = utcMonth.range;
var utcYear = newInterval(function(date) {
date.setUTCMonth(0, 1);
date.setUTCHours(0, 0, 0, 0);
}, function(date, step) {
date.setUTCFullYear(date.getUTCFullYear() + step);
}, function(start, end) {
return end.getUTCFullYear() - start.getUTCFullYear();
}, function(date) {
return date.getUTCFullYear();
});
// An optimized implementation for this simple case.
utcYear.every = function(k) {
return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
date.setUTCMonth(0, 1);
date.setUTCHours(0, 0, 0, 0);
}, function(date, step) {
date.setUTCFullYear(date.getUTCFullYear() + step * k);
});
};
var utcYears = utcYear.range;
function localDate(d) {
if (0 <= d.y && d.y < 100) {
var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
date.setFullYear(d.y);
return date;
}
return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
}
function utcDate(d) {
if (0 <= d.y && d.y < 100) {
var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
date.setUTCFullYear(d.y);
return date;
}
return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
}
function newYear(y) {
return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};
}
function formatLocale$1(locale) {
var locale_dateTime = locale.dateTime,
locale_date = locale.date,
locale_time = locale.time,
locale_periods = locale.periods,
locale_weekdays = locale.days,
locale_shortWeekdays = locale.shortDays,
locale_months = locale.months,
locale_shortMonths = locale.shortMonths;
var periodRe = formatRe(locale_periods),
periodLookup = formatLookup(locale_periods),
weekdayRe = formatRe(locale_weekdays),
weekdayLookup = formatLookup(locale_weekdays),
shortWeekdayRe = formatRe(locale_shortWeekdays),
shortWeekdayLookup = formatLookup(locale_shortWeekdays),
monthRe = formatRe(locale_months),
monthLookup = formatLookup(locale_months),
shortMonthRe = formatRe(locale_shortMonths),
shortMonthLookup = formatLookup(locale_shortMonths);
var formats = {
"a": formatShortWeekday,
"A": formatWeekday,
"b": formatShortMonth,
"B": formatMonth,
"c": null,
"d": formatDayOfMonth,
"e": formatDayOfMonth,
"f": formatMicroseconds,
"H": formatHour24,
"I": formatHour12,
"j": formatDayOfYear,
"L": formatMilliseconds,
"m": formatMonthNumber,
"M": formatMinutes,
"p": formatPeriod,
"Q": formatUnixTimestamp,
"s": formatUnixTimestampSeconds,
"S": formatSeconds,
"u": formatWeekdayNumberMonday,
"U": formatWeekNumberSunday,
"V": formatWeekNumberISO,
"w": formatWeekdayNumberSunday,
"W": formatWeekNumberMonday,
"x": null,
"X": null,
"y": formatYear,
"Y": formatFullYear,
"Z": formatZone,
"%": formatLiteralPercent
};
var utcFormats = {
"a": formatUTCShortWeekday,
"A": formatUTCWeekday,
"b": formatUTCShortMonth,
"B": formatUTCMonth,
"c": null,
"d": formatUTCDayOfMonth,
"e": formatUTCDayOfMonth,
"f": formatUTCMicroseconds,
"H": formatUTCHour24,
"I": formatUTCHour12,
"j": formatUTCDayOfYear,
"L": formatUTCMilliseconds,
"m": formatUTCMonthNumber,
"M": formatUTCMinutes,
"p": formatUTCPeriod,
"Q": formatUnixTimestamp,
"s": formatUnixTimestampSeconds,
"S": formatUTCSeconds,
"u": formatUTCWeekdayNumberMonday,
"U": formatUTCWeekNumberSunday,
"V": formatUTCWeekNumberISO,
"w": formatUTCWeekdayNumberSunday,
"W": formatUTCWeekNumberMonday,
"x": null,
"X": null,
"y": formatUTCYear,
"Y": formatUTCFullYear,
"Z": formatUTCZone,
"%": formatLiteralPercent
};
var parses = {
"a": parseShortWeekday,
"A": parseWeekday,
"b": parseShortMonth,
"B": parseMonth,
"c": parseLocaleDateTime,
"d": parseDayOfMonth,
"e": parseDayOfMonth,
"f": parseMicroseconds,
"H": parseHour24,
"I": parseHour24,
"j": parseDayOfYear,
"L": parseMilliseconds,
"m": parseMonthNumber,
"M": parseMinutes,
"p": parsePeriod,
"Q": parseUnixTimestamp,
"s": parseUnixTimestampSeconds,
"S": parseSeconds,
"u": parseWeekdayNumberMonday,
"U": parseWeekNumberSunday,
"V": parseWeekNumberISO,
"w": parseWeekdayNumberSunday,
"W": parseWeekNumberMonday,
"x": parseLocaleDate,
"X": parseLocaleTime,
"y": parseYear,
"Y": parseFullYear,
"Z": parseZone,
"%": parseLiteralPercent
};
// These recursive directive definitions must be deferred.
formats.x = newFormat(locale_date, formats);
formats.X = newFormat(locale_time, formats);
formats.c = newFormat(locale_dateTime, formats);
utcFormats.x = newFormat(locale_date, utcFormats);
utcFormats.X = newFormat(locale_time, utcFormats);
utcFormats.c = newFormat(locale_dateTime, utcFormats);
function newFormat(specifier, formats) {
return function(date) {
var string = [],
i = -1,
j = 0,
n = specifier.length,
c,
pad,
format;
if (!(date instanceof Date)) date = new Date(+date);
while (++i < n) {
if (specifier.charCodeAt(i) === 37) {
string.push(specifier.slice(j, i));
if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
else pad = c === "e" ? " " : "0";
if (format = formats[c]) c = format(date, pad);
string.push(c);
j = i + 1;
}
}
string.push(specifier.slice(j, i));
return string.join("");
};
}
function newParse(specifier, newDate) {
return function(string) {
var d = newYear(1900),
i = parseSpecifier(d, specifier, string += "", 0),
week, day$$1;
if (i != string.length) return null;
// If a UNIX timestamp is specified, return it.
if ("Q" in d) return new Date(d.Q);
// The am-pm flag is 0 for AM, and 1 for PM.
if ("p" in d) d.H = d.H % 12 + d.p * 12;
// Convert day-of-week and week-of-year to day-of-year.
if ("V" in d) {
if (d.V < 1 || d.V > 53) return null;
if (!("w" in d)) d.w = 1;
if ("Z" in d) {
week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay();
week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week);
week = utcDay.offset(week, (d.V - 1) * 7);
d.y = week.getUTCFullYear();
d.m = week.getUTCMonth();
d.d = week.getUTCDate() + (d.w + 6) % 7;
} else {
week = newDate(newYear(d.y)), day$$1 = week.getDay();
week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week);
week = day.offset(week, (d.V - 1) * 7);
d.y = week.getFullYear();
d.m = week.getMonth();
d.d = week.getDate() + (d.w + 6) % 7;
}
} else if ("W" in d || "U" in d) {
if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();
d.m = 0;
d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7;
}
// If a time zone is specified, all fields are interpreted as UTC and then
// offset according to the specified time zone.
if ("Z" in d) {
d.H += d.Z / 100 | 0;
d.M += d.Z % 100;
return utcDate(d);
}
// Otherwise, all fields are in local time.
return newDate(d);
};
}
function parseSpecifier(d, specifier, string, j) {
var i = 0,
n = specifier.length,
m = string.length,
c,
parse;
while (i < n) {
if (j >= m) return -1;
c = specifier.charCodeAt(i++);
if (c === 37) {
c = specifier.charAt(i++);
parse = parses[c in pads ? specifier.charAt(i++) : c];
if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
} else if (c != string.charCodeAt(j++)) {
return -1;
}
}
return j;
}
function parsePeriod(d, string, i) {
var n = periodRe.exec(string.slice(i));
return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
}
function parseShortWeekday(d, string, i) {
var n = shortWeekdayRe.exec(string.slice(i));
return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
}
function parseWeekday(d, string, i) {
var n = weekdayRe.exec(string.slice(i));
return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
}
function parseShortMonth(d, string, i) {
var n = shortMonthRe.exec(string.slice(i));
return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
}
function parseMonth(d, string, i) {
var n = monthRe.exec(string.slice(i));
return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
}
function parseLocaleDateTime(d, string, i) {
return parseSpecifier(d, locale_dateTime, string, i);
}
function parseLocaleDate(d, string, i) {
return parseSpecifier(d, locale_date, string, i);
}
function parseLocaleTime(d, string, i) {
return parseSpecifier(d, locale_time, string, i);
}
function formatShortWeekday(d) {
return locale_shortWeekdays[d.getDay()];
}
function formatWeekday(d) {
return locale_weekdays[d.getDay()];
}
function formatShortMonth(d) {
return locale_shortMonths[d.getMonth()];
}
function formatMonth(d) {
return locale_months[d.getMonth()];
}
function formatPeriod(d) {
return locale_periods[+(d.getHours() >= 12)];
}
function formatUTCShortWeekday(d) {
return locale_shortWeekdays[d.getUTCDay()];
}
function formatUTCWeekday(d) {
return locale_weekdays[d.getUTCDay()];
}
function formatUTCShortMonth(d) {
return locale_shortMonths[d.getUTCMonth()];
}
function formatUTCMonth(d) {
return locale_months[d.getUTCMonth()];
}
function formatUTCPeriod(d) {
return locale_periods[+(d.getUTCHours() >= 12)];
}
return {
format: function(specifier) {
var f = newFormat(specifier += "", formats);
f.toString = function() { return specifier; };
return f;
},
parse: function(specifier) {
var p = newParse(specifier += "", localDate);
p.toString = function() { return specifier; };
return p;
},
utcFormat: function(specifier) {
var f = newFormat(specifier += "", utcFormats);
f.toString = function() { return specifier; };
return f;
},
utcParse: function(specifier) {
var p = newParse(specifier, utcDate);
p.toString = function() { return specifier; };
return p;
}
};
}
var pads = {"-": "", "_": " ", "0": "0"},
numberRe = /^\s*\d+/, // note: ignores next directive
percentRe = /^%/,
requoteRe = /[\\^$*+?|[\]().{}]/g;
function pad(value, fill, width) {
var sign = value < 0 ? "-" : "",
string = (sign ? -value : value) + "",
length = string.length;
return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
}
function requote(s) {
return s.replace(requoteRe, "\\$&");
}
function formatRe(names) {
return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
}
function formatLookup(names) {
var map = {}, i = -1, n = names.length;
while (++i < n) map[names[i].toLowerCase()] = i;
return map;
}
function parseWeekdayNumberSunday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.w = +n[0], i + n[0].length) : -1;
}
function parseWeekdayNumberMonday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 1));
return n ? (d.u = +n[0], i + n[0].length) : -1;
}
function parseWeekNumberSunday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.U = +n[0], i + n[0].length) : -1;
}
function parseWeekNumberISO(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.V = +n[0], i + n[0].length) : -1;
}
function parseWeekNumberMonday(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.W = +n[0], i + n[0].length) : -1;
}
function parseFullYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 4));
return n ? (d.y = +n[0], i + n[0].length) : -1;
}
function parseYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
}
function parseZone(d, string, i) {
var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
}
function parseMonthNumber(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
}
function parseDayOfMonth(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.d = +n[0], i + n[0].length) : -1;
}
function parseDayOfYear(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 3));
return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
}
function parseHour24(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.H = +n[0], i + n[0].length) : -1;
}
function parseMinutes(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.M = +n[0], i + n[0].length) : -1;
}
function parseSeconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 2));
return n ? (d.S = +n[0], i + n[0].length) : -1;
}
function parseMilliseconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 3));
return n ? (d.L = +n[0], i + n[0].length) : -1;
}
function parseMicroseconds(d, string, i) {
var n = numberRe.exec(string.slice(i, i + 6));
return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
}
function parseLiteralPercent(d, string, i) {
var n = percentRe.exec(string.slice(i, i + 1));
return n ? i + n[0].length : -1;
}
function parseUnixTimestamp(d, string, i) {
var n = numberRe.exec(string.slice(i));
return n ? (d.Q = +n[0], i + n[0].length) : -1;
}
function parseUnixTimestampSeconds(d, string, i) {
var n = numberRe.exec(string.slice(i));
return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;
}
function formatDayOfMonth(d, p) {
return pad(d.getDate(), p, 2);
}
function formatHour24(d, p) {
return pad(d.getHours(), p, 2);
}
function formatHour12(d, p) {
return pad(d.getHours() % 12 || 12, p, 2);
}
function formatDayOfYear(d, p) {
return pad(1 + day.count(year(d), d), p, 3);
}
function formatMilliseconds(d, p) {
return pad(d.getMilliseconds(), p, 3);
}
function formatMicroseconds(d, p) {
return formatMilliseconds(d, p) + "000";
}
function formatMonthNumber(d, p) {
return pad(d.getMonth() + 1, p, 2);
}
function formatMinutes(d, p) {
return pad(d.getMinutes(), p, 2);
}
function formatSeconds(d, p) {
return pad(d.getSeconds(), p, 2);
}
function formatWeekdayNumberMonday(d) {
var day$$1 = d.getDay();
return day$$1 === 0 ? 7 : day$$1;
}
function formatWeekNumberSunday(d, p) {
return pad(sunday.count(year(d), d), p, 2);
}
function formatWeekNumberISO(d, p) {
var day$$1 = d.getDay();
d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d);
return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2);
}
function formatWeekdayNumberSunday(d) {
return d.getDay();
}
function formatWeekNumberMonday(d, p) {
return pad(monday.count(year(d), d), p, 2);
}
function formatYear(d, p) {
return pad(d.getFullYear() % 100, p, 2);
}
function formatFullYear(d, p) {
return pad(d.getFullYear() % 10000, p, 4);
}
function formatZone(d) {
var z = d.getTimezoneOffset();
return (z > 0 ? "-" : (z *= -1, "+"))
+ pad(z / 60 | 0, "0", 2)
+ pad(z % 60, "0", 2);
}
function formatUTCDayOfMonth(d, p) {
return pad(d.getUTCDate(), p, 2);
}
function formatUTCHour24(d, p) {
return pad(d.getUTCHours(), p, 2);
}
function formatUTCHour12(d, p) {
return pad(d.getUTCHours() % 12 || 12, p, 2);
}
function formatUTCDayOfYear(d, p) {
return pad(1 + utcDay.count(utcYear(d), d), p, 3);
}
function formatUTCMilliseconds(d, p) {
return pad(d.getUTCMilliseconds(), p, 3);
}
function formatUTCMicroseconds(d, p) {
return formatUTCMilliseconds(d, p) + "000";
}
function formatUTCMonthNumber(d, p) {
return pad(d.getUTCMonth() + 1, p, 2);
}
function formatUTCMinutes(d, p) {
return pad(d.getUTCMinutes(), p, 2);
}
function formatUTCSeconds(d, p) {
return pad(d.getUTCSeconds(), p, 2);
}
function formatUTCWeekdayNumberMonday(d) {
var dow = d.getUTCDay();
return dow === 0 ? 7 : dow;
}
function formatUTCWeekNumberSunday(d, p) {
return pad(utcSunday.count(utcYear(d), d), p, 2);
}
function formatUTCWeekNumberISO(d, p) {
var day$$1 = d.getUTCDay();
d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d);
return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);
}
function formatUTCWeekdayNumberSunday(d) {
return d.getUTCDay();
}
function formatUTCWeekNumberMonday(d, p) {
return pad(utcMonday.count(utcYear(d), d), p, 2);
}
function formatUTCYear(d, p) {
return pad(d.getUTCFullYear() % 100, p, 2);
}
function formatUTCFullYear(d, p) {
return pad(d.getUTCFullYear() % 10000, p, 4);
}
function formatUTCZone() {
return "+0000";
}
function formatLiteralPercent() {
return "%";
}
function formatUnixTimestamp(d) {
return +d;
}
function formatUnixTimestampSeconds(d) {
return Math.floor(+d / 1000);
}
var locale$1;
defaultLocale$1({
dateTime: "%x, %X",
date: "%-m/%-d/%Y",
time: "%-I:%M:%S %p",
periods: ["AM", "PM"],
days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
});
function defaultLocale$1(definition) {
locale$1 = formatLocale$1(definition);
exports.timeFormat = locale$1.format;
exports.timeParse = locale$1.parse;
exports.utcFormat = locale$1.utcFormat;
exports.utcParse = locale$1.utcParse;
return locale$1;
}
var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
function formatIsoNative(date) {
return date.toISOString();
}
var formatIso = Date.prototype.toISOString
? formatIsoNative
: exports.utcFormat(isoSpecifier);
function parseIsoNative(string) {
var date = new Date(string);
return isNaN(date) ? null : date;
}
var parseIso = +new Date("2000-01-01T00:00:00.000Z")
? parseIsoNative
: exports.utcParse(isoSpecifier);
var durationSecond$1 = 1000,
durationMinute$1 = durationSecond$1 * 60,
durationHour$1 = durationMinute$1 * 60,
durationDay$1 = durationHour$1 * 24,
durationWeek$1 = durationDay$1 * 7,
durationMonth = durationDay$1 * 30,
durationYear = durationDay$1 * 365;
function date$1(t) {
return new Date(t);
}
function number$3(t) {
return t instanceof Date ? +t : +new Date(+t);
}
function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) {
var scale = continuous(deinterpolateLinear, reinterpolate),
invert = scale.invert,
domain = scale.domain;
var formatMillisecond = format(".%L"),
formatSecond = format(":%S"),
formatMinute = format("%I:%M"),
formatHour = format("%I %p"),
formatDay = format("%a %d"),
formatWeek = format("%b %d"),
formatMonth = format("%B"),
formatYear = format("%Y");
var tickIntervals = [
[second$$1, 1, durationSecond$1],
[second$$1, 5, 5 * durationSecond$1],
[second$$1, 15, 15 * durationSecond$1],
[second$$1, 30, 30 * durationSecond$1],
[minute$$1, 1, durationMinute$1],
[minute$$1, 5, 5 * durationMinute$1],
[minute$$1, 15, 15 * durationMinute$1],
[minute$$1, 30, 30 * durationMinute$1],
[ hour$$1, 1, durationHour$1 ],
[ hour$$1, 3, 3 * durationHour$1 ],
[ hour$$1, 6, 6 * durationHour$1 ],
[ hour$$1, 12, 12 * durationHour$1 ],
[ day$$1, 1, durationDay$1 ],
[ day$$1, 2, 2 * durationDay$1 ],
[ week, 1, durationWeek$1 ],
[ month$$1, 1, durationMonth ],
[ month$$1, 3, 3 * durationMonth ],
[ year$$1, 1, durationYear ]
];
function tickFormat(date$$1) {
return (second$$1(date$$1) < date$$1 ? formatMillisecond
: minute$$1(date$$1) < date$$1 ? formatSecond
: hour$$1(date$$1) < date$$1 ? formatMinute
: day$$1(date$$1) < date$$1 ? formatHour
: month$$1(date$$1) < date$$1 ? (week(date$$1) < date$$1 ? formatDay : formatWeek)
: year$$1(date$$1) < date$$1 ? formatMonth
: formatYear)(date$$1);
}
function tickInterval(interval, start, stop, step) {
if (interval == null) interval = 10;
// If a desired tick count is specified, pick a reasonable tick interval
// based on the extent of the domain and a rough estimate of tick size.
// Otherwise, assume interval is already a time interval and use it.
if (typeof interval === "number") {
var target = Math.abs(stop - start) / interval,
i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
if (i === tickIntervals.length) {
step = tickStep(start / durationYear, stop / durationYear, interval);
interval = year$$1;
} else if (i) {
i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
step = i[1];
interval = i[0];
} else {
step = Math.max(tickStep(start, stop, interval), 1);
interval = millisecond$$1;
}
}
return step == null ? interval : interval.every(step);
}
scale.invert = function(y) {
return new Date(invert(y));
};
scale.domain = function(_) {
return arguments.length ? domain(map$2.call(_, number$3)) : domain().map(date$1);
};
scale.ticks = function(interval, step) {
var d = domain(),
t0 = d[0],
t1 = d[d.length - 1],
r = t1 < t0,
t;
if (r) t = t0, t0 = t1, t1 = t;
t = tickInterval(interval, t0, t1, step);
t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
return r ? t.reverse() : t;
};
scale.tickFormat = function(count, specifier) {
return specifier == null ? tickFormat : format(specifier);
};
scale.nice = function(interval, step) {
var d = domain();
return (interval = tickInterval(interval, d[0], d[d.length - 1], step))
? domain(nice(d, interval))
: scale;
};
scale.copy = function() {
return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format));
};
return scale;
}
function time() {
return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);
}
function utcTime() {
return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);
}
function sequential(interpolator) {
var x0 = 0,
x1 = 1,
k10 = 1,
clamp = false;
function scale(x) {
var t = (x - x0) * k10;
return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
}
scale.domain = function(_) {
return arguments.length ? (x0 = +_[0], x1 = +_[1], k10 = x0 === x1 ? 0 : 1 / (x1 - x0), scale) : [x0, x1];
};
scale.clamp = function(_) {
return arguments.length ? (clamp = !!_, scale) : clamp;
};
scale.interpolator = function(_) {
return arguments.length ? (interpolator = _, scale) : interpolator;
};
scale.copy = function() {
return sequential(interpolator).domain([x0, x1]).clamp(clamp);
};
return linearish(scale);
}
function diverging(interpolator) {
var x0 = 0,
x1 = 0.5,
x2 = 1,
k10 = 1,
k21 = 1,
clamp = false;
function scale(x) {
var t = 0.5 + ((x = +x) - x1) * (x < x1 ? k10 : k21);
return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
}
scale.domain = function(_) {
return arguments.length ? (x0 = +_[0], x1 = +_[1], x2 = +_[2], k10 = x0 === x1 ? 0 : 0.5 / (x1 - x0), k21 = x1 === x2 ? 0 : 0.5 / (x2 - x1), scale) : [x0, x1, x2];
};
scale.clamp = function(_) {
return arguments.length ? (clamp = !!_, scale) : clamp;
};
scale.interpolator = function(_) {
return arguments.length ? (interpolator = _, scale) : interpolator;
};
scale.copy = function() {
return diverging(interpolator).domain([x0, x1, x2]).clamp(clamp);
};
return linearish(scale);
}
function colors(specifier) {
var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;
while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
return colors;
}
var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf");
var Accent = colors("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666");
var Dark2 = colors("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666");
var Paired = colors("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928");
var Pastel1 = colors("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2");
var Pastel2 = colors("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc");
var Set1 = colors("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999");
var Set2 = colors("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3");
var Set3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f");
function ramp(scheme) {
return rgbBasis(scheme[scheme.length - 1]);
}
var scheme = new Array(3).concat(
"d8b365f5f5f55ab4ac",
"a6611adfc27d80cdc1018571",
"a6611adfc27df5f5f580cdc1018571",
"8c510ad8b365f6e8c3c7eae55ab4ac01665e",
"8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e",
"8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e",
"8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e",
"5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30",
"5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30"
).map(colors);
var BrBG = ramp(scheme);
var scheme$1 = new Array(3).concat(
"af8dc3f7f7f77fbf7b",
"7b3294c2a5cfa6dba0008837",
"7b3294c2a5cff7f7f7a6dba0008837",
"762a83af8dc3e7d4e8d9f0d37fbf7b1b7837",
"762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837",
"762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837",
"762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837",
"40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b",
"40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b"
).map(colors);
var PRGn = ramp(scheme$1);
var scheme$2 = new Array(3).concat(
"e9a3c9f7f7f7a1d76a",
"d01c8bf1b6dab8e1864dac26",
"d01c8bf1b6daf7f7f7b8e1864dac26",
"c51b7de9a3c9fde0efe6f5d0a1d76a4d9221",
"c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221",
"c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221",
"c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221",
"8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419",
"8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419"
).map(colors);
var PiYG = ramp(scheme$2);
var scheme$3 = new Array(3).concat(
"998ec3f7f7f7f1a340",
"5e3c99b2abd2fdb863e66101",
"5e3c99b2abd2f7f7f7fdb863e66101",
"542788998ec3d8daebfee0b6f1a340b35806",
"542788998ec3d8daebf7f7f7fee0b6f1a340b35806",
"5427888073acb2abd2d8daebfee0b6fdb863e08214b35806",
"5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806",
"2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08",
"2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08"
).map(colors);
var PuOr = ramp(scheme$3);
var scheme$4 = new Array(3).concat(
"ef8a62f7f7f767a9cf",
"ca0020f4a58292c5de0571b0",
"ca0020f4a582f7f7f792c5de0571b0",
"b2182bef8a62fddbc7d1e5f067a9cf2166ac",
"b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac",
"b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac",
"b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac",
"67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061",
"67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061"
).map(colors);
var RdBu = ramp(scheme$4);
var scheme$5 = new Array(3).concat(
"ef8a62ffffff999999",
"ca0020f4a582bababa404040",
"ca0020f4a582ffffffbababa404040",
"b2182bef8a62fddbc7e0e0e09999994d4d4d",
"b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d",
"b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d",
"b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d",
"67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a",
"67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a"
).map(colors);
var RdGy = ramp(scheme$5);
var scheme$6 = new Array(3).concat(
"fc8d59ffffbf91bfdb",
"d7191cfdae61abd9e92c7bb6",
"d7191cfdae61ffffbfabd9e92c7bb6",
"d73027fc8d59fee090e0f3f891bfdb4575b4",
"d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4",
"d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4",
"d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4",
"a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695",
"a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695"
).map(colors);
var RdYlBu = ramp(scheme$6);
var scheme$7 = new Array(3).concat(
"fc8d59ffffbf91cf60",
"d7191cfdae61a6d96a1a9641",
"d7191cfdae61ffffbfa6d96a1a9641",
"d73027fc8d59fee08bd9ef8b91cf601a9850",
"d73027fc8d59fee08bffffbfd9ef8b91cf601a9850",
"d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850",
"d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850",
"a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837",
"a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837"
).map(colors);
var RdYlGn = ramp(scheme$7);
var scheme$8 = new Array(3).concat(
"fc8d59ffffbf99d594",
"d7191cfdae61abdda42b83ba",
"d7191cfdae61ffffbfabdda42b83ba",
"d53e4ffc8d59fee08be6f59899d5943288bd",
"d53e4ffc8d59fee08bffffbfe6f59899d5943288bd",
"d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd",
"d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd",
"9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2",
"9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2"
).map(colors);
var Spectral = ramp(scheme$8);
var scheme$9 = new Array(3).concat(
"e5f5f999d8c92ca25f",
"edf8fbb2e2e266c2a4238b45",
"edf8fbb2e2e266c2a42ca25f006d2c",
"edf8fbccece699d8c966c2a42ca25f006d2c",
"edf8fbccece699d8c966c2a441ae76238b45005824",
"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824",
"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b"
).map(colors);
var BuGn = ramp(scheme$9);
var scheme$10 = new Array(3).concat(
"e0ecf49ebcda8856a7",
"edf8fbb3cde38c96c688419d",
"edf8fbb3cde38c96c68856a7810f7c",
"edf8fbbfd3e69ebcda8c96c68856a7810f7c",
"edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b",
"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b",
"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b"
).map(colors);
var BuPu = ramp(scheme$10);
var scheme$11 = new Array(3).concat(
"e0f3dba8ddb543a2ca",
"f0f9e8bae4bc7bccc42b8cbe",
"f0f9e8bae4bc7bccc443a2ca0868ac",
"f0f9e8ccebc5a8ddb57bccc443a2ca0868ac",
"f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e",
"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e",
"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081"
).map(colors);
var GnBu = ramp(scheme$11);
var scheme$12 = new Array(3).concat(
"fee8c8fdbb84e34a33",
"fef0d9fdcc8afc8d59d7301f",
"fef0d9fdcc8afc8d59e34a33b30000",
"fef0d9fdd49efdbb84fc8d59e34a33b30000",
"fef0d9fdd49efdbb84fc8d59ef6548d7301f990000",
"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000",
"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000"
).map(colors);
var OrRd = ramp(scheme$12);
var scheme$13 = new Array(3).concat(
"ece2f0a6bddb1c9099",
"f6eff7bdc9e167a9cf02818a",
"f6eff7bdc9e167a9cf1c9099016c59",
"f6eff7d0d1e6a6bddb67a9cf1c9099016c59",
"f6eff7d0d1e6a6bddb67a9cf3690c002818a016450",
"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450",
"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636"
).map(colors);
var PuBuGn = ramp(scheme$13);
var scheme$14 = new Array(3).concat(
"ece7f2a6bddb2b8cbe",
"f1eef6bdc9e174a9cf0570b0",
"f1eef6bdc9e174a9cf2b8cbe045a8d",
"f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d",
"f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b",
"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b",
"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858"
).map(colors);
var PuBu = ramp(scheme$14);
var scheme$15 = new Array(3).concat(
"e7e1efc994c7dd1c77",
"f1eef6d7b5d8df65b0ce1256",
"f1eef6d7b5d8df65b0dd1c77980043",
"f1eef6d4b9dac994c7df65b0dd1c77980043",
"f1eef6d4b9dac994c7df65b0e7298ace125691003f",
"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f",
"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f"
).map(colors);
var PuRd = ramp(scheme$15);
var scheme$16 = new Array(3).concat(
"fde0ddfa9fb5c51b8a",
"feebe2fbb4b9f768a1ae017e",
"feebe2fbb4b9f768a1c51b8a7a0177",
"feebe2fcc5c0fa9fb5f768a1c51b8a7a0177",
"feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177",
"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177",
"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a"
).map(colors);
var RdPu = ramp(scheme$16);
var scheme$17 = new Array(3).concat(
"edf8b17fcdbb2c7fb8",
"ffffcca1dab441b6c4225ea8",
"ffffcca1dab441b6c42c7fb8253494",
"ffffccc7e9b47fcdbb41b6c42c7fb8253494",
"ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84",
"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84",
"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58"
).map(colors);
var YlGnBu = ramp(scheme$17);
var scheme$18 = new Array(3).concat(
"f7fcb9addd8e31a354",
"ffffccc2e69978c679238443",
"ffffccc2e69978c67931a354006837",
"ffffccd9f0a3addd8e78c67931a354006837",
"ffffccd9f0a3addd8e78c67941ab5d238443005a32",
"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32",
"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529"
).map(colors);
var YlGn = ramp(scheme$18);
var scheme$19 = new Array(3).concat(
"fff7bcfec44fd95f0e",
"ffffd4fed98efe9929cc4c02",
"ffffd4fed98efe9929d95f0e993404",
"ffffd4fee391fec44ffe9929d95f0e993404",
"ffffd4fee391fec44ffe9929ec7014cc4c028c2d04",
"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04",
"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506"
).map(colors);
var YlOrBr = ramp(scheme$19);
var scheme$20 = new Array(3).concat(
"ffeda0feb24cf03b20",
"ffffb2fecc5cfd8d3ce31a1c",
"ffffb2fecc5cfd8d3cf03b20bd0026",
"ffffb2fed976feb24cfd8d3cf03b20bd0026",
"ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026",
"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026",
"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026"
).map(colors);
var YlOrRd = ramp(scheme$20);
var scheme$21 = new Array(3).concat(
"deebf79ecae13182bd",
"eff3ffbdd7e76baed62171b5",
"eff3ffbdd7e76baed63182bd08519c",
"eff3ffc6dbef9ecae16baed63182bd08519c",
"eff3ffc6dbef9ecae16baed64292c62171b5084594",
"f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594",
"f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b"
).map(colors);
var Blues = ramp(scheme$21);
var scheme$22 = new Array(3).concat(
"e5f5e0a1d99b31a354",
"edf8e9bae4b374c476238b45",
"edf8e9bae4b374c47631a354006d2c",
"edf8e9c7e9c0a1d99b74c47631a354006d2c",
"edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32",
"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32",
"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b"
).map(colors);
var Greens = ramp(scheme$22);
var scheme$23 = new Array(3).concat(
"f0f0f0bdbdbd636363",
"f7f7f7cccccc969696525252",
"f7f7f7cccccc969696636363252525",
"f7f7f7d9d9d9bdbdbd969696636363252525",
"f7f7f7d9d9d9bdbdbd969696737373525252252525",
"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525",
"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000"
).map(colors);
var Greys = ramp(scheme$23);
var scheme$24 = new Array(3).concat(
"efedf5bcbddc756bb1",
"f2f0f7cbc9e29e9ac86a51a3",
"f2f0f7cbc9e29e9ac8756bb154278f",
"f2f0f7dadaebbcbddc9e9ac8756bb154278f",
"f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486",
"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486",
"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d"
).map(colors);
var Purples = ramp(scheme$24);
var scheme$25 = new Array(3).concat(
"fee0d2fc9272de2d26",
"fee5d9fcae91fb6a4acb181d",
"fee5d9fcae91fb6a4ade2d26a50f15",
"fee5d9fcbba1fc9272fb6a4ade2d26a50f15",
"fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d",
"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d",
"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d"
).map(colors);
var Reds = ramp(scheme$25);
var scheme$26 = new Array(3).concat(
"fee6cefdae6be6550d",
"feeddefdbe85fd8d3cd94701",
"feeddefdbe85fd8d3ce6550da63603",
"feeddefdd0a2fdae6bfd8d3ce6550da63603",
"feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04",
"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04",
"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704"
).map(colors);
var Oranges = ramp(scheme$26);
var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0));
var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
var c = cubehelix();
function rainbow(t) {
if (t < 0 || t > 1) t -= Math.floor(t);
var ts = Math.abs(t - 0.5);
c.h = 360 * t - 100;
c.s = 1.5 - 1.5 * ts;
c.l = 0.8 - 0.9 * ts;
return c + "";
}
var c$1 = rgb(),
pi_1_3 = Math.PI / 3,
pi_2_3 = Math.PI * 2 / 3;
function sinebow(t) {
var x;
t = (0.5 - t) * Math.PI;
c$1.r = 255 * (x = Math.sin(t)) * x;
c$1.g = 255 * (x = Math.sin(t + pi_1_3)) * x;
c$1.b = 255 * (x = Math.sin(t + pi_2_3)) * x;
return c$1 + "";
}
function ramp$1(range) {
var n = range.length;
return function(t) {
return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];
};
}
var viridis = ramp$1(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));
var magma = ramp$1(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf"));
var inferno = ramp$1(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4"));
var plasma = ramp$1(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));
function constant$11(x) {
return function constant() {
return x;
};
}
var abs$1 = Math.abs;
var atan2$1 = Math.atan2;
var cos$2 = Math.cos;
var max$2 = Math.max;
var min$1 = Math.min;
var sin$2 = Math.sin;
var sqrt$2 = Math.sqrt;
var epsilon$3 = 1e-12;
var pi$4 = Math.PI;
var halfPi$3 = pi$4 / 2;
var tau$4 = 2 * pi$4;
function acos$1(x) {
return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x);
}
function asin$1(x) {
return x >= 1 ? halfPi$3 : x <= -1 ? -halfPi$3 : Math.asin(x);
}
function arcInnerRadius(d) {
return d.innerRadius;
}
function arcOuterRadius(d) {
return d.outerRadius;
}
function arcStartAngle(d) {
return d.startAngle;
}
function arcEndAngle(d) {
return d.endAngle;
}
function arcPadAngle(d) {
return d && d.padAngle; // Note: optional!
}
function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
var x10 = x1 - x0, y10 = y1 - y0,
x32 = x3 - x2, y32 = y3 - y2,
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
return [x0 + t * x10, y0 + t * y10];
}
// Compute perpendicular offset line of length rc.
// http://mathworld.wolfram.com/Circle-LineIntersection.html
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
var x01 = x0 - x1,
y01 = y0 - y1,
lo = (cw ? rc : -rc) / sqrt$2(x01 * x01 + y01 * y01),
ox = lo * y01,
oy = -lo * x01,
x11 = x0 + ox,
y11 = y0 + oy,
x10 = x1 + ox,
y10 = y1 + oy,
x00 = (x11 + x10) / 2,
y00 = (y11 + y10) / 2,
dx = x10 - x11,
dy = y10 - y11,
d2 = dx * dx + dy * dy,
r = r1 - rc,
D = x11 * y10 - x10 * y11,
d = (dy < 0 ? -1 : 1) * sqrt$2(max$2(0, r * r * d2 - D * D)),
cx0 = (D * dy - dx * d) / d2,
cy0 = (-D * dx - dy * d) / d2,
cx1 = (D * dy + dx * d) / d2,
cy1 = (-D * dx + dy * d) / d2,
dx0 = cx0 - x00,
dy0 = cy0 - y00,
dx1 = cx1 - x00,
dy1 = cy1 - y00;
// Pick the closer of the two intersection points.
// TODO Is there a faster way to determine which intersection to use?
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
return {
cx: cx0,
cy: cy0,
x01: -ox,
y01: -oy,
x11: cx0 * (r1 / r - 1),
y11: cy0 * (r1 / r - 1)
};
}
function arc() {
var innerRadius = arcInnerRadius,
outerRadius = arcOuterRadius,
cornerRadius = constant$11(0),
padRadius = null,
startAngle = arcStartAngle,
endAngle = arcEndAngle,
padAngle = arcPadAngle,
context = null;
function arc() {
var buffer,
r,
r0 = +innerRadius.apply(this, arguments),
r1 = +outerRadius.apply(this, arguments),
a0 = startAngle.apply(this, arguments) - halfPi$3,
a1 = endAngle.apply(this, arguments) - halfPi$3,
da = abs$1(a1 - a0),
cw = a1 > a0;
if (!context) context = buffer = path();
// Ensure that the outer radius is always larger than the inner radius.
if (r1 < r0) r = r1, r1 = r0, r0 = r;
// Is it a point?
if (!(r1 > epsilon$3)) context.moveTo(0, 0);
// Or is it a circle or annulus?
else if (da > tau$4 - epsilon$3) {
context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0));
context.arc(0, 0, r1, a0, a1, !cw);
if (r0 > epsilon$3) {
context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1));
context.arc(0, 0, r0, a1, a0, cw);
}
}
// Or is it a circular or annular sector?
else {
var a01 = a0,
a11 = a1,
a00 = a0,
a10 = a1,
da0 = da,
da1 = da,
ap = padAngle.apply(this, arguments) / 2,
rp = (ap > epsilon$3) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$2(r0 * r0 + r1 * r1)),
rc = min$1(abs$1(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
rc0 = rc,
rc1 = rc,
t0,
t1;
// Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
if (rp > epsilon$3) {
var p0 = asin$1(rp / r0 * sin$2(ap)),
p1 = asin$1(rp / r1 * sin$2(ap));
if ((da0 -= p0 * 2) > epsilon$3) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
else da0 = 0, a00 = a10 = (a0 + a1) / 2;
if ((da1 -= p1 * 2) > epsilon$3) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
else da1 = 0, a01 = a11 = (a0 + a1) / 2;
}
var x01 = r1 * cos$2(a01),
y01 = r1 * sin$2(a01),
x10 = r0 * cos$2(a10),
y10 = r0 * sin$2(a10);
// Apply rounded corners?
if (rc > epsilon$3) {
var x11 = r1 * cos$2(a11),
y11 = r1 * sin$2(a11),
x00 = r0 * cos$2(a00),
y00 = r0 * sin$2(a00);
// Restrict the corner radius according to the sector angle.
if (da < pi$4) {
var oc = da0 > epsilon$3 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
ax = x01 - oc[0],
ay = y01 - oc[1],
bx = x11 - oc[0],
by = y11 - oc[1],
kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$2(ax * ax + ay * ay) * sqrt$2(bx * bx + by * by))) / 2),
lc = sqrt$2(oc[0] * oc[0] + oc[1] * oc[1]);
rc0 = min$1(rc, (r0 - lc) / (kc - 1));
rc1 = min$1(rc, (r1 - lc) / (kc + 1));
}
}
// Is the sector collapsed to a line?
if (!(da1 > epsilon$3)) context.moveTo(x01, y01);
// Does the sector’s outer ring have rounded corners?
else if (rc1 > epsilon$3) {
t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
// Have the corners merged?
if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
// Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
}
}
// Or is the outer ring just a circular arc?
else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
// Is there no inner ring, and it’s a circular sector?
// Or perhaps it’s an annular sector collapsed due to padding?
if (!(r0 > epsilon$3) || !(da0 > epsilon$3)) context.lineTo(x10, y10);
// Does the sector’s inner ring (or point) have rounded corners?
else if (rc0 > epsilon$3) {
t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
// Have the corners merged?
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
// Otherwise, draw the two corners and the ring.
else {
context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw);
context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
}
}
// Or is the inner ring just a circular arc?
else context.arc(0, 0, r0, a10, a00, cw);
}
context.closePath();
if (buffer) return context = null, buffer + "" || null;
}
arc.centroid = function() {
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$4 / 2;
return [cos$2(a) * r, sin$2(a) * r];
};
arc.innerRadius = function(_) {
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : innerRadius;
};
arc.outerRadius = function(_) {
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : outerRadius;
};
arc.cornerRadius = function(_) {
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : cornerRadius;
};
arc.padRadius = function(_) {
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), arc) : padRadius;
};
arc.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : startAngle;
};
arc.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : endAngle;
};
arc.padAngle = function(_) {
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : padAngle;
};
arc.context = function(_) {
return arguments.length ? (context = _ == null ? null : _, arc) : context;
};
return arc;
}
function Linear(context) {
this._context = context;
}
Linear.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; // proceed
default: this._context.lineTo(x, y); break;
}
}
};
function curveLinear(context) {
return new Linear(context);
}
function x$3(p) {
return p[0];
}
function y$3(p) {
return p[1];
}
function line() {
var x$$1 = x$3,
y$$1 = y$3,
defined = constant$11(true),
context = null,
curve = curveLinear,
output = null;
function line(data) {
var i,
n = data.length,
d,
defined0 = false,
buffer;
if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) output.lineStart();
else output.lineEnd();
}
if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data));
}
if (buffer) return output = null, buffer + "" || null;
}
line.x = function(_) {
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : x$$1;
};
line.y = function(_) {
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : y$$1;
};
line.defined = function(_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), line) : defined;
};
line.curve = function(_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
};
line.context = function(_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
};
return line;
}
function area$3() {
var x0 = x$3,
x1 = null,
y0 = constant$11(0),
y1 = y$3,
defined = constant$11(true),
context = null,
curve = curveLinear,
output = null;
function area(data) {
var i,
j,
k,
n = data.length,
d,
defined0 = false,
buffer,
x0z = new Array(n),
y0z = new Array(n);
if (context == null) output = curve(buffer = path());
for (i = 0; i <= n; ++i) {
if (!(i < n && defined(d = data[i], i, data)) === defined0) {
if (defined0 = !defined0) {
j = i;
output.areaStart();
output.lineStart();
} else {
output.lineEnd();
output.lineStart();
for (k = i - 1; k >= j; --k) {
output.point(x0z[k], y0z[k]);
}
output.lineEnd();
output.areaEnd();
}
}
if (defined0) {
x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
}
}
if (buffer) return output = null, buffer + "" || null;
}
function arealine() {
return line().defined(defined).curve(curve).context(context);
}
area.x = function(_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), x1 = null, area) : x0;
};
area.x0 = function(_) {
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), area) : x0;
};
area.x1 = function(_) {
return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : x1;
};
area.y = function(_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), y1 = null, area) : y0;
};
area.y0 = function(_) {
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), area) : y0;
};
area.y1 = function(_) {
return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : y1;
};
area.lineX0 =
area.lineY0 = function() {
return arealine().x(x0).y(y0);
};
area.lineY1 = function() {
return arealine().x(x0).y(y1);
};
area.lineX1 = function() {
return arealine().x(x1).y(y0);
};
area.defined = function(_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), area) : defined;
};
area.curve = function(_) {
return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
};
area.context = function(_) {
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
};
return area;
}
function descending$1(a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
function identity$7(d) {
return d;
}
function pie() {
var value = identity$7,
sortValues = descending$1,
sort = null,
startAngle = constant$11(0),
endAngle = constant$11(tau$4),
padAngle = constant$11(0);
function pie(data) {
var i,
n = data.length,
j,
k,
sum = 0,
index = new Array(n),
arcs = new Array(n),
a0 = +startAngle.apply(this, arguments),
da = Math.min(tau$4, Math.max(-tau$4, endAngle.apply(this, arguments) - a0)),
a1,
p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),
pa = p * (da < 0 ? -1 : 1),
v;
for (i = 0; i < n; ++i) {
if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {
sum += v;
}
}
// Optionally sort the arcs by previously-computed values or by data.
if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });
else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });
// Compute the arcs! They are stored in the original data's order.
for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {
j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
data: data[j],
index: i,
value: v,
startAngle: a0,
endAngle: a1,
padAngle: p
};
}
return arcs;
}
pie.value = function(_) {
return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), pie) : value;
};
pie.sortValues = function(_) {
return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
};
pie.sort = function(_) {
return arguments.length ? (sort = _, sortValues = null, pie) : sort;
};
pie.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : startAngle;
};
pie.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : endAngle;
};
pie.padAngle = function(_) {
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : padAngle;
};
return pie;
}
var curveRadialLinear = curveRadial(curveLinear);
function Radial(curve) {
this._curve = curve;
}
Radial.prototype = {
areaStart: function() {
this._curve.areaStart();
},
areaEnd: function() {
this._curve.areaEnd();
},
lineStart: function() {
this._curve.lineStart();
},
lineEnd: function() {
this._curve.lineEnd();
},
point: function(a, r) {
this._curve.point(r * Math.sin(a), r * -Math.cos(a));
}
};
function curveRadial(curve) {
function radial(context) {
return new Radial(curve(context));
}
radial._curve = curve;
return radial;
}
function lineRadial(l) {
var c = l.curve;
l.angle = l.x, delete l.x;
l.radius = l.y, delete l.y;
l.curve = function(_) {
return arguments.length ? c(curveRadial(_)) : c()._curve;
};
return l;
}
function lineRadial$1() {
return lineRadial(line().curve(curveRadialLinear));
}
function areaRadial() {
var a = area$3().curve(curveRadialLinear),
c = a.curve,
x0 = a.lineX0,
x1 = a.lineX1,
y0 = a.lineY0,
y1 = a.lineY1;
a.angle = a.x, delete a.x;
a.startAngle = a.x0, delete a.x0;
a.endAngle = a.x1, delete a.x1;
a.radius = a.y, delete a.y;
a.innerRadius = a.y0, delete a.y0;
a.outerRadius = a.y1, delete a.y1;
a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0;
a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1;
a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0;
a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1;
a.curve = function(_) {
return arguments.length ? c(curveRadial(_)) : c()._curve;
};
return a;
}
function pointRadial(x, y) {
return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
}
var slice$6 = Array.prototype.slice;
function linkSource(d) {
return d.source;
}
function linkTarget(d) {
return d.target;
}
function link$2(curve) {
var source = linkSource,
target = linkTarget,
x$$1 = x$3,
y$$1 = y$3,
context = null;
function link() {
var buffer, argv = slice$6.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);
if (!context) context = buffer = path();
curve(context, +x$$1.apply(this, (argv[0] = s, argv)), +y$$1.apply(this, argv), +x$$1.apply(this, (argv[0] = t, argv)), +y$$1.apply(this, argv));
if (buffer) return context = null, buffer + "" || null;
}
link.source = function(_) {
return arguments.length ? (source = _, link) : source;
};
link.target = function(_) {
return arguments.length ? (target = _, link) : target;
};
link.x = function(_) {
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : x$$1;
};
link.y = function(_) {
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : y$$1;
};
link.context = function(_) {
return arguments.length ? (context = _ == null ? null : _, link) : context;
};
return link;
}
function curveHorizontal(context, x0, y0, x1, y1) {
context.moveTo(x0, y0);
context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);
}
function curveVertical(context, x0, y0, x1, y1) {
context.moveTo(x0, y0);
context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);
}
function curveRadial$1(context, x0, y0, x1, y1) {
var p0 = pointRadial(x0, y0),
p1 = pointRadial(x0, y0 = (y0 + y1) / 2),
p2 = pointRadial(x1, y0),
p3 = pointRadial(x1, y1);
context.moveTo(p0[0], p0[1]);
context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
}
function linkHorizontal() {
return link$2(curveHorizontal);
}
function linkVertical() {
return link$2(curveVertical);
}
function linkRadial() {
var l = link$2(curveRadial$1);
l.angle = l.x, delete l.x;
l.radius = l.y, delete l.y;
return l;
}
var circle$2 = {
draw: function(context, size) {
var r = Math.sqrt(size / pi$4);
context.moveTo(r, 0);
context.arc(0, 0, r, 0, tau$4);
}
};
var cross$2 = {
draw: function(context, size) {
var r = Math.sqrt(size / 5) / 2;
context.moveTo(-3 * r, -r);
context.lineTo(-r, -r);
context.lineTo(-r, -3 * r);
context.lineTo(r, -3 * r);
context.lineTo(r, -r);
context.lineTo(3 * r, -r);
context.lineTo(3 * r, r);
context.lineTo(r, r);
context.lineTo(r, 3 * r);
context.lineTo(-r, 3 * r);
context.lineTo(-r, r);
context.lineTo(-3 * r, r);
context.closePath();
}
};
var tan30 = Math.sqrt(1 / 3),
tan30_2 = tan30 * 2;
var diamond = {
draw: function(context, size) {
var y = Math.sqrt(size / tan30_2),
x = y * tan30;
context.moveTo(0, -y);
context.lineTo(x, 0);
context.lineTo(0, y);
context.lineTo(-x, 0);
context.closePath();
}
};
var ka = 0.89081309152928522810,
kr = Math.sin(pi$4 / 10) / Math.sin(7 * pi$4 / 10),
kx = Math.sin(tau$4 / 10) * kr,
ky = -Math.cos(tau$4 / 10) * kr;
var star = {
draw: function(context, size) {
var r = Math.sqrt(size * ka),
x = kx * r,
y = ky * r;
context.moveTo(0, -r);
context.lineTo(x, y);
for (var i = 1; i < 5; ++i) {
var a = tau$4 * i / 5,
c = Math.cos(a),
s = Math.sin(a);
context.lineTo(s * r, -c * r);
context.lineTo(c * x - s * y, s * x + c * y);
}
context.closePath();
}
};
var square = {
draw: function(context, size) {
var w = Math.sqrt(size),
x = -w / 2;
context.rect(x, x, w, w);
}
};
var sqrt3 = Math.sqrt(3);
var triangle = {
draw: function(context, size) {
var y = -Math.sqrt(size / (sqrt3 * 3));
context.moveTo(0, y * 2);
context.lineTo(-sqrt3 * y, -y);
context.lineTo(sqrt3 * y, -y);
context.closePath();
}
};
var c$2 = -0.5,
s = Math.sqrt(3) / 2,
k = 1 / Math.sqrt(12),
a = (k / 2 + 1) * 3;
var wye = {
draw: function(context, size) {
var r = Math.sqrt(size / a),
x0 = r / 2,
y0 = r * k,
x1 = x0,
y1 = r * k + r,
x2 = -x1,
y2 = y1;
context.moveTo(x0, y0);
context.lineTo(x1, y1);
context.lineTo(x2, y2);
context.lineTo(c$2 * x0 - s * y0, s * x0 + c$2 * y0);
context.lineTo(c$2 * x1 - s * y1, s * x1 + c$2 * y1);
context.lineTo(c$2 * x2 - s * y2, s * x2 + c$2 * y2);
context.lineTo(c$2 * x0 + s * y0, c$2 * y0 - s * x0);
context.lineTo(c$2 * x1 + s * y1, c$2 * y1 - s * x1);
context.lineTo(c$2 * x2 + s * y2, c$2 * y2 - s * x2);
context.closePath();
}
};
var symbols = [
circle$2,
cross$2,
diamond,
square,
star,
triangle,
wye
];
function symbol() {
var type = constant$11(circle$2),
size = constant$11(64),
context = null;
function symbol() {
var buffer;
if (!context) context = buffer = path();
type.apply(this, arguments).draw(context, +size.apply(this, arguments));
if (buffer) return context = null, buffer + "" || null;
}
symbol.type = function(_) {
return arguments.length ? (type = typeof _ === "function" ? _ : constant$11(_), symbol) : type;
};
symbol.size = function(_) {
return arguments.length ? (size = typeof _ === "function" ? _ : constant$11(+_), symbol) : size;
};
symbol.context = function(_) {
return arguments.length ? (context = _ == null ? null : _, symbol) : context;
};
return symbol;
}
function noop$3() {}
function point$2(that, x, y) {
that._context.bezierCurveTo(
(2 * that._x0 + that._x1) / 3,
(2 * that._y0 + that._y1) / 3,
(that._x0 + 2 * that._x1) / 3,
(that._y0 + 2 * that._y1) / 3,
(that._x0 + 4 * that._x1 + x) / 6,
(that._y0 + 4 * that._y1 + y) / 6
);
}
function Basis(context) {
this._context = context;
}
Basis.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 3: point$2(this, this._x1, this._y1); // proceed
case 2: this._context.lineTo(this._x1, this._y1); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
default: point$2(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function basis$2(context) {
return new Basis(context);
}
function BasisClosed(context) {
this._context = context;
}
BasisClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x2, this._y2);
this._context.closePath();
break;
}
case 2: {
this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x2, this._y2);
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
default: point$2(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function basisClosed$1(context) {
return new BasisClosed(context);
}
function BasisOpen(context) {
this._context = context;
}
BasisOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 = NaN;
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
case 3: this._point = 4; // proceed
default: point$2(this, x, y); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
}
};
function basisOpen(context) {
return new BasisOpen(context);
}
function Bundle(context, beta) {
this._basis = new Basis(context);
this._beta = beta;
}
Bundle.prototype = {
lineStart: function() {
this._x = [];
this._y = [];
this._basis.lineStart();
},
lineEnd: function() {
var x = this._x,
y = this._y,
j = x.length - 1;
if (j > 0) {
var x0 = x[0],
y0 = y[0],
dx = x[j] - x0,
dy = y[j] - y0,
i = -1,
t;
while (++i <= j) {
t = i / j;
this._basis.point(
this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
);
}
}
this._x = this._y = null;
this._basis.lineEnd();
},
point: function(x, y) {
this._x.push(+x);
this._y.push(+y);
}
};
var bundle = (function custom(beta) {
function bundle(context) {
return beta === 1 ? new Basis(context) : new Bundle(context, beta);
}
bundle.beta = function(beta) {
return custom(+beta);
};
return bundle;
})(0.85);
function point$3(that, x, y) {
that._context.bezierCurveTo(
that._x1 + that._k * (that._x2 - that._x0),
that._y1 + that._k * (that._y2 - that._y0),
that._x2 + that._k * (that._x1 - x),
that._y2 + that._k * (that._y1 - y),
that._x2,
that._y2
);
}
function Cardinal(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
Cardinal.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x2, this._y2); break;
case 3: point$3(this, this._x1, this._y1); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
case 2: this._point = 3; // proceed
default: point$3(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var cardinal = (function custom(tension) {
function cardinal(context) {
return new Cardinal(context, tension);
}
cardinal.tension = function(tension) {
return custom(+tension);
};
return cardinal;
})(0);
function CardinalClosed(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
CardinalClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 2: {
this._context.lineTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
default: point$3(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var cardinalClosed = (function custom(tension) {
function cardinal$$1(context) {
return new CardinalClosed(context, tension);
}
cardinal$$1.tension = function(tension) {
return custom(+tension);
};
return cardinal$$1;
})(0);
function CardinalOpen(context, tension) {
this._context = context;
this._k = (1 - tension) / 6;
}
CardinalOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
case 3: this._point = 4; // proceed
default: point$3(this, x, y); break;
}
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var cardinalOpen = (function custom(tension) {
function cardinal$$1(context) {
return new CardinalOpen(context, tension);
}
cardinal$$1.tension = function(tension) {
return custom(+tension);
};
return cardinal$$1;
})(0);
function point$4(that, x, y) {
var x1 = that._x1,
y1 = that._y1,
x2 = that._x2,
y2 = that._y2;
if (that._l01_a > epsilon$3) {
var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
n = 3 * that._l01_a * (that._l01_a + that._l12_a);
x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
}
if (that._l23_a > epsilon$3) {
var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
m = 3 * that._l23_a * (that._l23_a + that._l12_a);
x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
}
that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
}
function CatmullRom(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRom.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x2, this._y2); break;
case 3: this.point(this._x2, this._y2); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; // proceed
default: point$4(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var catmullRom = (function custom(alpha) {
function catmullRom(context) {
return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
}
catmullRom.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom;
})(0.5);
function CatmullRomClosed(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRomClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function() {
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 1: {
this._context.moveTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 2: {
this._context.lineTo(this._x3, this._y3);
this._context.closePath();
break;
}
case 3: {
this.point(this._x3, this._y3);
this.point(this._x4, this._y4);
this.point(this._x5, this._y5);
break;
}
}
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
default: point$4(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var catmullRomClosed = (function custom(alpha) {
function catmullRom$$1(context) {
return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
}
catmullRom$$1.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom$$1;
})(0.5);
function CatmullRomOpen(context, alpha) {
this._context = context;
this._alpha = alpha;
}
CatmullRomOpen.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 = this._x2 =
this._y0 = this._y1 = this._y2 = NaN;
this._l01_a = this._l12_a = this._l23_a =
this._l01_2a = this._l12_2a = this._l23_2a =
this._point = 0;
},
lineEnd: function() {
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) {
var x23 = this._x2 - x,
y23 = this._y2 - y;
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
}
switch (this._point) {
case 0: this._point = 1; break;
case 1: this._point = 2; break;
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
case 3: this._point = 4; // proceed
default: point$4(this, x, y); break;
}
this._l01_a = this._l12_a, this._l12_a = this._l23_a;
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
}
};
var catmullRomOpen = (function custom(alpha) {
function catmullRom$$1(context) {
return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
}
catmullRom$$1.alpha = function(alpha) {
return custom(+alpha);
};
return catmullRom$$1;
})(0.5);
function LinearClosed(context) {
this._context = context;
}
LinearClosed.prototype = {
areaStart: noop$3,
areaEnd: noop$3,
lineStart: function() {
this._point = 0;
},
lineEnd: function() {
if (this._point) this._context.closePath();
},
point: function(x, y) {
x = +x, y = +y;
if (this._point) this._context.lineTo(x, y);
else this._point = 1, this._context.moveTo(x, y);
}
};
function linearClosed(context) {
return new LinearClosed(context);
}
function sign$1(x) {
return x < 0 ? -1 : 1;
}
// Calculate the slopes of the tangents (Hermite-type interpolation) based on
// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
// NOV(II), P. 443, 1990.
function slope3(that, x2, y2) {
var h0 = that._x1 - that._x0,
h1 = x2 - that._x1,
s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
p = (s0 * h1 + s1 * h0) / (h0 + h1);
return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
}
// Calculate a one-sided slope.
function slope2(that, t) {
var h = that._x1 - that._x0;
return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
}
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
function point$5(that, t0, t1) {
var x0 = that._x0,
y0 = that._y0,
x1 = that._x1,
y1 = that._y1,
dx = (x1 - x0) / 3;
that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
}
function MonotoneX(context) {
this._context = context;
}
MonotoneX.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x0 = this._x1 =
this._y0 = this._y1 =
this._t0 = NaN;
this._point = 0;
},
lineEnd: function() {
switch (this._point) {
case 2: this._context.lineTo(this._x1, this._y1); break;
case 3: point$5(this, this._t0, slope2(this, this._t0)); break;
}
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line;
},
point: function(x, y) {
var t1 = NaN;
x = +x, y = +y;
if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break;
case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
default: point$5(this, this._t0, t1 = slope3(this, x, y)); break;
}
this._x0 = this._x1, this._x1 = x;
this._y0 = this._y1, this._y1 = y;
this._t0 = t1;
}
};
function MonotoneY(context) {
this._context = new ReflectContext(context);
}
(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
MonotoneX.prototype.point.call(this, y, x);
};
function ReflectContext(context) {
this._context = context;
}
ReflectContext.prototype = {
moveTo: function(x, y) { this._context.moveTo(y, x); },
closePath: function() { this._context.closePath(); },
lineTo: function(x, y) { this._context.lineTo(y, x); },
bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
};
function monotoneX(context) {
return new MonotoneX(context);
}
function monotoneY(context) {
return new MonotoneY(context);
}
function Natural(context) {
this._context = context;
}
Natural.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x = [];
this._y = [];
},
lineEnd: function() {
var x = this._x,
y = this._y,
n = x.length;
if (n) {
this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
if (n === 2) {
this._context.lineTo(x[1], y[1]);
} else {
var px = controlPoints(x),
py = controlPoints(y);
for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
}
}
}
if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
this._line = 1 - this._line;
this._x = this._y = null;
},
point: function(x, y) {
this._x.push(+x);
this._y.push(+y);
}
};
// See https://www.particleincell.com/2012/bezier-splines/ for derivation.
function controlPoints(x) {
var i,
n = x.length - 1,
m,
a = new Array(n),
b = new Array(n),
r = new Array(n);
a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
a[n - 1] = r[n - 1] / b[n - 1];
for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
b[n - 1] = (x[n] + a[n - 1]) / 2;
for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
return [a, b];
}
function natural(context) {
return new Natural(context);
}
function Step(context, t) {
this._context = context;
this._t = t;
}
Step.prototype = {
areaStart: function() {
this._line = 0;
},
areaEnd: function() {
this._line = NaN;
},
lineStart: function() {
this._x = this._y = NaN;
this._point = 0;
},
lineEnd: function() {
if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
},
point: function(x, y) {
x = +x, y = +y;
switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; // proceed
default: {
if (this._t <= 0) {
this._context.lineTo(this._x, y);
this._context.lineTo(x, y);
} else {
var x1 = this._x * (1 - this._t) + x * this._t;
this._context.lineTo(x1, this._y);
this._context.lineTo(x1, y);
}
break;
}
}
this._x = x, this._y = y;
}
};
function step(context) {
return new Step(context, 0.5);
}
function stepBefore(context) {
return new Step(context, 0);
}
function stepAfter(context) {
return new Step(context, 1);
}
function none$1(series, order) {
if (!((n = series.length) > 1)) return;
for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
s0 = s1, s1 = series[order[i]];
for (j = 0; j < m; ++j) {
s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];
}
}
}
function none$2(series) {
var n = series.length, o = new Array(n);
while (--n >= 0) o[n] = n;
return o;
}
function stackValue(d, key) {
return d[key];
}
function stack() {
var keys = constant$11([]),
order = none$2,
offset = none$1,
value = stackValue;
function stack(data) {
var kz = keys.apply(this, arguments),
i,
m = data.length,
n = kz.length,
sz = new Array(n),
oz;
for (i = 0; i < n; ++i) {
for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {
si[j] = sij = [0, +value(data[j], ki, j, data)];
sij.data = data[j];
}
si.key = ki;
}
for (i = 0, oz = order(sz); i < n; ++i) {
sz[oz[i]].index = i;
}
offset(sz, oz);
return sz;
}
stack.keys = function(_) {
return arguments.length ? (keys = typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : keys;
};
stack.value = function(_) {
return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), stack) : value;
};
stack.order = function(_) {
return arguments.length ? (order = _ == null ? none$2 : typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : order;
};
stack.offset = function(_) {
return arguments.length ? (offset = _ == null ? none$1 : _, stack) : offset;
};
return stack;
}
function expand(series, order) {
if (!((n = series.length) > 0)) return;
for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {
for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;
if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;
}
none$1(series, order);
}
function diverging$1(series, order) {
if (!((n = series.length) > 1)) return;
for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
for (yp = yn = 0, i = 0; i < n; ++i) {
if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
d[0] = yp, d[1] = yp += dy;
} else if (dy < 0) {
d[1] = yn, d[0] = yn += dy;
} else {
d[0] = yp;
}
}
}
}
function silhouette(series, order) {
if (!((n = series.length) > 0)) return;
for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {
for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;
s0[j][1] += s0[j][0] = -y / 2;
}
none$1(series, order);
}
function wiggle(series, order) {
if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;
for (var y = 0, j = 1, s0, m, n; j < m; ++j) {
for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {
var si = series[order[i]],
sij0 = si[j][1] || 0,
sij1 = si[j - 1][1] || 0,
s3 = (sij0 - sij1) / 2;
for (var k = 0; k < i; ++k) {
var sk = series[order[k]],
skj0 = sk[j][1] || 0,
skj1 = sk[j - 1][1] || 0;
s3 += skj0 - skj1;
}
s1 += sij0, s2 += s3 * sij0;
}
s0[j - 1][1] += s0[j - 1][0] = y;
if (s1) y -= s2 / s1;
}
s0[j - 1][1] += s0[j - 1][0] = y;
none$1(series, order);
}
function ascending$3(series) {
var sums = series.map(sum$2);
return none$2(series).sort(function(a, b) { return sums[a] - sums[b]; });
}
function sum$2(series) {
var s = 0, i = -1, n = series.length, v;
while (++i < n) if (v = +series[i][1]) s += v;
return s;
}
function descending$2(series) {
return ascending$3(series).reverse();
}
function insideOut(series) {
var n = series.length,
i,
j,
sums = series.map(sum$2),
order = none$2(series).sort(function(a, b) { return sums[b] - sums[a]; }),
top = 0,
bottom = 0,
tops = [],
bottoms = [];
for (i = 0; i < n; ++i) {
j = order[i];
if (top < bottom) {
top += sums[j];
tops.push(j);
} else {
bottom += sums[j];
bottoms.push(j);
}
}
return bottoms.reverse().concat(tops);
}
function reverse(series) {
return none$2(series).reverse();
}
function constant$12(x) {
return function() {
return x;
};
}
function x$4(d) {
return d[0];
}
function y$4(d) {
return d[1];
}
function RedBlackTree() {
this._ = null; // root node
}
function RedBlackNode(node) {
node.U = // parent node
node.C = // color - true for red, false for black
node.L = // left node
node.R = // right node
node.P = // previous node
node.N = null; // next node
}
RedBlackTree.prototype = {
constructor: RedBlackTree,
insert: function(after, node) {
var parent, grandpa, uncle;
if (after) {
node.P = after;
node.N = after.N;
if (after.N) after.N.P = node;
after.N = node;
if (after.R) {
after = after.R;
while (after.L) after = after.L;
after.L = node;
} else {
after.R = node;
}
parent = after;
} else if (this._) {
after = RedBlackFirst(this._);
node.P = null;
node.N = after;
after.P = after.L = node;
parent = after;
} else {
node.P = node.N = null;
this._ = node;
parent = null;
}
node.L = node.R = null;
node.U = parent;
node.C = true;
after = node;
while (parent && parent.C) {
grandpa = parent.U;
if (parent === grandpa.L) {
uncle = grandpa.R;
if (uncle && uncle.C) {
parent.C = uncle.C = false;
grandpa.C = true;
after = grandpa;
} else {
if (after === parent.R) {
RedBlackRotateLeft(this, parent);
after = parent;
parent = after.U;
}
parent.C = false;
grandpa.C = true;
RedBlackRotateRight(this, grandpa);
}
} else {
uncle = grandpa.L;
if (uncle && uncle.C) {
parent.C = uncle.C = false;
grandpa.C = true;
after = grandpa;
} else {
if (after === parent.L) {
RedBlackRotateRight(this, parent);
after = parent;
parent = after.U;
}
parent.C = false;
grandpa.C = true;
RedBlackRotateLeft(this, grandpa);
}
}
parent = after.U;
}
this._.C = false;
},
remove: function(node) {
if (node.N) node.N.P = node.P;
if (node.P) node.P.N = node.N;
node.N = node.P = null;
var parent = node.U,
sibling,
left = node.L,
right = node.R,
next,
red;
if (!left) next = right;
else if (!right) next = left;
else next = RedBlackFirst(right);
if (parent) {
if (parent.L === node) parent.L = next;
else parent.R = next;
} else {
this._ = next;
}
if (left && right) {
red = next.C;
next.C = node.C;
next.L = left;
left.U = next;
if (next !== right) {
parent = next.U;
next.U = node.U;
node = next.R;
parent.L = node;
next.R = right;
right.U = next;
} else {
next.U = parent;
parent = next;
node = next.R;
}
} else {
red = node.C;
node = next;
}
if (node) node.U = parent;
if (red) return;
if (node && node.C) { node.C = false; return; }
do {
if (node === this._) break;
if (node === parent.L) {
sibling = parent.R;
if (sibling.C) {
sibling.C = false;
parent.C = true;
RedBlackRotateLeft(this, parent);
sibling = parent.R;
}
if ((sibling.L && sibling.L.C)
|| (sibling.R && sibling.R.C)) {
if (!sibling.R || !sibling.R.C) {
sibling.L.C = false;
sibling.C = true;
RedBlackRotateRight(this, sibling);
sibling = parent.R;
}
sibling.C = parent.C;
parent.C = sibling.R.C = false;
RedBlackRotateLeft(this, parent);
node = this._;
break;
}
} else {
sibling = parent.L;
if (sibling.C) {
sibling.C = false;
parent.C = true;
RedBlackRotateRight(this, parent);
sibling = parent.L;
}
if ((sibling.L && sibling.L.C)
|| (sibling.R && sibling.R.C)) {
if (!sibling.L || !sibling.L.C) {
sibling.R.C = false;
sibling.C = true;
RedBlackRotateLeft(this, sibling);
sibling = parent.L;
}
sibling.C = parent.C;
parent.C = sibling.L.C = false;
RedBlackRotateRight(this, parent);
node = this._;
break;
}
}
sibling.C = true;
node = parent;
parent = parent.U;
} while (!node.C);
if (node) node.C = false;
}
};
function RedBlackRotateLeft(tree, node) {
var p = node,
q = node.R,
parent = p.U;
if (parent) {
if (parent.L === p) parent.L = q;
else parent.R = q;
} else {
tree._ = q;
}
q.U = parent;
p.U = q;
p.R = q.L;
if (p.R) p.R.U = p;
q.L = p;
}
function RedBlackRotateRight(tree, node) {
var p = node,
q = node.L,
parent = p.U;
if (parent) {
if (parent.L === p) parent.L = q;
else parent.R = q;
} else {
tree._ = q;
}
q.U = parent;
p.U = q;
p.L = q.R;
if (p.L) p.L.U = p;
q.R = p;
}
function RedBlackFirst(node) {
while (node.L) node = node.L;
return node;
}
function createEdge(left, right, v0, v1) {
var edge = [null, null],
index = edges.push(edge) - 1;
edge.left = left;
edge.right = right;
if (v0) setEdgeEnd(edge, left, right, v0);
if (v1) setEdgeEnd(edge, right, left, v1);
cells[left.index].halfedges.push(index);
cells[right.index].halfedges.push(index);
return edge;
}
function createBorderEdge(left, v0, v1) {
var edge = [v0, v1];
edge.left = left;
return edge;
}
function setEdgeEnd(edge, left, right, vertex) {
if (!edge[0] && !edge[1]) {
edge[0] = vertex;
edge.left = left;
edge.right = right;
} else if (edge.left === right) {
edge[1] = vertex;
} else {
edge[0] = vertex;
}
}
// Liang–Barsky line clipping.
function clipEdge(edge, x0, y0, x1, y1) {
var a = edge[0],
b = edge[1],
ax = a[0],
ay = a[1],
bx = b[0],
by = b[1],
t0 = 0,
t1 = 1,
dx = bx - ax,
dy = by - ay,
r;
r = x0 - ax;
if (!dx && r > 0) return;
r /= dx;
if (dx < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dx > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
r = x1 - ax;
if (!dx && r < 0) return;
r /= dx;
if (dx < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dx > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
r = y0 - ay;
if (!dy && r > 0) return;
r /= dy;
if (dy < 0) {
if (r < t0) return;
if (r < t1) t1 = r;
} else if (dy > 0) {
if (r > t1) return;
if (r > t0) t0 = r;
}
r = y1 - ay;
if (!dy && r < 0) return;
r /= dy;
if (dy < 0) {
if (r > t1) return;
if (r > t0) t0 = r;
} else if (dy > 0) {
if (r < t0) return;
if (r < t1) t1 = r;
}
if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check?
if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];
if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];
return true;
}
function connectEdge(edge, x0, y0, x1, y1) {
var v1 = edge[1];
if (v1) return true;
var v0 = edge[0],
left = edge.left,
right = edge.right,
lx = left[0],
ly = left[1],
rx = right[0],
ry = right[1],
fx = (lx + rx) / 2,
fy = (ly + ry) / 2,
fm,
fb;
if (ry === ly) {
if (fx < x0 || fx >= x1) return;
if (lx > rx) {
if (!v0) v0 = [fx, y0];
else if (v0[1] >= y1) return;
v1 = [fx, y1];
} else {
if (!v0) v0 = [fx, y1];
else if (v0[1] < y0) return;
v1 = [fx, y0];
}
} else {
fm = (lx - rx) / (ry - ly);
fb = fy - fm * fx;
if (fm < -1 || fm > 1) {
if (lx > rx) {
if (!v0) v0 = [(y0 - fb) / fm, y0];
else if (v0[1] >= y1) return;
v1 = [(y1 - fb) / fm, y1];
} else {
if (!v0) v0 = [(y1 - fb) / fm, y1];
else if (v0[1] < y0) return;
v1 = [(y0 - fb) / fm, y0];
}
} else {
if (ly < ry) {
if (!v0) v0 = [x0, fm * x0 + fb];
else if (v0[0] >= x1) return;
v1 = [x1, fm * x1 + fb];
} else {
if (!v0) v0 = [x1, fm * x1 + fb];
else if (v0[0] < x0) return;
v1 = [x0, fm * x0 + fb];
}
}
}
edge[0] = v0;
edge[1] = v1;
return true;
}
function clipEdges(x0, y0, x1, y1) {
var i = edges.length,
edge;
while (i--) {
if (!connectEdge(edge = edges[i], x0, y0, x1, y1)
|| !clipEdge(edge, x0, y0, x1, y1)
|| !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$4
|| Math.abs(edge[0][1] - edge[1][1]) > epsilon$4)) {
delete edges[i];
}
}
}
function createCell(site) {
return cells[site.index] = {
site: site,
halfedges: []
};
}
function cellHalfedgeAngle(cell, edge) {
var site = cell.site,
va = edge.left,
vb = edge.right;
if (site === vb) vb = va, va = site;
if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);
if (site === va) va = edge[1], vb = edge[0];
else va = edge[0], vb = edge[1];
return Math.atan2(va[0] - vb[0], vb[1] - va[1]);
}
function cellHalfedgeStart(cell, edge) {
return edge[+(edge.left !== cell.site)];
}
function cellHalfedgeEnd(cell, edge) {
return edge[+(edge.left === cell.site)];
}
function sortCellHalfedges() {
for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) {
if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) {
var index = new Array(m),
array = new Array(m);
for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]);
index.sort(function(i, j) { return array[j] - array[i]; });
for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];
for (j = 0; j < m; ++j) halfedges[j] = array[j];
}
}
}
function clipCells(x0, y0, x1, y1) {
var nCells = cells.length,
iCell,
cell,
site,
iHalfedge,
halfedges,
nHalfedges,
start,
startX,
startY,
end,
endX,
endY,
cover = true;
for (iCell = 0; iCell < nCells; ++iCell) {
if (cell = cells[iCell]) {
site = cell.site;
halfedges = cell.halfedges;
iHalfedge = halfedges.length;
// Remove any dangling clipped edges.
while (iHalfedge--) {
if (!edges[halfedges[iHalfedge]]) {
halfedges.splice(iHalfedge, 1);
}
}
// Insert any border edges as necessary.
iHalfedge = 0, nHalfedges = halfedges.length;
while (iHalfedge < nHalfedges) {
end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1];
start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];
if (Math.abs(endX - startX) > epsilon$4 || Math.abs(endY - startY) > epsilon$4) {
halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end,
Math.abs(endX - x0) < epsilon$4 && y1 - endY > epsilon$4 ? [x0, Math.abs(startX - x0) < epsilon$4 ? startY : y1]
: Math.abs(endY - y1) < epsilon$4 && x1 - endX > epsilon$4 ? [Math.abs(startY - y1) < epsilon$4 ? startX : x1, y1]
: Math.abs(endX - x1) < epsilon$4 && endY - y0 > epsilon$4 ? [x1, Math.abs(startX - x1) < epsilon$4 ? startY : y0]
: Math.abs(endY - y0) < epsilon$4 && endX - x0 > epsilon$4 ? [Math.abs(startY - y0) < epsilon$4 ? startX : x0, y0]
: null)) - 1);
++nHalfedges;
}
}
if (nHalfedges) cover = false;
}
}
// If there weren’t any edges, have the closest site cover the extent.
// It doesn’t matter which corner of the extent we measure!
if (cover) {
var dx, dy, d2, dc = Infinity;
for (iCell = 0, cover = null; iCell < nCells; ++iCell) {
if (cell = cells[iCell]) {
site = cell.site;
dx = site[0] - x0;
dy = site[1] - y0;
d2 = dx * dx + dy * dy;
if (d2 < dc) dc = d2, cover = cell;
}
}
if (cover) {
var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];
cover.halfedges.push(
edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1,
edges.push(createBorderEdge(site, v01, v11)) - 1,
edges.push(createBorderEdge(site, v11, v10)) - 1,
edges.push(createBorderEdge(site, v10, v00)) - 1
);
}
}
// Lastly delete any cells with no edges; these were entirely clipped.
for (iCell = 0; iCell < nCells; ++iCell) {
if (cell = cells[iCell]) {
if (!cell.halfedges.length) {
delete cells[iCell];
}
}
}
}
var circlePool = [];
var firstCircle;
function Circle() {
RedBlackNode(this);
this.x =
this.y =
this.arc =
this.site =
this.cy = null;
}
function attachCircle(arc) {
var lArc = arc.P,
rArc = arc.N;
if (!lArc || !rArc) return;
var lSite = lArc.site,
cSite = arc.site,
rSite = rArc.site;
if (lSite === rSite) return;
var bx = cSite[0],
by = cSite[1],
ax = lSite[0] - bx,
ay = lSite[1] - by,
cx = rSite[0] - bx,
cy = rSite[1] - by;
var d = 2 * (ax * cy - ay * cx);
if (d >= -epsilon2$2) return;
var ha = ax * ax + ay * ay,
hc = cx * cx + cy * cy,
x = (cy * ha - ay * hc) / d,
y = (ax * hc - cx * ha) / d;
var circle = circlePool.pop() || new Circle;
circle.arc = arc;
circle.site = cSite;
circle.x = x + bx;
circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom
arc.circle = circle;
var before = null,
node = circles._;
while (node) {
if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {
if (node.L) node = node.L;
else { before = node.P; break; }
} else {
if (node.R) node = node.R;
else { before = node; break; }
}
}
circles.insert(before, circle);
if (!before) firstCircle = circle;
}
function detachCircle(arc) {
var circle = arc.circle;
if (circle) {
if (!circle.P) firstCircle = circle.N;
circles.remove(circle);
circlePool.push(circle);
RedBlackNode(circle);
arc.circle = null;
}
}
var beachPool = [];
function Beach() {
RedBlackNode(this);
this.edge =
this.site =
this.circle = null;
}
function createBeach(site) {
var beach = beachPool.pop() || new Beach;
beach.site = site;
return beach;
}
function detachBeach(beach) {
detachCircle(beach);
beaches.remove(beach);
beachPool.push(beach);
RedBlackNode(beach);
}
function removeBeach(beach) {
var circle = beach.circle,
x = circle.x,
y = circle.cy,
vertex = [x, y],
previous = beach.P,
next = beach.N,
disappearing = [beach];
detachBeach(beach);
var lArc = previous;
while (lArc.circle
&& Math.abs(x - lArc.circle.x) < epsilon$4
&& Math.abs(y - lArc.circle.cy) < epsilon$4) {
previous = lArc.P;
disappearing.unshift(lArc);
detachBeach(lArc);
lArc = previous;
}
disappearing.unshift(lArc);
detachCircle(lArc);
var rArc = next;
while (rArc.circle
&& Math.abs(x - rArc.circle.x) < epsilon$4
&& Math.abs(y - rArc.circle.cy) < epsilon$4) {
next = rArc.N;
disappearing.push(rArc);
detachBeach(rArc);
rArc = next;
}
disappearing.push(rArc);
detachCircle(rArc);
var nArcs = disappearing.length,
iArc;
for (iArc = 1; iArc < nArcs; ++iArc) {
rArc = disappearing[iArc];
lArc = disappearing[iArc - 1];
setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
}
lArc = disappearing[0];
rArc = disappearing[nArcs - 1];
rArc.edge = createEdge(lArc.site, rArc.site, null, vertex);
attachCircle(lArc);
attachCircle(rArc);
}
function addBeach(site) {
var x = site[0],
directrix = site[1],
lArc,
rArc,
dxl,
dxr,
node = beaches._;
while (node) {
dxl = leftBreakPoint(node, directrix) - x;
if (dxl > epsilon$4) node = node.L; else {
dxr = x - rightBreakPoint(node, directrix);
if (dxr > epsilon$4) {
if (!node.R) {
lArc = node;
break;
}
node = node.R;
} else {
if (dxl > -epsilon$4) {
lArc = node.P;
rArc = node;
} else if (dxr > -epsilon$4) {
lArc = node;
rArc = node.N;
} else {
lArc = rArc = node;
}
break;
}
}
}
createCell(site);
var newArc = createBeach(site);
beaches.insert(lArc, newArc);
if (!lArc && !rArc) return;
if (lArc === rArc) {
detachCircle(lArc);
rArc = createBeach(lArc.site);
beaches.insert(newArc, rArc);
newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site);
attachCircle(lArc);
attachCircle(rArc);
return;
}
if (!rArc) { // && lArc
newArc.edge = createEdge(lArc.site, newArc.site);
return;
}
// else lArc !== rArc
detachCircle(lArc);
detachCircle(rArc);
var lSite = lArc.site,
ax = lSite[0],
ay = lSite[1],
bx = site[0] - ax,
by = site[1] - ay,
rSite = rArc.site,
cx = rSite[0] - ax,
cy = rSite[1] - ay,
d = 2 * (bx * cy - by * cx),
hb = bx * bx + by * by,
hc = cx * cx + cy * cy,
vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];
setEdgeEnd(rArc.edge, lSite, rSite, vertex);
newArc.edge = createEdge(lSite, site, null, vertex);
rArc.edge = createEdge(site, rSite, null, vertex);
attachCircle(lArc);
attachCircle(rArc);
}
function leftBreakPoint(arc, directrix) {
var site = arc.site,
rfocx = site[0],
rfocy = site[1],
pby2 = rfocy - directrix;
if (!pby2) return rfocx;
var lArc = arc.P;
if (!lArc) return -Infinity;
site = lArc.site;
var lfocx = site[0],
lfocy = site[1],
plby2 = lfocy - directrix;
if (!plby2) return lfocx;
var hl = lfocx - rfocx,
aby2 = 1 / pby2 - 1 / plby2,
b = hl / plby2;
if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
return (rfocx + lfocx) / 2;
}
function rightBreakPoint(arc, directrix) {
var rArc = arc.N;
if (rArc) return leftBreakPoint(rArc, directrix);
var site = arc.site;
return site[1] === directrix ? site[0] : Infinity;
}
var epsilon$4 = 1e-6;
var epsilon2$2 = 1e-12;
var beaches;
var cells;
var circles;
var edges;
function triangleArea(a, b, c) {
return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);
}
function lexicographic(a, b) {
return b[1] - a[1]
|| b[0] - a[0];
}
function Diagram(sites, extent) {
var site = sites.sort(lexicographic).pop(),
x,
y,
circle;
edges = [];
cells = new Array(sites.length);
beaches = new RedBlackTree;
circles = new RedBlackTree;
while (true) {
circle = firstCircle;
if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {
if (site[0] !== x || site[1] !== y) {
addBeach(site);
x = site[0], y = site[1];
}
site = sites.pop();
} else if (circle) {
removeBeach(circle.arc);
} else {
break;
}
}
sortCellHalfedges();
if (extent) {
var x0 = +extent[0][0],
y0 = +extent[0][1],
x1 = +extent[1][0],
y1 = +extent[1][1];
clipEdges(x0, y0, x1, y1);
clipCells(x0, y0, x1, y1);
}
this.edges = edges;
this.cells = cells;
beaches =
circles =
edges =
cells = null;
}
Diagram.prototype = {
constructor: Diagram,
polygons: function() {
var edges = this.edges;
return this.cells.map(function(cell) {
var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); });
polygon.data = cell.site.data;
return polygon;
});
},
triangles: function() {
var triangles = [],
edges = this.edges;
this.cells.forEach(function(cell, i) {
if (!(m = (halfedges = cell.halfedges).length)) return;
var site = cell.site,
halfedges,
j = -1,
m,
s0,
e1 = edges[halfedges[m - 1]],
s1 = e1.left === site ? e1.right : e1.left;
while (++j < m) {
s0 = s1;
e1 = edges[halfedges[j]];
s1 = e1.left === site ? e1.right : e1.left;
if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {
triangles.push([site.data, s0.data, s1.data]);
}
}
});
return triangles;
},
links: function() {
return this.edges.filter(function(edge) {
return edge.right;
}).map(function(edge) {
return {
source: edge.left.data,
target: edge.right.data
};
});
},
find: function(x, y, radius) {
var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell;
// Use the previously-found cell, or start with an arbitrary one.
while (!(cell = that.cells[i1])) if (++i1 >= n) return null;
var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy;
// Traverse the half-edges to find a closer cell, if any.
do {
cell = that.cells[i0 = i1], i1 = null;
cell.halfedges.forEach(function(e) {
var edge = that.edges[e], v = edge.left;
if ((v === cell.site || !v) && !(v = edge.right)) return;
var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy;
if (v2 < d2) d2 = v2, i1 = v.index;
});
} while (i1 !== null);
that._found = i0;
return radius == null || d2 <= radius * radius ? cell.site : null;
}
};
function voronoi() {
var x$$1 = x$4,
y$$1 = y$4,
extent = null;
function voronoi(data) {
return new Diagram(data.map(function(d, i) {
var s = [Math.round(x$$1(d, i, data) / epsilon$4) * epsilon$4, Math.round(y$$1(d, i, data) / epsilon$4) * epsilon$4];
s.index = i;
s.data = d;
return s;
}), extent);
}
voronoi.polygons = function(data) {
return voronoi(data).polygons();
};
voronoi.links = function(data) {
return voronoi(data).links();
};
voronoi.triangles = function(data) {
return voronoi(data).triangles();
};
voronoi.x = function(_) {
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : x$$1;
};
voronoi.y = function(_) {
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : y$$1;
};
voronoi.extent = function(_) {
return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];
};
voronoi.size = function(_) {
return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];
};
return voronoi;
}
function constant$13(x) {
return function() {
return x;
};
}
function ZoomEvent(target, type, transform) {
this.target = target;
this.type = type;
this.transform = transform;
}
function Transform(k, x, y) {
this.k = k;
this.x = x;
this.y = y;
}
Transform.prototype = {
constructor: Transform,
scale: function(k) {
return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
},
translate: function(x, y) {
return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
},
apply: function(point) {
return [point[0] * this.k + this.x, point[1] * this.k + this.y];
},
applyX: function(x) {
return x * this.k + this.x;
},
applyY: function(y) {
return y * this.k + this.y;
},
invert: function(location) {
return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
},
invertX: function(x) {
return (x - this.x) / this.k;
},
invertY: function(y) {
return (y - this.y) / this.k;
},
rescaleX: function(x) {
return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
},
rescaleY: function(y) {
return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
},
toString: function() {
return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
}
};
var identity$8 = new Transform(1, 0, 0);
transform$1.prototype = Transform.prototype;
function transform$1(node) {
return node.__zoom || identity$8;
}
function nopropagation$2() {
exports.event.stopImmediatePropagation();
}
function noevent$2() {
exports.event.preventDefault();
exports.event.stopImmediatePropagation();
}
// Ignore right-click, since that should open the context menu.
function defaultFilter$2() {
return !exports.event.button;
}
function defaultExtent$1() {
var e = this, w, h;
if (e instanceof SVGElement) {
e = e.ownerSVGElement || e;
w = e.width.baseVal.value;
h = e.height.baseVal.value;
} else {
w = e.clientWidth;
h = e.clientHeight;
}
return [[0, 0], [w, h]];
}
function defaultTransform() {
return this.__zoom || identity$8;
}
function defaultWheelDelta() {
return -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500;
}
function defaultTouchable$1() {
return "ontouchstart" in this;
}
function defaultConstrain(transform, extent, translateExtent) {
var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
return transform.translate(
dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
);
}
function zoom() {
var filter = defaultFilter$2,
extent = defaultExtent$1,
constrain = defaultConstrain,
wheelDelta = defaultWheelDelta,
touchable = defaultTouchable$1,
scaleExtent = [0, Infinity],
translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
duration = 250,
interpolate = interpolateZoom,
gestures = [],
listeners = dispatch("start", "zoom", "end"),
touchstarting,
touchending,
touchDelay = 500,
wheelDelay = 150,
clickDistance2 = 0;
function zoom(selection$$1) {
selection$$1
.property("__zoom", defaultTransform)
.on("wheel.zoom", wheeled)
.on("mousedown.zoom", mousedowned)
.on("dblclick.zoom", dblclicked)
.filter(touchable)
.on("touchstart.zoom", touchstarted)
.on("touchmove.zoom", touchmoved)
.on("touchend.zoom touchcancel.zoom", touchended)
.style("touch-action", "none")
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}
zoom.transform = function(collection, transform) {
var selection$$1 = collection.selection ? collection.selection() : collection;
selection$$1.property("__zoom", defaultTransform);
if (collection !== selection$$1) {
schedule(collection, transform);
} else {
selection$$1.interrupt().each(function() {
gesture(this, arguments)
.start()
.zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
.end();
});
}
};
zoom.scaleBy = function(selection$$1, k) {
zoom.scaleTo(selection$$1, function() {
var k0 = this.__zoom.k,
k1 = typeof k === "function" ? k.apply(this, arguments) : k;
return k0 * k1;
});
};
zoom.scaleTo = function(selection$$1, k) {
zoom.transform(selection$$1, function() {
var e = extent.apply(this, arguments),
t0 = this.__zoom,
p0 = centroid(e),
p1 = t0.invert(p0),
k1 = typeof k === "function" ? k.apply(this, arguments) : k;
return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
});
};
zoom.translateBy = function(selection$$1, x, y) {
zoom.transform(selection$$1, function() {
return constrain(this.__zoom.translate(
typeof x === "function" ? x.apply(this, arguments) : x,
typeof y === "function" ? y.apply(this, arguments) : y
), extent.apply(this, arguments), translateExtent);
});
};
zoom.translateTo = function(selection$$1, x, y) {
zoom.transform(selection$$1, function() {
var e = extent.apply(this, arguments),
t = this.__zoom,
p = centroid(e);
return constrain(identity$8.translate(p[0], p[1]).scale(t.k).translate(
typeof x === "function" ? -x.apply(this, arguments) : -x,
typeof y === "function" ? -y.apply(this, arguments) : -y
), e, translateExtent);
});
};
function scale(transform, k) {
k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
}
function translate(transform, p0, p1) {
var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
}
function centroid(extent) {
return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
}
function schedule(transition$$1, transform, center) {
transition$$1
.on("start.zoom", function() { gesture(this, arguments).start(); })
.on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
.tween("zoom", function() {
var that = this,
args = arguments,
g = gesture(that, args),
e = extent.apply(that, args),
p = center || centroid(e),
w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
a = that.__zoom,
b = typeof transform === "function" ? transform.apply(that, args) : transform,
i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
return function(t) {
if (t === 1) t = b; // Avoid rounding error on end.
else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
g.zoom(null, t);
};
});
}
function gesture(that, args) {
for (var i = 0, n = gestures.length, g; i < n; ++i) {
if ((g = gestures[i]).that === that) {
return g;
}
}
return new Gesture(that, args);
}
function Gesture(that, args) {
this.that = that;
this.args = args;
this.index = -1;
this.active = 0;
this.extent = extent.apply(that, args);
}
Gesture.prototype = {
start: function() {
if (++this.active === 1) {
this.index = gestures.push(this) - 1;
this.emit("start");
}
return this;
},
zoom: function(key, transform) {
if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
this.that.__zoom = transform;
this.emit("zoom");
return this;
},
end: function() {
if (--this.active === 0) {
gestures.splice(this.index, 1);
this.index = -1;
this.emit("end");
}
return this;
},
emit: function(type) {
customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
}
};
function wheeled() {
if (!filter.apply(this, arguments)) return;
var g = gesture(this, arguments),
t = this.__zoom,
k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
p = mouse(this);
// If the mouse is in the same location as before, reuse it.
// If there were recent wheel events, reset the wheel idle timeout.
if (g.wheel) {
if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
g.mouse[1] = t.invert(g.mouse[0] = p);
}
clearTimeout(g.wheel);
}
// If this wheel event won’t trigger a transform change, ignore it.
else if (t.k === k) return;
// Otherwise, capture the mouse point and location at the start.
else {
g.mouse = [p, t.invert(p)];
interrupt(this);
g.start();
}
noevent$2();
g.wheel = setTimeout(wheelidled, wheelDelay);
g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
function wheelidled() {
g.wheel = null;
g.end();
}
}
function mousedowned() {
if (touchending || !filter.apply(this, arguments)) return;
var g = gesture(this, arguments),
v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
p = mouse(this),
x0 = exports.event.clientX,
y0 = exports.event.clientY;
dragDisable(exports.event.view);
nopropagation$2();
g.mouse = [p, this.__zoom.invert(p)];
interrupt(this);
g.start();
function mousemoved() {
noevent$2();
if (!g.moved) {
var dx = exports.event.clientX - x0, dy = exports.event.clientY - y0;
g.moved = dx * dx + dy * dy > clickDistance2;
}
g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent));
}
function mouseupped() {
v.on("mousemove.zoom mouseup.zoom", null);
yesdrag(exports.event.view, g.moved);
noevent$2();
g.end();
}
}
function dblclicked() {
if (!filter.apply(this, arguments)) return;
var t0 = this.__zoom,
p0 = mouse(this),
p1 = t0.invert(p0),
k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2),
t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);
noevent$2();
if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0);
else select(this).call(zoom.transform, t1);
}
function touchstarted() {
if (!filter.apply(this, arguments)) return;
var g = gesture(this, arguments),
touches$$1 = exports.event.changedTouches,
started,
n = touches$$1.length, i, t, p;
nopropagation$2();
for (i = 0; i < n; ++i) {
t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
p = [p, this.__zoom.invert(p), t.identifier];
if (!g.touch0) g.touch0 = p, started = true;
else if (!g.touch1) g.touch1 = p;
}
// If this is a dbltap, reroute to the (optional) dblclick.zoom handler.
if (touchstarting) {
touchstarting = clearTimeout(touchstarting);
if (!g.touch1) {
g.end();
p = select(this).on("dblclick.zoom");
if (p) p.apply(this, arguments);
return;
}
}
if (started) {
touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
interrupt(this);
g.start();
}
}
function touchmoved() {
var g = gesture(this, arguments),
touches$$1 = exports.event.changedTouches,
n = touches$$1.length, i, t, p, l;
noevent$2();
if (touchstarting) touchstarting = clearTimeout(touchstarting);
for (i = 0; i < n; ++i) {
t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
}
t = g.that.__zoom;
if (g.touch1) {
var p0 = g.touch0[0], l0 = g.touch0[1],
p1 = g.touch1[0], l1 = g.touch1[1],
dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
t = scale(t, Math.sqrt(dp / dl));
p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
}
else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
else return;
g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
}
function touchended() {
var g = gesture(this, arguments),
touches$$1 = exports.event.changedTouches,
n = touches$$1.length, i, t;
nopropagation$2();
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() { touchending = null; }, touchDelay);
for (i = 0; i < n; ++i) {
t = touches$$1[i];
if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
}
if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
else g.end();
}
zoom.wheelDelta = function(_) {
return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$13(+_), zoom) : wheelDelta;
};
zoom.filter = function(_) {
return arguments.length ? (filter = typeof _ === "function" ? _ : constant$13(!!_), zoom) : filter;
};
zoom.touchable = function(_) {
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$13(!!_), zoom) : touchable;
};
zoom.extent = function(_) {
return arguments.length ? (extent = typeof _ === "function" ? _ : constant$13([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
};
zoom.scaleExtent = function(_) {
return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
};
zoom.translateExtent = function(_) {
return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
};
zoom.constrain = function(_) {
return arguments.length ? (constrain = _, zoom) : constrain;
};
zoom.duration = function(_) {
return arguments.length ? (duration = +_, zoom) : duration;
};
zoom.interpolate = function(_) {
return arguments.length ? (interpolate = _, zoom) : interpolate;
};
zoom.on = function() {
var value = listeners.on.apply(listeners, arguments);
return value === listeners ? zoom : value;
};
zoom.clickDistance = function(_) {
return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
};
return zoom;
}
exports.version = version;
exports.bisect = bisectRight;
exports.bisectRight = bisectRight;
exports.bisectLeft = bisectLeft;
exports.ascending = ascending;
exports.bisector = bisector;
exports.cross = cross;
exports.descending = descending;
exports.deviation = deviation;
exports.extent = extent;
exports.histogram = histogram;
exports.thresholdFreedmanDiaconis = freedmanDiaconis;
exports.thresholdScott = scott;
exports.thresholdSturges = thresholdSturges;
exports.max = max;
exports.mean = mean;
exports.median = median;
exports.merge = merge;
exports.min = min;
exports.pairs = pairs;
exports.permute = permute;
exports.quantile = threshold;
exports.range = sequence;
exports.scan = scan;
exports.shuffle = shuffle;
exports.sum = sum;
exports.ticks = ticks;
exports.tickIncrement = tickIncrement;
exports.tickStep = tickStep;
exports.transpose = transpose;
exports.variance = variance;
exports.zip = zip;
exports.axisTop = axisTop;
exports.axisRight = axisRight;
exports.axisBottom = axisBottom;
exports.axisLeft = axisLeft;
exports.brush = brush;
exports.brushX = brushX;
exports.brushY = brushY;
exports.brushSelection = brushSelection;
exports.chord = chord;
exports.ribbon = ribbon;
exports.nest = nest;
exports.set = set$2;
exports.map = map$1;
exports.keys = keys;
exports.values = values;
exports.entries = entries;
exports.color = color;
exports.rgb = rgb;
exports.hsl = hsl;
exports.lab = lab;
exports.hcl = hcl;
exports.lch = lch;
exports.gray = gray;
exports.cubehelix = cubehelix;
exports.contours = contours;
exports.contourDensity = density;
exports.dispatch = dispatch;
exports.drag = drag;
exports.dragDisable = dragDisable;
exports.dragEnable = yesdrag;
exports.dsvFormat = dsvFormat;
exports.csvParse = csvParse;
exports.csvParseRows = csvParseRows;
exports.csvFormat = csvFormat;
exports.csvFormatRows = csvFormatRows;
exports.tsvParse = tsvParse;
exports.tsvParseRows = tsvParseRows;
exports.tsvFormat = tsvFormat;
exports.tsvFormatRows = tsvFormatRows;
exports.easeLinear = linear$1;
exports.easeQuad = quadInOut;
exports.easeQuadIn = quadIn;
exports.easeQuadOut = quadOut;
exports.easeQuadInOut = quadInOut;
exports.easeCubic = cubicInOut;
exports.easeCubicIn = cubicIn;
exports.easeCubicOut = cubicOut;
exports.easeCubicInOut = cubicInOut;
exports.easePoly = polyInOut;
exports.easePolyIn = polyIn;
exports.easePolyOut = polyOut;
exports.easePolyInOut = polyInOut;
exports.easeSin = sinInOut;
exports.easeSinIn = sinIn;
exports.easeSinOut = sinOut;
exports.easeSinInOut = sinInOut;
exports.easeExp = expInOut;
exports.easeExpIn = expIn;
exports.easeExpOut = expOut;
exports.easeExpInOut = expInOut;
exports.easeCircle = circleInOut;
exports.easeCircleIn = circleIn;
exports.easeCircleOut = circleOut;
exports.easeCircleInOut = circleInOut;
exports.easeBounce = bounceOut;
exports.easeBounceIn = bounceIn;
exports.easeBounceOut = bounceOut;
exports.easeBounceInOut = bounceInOut;
exports.easeBack = backInOut;
exports.easeBackIn = backIn;
exports.easeBackOut = backOut;
exports.easeBackInOut = backInOut;
exports.easeElastic = elasticOut;
exports.easeElasticIn = elasticIn;
exports.easeElasticOut = elasticOut;
exports.easeElasticInOut = elasticInOut;
exports.blob = blob;
exports.buffer = buffer;
exports.dsv = dsv;
exports.csv = csv$1;
exports.tsv = tsv$1;
exports.image = image;
exports.json = json;
exports.text = text;
exports.xml = xml;
exports.html = html;
exports.svg = svg;
exports.forceCenter = center$1;
exports.forceCollide = collide;
exports.forceLink = link;
exports.forceManyBody = manyBody;
exports.forceRadial = radial;
exports.forceSimulation = simulation;
exports.forceX = x$2;
exports.forceY = y$2;
exports.formatDefaultLocale = defaultLocale;
exports.formatLocale = formatLocale;
exports.formatSpecifier = formatSpecifier;
exports.precisionFixed = precisionFixed;
exports.precisionPrefix = precisionPrefix;
exports.precisionRound = precisionRound;
exports.geoArea = area$1;
exports.geoBounds = bounds;
exports.geoCentroid = centroid;
exports.geoCircle = circle;
exports.geoClipAntimeridian = clipAntimeridian;
exports.geoClipCircle = clipCircle;
exports.geoClipExtent = extent$1;
exports.geoClipRectangle = clipRectangle;
exports.geoContains = contains$1;
exports.geoDistance = distance;
exports.geoGraticule = graticule;
exports.geoGraticule10 = graticule10;
exports.geoInterpolate = interpolate$1;
exports.geoLength = length$1;
exports.geoPath = index$1;
exports.geoAlbers = albers;
exports.geoAlbersUsa = albersUsa;
exports.geoAzimuthalEqualArea = azimuthalEqualArea;
exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw;
exports.geoAzimuthalEquidistant = azimuthalEquidistant;
exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw;
exports.geoConicConformal = conicConformal;
exports.geoConicConformalRaw = conicConformalRaw;
exports.geoConicEqualArea = conicEqualArea;
exports.geoConicEqualAreaRaw = conicEqualAreaRaw;
exports.geoConicEquidistant = conicEquidistant;
exports.geoConicEquidistantRaw = conicEquidistantRaw;
exports.geoEquirectangular = equirectangular;
exports.geoEquirectangularRaw = equirectangularRaw;
exports.geoGnomonic = gnomonic;
exports.geoGnomonicRaw = gnomonicRaw;
exports.geoIdentity = identity$5;
exports.geoProjection = projection;
exports.geoProjectionMutator = projectionMutator;
exports.geoMercator = mercator;
exports.geoMercatorRaw = mercatorRaw;
exports.geoNaturalEarth1 = naturalEarth1;
exports.geoNaturalEarth1Raw = naturalEarth1Raw;
exports.geoOrthographic = orthographic;
exports.geoOrthographicRaw = orthographicRaw;
exports.geoStereographic = stereographic;
exports.geoStereographicRaw = stereographicRaw;
exports.geoTransverseMercator = transverseMercator;
exports.geoTransverseMercatorRaw = transverseMercatorRaw;
exports.geoRotation = rotation;
exports.geoStream = geoStream;
exports.geoTransform = transform;
exports.cluster = cluster;
exports.hierarchy = hierarchy;
exports.pack = index$2;
exports.packSiblings = siblings;
exports.packEnclose = enclose;
exports.partition = partition;
exports.stratify = stratify;
exports.tree = tree;
exports.treemap = index$3;
exports.treemapBinary = binary;
exports.treemapDice = treemapDice;
exports.treemapSlice = treemapSlice;
exports.treemapSliceDice = sliceDice;
exports.treemapSquarify = squarify;
exports.treemapResquarify = resquarify;
exports.interpolate = interpolateValue;
exports.interpolateArray = array$1;
exports.interpolateBasis = basis$1;
exports.interpolateBasisClosed = basisClosed;
exports.interpolateDate = date;
exports.interpolateNumber = reinterpolate;
exports.interpolateObject = object;
exports.interpolateRound = interpolateRound;
exports.interpolateString = interpolateString;
exports.interpolateTransformCss = interpolateTransformCss;
exports.interpolateTransformSvg = interpolateTransformSvg;
exports.interpolateZoom = interpolateZoom;
exports.interpolateRgb = interpolateRgb;
exports.interpolateRgbBasis = rgbBasis;
exports.interpolateRgbBasisClosed = rgbBasisClosed;
exports.interpolateHsl = hsl$2;
exports.interpolateHslLong = hslLong;
exports.interpolateLab = lab$1;
exports.interpolateHcl = hcl$2;
exports.interpolateHclLong = hclLong;
exports.interpolateCubehelix = cubehelix$2;
exports.interpolateCubehelixLong = cubehelixLong;
exports.piecewise = piecewise;
exports.quantize = quantize;
exports.path = path;
exports.polygonArea = area$2;
exports.polygonCentroid = centroid$1;
exports.polygonHull = hull;
exports.polygonContains = contains$2;
exports.polygonLength = length$2;
exports.quadtree = quadtree;
exports.randomUniform = uniform;
exports.randomNormal = normal;
exports.randomLogNormal = logNormal;
exports.randomBates = bates;
exports.randomIrwinHall = irwinHall;
exports.randomExponential = exponential$1;
exports.scaleBand = band;
exports.scalePoint = point$1;
exports.scaleIdentity = identity$6;
exports.scaleLinear = linear$2;
exports.scaleLog = log$1;
exports.scaleOrdinal = ordinal;
exports.scaleImplicit = implicit;
exports.scalePow = pow$1;
exports.scaleSqrt = sqrt$1;
exports.scaleQuantile = quantile$$1;
exports.scaleQuantize = quantize$1;
exports.scaleThreshold = threshold$1;
exports.scaleTime = time;
exports.scaleUtc = utcTime;
exports.scaleSequential = sequential;
exports.scaleDiverging = diverging;
exports.schemeCategory10 = category10;
exports.schemeAccent = Accent;
exports.schemeDark2 = Dark2;
exports.schemePaired = Paired;
exports.schemePastel1 = Pastel1;
exports.schemePastel2 = Pastel2;
exports.schemeSet1 = Set1;
exports.schemeSet2 = Set2;
exports.schemeSet3 = Set3;
exports.interpolateBrBG = BrBG;
exports.schemeBrBG = scheme;
exports.interpolatePRGn = PRGn;
exports.schemePRGn = scheme$1;
exports.interpolatePiYG = PiYG;
exports.schemePiYG = scheme$2;
exports.interpolatePuOr = PuOr;
exports.schemePuOr = scheme$3;
exports.interpolateRdBu = RdBu;
exports.schemeRdBu = scheme$4;
exports.interpolateRdGy = RdGy;
exports.schemeRdGy = scheme$5;
exports.interpolateRdYlBu = RdYlBu;
exports.schemeRdYlBu = scheme$6;
exports.interpolateRdYlGn = RdYlGn;
exports.schemeRdYlGn = scheme$7;
exports.interpolateSpectral = Spectral;
exports.schemeSpectral = scheme$8;
exports.interpolateBuGn = BuGn;
exports.schemeBuGn = scheme$9;
exports.interpolateBuPu = BuPu;
exports.schemeBuPu = scheme$10;
exports.interpolateGnBu = GnBu;
exports.schemeGnBu = scheme$11;
exports.interpolateOrRd = OrRd;
exports.schemeOrRd = scheme$12;
exports.interpolatePuBuGn = PuBuGn;
exports.schemePuBuGn = scheme$13;
exports.interpolatePuBu = PuBu;
exports.schemePuBu = scheme$14;
exports.interpolatePuRd = PuRd;
exports.schemePuRd = scheme$15;
exports.interpolateRdPu = RdPu;
exports.schemeRdPu = scheme$16;
exports.interpolateYlGnBu = YlGnBu;
exports.schemeYlGnBu = scheme$17;
exports.interpolateYlGn = YlGn;
exports.schemeYlGn = scheme$18;
exports.interpolateYlOrBr = YlOrBr;
exports.schemeYlOrBr = scheme$19;
exports.interpolateYlOrRd = YlOrRd;
exports.schemeYlOrRd = scheme$20;
exports.interpolateBlues = Blues;
exports.schemeBlues = scheme$21;
exports.interpolateGreens = Greens;
exports.schemeGreens = scheme$22;
exports.interpolateGreys = Greys;
exports.schemeGreys = scheme$23;
exports.interpolatePurples = Purples;
exports.schemePurples = scheme$24;
exports.interpolateReds = Reds;
exports.schemeReds = scheme$25;
exports.interpolateOranges = Oranges;
exports.schemeOranges = scheme$26;
exports.interpolateCubehelixDefault = cubehelix$3;
exports.interpolateRainbow = rainbow;
exports.interpolateWarm = warm;
exports.interpolateCool = cool;
exports.interpolateSinebow = sinebow;
exports.interpolateViridis = viridis;
exports.interpolateMagma = magma;
exports.interpolateInferno = inferno;
exports.interpolatePlasma = plasma;
exports.create = create;
exports.creator = creator;
exports.local = local;
exports.matcher = matcher$1;
exports.mouse = mouse;
exports.namespace = namespace;
exports.namespaces = namespaces;
exports.clientPoint = point;
exports.select = select;
exports.selectAll = selectAll;
exports.selection = selection;
exports.selector = selector;
exports.selectorAll = selectorAll;
exports.style = styleValue;
exports.touch = touch;
exports.touches = touches;
exports.window = defaultView;
exports.customEvent = customEvent;
exports.arc = arc;
exports.area = area$3;
exports.line = line;
exports.pie = pie;
exports.areaRadial = areaRadial;
exports.radialArea = areaRadial;
exports.lineRadial = lineRadial$1;
exports.radialLine = lineRadial$1;
exports.pointRadial = pointRadial;
exports.linkHorizontal = linkHorizontal;
exports.linkVertical = linkVertical;
exports.linkRadial = linkRadial;
exports.symbol = symbol;
exports.symbols = symbols;
exports.symbolCircle = circle$2;
exports.symbolCross = cross$2;
exports.symbolDiamond = diamond;
exports.symbolSquare = square;
exports.symbolStar = star;
exports.symbolTriangle = triangle;
exports.symbolWye = wye;
exports.curveBasisClosed = basisClosed$1;
exports.curveBasisOpen = basisOpen;
exports.curveBasis = basis$2;
exports.curveBundle = bundle;
exports.curveCardinalClosed = cardinalClosed;
exports.curveCardinalOpen = cardinalOpen;
exports.curveCardinal = cardinal;
exports.curveCatmullRomClosed = catmullRomClosed;
exports.curveCatmullRomOpen = catmullRomOpen;
exports.curveCatmullRom = catmullRom;
exports.curveLinearClosed = linearClosed;
exports.curveLinear = curveLinear;
exports.curveMonotoneX = monotoneX;
exports.curveMonotoneY = monotoneY;
exports.curveNatural = natural;
exports.curveStep = step;
exports.curveStepAfter = stepAfter;
exports.curveStepBefore = stepBefore;
exports.stack = stack;
exports.stackOffsetExpand = expand;
exports.stackOffsetDiverging = diverging$1;
exports.stackOffsetNone = none$1;
exports.stackOffsetSilhouette = silhouette;
exports.stackOffsetWiggle = wiggle;
exports.stackOrderAscending = ascending$3;
exports.stackOrderDescending = descending$2;
exports.stackOrderInsideOut = insideOut;
exports.stackOrderNone = none$2;
exports.stackOrderReverse = reverse;
exports.timeInterval = newInterval;
exports.timeMillisecond = millisecond;
exports.timeMilliseconds = milliseconds;
exports.utcMillisecond = millisecond;
exports.utcMilliseconds = milliseconds;
exports.timeSecond = second;
exports.timeSeconds = seconds;
exports.utcSecond = second;
exports.utcSeconds = seconds;
exports.timeMinute = minute;
exports.timeMinutes = minutes;
exports.timeHour = hour;
exports.timeHours = hours;
exports.timeDay = day;
exports.timeDays = days;
exports.timeWeek = sunday;
exports.timeWeeks = sundays;
exports.timeSunday = sunday;
exports.timeSundays = sundays;
exports.timeMonday = monday;
exports.timeMondays = mondays;
exports.timeTuesday = tuesday;
exports.timeTuesdays = tuesdays;
exports.timeWednesday = wednesday;
exports.timeWednesdays = wednesdays;
exports.timeThursday = thursday;
exports.timeThursdays = thursdays;
exports.timeFriday = friday;
exports.timeFridays = fridays;
exports.timeSaturday = saturday;
exports.timeSaturdays = saturdays;
exports.timeMonth = month;
exports.timeMonths = months;
exports.timeYear = year;
exports.timeYears = years;
exports.utcMinute = utcMinute;
exports.utcMinutes = utcMinutes;
exports.utcHour = utcHour;
exports.utcHours = utcHours;
exports.utcDay = utcDay;
exports.utcDays = utcDays;
exports.utcWeek = utcSunday;
exports.utcWeeks = utcSundays;
exports.utcSunday = utcSunday;
exports.utcSundays = utcSundays;
exports.utcMonday = utcMonday;
exports.utcMondays = utcMondays;
exports.utcTuesday = utcTuesday;
exports.utcTuesdays = utcTuesdays;
exports.utcWednesday = utcWednesday;
exports.utcWednesdays = utcWednesdays;
exports.utcThursday = utcThursday;
exports.utcThursdays = utcThursdays;
exports.utcFriday = utcFriday;
exports.utcFridays = utcFridays;
exports.utcSaturday = utcSaturday;
exports.utcSaturdays = utcSaturdays;
exports.utcMonth = utcMonth;
exports.utcMonths = utcMonths;
exports.utcYear = utcYear;
exports.utcYears = utcYears;
exports.timeFormatDefaultLocale = defaultLocale$1;
exports.timeFormatLocale = formatLocale$1;
exports.isoFormat = formatIso;
exports.isoParse = parseIso;
exports.now = now;
exports.timer = timer;
exports.timerFlush = timerFlush;
exports.timeout = timeout$1;
exports.interval = interval$1;
exports.transition = transition;
exports.active = active;
exports.interrupt = interrupt;
exports.voronoi = voronoi;
exports.zoom = zoom;
exports.zoomTransform = transform$1;
exports.zoomIdentity = identity$8;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//}}}
/***
|Name|diff2html.js|
|Source|https://github.com/rtfpessoa/diff2html|
|Documentation|https://diff2html.xyz|
|Version|2.12.0|
|License|[[MIT|https://opensource.org/licenses/MIT]]|
|Description|diff2html generates pretty HTML diffs from git or unified diff output|
!!!!!CSS
//{{{
/*
*
* Diff to HTML (diff2html.css)
* Author: rtfpessoa
*
* /
.d2h-wrapper {
text-align: left;
}
.d2h-file-header {
height: 35px;
padding: 5px 10px;
border-bottom: 1px solid #d8d8d8;
background-color: #f7f7f7;
}
.d2h-file-stats {
display: flex;
margin-left: auto;
font-size: 14px;
}
.d2h-lines-added {
text-align: right;
border: 1px solid #b4e2b4;
border-radius: 5px 0 0 5px;
color: #399839;
padding: 2px;
vertical-align: middle;
}
.d2h-lines-deleted {
text-align: left;
border: 1px solid #e9aeae;
border-radius: 0 5px 5px 0;
color: #c33;
padding: 2px;
vertical-align: middle;
margin-left: 1px;
}
.d2h-file-name-wrapper {
display: flex;
align-items: center;
width: 100%;
font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 15px;
}
.d2h-file-name {
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
}
.d2h-file-wrapper {
border: 1px solid #ddd;
border-radius: 3px;
margin-bottom: 1em;
}
.d2h-diff-table {
width: 100%;
border-collapse: collapse;
font-family: "Menlo", "Consolas", monospace;
font-size: 13px;
}
.d2h-files-diff {
display: block;
width: 100%;
height: 100%;
}
.d2h-file-diff {
overflow-y: hidden;
}
.d2h-file-side-diff {
display: inline-block;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
margin-right: -4px;
margin-bottom: -8px;
}
.d2h-code-line {
display: inline-block;
white-space: nowrap;
/* Compensate for the absolute positioning of the line numbers * /
padding: 0 8em;
}
.d2h-code-side-line {
display: inline-block;
white-space: nowrap;
/* Compensate for the absolute positioning of the line numbers * /
padding: 0 4.5em;
}
.d2h-code-line del,
.d2h-code-side-line del {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #ffb6ba;
border-radius: 0.2em;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #97f295;
border-radius: 0.2em;
text-align: left;
}
.d2h-code-line-prefix {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.d2h-code-line-ctn {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.line-num1 {
box-sizing: border-box;
float: left;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.line-num2 {
box-sizing: border-box;
float: right;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.d2h-code-linenumber {
box-sizing: border-box;
width: 7.5em;
/* Keep the numbers fixed on line contents scroll * /
position: absolute;
display: inline-block;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
}
.d2h-code-linenumber:after {
content: '\200b';
}
.d2h-code-side-linenumber {
/* Keep the numbers fixed on line contents scroll * /
position: absolute;
display: inline-block;
box-sizing: border-box;
width: 4em;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-code-side-linenumber:after {
content: '\200b';
}
.d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder {
background-color: #f1f1f1;
border-color: #e1e1e1;
}
/*
* Changes Highlight
* /
.d2h-del {
background-color: #fee8e9;
border-color: #e9aeae;
}
.d2h-ins {
background-color: #dfd;
border-color: #b4e2b4;
}
.d2h-info {
background-color: #f8fafd;
color: rgba(0, 0, 0, 0.3);
border-color: #d5e4f2;
}
.d2h-file-diff .d2h-del.d2h-change {
background-color: #fdf2d0;
}
.d2h-file-diff .d2h-ins.d2h-change {
background-color: #ded;
}
/*
* File Summary List
* /
.d2h-file-list-wrapper {
margin-bottom: 10px;
}
.d2h-file-list-wrapper a {
text-decoration: none;
color: #3572b0;
}
.d2h-file-list-wrapper a:visited {
color: #3572b0;
}
.d2h-file-list-header {
text-align: left;
}
.d2h-file-list-title {
font-weight: bold;
}
.d2h-file-list-line {
display: flex;
text-align: left;
}
.d2h-file-list {
display: block;
list-style: none;
padding: 0;
margin: 0;
}
.d2h-file-list > li {
border-bottom: #ddd solid 1px;
padding: 5px 10px;
margin: 0;
}
.d2h-file-list > li:last-child {
border-bottom: none;
}
.d2h-file-switch {
display: none;
font-size: 10px;
cursor: pointer;
}
.d2h-icon {
vertical-align: middle;
margin-right: 10px;
fill: currentColor;
}
.d2h-deleted {
color: #c33;
}
.d2h-added {
color: #399839;
}
.d2h-changed {
color: #d0b44c;
}
.d2h-moved {
color: #3572b0;
}
.d2h-tag {
display: flex;
font-size: 10px;
margin-left: 5px;
padding: 0 2px;
background-color: #fff;
}
.d2h-deleted-tag {
border: #c33 1px solid;
}
.d2h-added-tag {
border: #399839 1px solid;
}
.d2h-changed-tag {
border: #d0b44c 1px solid;
}
.d2h-moved-tag {
border: #3572b0 1px solid;
}
/*
* Selection util.
* /
.selecting-left .d2h-code-line,
.selecting-left .d2h-code-line *,
.selecting-right td.d2h-code-linenumber,
.selecting-right td.d2h-code-linenumber *,
.selecting-left .d2h-code-side-line,
.selecting-left .d2h-code-side-line *,
.selecting-right td.d2h-code-side-linenumber,
.selecting-right td.d2h-code-side-linenumber * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.selecting-left .d2h-code-line::-moz-selection,
.selecting-left .d2h-code-line *::-moz-selection,
.selecting-right td.d2h-code-linenumber::-moz-selection,
.selecting-left .d2h-code-side-line::-moz-selection,
.selecting-left .d2h-code-side-line *::-moz-selection,
.selecting-right td.d2h-code-side-linenumber::-moz-selection,
.selecting-right td.d2h-code-side-linenumber *::-moz-selection {
background: transparent;
}
.selecting-left .d2h-code-line::selection,
.selecting-left .d2h-code-line *::selection,
.selecting-right td.d2h-code-linenumber::selection,
.selecting-left .d2h-code-side-line::selection,
.selecting-left .d2h-code-side-line *::selection,
.selecting-right td.d2h-code-side-linenumber::selection,
.selecting-right td.d2h-code-side-linenumber *::selection {
background: transparent;
}
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy91aS9jc3MvZGlmZjJodG1sLmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7RUFLRTs7QUFFRjtFQUNFLGdCQUFnQjtBQUNsQjs7QUFFQTtFQUNFLFlBQVk7RUFDWixpQkFBaUI7RUFDakIsZ0NBQWdDO0VBQ2hDLHlCQUF5QjtBQUMzQjs7QUFFQTtFQUdFLGFBQWE7RUFDYixpQkFBaUI7RUFDakIsZUFBZTtBQUNqQjs7QUFFQTtFQUNFLGlCQUFpQjtFQUNqQix5QkFBeUI7RUFDekIsMEJBQTBCO0VBQzFCLGNBQWM7RUFDZCxZQUFZO0VBQ1osc0JBQXNCO0FBQ3hCOztBQUVBO0VBQ0UsZ0JBQWdCO0VBQ2hCLHlCQUF5QjtFQUN6QiwwQkFBMEI7RUFDMUIsV0FBVztFQUNYLFlBQVk7RUFDWixzQkFBc0I7RUFDdEIsZ0JBQWdCO0FBQ2xCOztBQUVBO0VBR0UsYUFBYTtFQUdiLG1CQUFtQjtFQUNuQixXQUFXO0VBQ1gsOEVBQThFO0VBQzlFLGVBQWU7QUFDakI7O0FBRUE7RUFDRSxtQkFBbUI7RUFDbkIsdUJBQXVCO0VBQ3ZCLGtCQUFrQjtBQUNwQjs7QUFFQTtFQUNFLHNCQUFzQjtFQUN0QixrQkFBa0I7RUFDbEIsa0JBQWtCO0FBQ3BCOztBQUVBO0VBQ0UsV0FBVztFQUNYLHlCQUF5QjtFQUN6QiwyQ0FBMkM7RUFDM0MsZUFBZTtBQUNqQjs7QUFFQTtFQUNFLGNBQWM7RUFDZCxXQUFXO0VBQ1gsWUFBWTtBQUNkOztBQUVBO0VBQ0Usa0JBQWtCO0FBQ3BCOztBQUVBO0VBQ0UscUJBQXFCO0VBQ3JCLGtCQUFrQjtFQUNsQixrQkFBa0I7RUFDbEIsVUFBVTtFQUNWLGtCQUFrQjtFQUNsQixtQkFBbUI7QUFDckI7O0FBRUE7RUFDRSxxQkFBcUI7RUFDckIsbUJBQW1CO0VBQ25CLGdFQUFnRTtFQUNoRSxjQUFjO0FBQ2hCOztBQUVBO0VBQ0UscUJBQXFCO0VBQ3JCLG1CQUFtQjtFQUNuQixnRUFBZ0U7RUFDaEUsZ0JBQWdCO0FBQ2xC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Q25COztBQUVBOzs7Ozs7O0VBT0UsdUJBQXVCO0FBQ3pCOztBQUVBOzs7Ozs7O0VBT0UsdUJBQXVCO0FBQ3pCIiwiZmlsZSI6ImRpZmYyaHRtbC5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICpcbiAqIERpZmYgdG8gSFRNTCAoZGlmZjJodG1sLmNzcylcbiAqIEF1dGhvcjogcnRmcGVzc29hXG4gKlxuICovXG5cbi5kMmgtd3JhcHBlciB7XG4gIHRleHQtYWxpZ246IGxlZnQ7XG59XG5cbi5kMmgtZmlsZS1oZWFkZXIge1xuICBoZWlnaHQ6IDM1cHg7XG4gIHBhZGRpbmc6IDVweCAxMHB4O1xuICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2Q4ZDhkODtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2Y3ZjdmNztcbn1cblxuLmQyaC1maWxlLXN0YXRzIHtcbiAgZGlzcGxheTogLXdlYmtpdC1ib3g7XG4gIGRpc3BsYXk6IC1tcy1mbGV4Ym94O1xuICBkaXNwbGF5OiBmbGV4O1xuICBtYXJnaW4tbGVmdDogYXV0bztcbiAgZm9udC1zaXplOiAxNHB4O1xufVxuXG4uZDJoLWxpbmVzLWFkZGVkIHtcbiAgdGV4dC1hbGlnbjogcmlnaHQ7XG4gIGJvcmRlcjogMXB4IHNvbGlkICNiNGUyYjQ7XG4gIGJvcmRlci1yYWRpdXM6IDVweCAwIDAgNXB4O1xuICBjb2xvcjogIzM5OTgzOTtcbiAgcGFkZGluZzogMnB4O1xuICB2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xufVxuXG4uZDJoLWxpbmVzLWRlbGV0ZWQge1xuICB0ZXh0LWFsaWduOiBsZWZ0O1xuICBib3JkZXI6IDFweCBzb2xpZCAjZTlhZWFlO1xuICBib3JkZXItcmFkaXVzOiAwIDVweCA1cHggMDtcbiAgY29sb3I6ICNjMzM7XG4gIHBhZGRpbmc6IDJweDtcbiAgdmVydGljYWwtYWxpZ246IG1pZGRsZTtcbiAgbWFyZ2luLWxlZnQ6IDFweDtcbn1cblxuLmQyaC1maWxlLW5hbWUtd3JhcHBlciB7XG4gIGRpc3BsYXk6IC13ZWJraXQtYm94O1xuICBkaXNwbGF5OiAtbXMtZmxleGJveDtcbiAgZGlzcGxheTogZmxleDtcbiAgLXdlYmtpdC1ib3gtYWxpZ246IGNlbnRlcjtcbiAgLW1zLWZsZXgtYWxpZ246IGNlbnRlcjtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgd2lkdGg6IDEwMCU7XG4gIGZvbnQtZmFtaWx5OiBcIlNvdXJjZSBTYW5zIFByb1wiLCBcIkhlbHZldGljYSBOZXVlXCIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIGZvbnQtc2l6ZTogMTVweDtcbn1cblxuLmQyaC1maWxlLW5hbWUge1xuICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICB0ZXh0LW92ZXJmbG93OiBlbGxpcHNpcztcbiAgb3ZlcmZsb3cteDogaGlkZGVuO1xufVxuXG4uZDJoLWZpbGUtd3JhcHBlciB7XG4gIGJvcmRlcjogMXB4IHNvbGlkICNkZGQ7XG4gIGJvcmRlci1yYWRpdXM6IDNweDtcbiAgbWFyZ2luLWJvdHRvbTogMWVtO1xufVxuXG4uZDJoLWRpZmYtdGFibGUge1xuICB3aWR0aDogMTAwJTtcbiAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTtcbiAgZm9udC1mYW1pbHk6IFwiTWVubG9cIiwgXCJDb25zb2xhc1wiLCBtb25vc3BhY2U7XG4gIGZvbnQtc2l6ZTogMTNweDtcbn1cblxuLmQyaC1maWxlcy1kaWZmIHtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG59XG5cbi5kMmgtZmlsZS1kaWZmIHtcbiAgb3ZlcmZsb3cteTogaGlkZGVuO1xufVxuXG4uZDJoLWZpbGUtc2lkZS1kaWZmIHtcbiAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICBvdmVyZmxvdy14OiBzY3JvbGw7XG4gIG92ZXJmbG93LXk6IGhpZGRlbjtcbiAgd2lkdGg6IDUwJTtcbiAgbWFyZ2luLXJpZ2h0OiAtNHB4O1xuICBtYXJnaW4tYm90dG9tOiAtOHB4O1xufVxuXG4uZDJoLWNvZGUtbGluZSB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgd2hpdGUtc3BhY2U6IG5vd3JhcDtcbiAgLyogQ29tcGVuc2F0ZSBmb3IgdGhlIGFic29sdXRlIHBvc2l0aW9uaW5nIG9mIHRoZSBsaW5lIG51bWJlcnMgKi9cbiAgcGFkZGluZzogMCA4ZW07XG59XG5cbi5kMmgtY29kZS1zaWRlLWxpbmUge1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gIHdoaXRlLXNwYWNlOiBub3dyYXA7XG4gIC8qIENvbXBlbnNhdGUgZm9yIHRoZSBhYnNvbHV0ZSBwb3NpdGlvbmluZyBvZiB0aGUgbGluZSBudW1iZXJzICovXG4gIHBhZGRpbmc6IDAgNC41ZW07XG59XG5cbi5kMmgtY29kZS1saW5lIGRlbCxcbi5kMmgtY29kZS1zaWRlLWxpbmUgZGVsIHtcbiAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICBtYXJnaW4tdG9wOiAtMXB4O1xuICB0ZXh0LWRlY29yYXRpb246IG5vbmU7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmZmI2YmE7XG4gIGJvcmRlci1yYWRpdXM6IDAuMmVtO1xufVxuXG4uZDJoLWNvZGUtbGluZSBpbnMsXG4uZDJoLWNvZGUtc2lkZS1saW5lIGlucyB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgbWFyZ2luLXRvcDogLTFweDtcbiAgdGV4dC1kZWNvcmF0aW9uOiBub25lO1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjOTdmMjk1O1xuICBib3JkZXItcmFkaXVzOiAwLjJlbTtcbiAgdGV4dC1hbGlnbjogbGVmdDtcbn1cblxuLmQyaC1jb2RlLWxpbmUtcHJlZml4IHtcbiAgZGlzcGxheTogaW5saW5lO1xuICBiYWNrZ3JvdW5kOiBub25lO1xuICBwYWRkaW5nOiAwO1xuICB3b3JkLXdyYXA6IG5vcm1hbDtcbiAgd2hpdGUtc3BhY2U6IHByZTtcbn1cblxuLmQyaC1jb2RlLWxpbmUtY3RuIHtcbiAgZGlzcGxheTogaW5saW5lO1xuICBiYWNrZ3JvdW5kOiBub25lO1xuICBwYWRkaW5nOiAwO1xuICB3b3JkLXdyYXA6IG5vcm1hbDtcbiAgd2hpdGUtc3BhY2U6IHByZTtcbn1cblxuLmxpbmUtbnVtMSB7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIGZsb2F0OiBsZWZ0O1xuICB3aWR0aDogMy41ZW07XG4gIG92ZXJmbG93OiBoaWRkZW47XG4gIHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzO1xuICBwYWRkaW5nOiAwIDAuNWVtIDAgMC41ZW07XG59XG5cbi5saW5lLW51bTIge1xuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICBmbG9hdDogcmlnaHQ7XG4gIHdpZHRoOiAzLjVlbTtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7XG4gIHBhZGRpbmc6IDAgMC41ZW0gMCAwLjVlbTtcbn1cblxuLmQyaC1jb2RlLWxpbmVudW1iZXIge1xuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xuICB3aWR0aDogNy41ZW07XG4gIC8qIEtlZXAgdGhlIG51bWJlcnMgZml4ZWQgb24gbGluZSBjb250ZW50cyBzY3JvbGwgKi9cbiAgcG9zaXRpb246IGFic29sdXRlO1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmZmY7XG4gIGNvbG9yOiByZ2JhKDAsIDAsIDAsIDAuMyk7XG4gIHRleHQtYWxpZ246IHJpZ2h0O1xuICBib3JkZXI6IHNvbGlkICNlZWVlZWU7XG4gIGJvcmRlci13aWR0aDogMCAxcHggMCAxcHg7XG4gIGN1cnNvcjogcG9pbnRlcjtcbn1cblxuLmQyaC1jb2RlLWxpbmVudW1iZXI6YWZ0ZXIge1xuICBjb250ZW50OiAnXFwyMDBiJztcbn1cblxuLmQyaC1jb2RlLXNpZGUtbGluZW51bWJlciB7XG4gIC8qIEtlZXAgdGhlIG51bWJlcnMgZml4ZWQgb24gbGluZSBjb250ZW50cyBzY3JvbGwgKi9cbiAgcG9zaXRpb246IGFic29sdXRlO1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIHdpZHRoOiA0ZW07XG4gIGJhY2tncm91bmQtY29sb3I6ICNmZmY7XG4gIGNvbG9yOiByZ2JhKDAsIDAsIDAsIDAuMyk7XG4gIHRleHQtYWxpZ246IHJpZ2h0O1xuICBib3JkZXI6IHNvbGlkICNlZWVlZWU7XG4gIGJvcmRlci13aWR0aDogMCAxcHggMCAxcHg7XG4gIGN1cnNvcjogcG9pbnRlcjtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7XG59XG5cbi5kMmgtY29kZS1zaWRlLWxpbmVudW1iZXI6YWZ0ZXIge1xuICBjb250ZW50OiAnXFwyMDBiJztcbn1cblxuLmQyaC1jb2RlLXNpZGUtZW1wdHlwbGFjZWhvbGRlcixcbi5kMmgtZW1wdHlwbGFjZWhvbGRlciB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmMWYxZjE7XG4gIGJvcmRlci1jb2xvcjogI2UxZTFlMTtcbn1cblxuLypcbiAqIENoYW5nZXMgSGlnaGxpZ2h0XG4gKi9cblxuLmQyaC1kZWwge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmVlOGU5O1xuICBib3JkZXItY29sb3I6ICNlOWFlYWU7XG59XG5cbi5kMmgtaW5zIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2RmZDtcbiAgYm9yZGVyLWNvbG9yOiAjYjRlMmI0O1xufVxuXG4uZDJoLWluZm8ge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmYWZkO1xuICBjb2xvcjogcmdiYSgwLCAwLCAwLCAwLjMpO1xuICBib3JkZXItY29sb3I6ICNkNWU0ZjI7XG59XG5cbi5kMmgtZmlsZS1kaWZmIC5kMmgtZGVsLmQyaC1jaGFuZ2Uge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmRmMmQwO1xufVxuXG4uZDJoLWZpbGUtZGlmZiAuZDJoLWlucy5kMmgtY2hhbmdlIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2RlZDtcbn1cblxuLypcbiAqIEZpbGUgU3VtbWFyeSBMaXN0XG4gKi9cblxuLmQyaC1maWxlLWxpc3Qtd3JhcHBlciB7XG4gIG1hcmdpbi1ib3R0b206IDEwcHg7XG59XG5cbi5kMmgtZmlsZS1saXN0LXdyYXBwZXIgYSB7XG4gIHRleHQtZGVjb3JhdGlvbjogbm9uZTtcbiAgY29sb3I6ICMzNTcyYjA7XG59XG5cbi5kMmgtZmlsZS1saXN0LXdyYXBwZXIgYTp2aXNpdGVkIHtcbiAgY29sb3I6ICMzNTcyYjA7XG59XG5cbi5kMmgtZmlsZS1saXN0LWhlYWRlciB7XG4gIHRleHQtYWxpZ246IGxlZnQ7XG59XG5cbi5kMmgtZmlsZS1saXN0LXRpdGxlIHtcbiAgZm9udC13ZWlnaHQ6IGJvbGQ7XG59XG5cbi5kMmgtZmlsZS1saXN0LWxpbmUge1xuICBkaXNwbGF5OiAtd2Via2l0LWJveDtcbiAgZGlzcGxheTogLW1zLWZsZXhib3g7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIHRleHQtYWxpZ246IGxlZnQ7XG59XG5cbi5kMmgtZmlsZS1saXN0IHtcbiAgZGlzcGxheTogYmxvY2s7XG4gIGxpc3Qtc3R5bGU6IG5vbmU7XG4gIHBhZGRpbmc6IDA7XG4gIG1hcmdpbjogMDtcbn1cblxuLmQyaC1maWxlLWxpc3QgPiBsaSB7XG4gIGJvcmRlci1ib3R0b206ICNkZGQgc29saWQgMXB4O1xuICBwYWRkaW5nOiA1cHggMTBweDtcbiAgbWFyZ2luOiAwO1xufVxuXG4uZDJoLWZpbGUtbGlzdCA+IGxpOmxhc3QtY2hpbGQge1xuICBib3JkZXItYm90dG9tOiBub25lO1xufVxuXG4uZDJoLWZpbGUtc3dpdGNoIHtcbiAgZGlzcGxheTogbm9uZTtcbiAgZm9udC1zaXplOiAxMHB4O1xuICBjdXJzb3I6IHBvaW50ZXI7XG59XG5cbi5kMmgtaWNvbiB7XG4gIHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7XG4gIG1hcmdpbi1yaWdodDogMTBweDtcbiAgZmlsbDogY3VycmVudENvbG9yO1xufVxuXG4uZDJoLWRlbGV0ZWQge1xuICBjb2xvcjogI2MzMztcbn1cblxuLmQyaC1hZGRlZCB7XG4gIGNvbG9yOiAjMzk5ODM5O1xufVxuXG4uZDJoLWNoYW5nZWQge1xuICBjb2xvcjogI2QwYjQ0Yztcbn1cblxuLmQyaC1tb3ZlZCB7XG4gIGNvbG9yOiAjMzU3MmIwO1xufVxuXG4uZDJoLXRhZyB7XG4gIGRpc3BsYXk6IC13ZWJraXQtYm94O1xuICBkaXNwbGF5OiAtbXMtZmxleGJveDtcbiAgZGlzcGxheTogZmxleDtcbiAgZm9udC1zaXplOiAxMHB4O1xuICBtYXJnaW4tbGVmdDogNXB4O1xuICBwYWRkaW5nOiAwIDJweDtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjtcbn1cblxuLmQyaC1kZWxldGVkLXRhZyB7XG4gIGJvcmRlcjogI2MzMyAxcHggc29saWQ7XG59XG5cbi5kMmgtYWRkZWQtdGFnIHtcbiAgYm9yZGVyOiAjMzk5ODM5IDFweCBzb2xpZDtcbn1cblxuLmQyaC1jaGFuZ2VkLXRhZyB7XG4gIGJvcmRlcjogI2QwYjQ0YyAxcHggc29saWQ7XG59XG5cbi5kMmgtbW92ZWQtdGFnIHtcbiAgYm9yZGVyOiAjMzU3MmIwIDFweCBzb2xpZDtcbn1cblxuLypcbiAqIFNlbGVjdGlvbiB1dGlsLlxuICovXG5cbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtbGluZSxcbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtbGluZSAqLFxuLnNlbGVjdGluZy1yaWdodCB0ZC5kMmgtY29kZS1saW5lbnVtYmVyLFxuLnNlbGVjdGluZy1yaWdodCB0ZC5kMmgtY29kZS1saW5lbnVtYmVyICosXG4uc2VsZWN0aW5nLWxlZnQgLmQyaC1jb2RlLXNpZGUtbGluZSxcbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtc2lkZS1saW5lICosXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLXNpZGUtbGluZW51bWJlcixcbi5zZWxlY3RpbmctcmlnaHQgdGQuZDJoLWNvZGUtc2lkZS1saW5lbnVtYmVyICoge1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIC13ZWJraXQtdXNlci1zZWxlY3Q6IG5vbmU7XG4gIC1tb3otdXNlci1zZWxlY3Q6IG5vbmU7XG4gIC1tcy11c2VyLXNlbGVjdDogbm9uZTtcbiAgdXNlci1zZWxlY3Q6IG5vbmU7XG59XG5cbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtbGluZTo6LW1vei1zZWxlY3Rpb24sXG4uc2VsZWN0aW5nLWxlZnQgLmQyaC1jb2RlLWxpbmUgKjo6LW1vei1zZWxlY3Rpb24sXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLWxpbmVudW1iZXI6Oi1tb3otc2VsZWN0aW9uLFxuLnNlbGVjdGluZy1sZWZ0IC5kMmgtY29kZS1zaWRlLWxpbmU6Oi1tb3otc2VsZWN0aW9uLFxuLnNlbGVjdGluZy1sZWZ0IC5kMmgtY29kZS1zaWRlLWxpbmUgKjo6LW1vei1zZWxlY3Rpb24sXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLXNpZGUtbGluZW51bWJlcjo6LW1vei1zZWxlY3Rpb24sXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLXNpZGUtbGluZW51bWJlciAqOjotbW96LXNlbGVjdGlvbiB7XG4gIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50O1xufVxuXG4uc2VsZWN0aW5nLWxlZnQgLmQyaC1jb2RlLWxpbmU6OnNlbGVjdGlvbixcbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtbGluZSAqOjpzZWxlY3Rpb24sXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLWxpbmVudW1iZXI6OnNlbGVjdGlvbixcbi5zZWxlY3RpbmctbGVmdCAuZDJoLWNvZGUtc2lkZS1saW5lOjpzZWxlY3Rpb24sXG4uc2VsZWN0aW5nLWxlZnQgLmQyaC1jb2RlLXNpZGUtbGluZSAqOjpzZWxlY3Rpb24sXG4uc2VsZWN0aW5nLXJpZ2h0IHRkLmQyaC1jb2RlLXNpZGUtbGluZW51bWJlcjo6c2VsZWN0aW9uLFxuLnNlbGVjdGluZy1yaWdodCB0ZC5kMmgtY29kZS1zaWRlLWxpbmVudW1iZXIgKjo6c2VsZWN0aW9uIHtcbiAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7XG59XG4iXX0= * /
//}}}
!!!!!Code
***/
//{{{
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
},{}],2:[function(require,module,exports){
/*!
diff v4.0.1
Software License Agreement (BSD License)
Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
All rights reserved.
Redistribution and use of this software in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of Kevin Decker nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@license
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.Diff = {}));
}(this, function (exports) { 'use strict';
function Diff() {}
Diff.prototype = {
diff: function diff(oldString, newString) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var callback = options.callback;
if (typeof options === 'function') {
callback = options;
options = {};
}
this.options = options;
var self = this;
function done(value) {
if (callback) {
setTimeout(function () {
callback(undefined, value);
}, 0);
return true;
} else {
return value;
}
} // Allow subclasses to massage the input prior to running
oldString = this.castInput(oldString);
newString = this.castInput(newString);
oldString = this.removeEmpty(this.tokenize(oldString));
newString = this.removeEmpty(this.tokenize(newString));
var newLen = newString.length,
oldLen = oldString.length;
var editLength = 1;
var maxEditLength = newLen + oldLen;
var bestPath = [{
newPos: -1,
components: []
}]; // Seed editLength = 0, i.e. the content starts with the same values
var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
// Identity per the equality and tokenizer
return done([{
value: this.join(newString),
count: newString.length
}]);
} // Main worker method. checks all permutations of a given edit length for acceptance.
function execEditLength() {
for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
var basePath = void 0;
var addPath = bestPath[diagonalPath - 1],
removePath = bestPath[diagonalPath + 1],
_oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
if (addPath) {
// No one else is going to attempt to use this value, clear it
bestPath[diagonalPath - 1] = undefined;
}
var canAdd = addPath && addPath.newPos + 1 < newLen,
canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen;
if (!canAdd && !canRemove) {
// If this path is a terminal then prune
bestPath[diagonalPath] = undefined;
continue;
} // Select the diagonal that we want to branch from. We select the prior
// path whose position in the new string is the farthest from the origin
// and does not pass the bounds of the diff graph
if (!canAdd || canRemove && addPath.newPos < removePath.newPos) {
basePath = clonePath(removePath);
self.pushComponent(basePath.components, undefined, true);
} else {
basePath = addPath; // No need to clone, we've pulled it from the list
basePath.newPos++;
self.pushComponent(basePath.components, true, undefined);
}
_oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); // If we have hit the end of both strings, then we are done
if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) {
return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken));
} else {
// Otherwise track this path as a potential candidate and continue.
bestPath[diagonalPath] = basePath;
}
}
editLength++;
} // Performs the length of edit iteration. Is a bit fugly as this has to support the
// sync and async mode which is never fun. Loops over execEditLength until a value
// is produced.
if (callback) {
(function exec() {
setTimeout(function () {
// This should not happen, but we want to be safe.
/* istanbul ignore next */
if (editLength > maxEditLength) {
return callback();
}
if (!execEditLength()) {
exec();
}
}, 0);
})();
} else {
while (editLength <= maxEditLength) {
var ret = execEditLength();
if (ret) {
return ret;
}
}
}
},
pushComponent: function pushComponent(components, added, removed) {
var last = components[components.length - 1];
if (last && last.added === added && last.removed === removed) {
// We need to clone here as the component clone operation is just
// as shallow array clone
components[components.length - 1] = {
count: last.count + 1,
added: added,
removed: removed
};
} else {
components.push({
count: 1,
added: added,
removed: removed
});
}
},
extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) {
var newLen = newString.length,
oldLen = oldString.length,
newPos = basePath.newPos,
oldPos = newPos - diagonalPath,
commonCount = 0;
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {
newPos++;
oldPos++;
commonCount++;
}
if (commonCount) {
basePath.components.push({
count: commonCount
});
}
basePath.newPos = newPos;
return oldPos;
},
equals: function equals(left, right) {
if (this.options.comparator) {
return this.options.comparator(left, right);
} else {
return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase();
}
},
removeEmpty: function removeEmpty(array) {
var ret = [];
for (var i = 0; i < array.length; i++) {
if (array[i]) {
ret.push(array[i]);
}
}
return ret;
},
castInput: function castInput(value) {
return value;
},
tokenize: function tokenize(value) {
return value.split('');
},
join: function join(chars) {
return chars.join('');
}
};
function buildValues(diff, components, newString, oldString, useLongestToken) {
var componentPos = 0,
componentLen = components.length,
newPos = 0,
oldPos = 0;
for (; componentPos < componentLen; componentPos++) {
var component = components[componentPos];
if (!component.removed) {
if (!component.added && useLongestToken) {
var value = newString.slice(newPos, newPos + component.count);
value = value.map(function (value, i) {
var oldValue = oldString[oldPos + i];
return oldValue.length > value.length ? oldValue : value;
});
component.value = diff.join(value);
} else {
component.value = diff.join(newString.slice(newPos, newPos + component.count));
}
newPos += component.count; // Common case
if (!component.added) {
oldPos += component.count;
}
} else {
component.value = diff.join(oldString.slice(oldPos, oldPos + component.count));
oldPos += component.count; // Reverse add and remove so removes are output first to match common convention
// The diffing algorithm is tied to add then remove output and this is the simplest
// route to get the desired output with minimal overhead.
if (componentPos && components[componentPos - 1].added) {
var tmp = components[componentPos - 1];
components[componentPos - 1] = components[componentPos];
components[componentPos] = tmp;
}
}
} // Special case handle for when one terminal is ignored (i.e. whitespace).
// For this case we merge the terminal into the prior string and drop the change.
// This is only available for string mode.
var lastComponent = components[componentLen - 1];
if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) {
components[componentLen - 2].value += lastComponent.value;
components.pop();
}
return components;
}
function clonePath(path) {
return {
newPos: path.newPos,
components: path.components.slice(0)
};
}
var characterDiff = new Diff();
function diffChars(oldStr, newStr, options) {
return characterDiff.diff(oldStr, newStr, options);
}
function generateOptions(options, defaults) {
if (typeof options === 'function') {
defaults.callback = options;
} else if (options) {
for (var name in options) {
/* istanbul ignore else */
if (options.hasOwnProperty(name)) {
defaults[name] = options[name];
}
}
}
return defaults;
}
//
// Ranges and exceptions:
// Latin-1 Supplement, 0080–00FF
// - U+00D7 × Multiplication sign
// - U+00F7 ÷ Division sign
// Latin Extended-A, 0100–017F
// Latin Extended-B, 0180–024F
// IPA Extensions, 0250–02AF
// Spacing Modifier Letters, 02B0–02FF
// - U+02C7 ˇ ˇ Caron
// - U+02D8 ˘ ˘ Breve
// - U+02D9 ˙ ˙ Dot Above
// - U+02DA ˚ ˚ Ring Above
// - U+02DB ˛ ˛ Ogonek
// - U+02DC ˜ ˜ Small Tilde
// - U+02DD ˝ ˝ Double Acute Accent
// Latin Extended Additional, 1E00–1EFF
var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/;
var reWhitespace = /\S/;
var wordDiff = new Diff();
wordDiff.equals = function (left, right) {
if (this.options.ignoreCase) {
left = left.toLowerCase();
right = right.toLowerCase();
}
return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
};
wordDiff.tokenize = function (value) {
var tokens = value.split(/(\s+|[()[\]{}'"]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set.
for (var i = 0; i < tokens.length - 1; i++) {
// If we have an empty string in the next field and we have only word chars before and after, merge
if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) {
tokens[i] += tokens[i + 2];
tokens.splice(i + 1, 2);
i--;
}
}
return tokens;
};
function diffWords(oldStr, newStr, options) {
options = generateOptions(options, {
ignoreWhitespace: true
});
return wordDiff.diff(oldStr, newStr, options);
}
function diffWordsWithSpace(oldStr, newStr, options) {
return wordDiff.diff(oldStr, newStr, options);
}
var lineDiff = new Diff();
lineDiff.tokenize = function (value) {
var retLines = [],
linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
linesAndNewlines.pop();
} // Merge the content and line separators into single tokens
for (var i = 0; i < linesAndNewlines.length; i++) {
var line = linesAndNewlines[i];
if (i % 2 && !this.options.newlineIsToken) {
retLines[retLines.length - 1] += line;
} else {
if (this.options.ignoreWhitespace) {
line = line.trim();
}
retLines.push(line);
}
}
return retLines;
};
function diffLines(oldStr, newStr, callback) {
return lineDiff.diff(oldStr, newStr, callback);
}
function diffTrimmedLines(oldStr, newStr, callback) {
var options = generateOptions(callback, {
ignoreWhitespace: true
});
return lineDiff.diff(oldStr, newStr, options);
}
var sentenceDiff = new Diff();
sentenceDiff.tokenize = function (value) {
return value.split(/(\S.+?[.!?])(?=\s+|$)/);
};
function diffSentences(oldStr, newStr, callback) {
return sentenceDiff.diff(oldStr, newStr, callback);
}
var cssDiff = new Diff();
cssDiff.tokenize = function (value) {
return value.split(/([{}:;,]|\s+)/);
};
function diffCss(oldStr, newStr, callback) {
return cssDiff.diff(oldStr, newStr, callback);
}
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
var objectPrototypeToString = Object.prototype.toString;
var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
// dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
jsonDiff.useLongestToken = true;
jsonDiff.tokenize = lineDiff.tokenize;
jsonDiff.castInput = function (value) {
var _this$options = this.options,
undefinedReplacement = _this$options.undefinedReplacement,
_this$options$stringi = _this$options.stringifyReplacer,
stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) {
return typeof v === 'undefined' ? undefinedReplacement : v;
} : _this$options$stringi;
return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' ');
};
jsonDiff.equals = function (left, right) {
return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
};
function diffJson(oldObj, newObj, options) {
return jsonDiff.diff(oldObj, newObj, options);
} // This function handles the presence of circular references by bailing out when encountering an
// object that is already on the "stack" of items being processed. Accepts an optional replacer
function canonicalize(obj, stack, replacementStack, replacer, key) {
stack = stack || [];
replacementStack = replacementStack || [];
if (replacer) {
obj = replacer(key, obj);
}
var i;
for (i = 0; i < stack.length; i += 1) {
if (stack[i] === obj) {
return replacementStack[i];
}
}
var canonicalizedObj;
if ('[object Array]' === objectPrototypeToString.call(obj)) {
stack.push(obj);
canonicalizedObj = new Array(obj.length);
replacementStack.push(canonicalizedObj);
for (i = 0; i < obj.length; i += 1) {
canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key);
}
stack.pop();
replacementStack.pop();
return canonicalizedObj;
}
if (obj && obj.toJSON) {
obj = obj.toJSON();
}
if (_typeof(obj) === 'object' && obj !== null) {
stack.push(obj);
canonicalizedObj = {};
replacementStack.push(canonicalizedObj);
var sortedKeys = [],
_key;
for (_key in obj) {
/* istanbul ignore else */
if (obj.hasOwnProperty(_key)) {
sortedKeys.push(_key);
}
}
sortedKeys.sort();
for (i = 0; i < sortedKeys.length; i += 1) {
_key = sortedKeys[i];
canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key);
}
stack.pop();
replacementStack.pop();
} else {
canonicalizedObj = obj;
}
return canonicalizedObj;
}
var arrayDiff = new Diff();
arrayDiff.tokenize = function (value) {
return value.slice();
};
arrayDiff.join = arrayDiff.removeEmpty = function (value) {
return value;
};
function diffArrays(oldArr, newArr, callback) {
return arrayDiff.diff(oldArr, newArr, callback);
}
function parsePatch(uniDiff) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/),
delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [],
list = [],
i = 0;
function parseIndex() {
var index = {};
list.push(index); // Parse diff metadata
while (i < diffstr.length) {
var line = diffstr[i]; // File header found, end parsing diff metadata
if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) {
break;
} // Diff index
var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line);
if (header) {
index.index = header[1];
}
i++;
} // Parse file headers if they are defined. Unified diff requires them, but
// there's no technical issues to have an isolated hunk without file header
parseFileHeader(index);
parseFileHeader(index); // Parse hunks
index.hunks = [];
while (i < diffstr.length) {
var _line = diffstr[i];
if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) {
break;
} else if (/^@@/.test(_line)) {
index.hunks.push(parseHunk());
} else if (_line && options.strict) {
// Ignore unexpected content unless in strict mode
throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line));
} else {
i++;
}
}
} // Parses the --- and +++ headers, if none are found, no lines
// are consumed.
function parseFileHeader(index) {
var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]);
if (fileHeader) {
var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
var data = fileHeader[2].split('\t', 2);
var fileName = data[0].replace(/\\\\/g, '\\');
if (/^".*"$/.test(fileName)) {
fileName = fileName.substr(1, fileName.length - 2);
}
index[keyPrefix + 'FileName'] = fileName;
index[keyPrefix + 'Header'] = (data[1] || '').trim();
i++;
}
} // Parses a hunk
// This assumes that we are at the start of a hunk.
function parseHunk() {
var chunkHeaderIndex = i,
chunkHeaderLine = diffstr[i++],
chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
var hunk = {
oldStart: +chunkHeader[1],
oldLines: +chunkHeader[2] || 1,
newStart: +chunkHeader[3],
newLines: +chunkHeader[4] || 1,
lines: [],
linedelimiters: []
};
var addCount = 0,
removeCount = 0;
for (; i < diffstr.length; i++) {
// Lines starting with '---' could be mistaken for the "remove line" operation
// But they could be the header for the next file. Therefore prune such cases out.
if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) {
break;
}
var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0];
if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
hunk.lines.push(diffstr[i]);
hunk.linedelimiters.push(delimiters[i] || '\n');
if (operation === '+') {
addCount++;
} else if (operation === '-') {
removeCount++;
} else if (operation === ' ') {
addCount++;
removeCount++;
}
} else {
break;
}
} // Handle the empty block count case
if (!addCount && hunk.newLines === 1) {
hunk.newLines = 0;
}
if (!removeCount && hunk.oldLines === 1) {
hunk.oldLines = 0;
} // Perform optional sanity checking
if (options.strict) {
if (addCount !== hunk.newLines) {
throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
}
if (removeCount !== hunk.oldLines) {
throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
}
}
return hunk;
}
while (i < diffstr.length) {
parseIndex();
}
return list;
}
// Iterator that traverses in the range of [min, max], stepping
// by distance from a given start position. I.e. for [0, 4], with
// start of 2, this will iterate 2, 3, 1, 4, 0.
function distanceIterator (start, minLine, maxLine) {
var wantForward = true,
backwardExhausted = false,
forwardExhausted = false,
localOffset = 1;
return function iterator() {
if (wantForward && !forwardExhausted) {
if (backwardExhausted) {
localOffset++;
} else {
wantForward = false;
} // Check if trying to fit beyond text length, and if not, check it fits
// after offset location (or desired location on first iteration)
if (start + localOffset <= maxLine) {
return localOffset;
}
forwardExhausted = true;
}
if (!backwardExhausted) {
if (!forwardExhausted) {
wantForward = true;
} // Check if trying to fit before text beginning, and if not, check it fits
// before offset location
if (minLine <= start - localOffset) {
return -localOffset++;
}
backwardExhausted = true;
return iterator();
} // We tried to fit hunk before text beginning and beyond text length, then
// hunk can't fit on the text. Return undefined
};
}
function applyPatch(source, uniDiff) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (typeof uniDiff === 'string') {
uniDiff = parsePatch(uniDiff);
}
if (Array.isArray(uniDiff)) {
if (uniDiff.length > 1) {
throw new Error('applyPatch only works with a single input.');
}
uniDiff = uniDiff[0];
} // Apply the diff to the input
var lines = source.split(/\r\n|[\n\v\f\r\x85]/),
delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [],
hunks = uniDiff.hunks,
compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) {
return line === patchContent;
},
errorCount = 0,
fuzzFactor = options.fuzzFactor || 0,
minLine = 0,
offset = 0,
removeEOFNL,
addEOFNL;
/**
* Checks if the hunk exactly fits on the provided location
*/
function hunkFits(hunk, toPos) {
for (var j = 0; j < hunk.lines.length; j++) {
var line = hunk.lines[j],
operation = line.length > 0 ? line[0] : ' ',
content = line.length > 0 ? line.substr(1) : line;
if (operation === ' ' || operation === '-') {
// Context sanity check
if (!compareLine(toPos + 1, lines[toPos], operation, content)) {
errorCount++;
if (errorCount > fuzzFactor) {
return false;
}
}
toPos++;
}
}
return true;
} // Search best fit offsets for each hunk based on the previous ones
for (var i = 0; i < hunks.length; i++) {
var hunk = hunks[i],
maxLine = lines.length - hunk.oldLines,
localOffset = 0,
toPos = offset + hunk.oldStart - 1;
var iterator = distanceIterator(toPos, minLine, maxLine);
for (; localOffset !== undefined; localOffset = iterator()) {
if (hunkFits(hunk, toPos + localOffset)) {
hunk.offset = offset += localOffset;
break;
}
}
if (localOffset === undefined) {
return false;
} // Set lower text limit to end of the current hunk, so next ones don't try
// to fit over already patched text
minLine = hunk.offset + hunk.oldStart + hunk.oldLines;
} // Apply patch hunks
var diffOffset = 0;
for (var _i = 0; _i < hunks.length; _i++) {
var _hunk = hunks[_i],
_toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1;
diffOffset += _hunk.newLines - _hunk.oldLines;
if (_toPos < 0) {
// Creating a new file
_toPos = 0;
}
for (var j = 0; j < _hunk.lines.length; j++) {
var line = _hunk.lines[j],
operation = line.length > 0 ? line[0] : ' ',
content = line.length > 0 ? line.substr(1) : line,
delimiter = _hunk.linedelimiters[j];
if (operation === ' ') {
_toPos++;
} else if (operation === '-') {
lines.splice(_toPos, 1);
delimiters.splice(_toPos, 1);
/* istanbul ignore else */
} else if (operation === '+') {
lines.splice(_toPos, 0, content);
delimiters.splice(_toPos, 0, delimiter);
_toPos++;
} else if (operation === '\\') {
var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null;
if (previousOperation === '+') {
removeEOFNL = true;
} else if (previousOperation === '-') {
addEOFNL = true;
}
}
}
} // Handle EOFNL insertion/removal
if (removeEOFNL) {
while (!lines[lines.length - 1]) {
lines.pop();
delimiters.pop();
}
} else if (addEOFNL) {
lines.push('');
delimiters.push('\n');
}
for (var _k = 0; _k < lines.length - 1; _k++) {
lines[_k] = lines[_k] + delimiters[_k];
}
return lines.join('');
} // Wrapper that supports multiple file patches via callbacks.
function applyPatches(uniDiff, options) {
if (typeof uniDiff === 'string') {
uniDiff = parsePatch(uniDiff);
}
var currentIndex = 0;
function processIndex() {
var index = uniDiff[currentIndex++];
if (!index) {
return options.complete();
}
options.loadFile(index, function (err, data) {
if (err) {
return options.complete(err);
}
var updatedContent = applyPatch(data, index, options);
options.patched(index, updatedContent, function (err) {
if (err) {
return options.complete(err);
}
processIndex();
});
});
}
processIndex();
}
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
if (!options) {
options = {};
}
if (typeof options.context === 'undefined') {
options.context = 4;
}
var diff = diffLines(oldStr, newStr, options);
diff.push({
value: '',
lines: []
}); // Append an empty value to make cleanup easier
function contextLines(lines) {
return lines.map(function (entry) {
return ' ' + entry;
});
}
var hunks = [];
var oldRangeStart = 0,
newRangeStart = 0,
curRange = [],
oldLine = 1,
newLine = 1;
var _loop = function _loop(i) {
var current = diff[i],
lines = current.lines || current.value.replace(/\n$/, '').split('\n');
current.lines = lines;
if (current.added || current.removed) {
var _curRange;
// If we have previous context, start with that
if (!oldRangeStart) {
var prev = diff[i - 1];
oldRangeStart = oldLine;
newRangeStart = newLine;
if (prev) {
curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
oldRangeStart -= curRange.length;
newRangeStart -= curRange.length;
}
} // Output our changes
(_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) {
return (current.added ? '+' : '-') + entry;
}))); // Track the updated file position
if (current.added) {
newLine += lines.length;
} else {
oldLine += lines.length;
}
} else {
// Identical context lines. Track line changes
if (oldRangeStart) {
// Close out any changes that have been output (or join overlapping)
if (lines.length <= options.context * 2 && i < diff.length - 2) {
var _curRange2;
// Overlapping
(_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines)));
} else {
var _curRange3;
// end the range and output
var contextSize = Math.min(lines.length, options.context);
(_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize))));
var hunk = {
oldStart: oldRangeStart,
oldLines: oldLine - oldRangeStart + contextSize,
newStart: newRangeStart,
newLines: newLine - newRangeStart + contextSize,
lines: curRange
};
if (i >= diff.length - 2 && lines.length <= options.context) {
// EOF is inside this hunk
var oldEOFNewline = /\n$/.test(oldStr);
var newEOFNewline = /\n$/.test(newStr);
var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines;
if (!oldEOFNewline && noNlBeforeAdds) {
// special case: old has no eol and no trailing context; no-nl can end up before adds
curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file');
}
if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) {
curRange.push('\\ No newline at end of file');
}
}
hunks.push(hunk);
oldRangeStart = 0;
newRangeStart = 0;
curRange = [];
}
}
oldLine += lines.length;
newLine += lines.length;
}
};
for (var i = 0; i < diff.length; i++) {
_loop(i);
}
return {
oldFileName: oldFileName,
newFileName: newFileName,
oldHeader: oldHeader,
newHeader: newHeader,
hunks: hunks
};
}
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
var ret = [];
if (oldFileName == newFileName) {
ret.push('Index: ' + oldFileName);
}
ret.push('===================================================================');
ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader));
ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader));
for (var i = 0; i < diff.hunks.length; i++) {
var hunk = diff.hunks[i];
ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@');
ret.push.apply(ret, hunk.lines);
}
return ret.join('\n') + '\n';
}
function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
}
function arrayEqual(a, b) {
if (a.length !== b.length) {
return false;
}
return arrayStartsWith(a, b);
}
function arrayStartsWith(array, start) {
if (start.length > array.length) {
return false;
}
for (var i = 0; i < start.length; i++) {
if (start[i] !== array[i]) {
return false;
}
}
return true;
}
function calcLineCount(hunk) {
var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines),
oldLines = _calcOldNewLineCount.oldLines,
newLines = _calcOldNewLineCount.newLines;
if (oldLines !== undefined) {
hunk.oldLines = oldLines;
} else {
delete hunk.oldLines;
}
if (newLines !== undefined) {
hunk.newLines = newLines;
} else {
delete hunk.newLines;
}
}
function merge(mine, theirs, base) {
mine = loadPatch(mine, base);
theirs = loadPatch(theirs, base);
var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning.
// Leaving sanity checks on this to the API consumer that may know more about the
// meaning in their own context.
if (mine.index || theirs.index) {
ret.index = mine.index || theirs.index;
}
if (mine.newFileName || theirs.newFileName) {
if (!fileNameChanged(mine)) {
// No header or no change in ours, use theirs (and ours if theirs does not exist)
ret.oldFileName = theirs.oldFileName || mine.oldFileName;
ret.newFileName = theirs.newFileName || mine.newFileName;
ret.oldHeader = theirs.oldHeader || mine.oldHeader;
ret.newHeader = theirs.newHeader || mine.newHeader;
} else if (!fileNameChanged(theirs)) {
// No header or no change in theirs, use ours
ret.oldFileName = mine.oldFileName;
ret.newFileName = mine.newFileName;
ret.oldHeader = mine.oldHeader;
ret.newHeader = mine.newHeader;
} else {
// Both changed... figure it out
ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName);
ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName);
ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader);
ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader);
}
}
ret.hunks = [];
var mineIndex = 0,
theirsIndex = 0,
mineOffset = 0,
theirsOffset = 0;
while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) {
var mineCurrent = mine.hunks[mineIndex] || {
oldStart: Infinity
},
theirsCurrent = theirs.hunks[theirsIndex] || {
oldStart: Infinity
};
if (hunkBefore(mineCurrent, theirsCurrent)) {
// This patch does not overlap with any of the others, yay.
ret.hunks.push(cloneHunk(mineCurrent, mineOffset));
mineIndex++;
theirsOffset += mineCurrent.newLines - mineCurrent.oldLines;
} else if (hunkBefore(theirsCurrent, mineCurrent)) {
// This patch does not overlap with any of the others, yay.
ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset));
theirsIndex++;
mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines;
} else {
// Overlap, merge as best we can
var mergedHunk = {
oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart),
oldLines: 0,
newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset),
newLines: 0,
lines: []
};
mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines);
theirsIndex++;
mineIndex++;
ret.hunks.push(mergedHunk);
}
}
return ret;
}
function loadPatch(param, base) {
if (typeof param === 'string') {
if (/^@@/m.test(param) || /^Index:/m.test(param)) {
return parsePatch(param)[0];
}
if (!base) {
throw new Error('Must provide a base reference or pass in a patch');
}
return structuredPatch(undefined, undefined, base, param);
}
return param;
}
function fileNameChanged(patch) {
return patch.newFileName && patch.newFileName !== patch.oldFileName;
}
function selectField(index, mine, theirs) {
if (mine === theirs) {
return mine;
} else {
index.conflict = true;
return {
mine: mine,
theirs: theirs
};
}
}
function hunkBefore(test, check) {
return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart;
}
function cloneHunk(hunk, offset) {
return {
oldStart: hunk.oldStart,
oldLines: hunk.oldLines,
newStart: hunk.newStart + offset,
newLines: hunk.newLines,
lines: hunk.lines
};
}
function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) {
// This will generally result in a conflicted hunk, but there are cases where the context
// is the only overlap where we can successfully merge the content here.
var mine = {
offset: mineOffset,
lines: mineLines,
index: 0
},
their = {
offset: theirOffset,
lines: theirLines,
index: 0
}; // Handle any leading content
insertLeading(hunk, mine, their);
insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each.
while (mine.index < mine.lines.length && their.index < their.lines.length) {
var mineCurrent = mine.lines[mine.index],
theirCurrent = their.lines[their.index];
if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) {
// Both modified ...
mutualChange(hunk, mine, their);
} else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') {
var _hunk$lines;
// Mine inserted
(_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine)));
} else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') {
var _hunk$lines2;
// Theirs inserted
(_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their)));
} else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') {
// Mine removed or edited
removal(hunk, mine, their);
} else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') {
// Their removed or edited
removal(hunk, their, mine, true);
} else if (mineCurrent === theirCurrent) {
// Context identity
hunk.lines.push(mineCurrent);
mine.index++;
their.index++;
} else {
// Context mismatch
conflict(hunk, collectChange(mine), collectChange(their));
}
} // Now push anything that may be remaining
insertTrailing(hunk, mine);
insertTrailing(hunk, their);
calcLineCount(hunk);
}
function mutualChange(hunk, mine, their) {
var myChanges = collectChange(mine),
theirChanges = collectChange(their);
if (allRemoves(myChanges) && allRemoves(theirChanges)) {
// Special case for remove changes that are supersets of one another
if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) {
var _hunk$lines3;
(_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges));
return;
} else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) {
var _hunk$lines4;
(_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges));
return;
}
} else if (arrayEqual(myChanges, theirChanges)) {
var _hunk$lines5;
(_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges));
return;
}
conflict(hunk, myChanges, theirChanges);
}
function removal(hunk, mine, their, swap) {
var myChanges = collectChange(mine),
theirChanges = collectContext(their, myChanges);
if (theirChanges.merged) {
var _hunk$lines6;
(_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged));
} else {
conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges);
}
}
function conflict(hunk, mine, their) {
hunk.conflict = true;
hunk.lines.push({
conflict: true,
mine: mine,
theirs: their
});
}
function insertLeading(hunk, insert, their) {
while (insert.offset < their.offset && insert.index < insert.lines.length) {
var line = insert.lines[insert.index++];
hunk.lines.push(line);
insert.offset++;
}
}
function insertTrailing(hunk, insert) {
while (insert.index < insert.lines.length) {
var line = insert.lines[insert.index++];
hunk.lines.push(line);
}
}
function collectChange(state) {
var ret = [],
operation = state.lines[state.index][0];
while (state.index < state.lines.length) {
var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change.
if (operation === '-' && line[0] === '+') {
operation = '+';
}
if (operation === line[0]) {
ret.push(line);
state.index++;
} else {
break;
}
}
return ret;
}
function collectContext(state, matchChanges) {
var changes = [],
merged = [],
matchIndex = 0,
contextChanges = false,
conflicted = false;
while (matchIndex < matchChanges.length && state.index < state.lines.length) {
var change = state.lines[state.index],
match = matchChanges[matchIndex]; // Once we've hit our add, then we are done
if (match[0] === '+') {
break;
}
contextChanges = contextChanges || change[0] !== ' ';
merged.push(match);
matchIndex++; // Consume any additions in the other block as a conflict to attempt
// to pull in the remaining context after this
if (change[0] === '+') {
conflicted = true;
while (change[0] === '+') {
changes.push(change);
change = state.lines[++state.index];
}
}
if (match.substr(1) === change.substr(1)) {
changes.push(change);
state.index++;
} else {
conflicted = true;
}
}
if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) {
conflicted = true;
}
if (conflicted) {
return changes;
}
while (matchIndex < matchChanges.length) {
merged.push(matchChanges[matchIndex++]);
}
return {
merged: merged,
changes: changes
};
}
function allRemoves(changes) {
return changes.reduce(function (prev, change) {
return prev && change[0] === '-';
}, true);
}
function skipRemoveSuperset(state, removeChanges, delta) {
for (var i = 0; i < delta; i++) {
var changeContent = removeChanges[removeChanges.length - delta + i].substr(1);
if (state.lines[state.index + i] !== ' ' + changeContent) {
return false;
}
}
state.index += delta;
return true;
}
function calcOldNewLineCount(lines) {
var oldLines = 0;
var newLines = 0;
lines.forEach(function (line) {
if (typeof line !== 'string') {
var myCount = calcOldNewLineCount(line.mine);
var theirCount = calcOldNewLineCount(line.theirs);
if (oldLines !== undefined) {
if (myCount.oldLines === theirCount.oldLines) {
oldLines += myCount.oldLines;
} else {
oldLines = undefined;
}
}
if (newLines !== undefined) {
if (myCount.newLines === theirCount.newLines) {
newLines += myCount.newLines;
} else {
newLines = undefined;
}
}
} else {
if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) {
newLines++;
}
if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) {
oldLines++;
}
}
});
return {
oldLines: oldLines,
newLines: newLines
};
}
// See: http://code.google.com/p/google-diff-match-patch/wiki/API
function convertChangesToDMP(changes) {
var ret = [],
change,
operation;
for (var i = 0; i < changes.length; i++) {
change = changes[i];
if (change.added) {
operation = 1;
} else if (change.removed) {
operation = -1;
} else {
operation = 0;
}
ret.push([operation, change.value]);
}
return ret;
}
function convertChangesToXML(changes) {
var ret = [];
for (var i = 0; i < changes.length; i++) {
var change = changes[i];
if (change.added) {
ret.push('<ins>');
} else if (change.removed) {
ret.push('<del>');
}
ret.push(escapeHTML(change.value));
if (change.added) {
ret.push('</ins>');
} else if (change.removed) {
ret.push('</del>');
}
}
return ret.join('');
}
function escapeHTML(s) {
var n = s;
n = n.replace(/&/g, '&');
n = n.replace(/</g, '<');
n = n.replace(/>/g, '>');
n = n.replace(/"/g, '"');
return n;
}
/* See LICENSE file for terms of use */
exports.Diff = Diff;
exports.diffChars = diffChars;
exports.diffWords = diffWords;
exports.diffWordsWithSpace = diffWordsWithSpace;
exports.diffLines = diffLines;
exports.diffTrimmedLines = diffTrimmedLines;
exports.diffSentences = diffSentences;
exports.diffCss = diffCss;
exports.diffJson = diffJson;
exports.diffArrays = diffArrays;
exports.structuredPatch = structuredPatch;
exports.createTwoFilesPatch = createTwoFilesPatch;
exports.createPatch = createPatch;
exports.applyPatch = applyPatch;
exports.applyPatches = applyPatches;
exports.parsePatch = parsePatch;
exports.merge = merge;
exports.convertChangesToDMP = convertChangesToDMP;
exports.convertChangesToXML = convertChangesToXML;
exports.canonicalize = canonicalize;
Object.defineProperty(exports, '__esModule', { value: true });
}));
},{}],3:[function(require,module,exports){
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function (Hogan) {
// Setup regex assignments
// remove whitespace according to Mustache spec
var rIsWhitespace = /\S/,
rQuot = /\"/g,
rNewline = /\n/g,
rCr = /\r/g,
rSlash = /\\/g,
rLineSep = /\u2028/,
rParagraphSep = /\u2029/;
Hogan.tags = {
'#': 1, '^': 2, '<': 3, '$': 4,
'/': 5, '!': 6, '>': 7, '=': 8, '_v': 9,
'{': 10, '&': 11, '_t': 12
};
Hogan.scan = function scan(text, delimiters) {
var len = text.length,
IN_TEXT = 0,
IN_TAG_TYPE = 1,
IN_TAG = 2,
state = IN_TEXT,
tagType = null,
tag = null,
buf = '',
tokens = [],
seenTag = false,
i = 0,
lineStart = 0,
otag = '{{',
ctag = '}}';
function addBuf() {
if (buf.length > 0) {
tokens.push({tag: '_t', text: new String(buf)});
buf = '';
}
}
function lineIsWhitespace() {
var isAllWhitespace = true;
for (var j = lineStart; j < tokens.length; j++) {
isAllWhitespace =
(Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) ||
(tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null);
if (!isAllWhitespace) {
return false;
}
}
return isAllWhitespace;
}
function filterLine(haveSeenTag, noNewLine) {
addBuf();
if (haveSeenTag && lineIsWhitespace()) {
for (var j = lineStart, next; j < tokens.length; j++) {
if (tokens[j].text) {
if ((next = tokens[j+1]) && next.tag == '>') {
// set indent to token value
next.indent = tokens[j].text.toString()
}
tokens.splice(j, 1);
}
}
} else if (!noNewLine) {
tokens.push({tag:'\n'});
}
seenTag = false;
lineStart = tokens.length;
}
function changeDelimiters(text, index) {
var close = '=' + ctag,
closeIndex = text.indexOf(close, index),
delimiters = trim(
text.substring(text.indexOf('=', index) + 1, closeIndex)
).split(' ');
otag = delimiters[0];
ctag = delimiters[delimiters.length - 1];
return closeIndex + close.length - 1;
}
if (delimiters) {
delimiters = delimiters.split(' ');
otag = delimiters[0];
ctag = delimiters[1];
}
for (i = 0; i < len; i++) {
if (state == IN_TEXT) {
if (tagChange(otag, text, i)) {
--i;
addBuf();
state = IN_TAG_TYPE;
} else {
if (text.charAt(i) == '\n') {
filterLine(seenTag);
} else {
buf += text.charAt(i);
}
}
} else if (state == IN_TAG_TYPE) {
i += otag.length - 1;
tag = Hogan.tags[text.charAt(i + 1)];
tagType = tag ? text.charAt(i + 1) : '_v';
if (tagType == '=') {
i = changeDelimiters(text, i);
state = IN_TEXT;
} else {
if (tag) {
i++;
}
state = IN_TAG;
}
seenTag = i;
} else {
if (tagChange(ctag, text, i)) {
tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
i: (tagType == '/') ? seenTag - otag.length : i + ctag.length});
buf = '';
i += ctag.length - 1;
state = IN_TEXT;
if (tagType == '{') {
if (ctag == '}}') {
i++;
} else {
cleanTripleStache(tokens[tokens.length - 1]);
}
}
} else {
buf += text.charAt(i);
}
}
}
filterLine(seenTag, true);
return tokens;
}
function cleanTripleStache(token) {
if (token.n.substr(token.n.length - 1) === '}') {
token.n = token.n.substring(0, token.n.length - 1);
}
}
function trim(s) {
if (s.trim) {
return s.trim();
}
return s.replace(/^\s*|\s*$/g, '');
}
function tagChange(tag, text, index) {
if (text.charAt(index) != tag.charAt(0)) {
return false;
}
for (var i = 1, l = tag.length; i < l; i++) {
if (text.charAt(index + i) != tag.charAt(i)) {
return false;
}
}
return true;
}
// the tags allowed inside super templates
var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true};
function buildTree(tokens, kind, stack, customTags) {
var instructions = [],
opener = null,
tail = null,
token = null;
tail = stack[stack.length - 1];
while (tokens.length > 0) {
token = tokens.shift();
if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) {
throw new Error('Illegal content in < super tag.');
}
if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) {
stack.push(token);
token.nodes = buildTree(tokens, token.tag, stack, customTags);
} else if (token.tag == '/') {
if (stack.length === 0) {
throw new Error('Closing tag without opener: /' + token.n);
}
opener = stack.pop();
if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
}
opener.end = token.i;
return instructions;
} else if (token.tag == '\n') {
token.last = (tokens.length == 0) || (tokens[0].tag == '\n');
}
instructions.push(token);
}
if (stack.length > 0) {
throw new Error('missing closing tag: ' + stack.pop().n);
}
return instructions;
}
function isOpener(token, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].o == token.n) {
token.tag = '#';
return true;
}
}
}
function isCloser(close, open, tags) {
for (var i = 0, l = tags.length; i < l; i++) {
if (tags[i].c == close && tags[i].o == open) {
return true;
}
}
}
function stringifySubstitutions(obj) {
var items = [];
for (var key in obj) {
items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}');
}
return "{ " + items.join(",") + " }";
}
function stringifyPartials(codeObj) {
var partials = [];
for (var key in codeObj.partials) {
partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}");
}
return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs);
}
Hogan.stringify = function(codeObj, text, options) {
return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) + "}";
}
var serialNo = 0;
Hogan.generate = function(tree, text, options) {
serialNo = 0;
var context = { code: '', subs: {}, partials: {} };
Hogan.walk(tree, context);
if (options.asString) {
return this.stringify(context, text, options);
}
return this.makeTemplate(context, text, options);
}
Hogan.wrapMain = function(code) {
return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();';
}
Hogan.template = Hogan.Template;
Hogan.makeTemplate = function(codeObj, text, options) {
var template = this.makePartials(codeObj);
template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code));
return new this.template(template, text, this, options);
}
Hogan.makePartials = function(codeObj) {
var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name};
for (key in template.partials) {
template.partials[key] = this.makePartials(template.partials[key]);
}
for (key in codeObj.subs) {
template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]);
}
return template;
}
function esc(s) {
return s.replace(rSlash, '\\\\')
.replace(rQuot, '\\\"')
.replace(rNewline, '\\n')
.replace(rCr, '\\r')
.replace(rLineSep, '\\u2028')
.replace(rParagraphSep, '\\u2029');
}
function chooseMethod(s) {
return (~s.indexOf('.')) ? 'd' : 'f';
}
function createPartial(node, context) {
var prefix = "<" + (context.prefix || "");
var sym = prefix + node.n + serialNo++;
context.partials[sym] = {name: node.n, partials: {}};
context.code += 't.b(t.rp("' + esc(sym) + '",c,p,"' + (node.indent || '') + '"));';
return sym;
}
Hogan.codegen = {
'#': function(node, context) {
context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' +
'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' +
't.rs(c,p,' + 'function(c,p,t){';
Hogan.walk(node.nodes, context);
context.code += '});c.pop();}';
},
'^': function(node, context) {
context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){';
Hogan.walk(node.nodes, context);
context.code += '};';
},
'>': createPartial,
'<': function(node, context) {
var ctx = {partials: {}, code: '', subs: {}, inPartial: true};
Hogan.walk(node.nodes, ctx);
var template = context.partials[createPartial(node, context)];
template.subs = ctx.subs;
template.partials = ctx.partials;
},
'$': function(node, context) {
var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n};
Hogan.walk(node.nodes, ctx);
context.subs[node.n] = ctx.code;
if (!context.inPartial) {
context.code += 't.sub("' + esc(node.n) + '",c,p,i);';
}
},
'\n': function(node, context) {
context.code += write('"\\n"' + (node.last ? '' : ' + i'));
},
'_v': function(node, context) {
context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
},
'_t': function(node, context) {
context.code += write('"' + esc(node.text) + '"');
},
'{': tripleStache,
'&': tripleStache
}
function tripleStache(node, context) {
context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
}
function write(s) {
return 't.b(' + s + ');';
}
Hogan.walk = function(nodelist, context) {
var func;
for (var i = 0, l = nodelist.length; i < l; i++) {
func = Hogan.codegen[nodelist[i].tag];
func && func(nodelist[i], context);
}
return context;
}
Hogan.parse = function(tokens, text, options) {
options = options || {};
return buildTree(tokens, '', [], options.sectionTags || []);
}
Hogan.cache = {};
Hogan.cacheKey = function(text, options) {
return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||');
}
Hogan.compile = function(text, options) {
options = options || {};
var key = Hogan.cacheKey(text, options);
var template = this.cache[key];
if (template) {
var partials = template.partials;
for (var name in partials) {
delete partials[name].instance;
}
return template;
}
template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
return this.cache[key] = template;
}
})(typeof exports !== 'undefined' ? exports : Hogan);
},{}],4:[function(require,module,exports){
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This file is for use with Node.js. See dist/ for browser files.
var Hogan = require('./compiler');
Hogan.Template = require('./template').Template;
Hogan.template = Hogan.Template;
module.exports = Hogan;
},{"./compiler":3,"./template":5}],5:[function(require,module,exports){
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Hogan = {};
(function (Hogan) {
Hogan.Template = function (codeObj, text, compiler, options) {
codeObj = codeObj || {};
this.r = codeObj.code || this.r;
this.c = compiler;
this.options = options || {};
this.text = text || '';
this.partials = codeObj.partials || {};
this.subs = codeObj.subs || {};
this.buf = '';
}
Hogan.Template.prototype = {
// render: replaced by generated code.
r: function (context, partials, indent) { return ''; },
// variable escaping
v: hoganEscape,
// triple stache
t: coerceToString,
render: function render(context, partials, indent) {
return this.ri([context], partials || {}, indent);
},
// render internal -- a hook for overrides that catches partials too
ri: function (context, partials, indent) {
return this.r(context, partials, indent);
},
// ensurePartial
ep: function(symbol, partials) {
var partial = this.partials[symbol];
// check to see that if we've instantiated this partial before
var template = partials[partial.name];
if (partial.instance && partial.base == template) {
return partial.instance;
}
if (typeof template == 'string') {
if (!this.c) {
throw new Error("No compiler available.");
}
template = this.c.compile(template, this.options);
}
if (!template) {
return null;
}
// We use this to check whether the partials dictionary has changed
this.partials[symbol].base = template;
if (partial.subs) {
// Make sure we consider parent template now
if (!partials.stackText) partials.stackText = {};
for (key in partial.subs) {
if (!partials.stackText[key]) {
partials.stackText[key] = (this.activeSub !== undefined && partials.stackText[this.activeSub]) ? partials.stackText[this.activeSub] : this.text;
}
}
template = createSpecializedPartial(template, partial.subs, partial.partials,
this.stackSubs, this.stackPartials, partials.stackText);
}
this.partials[symbol].instance = template;
return template;
},
// tries to find a partial in the current scope and render it
rp: function(symbol, context, partials, indent) {
var partial = this.ep(symbol, partials);
if (!partial) {
return '';
}
return partial.ri(context, partials, indent);
},
// render a section
rs: function(context, partials, section) {
var tail = context[context.length - 1];
if (!isArray(tail)) {
section(context, partials, this);
return;
}
for (var i = 0; i < tail.length; i++) {
context.push(tail[i]);
section(context, partials, this);
context.pop();
}
},
// maybe start a section
s: function(val, ctx, partials, inverted, start, end, tags) {
var pass;
if (isArray(val) && val.length === 0) {
return false;
}
if (typeof val == 'function') {
val = this.ms(val, ctx, partials, inverted, start, end, tags);
}
pass = !!val;
if (!inverted && pass && ctx) {
ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
}
return pass;
},
// find values with dotted names
d: function(key, ctx, partials, returnFound) {
var found,
names = key.split('.'),
val = this.f(names[0], ctx, partials, returnFound),
doModelGet = this.options.modelGet,
cx = null;
if (key === '.' && isArray(ctx[ctx.length - 2])) {
val = ctx[ctx.length - 1];
} else {
for (var i = 1; i < names.length; i++) {
found = findInScope(names[i], val, doModelGet);
if (found !== undefined) {
cx = val;
val = found;
} else {
val = '';
}
}
}
if (returnFound && !val) {
return false;
}
if (!returnFound && typeof val == 'function') {
ctx.push(cx);
val = this.mv(val, ctx, partials);
ctx.pop();
}
return val;
},
// find values with normal names
f: function(key, ctx, partials, returnFound) {
var val = false,
v = null,
found = false,
doModelGet = this.options.modelGet;
for (var i = ctx.length - 1; i >= 0; i--) {
v = ctx[i];
val = findInScope(key, v, doModelGet);
if (val !== undefined) {
found = true;
break;
}
}
if (!found) {
return (returnFound) ? false : "";
}
if (!returnFound && typeof val == 'function') {
val = this.mv(val, ctx, partials);
}
return val;
},
// higher order templates
ls: function(func, cx, partials, text, tags) {
var oldTags = this.options.delimiters;
this.options.delimiters = tags;
this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials));
this.options.delimiters = oldTags;
return false;
},
// compile text
ct: function(text, cx, partials) {
if (this.options.disableLambda) {
throw new Error('Lambda features disabled.');
}
return this.c.compile(text, this.options).render(cx, partials);
},
// template result buffering
b: function(s) { this.buf += s; },
fl: function() { var r = this.buf; this.buf = ''; return r; },
// method replace section
ms: function(func, ctx, partials, inverted, start, end, tags) {
var textSource,
cx = ctx[ctx.length - 1],
result = func.call(cx);
if (typeof result == 'function') {
if (inverted) {
return true;
} else {
textSource = (this.activeSub && this.subsText && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text;
return this.ls(result, cx, partials, textSource.substring(start, end), tags);
}
}
return result;
},
// method replace variable
mv: function(func, ctx, partials) {
var cx = ctx[ctx.length - 1];
var result = func.call(cx);
if (typeof result == 'function') {
return this.ct(coerceToString(result.call(cx)), cx, partials);
}
return result;
},
sub: function(name, context, partials, indent) {
var f = this.subs[name];
if (f) {
this.activeSub = name;
f(context, partials, this, indent);
this.activeSub = false;
}
}
};
//Find a key in an object
function findInScope(key, scope, doModelGet) {
var val;
if (scope && typeof scope == 'object') {
if (scope[key] !== undefined) {
val = scope[key];
// try lookup with get for backbone or similar model data
} else if (doModelGet && scope.get && typeof scope.get == 'function') {
val = scope.get(key);
}
}
return val;
}
function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, stackText) {
function PartialTemplate() {};
PartialTemplate.prototype = instance;
function Substitutions() {};
Substitutions.prototype = instance.subs;
var key;
var partial = new PartialTemplate();
partial.subs = new Substitutions();
partial.subsText = {}; //hehe. substext.
partial.buf = '';
stackSubs = stackSubs || {};
partial.stackSubs = stackSubs;
partial.subsText = stackText;
for (key in subs) {
if (!stackSubs[key]) stackSubs[key] = subs[key];
}
for (key in stackSubs) {
partial.subs[key] = stackSubs[key];
}
stackPartials = stackPartials || {};
partial.stackPartials = stackPartials;
for (key in partials) {
if (!stackPartials[key]) stackPartials[key] = partials[key];
}
for (key in stackPartials) {
partial.partials[key] = stackPartials[key];
}
return partial;
}
var rAmp = /&/g,
rLt = /</g,
rGt = />/g,
rApos = /\'/g,
rQuot = /\"/g,
hChars = /[&<>\"\']/;
function coerceToString(val) {
return String((val === null || val === undefined) ? '' : val);
}
function hoganEscape(str) {
str = coerceToString(str);
return hChars.test(str) ?
str
.replace(rAmp, '&')
.replace(rLt, '<')
.replace(rGt, '>')
.replace(rApos, ''')
.replace(rQuot, '"') :
str;
}
var isArray = Array.isArray || function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
};
})(typeof exports !== 'undefined' ? exports : Hogan);
},{}],6:[function(require,module,exports){
/*!
* @name JavaScript/NodeJS Merge v1.2.1
* @author yeikos
* @repository https://github.com/yeikos/js.merge
* Copyright 2014 yeikos - MIT license
* https://raw.github.com/yeikos/js.merge/master/LICENSE
*/
;(function(isNode) {
/**
* Merge one or more objects
* @param bool? clone
* @param mixed,... arguments
* @return object
*/
var Public = function(clone) {
return merge(clone === true, false, arguments);
}, publicName = 'merge';
/**
* Merge two or more objects recursively
* @param bool? clone
* @param mixed,... arguments
* @return object
*/
Public.recursive = function(clone) {
return merge(clone === true, true, arguments);
};
/**
* Clone the input removing any reference
* @param mixed input
* @return mixed
*/
Public.clone = function(input) {
var output = input,
type = typeOf(input),
index, size;
if (type === 'array') {
output = [];
size = input.length;
for (index=0;index<size;++index)
output[index] = Public.clone(input[index]);
} else if (type === 'object') {
output = {};
for (index in input)
output[index] = Public.clone(input[index]);
}
return output;
};
/**
* Merge two objects recursively
* @param mixed input
* @param mixed extend
* @return mixed
*/
function merge_recursive(base, extend) {
if (typeOf(base) !== 'object')
return extend;
for (var key in extend) {
if (typeOf(base[key]) === 'object' && typeOf(extend[key]) === 'object') {
base[key] = merge_recursive(base[key], extend[key]);
} else {
base[key] = extend[key];
}
}
return base;
}
/**
* Merge two or more objects
* @param bool clone
* @param bool recursive
* @param array argv
* @return object
*/
function merge(clone, recursive, argv) {
var result = argv[0],
size = argv.length;
if (clone || typeOf(result) !== 'object')
result = {};
for (var index=0;index<size;++index) {
var item = argv[index],
type = typeOf(item);
if (type !== 'object') continue;
for (var key in item) {
if (key === '__proto__') continue;
var sitem = clone ? Public.clone(item[key]) : item[key];
if (recursive) {
result[key] = merge_recursive(result[key], sitem);
} else {
result[key] = sitem;
}
}
}
return result;
}
/**
* Get type of variable
* @param mixed input
* @return string
*
* @see http://jsperf.com/typeofvar
*/
function typeOf(input) {
return ({}).toString.call(input).slice(8, -1).toLowerCase();
}
if (isNode) {
module.exports = Public;
} else {
window[publicName] = Public;
}
})(typeof module === 'object' && module && typeof module.exports === 'object' && module.exports);
},{}],7:[function(require,module,exports){
(function (process){
// .dirname, .basename, and .extname methods are extracted from Node.js v8.11.1,
// backported and transplited with Babel, with backwards-compat fixes
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = parts.length - 1; i >= 0; i--) {
var last = parts[i];
if (last === '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : process.cwd();
// Skip empty and invalid entries
if (typeof path !== 'string') {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = exports.isAbsolute(path),
trailingSlash = substr(path, -1) === '/';
// Normalize the path
path = normalizeArray(filter(path.split('/'), function(p) {
return !!p;
}), !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
exports.join = function() {
var paths = Array.prototype.slice.call(arguments, 0);
return exports.normalize(filter(paths, function(p, index) {
if (typeof p !== 'string') {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
}).join('/'));
};
// path.relative(from, to)
// posix version
exports.relative = function(from, to) {
from = exports.resolve(from).substr(1);
to = exports.resolve(to).substr(1);
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end - start + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.sep = '/';
exports.delimiter = ':';
exports.dirname = function (path) {
if (typeof path !== 'string') path = path + '';
if (path.length === 0) return '.';
var code = path.charCodeAt(0);
var hasRoot = code === 47 /*/*/;
var end = -1;
var matchedSlash = true;
for (var i = path.length - 1; i >= 1; --i) {
code = path.charCodeAt(i);
if (code === 47 /*/*/) {
if (!matchedSlash) {
end = i;
break;
}
} else {
// We saw the first non-path separator
matchedSlash = false;
}
}
if (end === -1) return hasRoot ? '/' : '.';
if (hasRoot && end === 1) {
// return '//';
// Backwards-compat fix:
return '/';
}
return path.slice(0, end);
};
function basename(path) {
if (typeof path !== 'string') path = path + '';
var start = 0;
var end = -1;
var matchedSlash = true;
var i;
for (i = path.length - 1; i >= 0; --i) {
if (path.charCodeAt(i) === 47 /*/*/) {
// If we reached a path separator that was not part of a set of path
// separators at the end of the string, stop now
if (!matchedSlash) {
start = i + 1;
break;
}
} else if (end === -1) {
// We saw the first non-path separator, mark this as the end of our
// path component
matchedSlash = false;
end = i + 1;
}
}
if (end === -1) return '';
return path.slice(start, end);
}
// Uses a mixed approach for backwards-compatibility, as ext behavior changed
// in new Node.js versions, so only basename() above is backported here
exports.basename = function (path, ext) {
var f = basename(path);
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function (path) {
if (typeof path !== 'string') path = path + '';
var startDot = -1;
var startPart = 0;
var end = -1;
var matchedSlash = true;
// Track the state of characters (if any) we see before our first dot and
// after any path separator we find
var preDotState = 0;
for (var i = path.length - 1; i >= 0; --i) {
var code = path.charCodeAt(i);
if (code === 47 /*/*/) {
// If we reached a path separator that was not part of a set of path
// separators at the end of the string, stop now
if (!matchedSlash) {
startPart = i + 1;
break;
}
continue;
}
if (end === -1) {
// We saw the first non-path separator, mark this as the end of our
// extension
matchedSlash = false;
end = i + 1;
}
if (code === 46 /*.*/) {
// If this is our first dot, mark it as the start of our extension
if (startDot === -1)
startDot = i;
else if (preDotState !== 1)
preDotState = 1;
} else if (startDot !== -1) {
// We saw a non-dot and non-path separator before our dot, so we should
// have a good chance at having a non-empty extension
preDotState = -1;
}
}
if (startDot === -1 || end === -1 ||
// We saw a non-dot character immediately before the dot
preDotState === 0 ||
// The (right-most) trimmed path component is exactly '..'
preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
return '';
}
return path.slice(startDot, end);
};
function filter (xs, f) {
if (xs.filter) return xs.filter(f);
var res = [];
for (var i = 0; i < xs.length; i++) {
if (f(xs[i], i, xs)) res.push(xs[i]);
}
return res;
}
// String.prototype.substr - negative index don't work in IE8
var substr = 'ab'.substr(-1) === 'b'
? function (str, start, len) { return str.substr(start, len) }
: function (str, start, len) {
if (start < 0) start = str.length + start;
return str.substr(start, len);
}
;
}).call(this,require('_process'))
},{"_process":8}],8:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
// if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch(e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch(e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
}
// if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e){
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e){
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) { return [] }
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],9:[function(require,module,exports){
/*
*
* Diff Parser (diff-parser.js)
* Author: rtfpessoa
*
*/
(function() {
var utils = require('./utils.js').Utils;
var LINE_TYPE = {
INSERTS: 'd2h-ins',
DELETES: 'd2h-del',
INSERT_CHANGES: 'd2h-ins d2h-change',
DELETE_CHANGES: 'd2h-del d2h-change',
CONTEXT: 'd2h-cntx',
INFO: 'd2h-info'
};
function DiffParser() {
}
DiffParser.prototype.LINE_TYPE = LINE_TYPE;
DiffParser.prototype.generateDiffJson = function(diffInput, configuration) {
var config = configuration || {};
var files = [];
var currentFile = null;
var currentBlock = null;
var oldLine = null;
var oldLine2 = null; // Used for combined diff
var newLine = null;
var possibleOldName;
var possibleNewName;
/* Diff Header */
var oldFileNameHeader = '--- ';
var newFileNameHeader = '+++ ';
var hunkHeaderPrefix = '@@';
/* Add previous block(if exists) before start a new file */
function saveBlock() {
if (currentBlock) {
currentFile.blocks.push(currentBlock);
currentBlock = null;
}
}
/*
* Add previous file(if exists) before start a new one
* if it has name (to avoid binary files errors)
*/
function saveFile() {
if (currentFile) {
if (!currentFile.oldName) {
currentFile.oldName = possibleOldName;
}
if (!currentFile.newName) {
currentFile.newName = possibleNewName;
}
if (currentFile.newName) {
files.push(currentFile);
currentFile = null;
}
}
possibleOldName = undefined;
possibleNewName = undefined;
}
/* Create file structure */
function startFile() {
saveBlock();
saveFile();
currentFile = {};
currentFile.blocks = [];
currentFile.deletedLines = 0;
currentFile.addedLines = 0;
}
function startBlock(line) {
saveBlock();
var values;
/**
* From Range:
* -<start line>[,<number of lines>]
*
* To Range:
* +<start line>[,<number of lines>]
*
* @@ from-file-range to-file-range @@
*
* @@@ from-file-range from-file-range to-file-range @@@
*
* number of lines is optional, if omited consider 0
*/
if ((values = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@.*/.exec(line))) {
currentFile.isCombined = false;
oldLine = values[1];
newLine = values[2];
} else if ((values = /^@@@ -(\d+)(?:,\d+)? -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@@.*/.exec(line))) {
currentFile.isCombined = true;
oldLine = values[1];
oldLine2 = values[2];
newLine = values[3];
} else {
if (utils.startsWith(line, hunkHeaderPrefix)) {
console.error('Failed to parse lines, starting in 0!');
}
oldLine = 0;
newLine = 0;
currentFile.isCombined = false;
}
/* Create block metadata */
currentBlock = {};
currentBlock.lines = [];
currentBlock.oldStartLine = oldLine;
currentBlock.oldStartLine2 = oldLine2;
currentBlock.newStartLine = newLine;
currentBlock.header = line;
}
function createLine(line) {
var currentLine = {};
currentLine.content = line;
var newLinePrefixes = !currentFile.isCombined ? ['+'] : ['+', ' +'];
var delLinePrefixes = !currentFile.isCombined ? ['-'] : ['-', ' -'];
/* Fill the line data */
if (utils.startsWith(line, newLinePrefixes)) {
currentFile.addedLines++;
currentLine.type = LINE_TYPE.INSERTS;
currentLine.oldNumber = null;
currentLine.newNumber = newLine++;
currentBlock.lines.push(currentLine);
} else if (utils.startsWith(line, delLinePrefixes)) {
currentFile.deletedLines++;
currentLine.type = LINE_TYPE.DELETES;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = null;
currentBlock.lines.push(currentLine);
} else {
currentLine.type = LINE_TYPE.CONTEXT;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = newLine++;
currentBlock.lines.push(currentLine);
}
}
/*
* Checks if there is a hunk header coming before a new file starts
*
* Hunk header is a group of three lines started by ( `--- ` , `+++ ` , `@@` )
*/
function existHunkHeader(line, lineIdx) {
var idx = lineIdx;
while (idx < diffLines.length - 3) {
if (utils.startsWith(line, 'diff')) {
return false;
}
if (
utils.startsWith(diffLines[idx], oldFileNameHeader) &&
utils.startsWith(diffLines[idx + 1], newFileNameHeader) &&
utils.startsWith(diffLines[idx + 2], hunkHeaderPrefix)
) {
return true;
}
idx++;
}
return false;
}
var diffLines =
diffInput.replace(/\\ No newline at end of file/g, '')
.replace(/\r\n?/g, '\n')
.split('\n');
/* Diff */
var oldMode = /^old mode (\d{6})/;
var newMode = /^new mode (\d{6})/;
var deletedFileMode = /^deleted file mode (\d{6})/;
var newFileMode = /^new file mode (\d{6})/;
var copyFrom = /^copy from "?(.+)"?/;
var copyTo = /^copy to "?(.+)"?/;
var renameFrom = /^rename from "?(.+)"?/;
var renameTo = /^rename to "?(.+)"?/;
var similarityIndex = /^similarity index (\d+)%/;
var dissimilarityIndex = /^dissimilarity index (\d+)%/;
var index = /^index ([0-9a-z]+)\.\.([0-9a-z]+)\s*(\d{6})?/;
var binaryFiles = /^Binary files (.*) and (.*) differ/;
var binaryDiff = /^GIT binary patch/;
/* Combined Diff */
var combinedIndex = /^index ([0-9a-z]+),([0-9a-z]+)\.\.([0-9a-z]+)/;
var combinedMode = /^mode (\d{6}),(\d{6})\.\.(\d{6})/;
var combinedNewFile = /^new file mode (\d{6})/;
var combinedDeletedFile = /^deleted file mode (\d{6}),(\d{6})/;
diffLines.forEach(function(line, lineIndex) {
// Unmerged paths, and possibly other non-diffable files
// https://github.com/scottgonzalez/pretty-diff/issues/11
// Also, remove some useless lines
if (!line || utils.startsWith(line, '*')) {
return;
}
// Used to store regex capture groups
var values;
var prevLine = diffLines[lineIndex - 1];
var nxtLine = diffLines[lineIndex + 1];
var afterNxtLine = diffLines[lineIndex + 2];
if (utils.startsWith(line, 'diff')) {
startFile();
// diff --git a/blocked_delta_results.png b/blocked_delta_results.png
var gitDiffStart = /^diff --git "?(.+)"? "?(.+)"?/;
if ((values = gitDiffStart.exec(line))) {
possibleOldName = _getFilename(null, values[1], config.dstPrefix);
possibleNewName = _getFilename(null, values[2], config.srcPrefix);
}
currentFile.isGitDiff = true;
return;
}
if (!currentFile || // If we do not have a file yet, we should crete one
(
!currentFile.isGitDiff && currentFile && // If we already have some file in progress and
(
utils.startsWith(line, oldFileNameHeader) && // If we get to an old file path header line
// And is followed by the new file path header line and the hunk header line
utils.startsWith(nxtLine, newFileNameHeader) && utils.startsWith(afterNxtLine, hunkHeaderPrefix)
)
)
) {
startFile();
}
/*
* We need to make sure that we have the three lines of the header.
* This avoids cases like the ones described in:
* - https://github.com/rtfpessoa/diff2html/issues/87
*/
if (
(utils.startsWith(line, oldFileNameHeader) &&
utils.startsWith(nxtLine, newFileNameHeader)) ||
(utils.startsWith(line, newFileNameHeader) &&
utils.startsWith(prevLine, oldFileNameHeader))
) {
/*
* --- Date Timestamp[FractionalSeconds] TimeZone
* --- 2002-02-21 23:30:39.942229878 -0800
*/
if (currentFile && !currentFile.oldName &&
utils.startsWith(line, '--- ') && (values = getSrcFilename(line, config))) {
currentFile.oldName = values;
currentFile.language = getExtension(currentFile.oldName, currentFile.language);
return;
}
/*
* +++ Date Timestamp[FractionalSeconds] TimeZone
* +++ 2002-02-21 23:30:39.942229878 -0800
*/
if (currentFile && !currentFile.newName &&
utils.startsWith(line, '+++ ') && (values = getDstFilename(line, config))) {
currentFile.newName = values;
currentFile.language = getExtension(currentFile.newName, currentFile.language);
return;
}
}
if (
(currentFile && utils.startsWith(line, hunkHeaderPrefix)) ||
(currentFile.isGitDiff && currentFile && currentFile.oldName && currentFile.newName && !currentBlock)
) {
startBlock(line);
return;
}
/*
* There are three types of diff lines. These lines are defined by the way they start.
* 1. New line starts with: +
* 2. Old line starts with: -
* 3. Context line starts with: <SPACE>
*/
if (currentBlock && (utils.startsWith(line, '+') || utils.startsWith(line, '-') || utils.startsWith(line, ' '))) {
createLine(line);
return;
}
var doesNotExistHunkHeader = !existHunkHeader(line, lineIndex);
/*
* Git diffs provide more information regarding files modes, renames, copies,
* commits between changes and similarity indexes
*/
if ((values = oldMode.exec(line))) {
currentFile.oldMode = values[1];
} else if ((values = newMode.exec(line))) {
currentFile.newMode = values[1];
} else if ((values = deletedFileMode.exec(line))) {
currentFile.deletedFileMode = values[1];
currentFile.isDeleted = true;
} else if ((values = newFileMode.exec(line))) {
currentFile.newFileMode = values[1];
currentFile.isNew = true;
} else if ((values = copyFrom.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.oldName = values[1];
}
currentFile.isCopy = true;
} else if ((values = copyTo.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.newName = values[1];
}
currentFile.isCopy = true;
} else if ((values = renameFrom.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.oldName = values[1];
}
currentFile.isRename = true;
} else if ((values = renameTo.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.newName = values[1];
}
currentFile.isRename = true;
} else if ((values = binaryFiles.exec(line))) {
currentFile.isBinary = true;
currentFile.oldName = _getFilename(null, values[1], config.srcPrefix);
currentFile.newName = _getFilename(null, values[2], config.dstPrefix);
startBlock('Binary file');
} else if ((values = binaryDiff.exec(line))) {
currentFile.isBinary = true;
startBlock(line);
} else if ((values = similarityIndex.exec(line))) {
currentFile.unchangedPercentage = values[1];
} else if ((values = dissimilarityIndex.exec(line))) {
currentFile.changedPercentage = values[1];
} else if ((values = index.exec(line))) {
currentFile.checksumBefore = values[1];
currentFile.checksumAfter = values[2];
values[3] && (currentFile.mode = values[3]);
} else if ((values = combinedIndex.exec(line))) {
currentFile.checksumBefore = [values[2], values[3]];
currentFile.checksumAfter = values[1];
} else if ((values = combinedMode.exec(line))) {
currentFile.oldMode = [values[2], values[3]];
currentFile.newMode = values[1];
} else if ((values = combinedNewFile.exec(line))) {
currentFile.newFileMode = values[1];
currentFile.isNew = true;
} else if ((values = combinedDeletedFile.exec(line))) {
currentFile.deletedFileMode = values[1];
currentFile.isDeleted = true;
}
});
saveBlock();
saveFile();
return files;
};
function getExtension(filename, language) {
var nameSplit = filename.split('.');
if (nameSplit.length > 1) {
return nameSplit[nameSplit.length - 1];
}
return language;
}
function getSrcFilename(line, cfg) {
return _getFilename('---', line, cfg.srcPrefix);
}
function getDstFilename(line, cfg) {
return _getFilename('\\+\\+\\+', line, cfg.dstPrefix);
}
function _getFilename(linePrefix, line, extraPrefix) {
var prefixes = ['a/', 'b/', 'i/', 'w/', 'c/', 'o/'];
if (extraPrefix) {
prefixes.push(extraPrefix);
}
var FilenameRegExp;
if (linePrefix) {
FilenameRegExp = new RegExp('^' + linePrefix + ' "?(.+?)"?$');
} else {
FilenameRegExp = new RegExp('^"?(.+?)"?$');
}
var filename;
var values = FilenameRegExp.exec(line);
if (values && values[1]) {
filename = values[1];
var matchingPrefixes = prefixes.filter(function(p) {
return filename.indexOf(p) === 0;
});
if (matchingPrefixes[0]) {
// Remove prefix if exists
filename = filename.slice(matchingPrefixes[0].length);
}
// Cleanup timestamps generated by the unified diff (diff command) as specified in
// https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
// Ie: 2016-10-25 11:37:14.000000000 +0200
filename = filename.replace(/\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)? [-+]\d{4}.*$/, '');
}
return filename;
}
module.exports.DiffParser = new DiffParser();
})();
},{"./utils.js":19}],10:[function(require,module,exports){
(function (global){
/*
*
* Diff to HTML (diff2html.js)
* Author: rtfpessoa
*
*/
(function() {
var diffParser = require('./diff-parser.js').DiffParser;
var htmlPrinter = require('./html-printer.js').HtmlPrinter;
var utils = require('./utils.js').Utils;
function Diff2Html() {
}
var defaultConfig = {
inputFormat: 'diff',
outputFormat: 'line-by-line',
showFiles: false,
diffStyle: 'word',
matching: 'none',
matchWordsThreshold: 0.25,
matchingMaxComparisons: 2500,
maxLineSizeInBlockForComparison: 200,
maxLineLengthHighlight: 10000,
templates: {},
rawTemplates: {},
renderNothingWhenEmpty: false
};
/*
* Generates json object from string diff input
*/
Diff2Html.prototype.getJsonFromDiff = function(diffInput, config) {
var cfg = utils.safeConfig(config, defaultConfig);
return diffParser.generateDiffJson(diffInput, cfg);
};
/*
* Generates the html diff. The config parameter configures the output/input formats and other options
*/
Diff2Html.prototype.getPrettyHtml = function(diffInput, config) {
var cfg = utils.safeConfig(config, defaultConfig);
var diffJson = diffInput;
if (!cfg.inputFormat || cfg.inputFormat === 'diff') {
diffJson = diffParser.generateDiffJson(diffInput, cfg);
}
var fileList = '';
if (cfg.showFiles === true) {
fileList = htmlPrinter.generateFileListSummary(diffJson, cfg);
}
var diffOutput = '';
if (cfg.outputFormat === 'side-by-side') {
diffOutput = htmlPrinter.generateSideBySideJsonHtml(diffJson, cfg);
} else {
diffOutput = htmlPrinter.generateLineByLineJsonHtml(diffJson, cfg);
}
return fileList + diffOutput;
};
/*
* Deprecated methods - The following methods exist only to maintain compatibility with previous versions
*/
/*
* Generates pretty html from string diff input
*/
Diff2Html.prototype.getPrettyHtmlFromDiff = function(diffInput, config) {
var cfg = utils.safeConfig(config, defaultConfig);
cfg.inputFormat = 'diff';
cfg.outputFormat = 'line-by-line';
return this.getPrettyHtml(diffInput, cfg);
};
/*
* Generates pretty html from a json object
*/
Diff2Html.prototype.getPrettyHtmlFromJson = function(diffJson, config) {
var cfg = utils.safeConfig(config, defaultConfig);
cfg.inputFormat = 'json';
cfg.outputFormat = 'line-by-line';
return this.getPrettyHtml(diffJson, cfg);
};
/*
* Generates pretty side by side html from string diff input
*/
Diff2Html.prototype.getPrettySideBySideHtmlFromDiff = function(diffInput, config) {
var cfg = utils.safeConfig(config, defaultConfig);
cfg.inputFormat = 'diff';
cfg.outputFormat = 'side-by-side';
return this.getPrettyHtml(diffInput, cfg);
};
/*
* Generates pretty side by side html from a json object
*/
Diff2Html.prototype.getPrettySideBySideHtmlFromJson = function(diffJson, config) {
var cfg = utils.safeConfig(config, defaultConfig);
cfg.inputFormat = 'json';
cfg.outputFormat = 'side-by-side';
return this.getPrettyHtml(diffJson, cfg);
};
var diffObject = new Diff2Html();
module.exports.Diff2Html = diffObject;
// Expose diff2html in the browser
global.Diff2Html = diffObject;
})();
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./diff-parser.js":9,"./html-printer.js":13,"./utils.js":19}],11:[function(require,module,exports){
/*
*
* FileListPrinter (file-list-printer.js)
* Author: nmatpt
*
*/
(function() {
var printerUtils = require('./printer-utils.js').PrinterUtils;
var hoganUtils;
var baseTemplatesPath = 'file-summary';
var iconsBaseTemplatesPath = 'icon';
function FileListPrinter(config) {
this.config = config;
var HoganJsUtils = require('./hoganjs-utils.js').HoganJsUtils;
hoganUtils = new HoganJsUtils(config);
}
FileListPrinter.prototype.generateFileList = function(diffFiles) {
var lineTemplate = hoganUtils.template(baseTemplatesPath, 'line');
var files = diffFiles.map(function(file) {
var fileTypeName = printerUtils.getFileTypeIcon(file);
var iconTemplate = hoganUtils.template(iconsBaseTemplatesPath, fileTypeName);
return lineTemplate.render({
fileHtmlId: printerUtils.getHtmlId(file),
oldName: file.oldName,
newName: file.newName,
fileName: printerUtils.getDiffName(file),
deletedLines: '-' + file.deletedLines,
addedLines: '+' + file.addedLines
}, {
fileIcon: iconTemplate
});
}).join('\n');
return hoganUtils.render(baseTemplatesPath, 'wrapper', {
filesNumber: diffFiles.length,
files: files
});
};
module.exports.FileListPrinter = FileListPrinter;
})();
},{"./hoganjs-utils.js":12,"./printer-utils.js":15}],12:[function(require,module,exports){
(function (__dirname){
/*
*
* Utils (hoganjs-utils.js)
* Author: rtfpessoa
*
*/
(function() {
var fs = require('fs');
var path = require('path');
var hogan = require('hogan.js');
var hoganTemplates = require('./templates/diff2html-templates.js');
var extraTemplates;
function HoganJsUtils(configuration) {
this.config = configuration || {};
extraTemplates = this.config.templates || {};
var rawTemplates = this.config.rawTemplates || {};
for (var templateName in rawTemplates) {
if (rawTemplates.hasOwnProperty(templateName)) {
if (!extraTemplates[templateName]) extraTemplates[templateName] = this.compile(rawTemplates[templateName]);
}
}
}
HoganJsUtils.prototype.render = function(namespace, view, params) {
var template = this.template(namespace, view);
if (template) {
return template.render(params);
}
return null;
};
HoganJsUtils.prototype.template = function(namespace, view) {
var templateKey = this._templateKey(namespace, view);
return this._getTemplate(templateKey);
};
HoganJsUtils.prototype._getTemplate = function(templateKey) {
var template;
if (!this.config.noCache) {
template = this._readFromCache(templateKey);
}
if (!template) {
template = this._loadTemplate(templateKey);
}
return template;
};
HoganJsUtils.prototype._loadTemplate = function(templateKey) {
var template;
try {
if (fs.readFileSync) {
var templatesPath = path.resolve(__dirname, 'templates');
var templatePath = path.join(templatesPath, templateKey);
var templateContent = fs.readFileSync(templatePath + '.mustache', 'utf8');
template = hogan.compile(templateContent);
hoganTemplates[templateKey] = template;
}
} catch (e) {
console.error('Failed to read (template: ' + templateKey + ') from fs: ' + e.message);
}
return template;
};
HoganJsUtils.prototype._readFromCache = function(templateKey) {
return extraTemplates[templateKey] || hoganTemplates[templateKey];
};
HoganJsUtils.prototype._templateKey = function(namespace, view) {
return namespace + '-' + view;
};
HoganJsUtils.prototype.compile = function(templateStr) {
return hogan.compile(templateStr);
};
module.exports.HoganJsUtils = HoganJsUtils;
})();
}).call(this,"/src")
},{"./templates/diff2html-templates.js":18,"fs":1,"hogan.js":4,"path":7}],13:[function(require,module,exports){
/*
*
* HtmlPrinter (html-printer.js)
* Author: rtfpessoa
*
*/
(function() {
var LineByLinePrinter = require('./line-by-line-printer.js').LineByLinePrinter;
var SideBySidePrinter = require('./side-by-side-printer.js').SideBySidePrinter;
var FileListPrinter = require('./file-list-printer.js').FileListPrinter;
function HtmlPrinter() {
}
HtmlPrinter.prototype.generateLineByLineJsonHtml = function(diffFiles, config) {
var lineByLinePrinter = new LineByLinePrinter(config);
return lineByLinePrinter.generateLineByLineJsonHtml(diffFiles);
};
HtmlPrinter.prototype.generateSideBySideJsonHtml = function(diffFiles, config) {
var sideBySidePrinter = new SideBySidePrinter(config);
return sideBySidePrinter.generateSideBySideJsonHtml(diffFiles);
};
HtmlPrinter.prototype.generateFileListSummary = function(diffJson, config) {
var fileListPrinter = new FileListPrinter(config);
return fileListPrinter.generateFileList(diffJson);
};
module.exports.HtmlPrinter = new HtmlPrinter();
})();
},{"./file-list-printer.js":11,"./line-by-line-printer.js":14,"./side-by-side-printer.js":17}],14:[function(require,module,exports){
/*
*
* LineByLinePrinter (line-by-line-printer.js)
* Author: rtfpessoa
*
*/
(function() {
var diffParser = require('./diff-parser.js').DiffParser;
var printerUtils = require('./printer-utils.js').PrinterUtils;
var utils = require('./utils.js').Utils;
var Rematch = require('./rematch.js').Rematch;
var hoganUtils;
var genericTemplatesPath = 'generic';
var baseTemplatesPath = 'line-by-line';
var iconsBaseTemplatesPath = 'icon';
var tagsBaseTemplatesPath = 'tag';
function LineByLinePrinter(config) {
this.config = config;
var HoganJsUtils = require('./hoganjs-utils.js').HoganJsUtils;
hoganUtils = new HoganJsUtils(config);
}
LineByLinePrinter.prototype.makeFileDiffHtml = function(file, diffs) {
if (this.config.renderNothingWhenEmpty && file.blocks && !file.blocks.length) return '';
var fileDiffTemplate = hoganUtils.template(baseTemplatesPath, 'file-diff');
var filePathTemplate = hoganUtils.template(genericTemplatesPath, 'file-path');
var fileIconTemplate = hoganUtils.template(iconsBaseTemplatesPath, 'file');
var fileTagTemplate = hoganUtils.template(tagsBaseTemplatesPath, printerUtils.getFileTypeIcon(file));
return fileDiffTemplate.render({
file: file,
fileHtmlId: printerUtils.getHtmlId(file),
diffs: diffs,
filePath: filePathTemplate.render({
fileDiffName: printerUtils.getDiffName(file)
}, {
fileIcon: fileIconTemplate,
fileTag: fileTagTemplate
})
});
};
LineByLinePrinter.prototype.makeLineByLineHtmlWrapper = function(content) {
return hoganUtils.render(genericTemplatesPath, 'wrapper', {'content': content});
};
LineByLinePrinter.prototype.generateLineByLineJsonHtml = function(diffFiles) {
var that = this;
var htmlDiffs = diffFiles.map(function(file) {
var diffs;
if (file.blocks.length) {
diffs = that._generateFileHtml(file);
} else {
diffs = that._generateEmptyDiff();
}
return that.makeFileDiffHtml(file, diffs);
});
return this.makeLineByLineHtmlWrapper(htmlDiffs.join('\n'));
};
var matcher = Rematch.rematch(function(a, b) {
var amod = a.content.substr(1);
var bmod = b.content.substr(1);
return Rematch.distance(amod, bmod);
});
LineByLinePrinter.prototype.makeColumnLineNumberHtml = function(block) {
return hoganUtils.render(genericTemplatesPath, 'column-line-number', {
diffParser: diffParser,
blockHeader: utils.escape(block.header),
lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line'
});
};
LineByLinePrinter.prototype._generateFileHtml = function(file) {
var that = this;
return file.blocks.map(function(block) {
var lines = that.makeColumnLineNumberHtml(block);
var oldLines = [];
var newLines = [];
function processChangeBlock() {
var matches;
var insertType;
var deleteType;
var comparisons = oldLines.length * newLines.length;
var maxLineSizeInBlock = Math.max.apply(null,
[0].concat((oldLines.concat(newLines)).map(
function(elem) {
return elem.content.length;
}
)));
var doMatching = comparisons < that.config.matchingMaxComparisons &&
maxLineSizeInBlock < that.config.maxLineSizeInBlockForComparison &&
(that.config.matching === 'lines' || that.config.matching === 'words');
if (doMatching) {
matches = matcher(oldLines, newLines);
insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
} else {
matches = [[oldLines, newLines]];
insertType = diffParser.LINE_TYPE.INSERTS;
deleteType = diffParser.LINE_TYPE.DELETES;
}
matches.forEach(function(match) {
oldLines = match[0];
newLines = match[1];
var processedOldLines = [];
var processedNewLines = [];
var common = Math.min(oldLines.length, newLines.length);
var oldLine, newLine;
for (var j = 0; j < common; j++) {
oldLine = oldLines[j];
newLine = newLines[j];
that.config.isCombined = file.isCombined;
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);
processedOldLines +=
that.makeLineHtml(file.isCombined, deleteType, oldLine.oldNumber, oldLine.newNumber,
diff.first.line, diff.first.prefix);
processedNewLines +=
that.makeLineHtml(file.isCombined, insertType, newLine.oldNumber, newLine.newNumber,
diff.second.line, diff.second.prefix);
}
lines += processedOldLines + processedNewLines;
lines += that._processLines(file.isCombined, oldLines.slice(common), newLines.slice(common));
});
oldLines = [];
newLines = [];
}
for (var i = 0; i < block.lines.length; i++) {
var line = block.lines[i];
var escapedLine = utils.escape(line.content);
if (line.type !== diffParser.LINE_TYPE.INSERTS &&
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
processChangeBlock();
}
if (line.type === diffParser.LINE_TYPE.CONTEXT) {
lines += that.makeLineHtml(file.isCombined, line.type, line.oldNumber, line.newNumber, escapedLine);
} else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
lines += that.makeLineHtml(file.isCombined, line.type, line.oldNumber, line.newNumber, escapedLine);
} else if (line.type === diffParser.LINE_TYPE.DELETES) {
oldLines.push(line);
} else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
newLines.push(line);
} else {
console.error('Unknown state in html line-by-line generator');
processChangeBlock();
}
}
processChangeBlock();
return lines;
}).join('\n');
};
LineByLinePrinter.prototype._processLines = function(isCombined, oldLines, newLines) {
var lines = '';
for (var i = 0; i < oldLines.length; i++) {
var oldLine = oldLines[i];
var oldEscapedLine = utils.escape(oldLine.content);
lines += this.makeLineHtml(isCombined, oldLine.type, oldLine.oldNumber, oldLine.newNumber, oldEscapedLine);
}
for (var j = 0; j < newLines.length; j++) {
var newLine = newLines[j];
var newEscapedLine = utils.escape(newLine.content);
lines += this.makeLineHtml(isCombined, newLine.type, newLine.oldNumber, newLine.newNumber, newEscapedLine);
}
return lines;
};
LineByLinePrinter.prototype.makeLineHtml = function(isCombined, type, oldNumber, newNumber, content, possiblePrefix) {
var lineNumberTemplate = hoganUtils.render(baseTemplatesPath, 'numbers', {
oldNumber: utils.valueOrEmpty(oldNumber),
newNumber: utils.valueOrEmpty(newNumber)
});
var lineWithoutPrefix = content;
var prefix = possiblePrefix;
if (!prefix) {
var lineWithPrefix = printerUtils.separatePrefix(isCombined, content);
prefix = lineWithPrefix.prefix;
lineWithoutPrefix = lineWithPrefix.line;
}
if (prefix === ' ') {
prefix = ' ';
}
return hoganUtils.render(genericTemplatesPath, 'line',
{
type: type,
lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line',
prefix: prefix,
content: lineWithoutPrefix,
lineNumber: lineNumberTemplate
});
};
LineByLinePrinter.prototype._generateEmptyDiff = function() {
return hoganUtils.render(genericTemplatesPath, 'empty-diff', {
contentClass: 'd2h-code-line',
diffParser: diffParser
});
};
module.exports.LineByLinePrinter = LineByLinePrinter;
})();
},{"./diff-parser.js":9,"./hoganjs-utils.js":12,"./printer-utils.js":15,"./rematch.js":16,"./utils.js":19}],15:[function(require,module,exports){
/*
*
* PrinterUtils (printer-utils.js)
* Author: rtfpessoa
*
*/
(function() {
var jsDiff = require('diff');
var utils = require('./utils.js').Utils;
var Rematch = require('./rematch.js').Rematch;
var separator = '/';
function PrinterUtils() {
}
PrinterUtils.prototype.separatePrefix = function(isCombined, line) {
var prefix;
var lineWithoutPrefix;
if (isCombined) {
prefix = line.substring(0, 2);
lineWithoutPrefix = line.substring(2);
} else {
prefix = line.substring(0, 1);
lineWithoutPrefix = line.substring(1);
}
return {
'prefix': prefix,
'line': lineWithoutPrefix
};
};
PrinterUtils.prototype.getHtmlId = function(file) {
var hashCode = function(text) {
var i, chr, len;
var hash = 0;
for (i = 0, len = text.length; i < len; i++) {
chr = text.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
return 'd2h-' + hashCode(this.getDiffName(file)).toString().slice(-6);
};
PrinterUtils.prototype.getDiffName = function(file) {
var oldFilename = unifyPath(file.oldName);
var newFilename = unifyPath(file.newName);
if (oldFilename && newFilename && oldFilename !== newFilename && !isDevNullName(oldFilename) && !isDevNullName(newFilename)) {
var prefixPaths = [];
var suffixPaths = [];
var oldFilenameParts = oldFilename.split(separator);
var newFilenameParts = newFilename.split(separator);
var oldFilenamePartsSize = oldFilenameParts.length;
var newFilenamePartsSize = newFilenameParts.length;
var i = 0;
var j = oldFilenamePartsSize - 1;
var k = newFilenamePartsSize - 1;
while (i < j && i < k) {
if (oldFilenameParts[i] === newFilenameParts[i]) {
prefixPaths.push(newFilenameParts[i]);
i += 1;
} else {
break;
}
}
while (j > i && k > i) {
if (oldFilenameParts[j] === newFilenameParts[k]) {
suffixPaths.unshift(newFilenameParts[k]);
j -= 1;
k -= 1;
} else {
break;
}
}
var finalPrefix = prefixPaths.join(separator);
var finalSuffix = suffixPaths.join(separator);
var oldRemainingPath = oldFilenameParts.slice(i, j + 1).join(separator);
var newRemainingPath = newFilenameParts.slice(i, k + 1).join(separator);
if (finalPrefix.length && finalSuffix.length) {
return finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix;
} else if (finalPrefix.length) {
return finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}';
} else if (finalSuffix.length) {
return '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix;
}
return oldFilename + ' → ' + newFilename;
} else if (newFilename && !isDevNullName(newFilename)) {
return newFilename;
} else if (oldFilename) {
return oldFilename;
}
return 'unknown/file/path';
};
PrinterUtils.prototype.getFileTypeIcon = function(file) {
var templateName = 'file-changed';
if (file.isRename) {
templateName = 'file-renamed';
} else if (file.isCopy) {
templateName = 'file-renamed';
} else if (file.isNew) {
templateName = 'file-added';
} else if (file.isDeleted) {
templateName = 'file-deleted';
} else if (file.newName !== file.oldName) {
// If file is not Added, not Deleted and the names changed it must be a rename :)
templateName = 'file-renamed';
}
return templateName;
};
PrinterUtils.prototype.diffHighlight = function(diffLine1, diffLine2, config) {
var linePrefix1, linePrefix2, unprefixedLine1, unprefixedLine2;
var prefixSize = 1;
if (config.isCombined) {
prefixSize = 2;
}
linePrefix1 = diffLine1.substr(0, prefixSize);
linePrefix2 = diffLine2.substr(0, prefixSize);
unprefixedLine1 = diffLine1.substr(prefixSize);
unprefixedLine2 = diffLine2.substr(prefixSize);
if (unprefixedLine1.length > config.maxLineLengthHighlight ||
unprefixedLine2.length > config.maxLineLengthHighlight) {
return {
first: {
prefix: linePrefix1,
line: utils.escape(unprefixedLine1)
},
second: {
prefix: linePrefix2,
line: utils.escape(unprefixedLine2)
}
};
}
var diff;
if (config.diffStyle === 'char') {
diff = jsDiff.diffChars(unprefixedLine1, unprefixedLine2);
} else {
diff = jsDiff.diffWordsWithSpace(unprefixedLine1, unprefixedLine2);
}
var highlightedLine = '';
var changedWords = [];
if (config.diffStyle === 'word' && config.matching === 'words') {
var treshold = 0.25;
if (typeof (config.matchWordsThreshold) !== 'undefined') {
treshold = config.matchWordsThreshold;
}
var matcher = Rematch.rematch(function(a, b) {
var amod = a.value;
var bmod = b.value;
return Rematch.distance(amod, bmod);
});
var removed = diff.filter(function isRemoved(element) {
return element.removed;
});
var added = diff.filter(function isAdded(element) {
return element.added;
});
var chunks = matcher(added, removed);
chunks.forEach(function(chunk) {
if (chunk[0].length === 1 && chunk[1].length === 1) {
var dist = Rematch.distance(chunk[0][0].value, chunk[1][0].value);
if (dist < treshold) {
changedWords.push(chunk[0][0]);
changedWords.push(chunk[1][0]);
}
}
});
}
diff.forEach(function(part) {
var addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
var elemType = part.added ? 'ins' : part.removed ? 'del' : null;
var escapedValue = utils.escape(part.value);
if (elemType !== null) {
highlightedLine += '<' + elemType + addClass + '>' + escapedValue + '</' + elemType + '>';
} else {
highlightedLine += escapedValue;
}
});
return {
first: {
prefix: linePrefix1,
line: removeIns(highlightedLine)
},
second: {
prefix: linePrefix2,
line: removeDel(highlightedLine)
}
};
};
function unifyPath(path) {
if (path) {
return path.replace('\\', '/');
}
return path;
}
function isDevNullName(name) {
return name.indexOf('dev/null') !== -1;
}
function removeIns(line) {
return line.replace(/(<ins[^>]*>((.|\n)*?)<\/ins>)/g, '');
}
function removeDel(line) {
return line.replace(/(<del[^>]*>((.|\n)*?)<\/del>)/g, '');
}
module.exports.PrinterUtils = new PrinterUtils();
})();
},{"./rematch.js":16,"./utils.js":19,"diff":2}],16:[function(require,module,exports){
/*
*
* Rematch (rematch.js)
* Matching two sequences of objects by similarity
* Author: W. Illmeyer, Nexxar GmbH
*
*/
(function() {
var Rematch = {};
/*
Copyright (c) 2011 Andrei Mackenzie
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function levenshtein(a, b) {
if (a.length === 0) {
return b.length;
}
if (b.length === 0) {
return a.length;
}
var matrix = [];
// Increment along the first column of each row
var i;
for (i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
// Increment each column in the first row
var j;
for (j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
// Fill in the rest of the matrix
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // Substitution
Math.min(matrix[i][j - 1] + 1, // Insertion
matrix[i - 1][j] + 1)); // Deletion
}
}
}
return matrix[b.length][a.length];
}
Rematch.levenshtein = levenshtein;
Rematch.distance = function distance(x, y) {
x = x.trim();
y = y.trim();
var lev = levenshtein(x, y);
var score = lev / (x.length + y.length);
return score;
};
Rematch.rematch = function rematch(distanceFunction) {
function findBestMatch(a, b, cache) {
var bestMatchDist = Infinity;
var bestMatch;
for (var i = 0; i < a.length; ++i) {
for (var j = 0; j < b.length; ++j) {
var cacheKey = JSON.stringify([a[i], b[j]]);
var md;
if (cache.hasOwnProperty(cacheKey)) {
md = cache[cacheKey];
} else {
md = distanceFunction(a[i], b[j]);
cache[cacheKey] = md;
}
if (md < bestMatchDist) {
bestMatchDist = md;
bestMatch = {indexA: i, indexB: j, score: bestMatchDist};
}
}
}
return bestMatch;
}
function group(a, b, level, cache) {
if (typeof (cache) === 'undefined') {
cache = {};
}
var bm = findBestMatch(a, b, cache);
if (!level) {
level = 0;
}
if (!bm || (a.length + b.length < 3)) {
return [[a, b]];
}
var a1 = a.slice(0, bm.indexA);
var b1 = b.slice(0, bm.indexB);
var aMatch = [a[bm.indexA]];
var bMatch = [b[bm.indexB]];
var tailA = bm.indexA + 1;
var tailB = bm.indexB + 1;
var a2 = a.slice(tailA);
var b2 = b.slice(tailB);
var group1 = group(a1, b1, level + 1, cache);
var groupMatch = group(aMatch, bMatch, level + 1, cache);
var group2 = group(a2, b2, level + 1, cache);
var result = groupMatch;
if (bm.indexA > 0 || bm.indexB > 0) {
result = group1.concat(result);
}
if (a.length > tailA || b.length > tailB) {
result = result.concat(group2);
}
return result;
}
return group;
};
module.exports.Rematch = Rematch;
})();
},{}],17:[function(require,module,exports){
/*
*
* HtmlPrinter (html-printer.js)
* Author: rtfpessoa
*
*/
(function() {
var diffParser = require('./diff-parser.js').DiffParser;
var printerUtils = require('./printer-utils.js').PrinterUtils;
var utils = require('./utils.js').Utils;
var Rematch = require('./rematch.js').Rematch;
var hoganUtils;
var genericTemplatesPath = 'generic';
var baseTemplatesPath = 'side-by-side';
var iconsBaseTemplatesPath = 'icon';
var tagsBaseTemplatesPath = 'tag';
var matcher = Rematch.rematch(function(a, b) {
var amod = a.content.substr(1);
var bmod = b.content.substr(1);
return Rematch.distance(amod, bmod);
});
function SideBySidePrinter(config) {
this.config = config;
var HoganJsUtils = require('./hoganjs-utils.js').HoganJsUtils;
hoganUtils = new HoganJsUtils(config);
}
SideBySidePrinter.prototype.makeDiffHtml = function(file, diffs) {
var fileDiffTemplate = hoganUtils.template(baseTemplatesPath, 'file-diff');
var filePathTemplate = hoganUtils.template(genericTemplatesPath, 'file-path');
var fileIconTemplate = hoganUtils.template(iconsBaseTemplatesPath, 'file');
var fileTagTemplate = hoganUtils.template(tagsBaseTemplatesPath, printerUtils.getFileTypeIcon(file));
return fileDiffTemplate.render({
file: file,
fileHtmlId: printerUtils.getHtmlId(file),
diffs: diffs,
filePath: filePathTemplate.render({
fileDiffName: printerUtils.getDiffName(file)
}, {
fileIcon: fileIconTemplate,
fileTag: fileTagTemplate
})
});
};
SideBySidePrinter.prototype.generateSideBySideJsonHtml = function(diffFiles) {
var that = this;
var content = diffFiles.map(function(file) {
var diffs;
if (file.blocks.length) {
diffs = that.generateSideBySideFileHtml(file);
} else {
diffs = that.generateEmptyDiff();
}
return that.makeDiffHtml(file, diffs);
}).join('\n');
return hoganUtils.render(genericTemplatesPath, 'wrapper', {'content': content});
};
SideBySidePrinter.prototype.makeSideHtml = function(blockHeader) {
return hoganUtils.render(genericTemplatesPath, 'column-line-number', {
diffParser: diffParser,
blockHeader: utils.escape(blockHeader),
lineClass: 'd2h-code-side-linenumber',
contentClass: 'd2h-code-side-line'
});
};
SideBySidePrinter.prototype.generateSideBySideFileHtml = function(file) {
var that = this;
var fileHtml = {};
fileHtml.left = '';
fileHtml.right = '';
file.blocks.forEach(function(block) {
fileHtml.left += that.makeSideHtml(block.header);
fileHtml.right += that.makeSideHtml('');
var oldLines = [];
var newLines = [];
function processChangeBlock() {
var matches;
var insertType;
var deleteType;
var comparisons = oldLines.length * newLines.length;
var maxLineSizeInBlock = Math.max.apply(null, (oldLines.concat(newLines)).map(function(elem) {
return elem.length;
}));
var doMatching = comparisons < that.config.matchingMaxComparisons &&
maxLineSizeInBlock < that.config.maxLineSizeInBlockForComparison &&
(that.config.matching === 'lines' || that.config.matching === 'words');
if (doMatching) {
matches = matcher(oldLines, newLines);
insertType = diffParser.LINE_TYPE.INSERT_CHANGES;
deleteType = diffParser.LINE_TYPE.DELETE_CHANGES;
} else {
matches = [[oldLines, newLines]];
insertType = diffParser.LINE_TYPE.INSERTS;
deleteType = diffParser.LINE_TYPE.DELETES;
}
matches.forEach(function(match) {
oldLines = match[0];
newLines = match[1];
var common = Math.min(oldLines.length, newLines.length);
var max = Math.max(oldLines.length, newLines.length);
for (var j = 0; j < common; j++) {
var oldLine = oldLines[j];
var newLine = newLines[j];
that.config.isCombined = file.isCombined;
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, that.config);
fileHtml.left +=
that.generateSingleLineHtml(file.isCombined, deleteType, oldLine.oldNumber,
diff.first.line, diff.first.prefix);
fileHtml.right +=
that.generateSingleLineHtml(file.isCombined, insertType, newLine.newNumber,
diff.second.line, diff.second.prefix);
}
if (max > common) {
var oldSlice = oldLines.slice(common);
var newSlice = newLines.slice(common);
var tmpHtml = that.processLines(file.isCombined, oldSlice, newSlice);
fileHtml.left += tmpHtml.left;
fileHtml.right += tmpHtml.right;
}
});
oldLines = [];
newLines = [];
}
for (var i = 0; i < block.lines.length; i++) {
var line = block.lines[i];
var prefix = line.content[0];
var escapedLine = utils.escape(line.content.substr(1));
if (line.type !== diffParser.LINE_TYPE.INSERTS &&
(newLines.length > 0 || (line.type !== diffParser.LINE_TYPE.DELETES && oldLines.length > 0))) {
processChangeBlock();
}
if (line.type === diffParser.LINE_TYPE.CONTEXT) {
fileHtml.left += that.generateSingleLineHtml(file.isCombined, line.type, line.oldNumber, escapedLine, prefix);
fileHtml.right += that.generateSingleLineHtml(file.isCombined, line.type, line.newNumber, escapedLine, prefix);
} else if (line.type === diffParser.LINE_TYPE.INSERTS && !oldLines.length) {
fileHtml.left += that.generateSingleLineHtml(file.isCombined, diffParser.LINE_TYPE.CONTEXT, '', '', '');
fileHtml.right += that.generateSingleLineHtml(file.isCombined, line.type, line.newNumber, escapedLine, prefix);
} else if (line.type === diffParser.LINE_TYPE.DELETES) {
oldLines.push(line);
} else if (line.type === diffParser.LINE_TYPE.INSERTS && Boolean(oldLines.length)) {
newLines.push(line);
} else {
console.error('unknown state in html side-by-side generator');
processChangeBlock();
}
}
processChangeBlock();
});
return fileHtml;
};
SideBySidePrinter.prototype.processLines = function(isCombined, oldLines, newLines) {
var that = this;
var fileHtml = {};
fileHtml.left = '';
fileHtml.right = '';
var maxLinesNumber = Math.max(oldLines.length, newLines.length);
for (var i = 0; i < maxLinesNumber; i++) {
var oldLine = oldLines[i];
var newLine = newLines[i];
var oldContent;
var newContent;
var oldPrefix;
var newPrefix;
if (oldLine) {
oldContent = utils.escape(oldLine.content.substr(1));
oldPrefix = oldLine.content[0];
}
if (newLine) {
newContent = utils.escape(newLine.content.substr(1));
newPrefix = newLine.content[0];
}
if (oldLine && newLine) {
fileHtml.left += that.generateSingleLineHtml(isCombined, oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
fileHtml.right += that.generateSingleLineHtml(isCombined, newLine.type, newLine.newNumber, newContent, newPrefix);
} else if (oldLine) {
fileHtml.left += that.generateSingleLineHtml(isCombined, oldLine.type, oldLine.oldNumber, oldContent, oldPrefix);
fileHtml.right += that.generateSingleLineHtml(isCombined, diffParser.LINE_TYPE.CONTEXT, '', '', '');
} else if (newLine) {
fileHtml.left += that.generateSingleLineHtml(isCombined, diffParser.LINE_TYPE.CONTEXT, '', '', '');
fileHtml.right += that.generateSingleLineHtml(isCombined, newLine.type, newLine.newNumber, newContent, newPrefix);
} else {
console.error('How did it get here?');
}
}
return fileHtml;
};
SideBySidePrinter.prototype.generateSingleLineHtml = function(isCombined, type, number, content, possiblePrefix) {
var lineWithoutPrefix = content;
var prefix = possiblePrefix;
var lineClass = 'd2h-code-side-linenumber';
var contentClass = 'd2h-code-side-line';
if (!number && !content) {
lineClass += ' d2h-code-side-emptyplaceholder';
contentClass += ' d2h-code-side-emptyplaceholder';
type += ' d2h-emptyplaceholder';
}
if (!prefix) {
var lineWithPrefix = printerUtils.separatePrefix(isCombined, content);
prefix = lineWithPrefix.prefix;
lineWithoutPrefix = lineWithPrefix.line;
}
if (prefix === ' ') {
prefix = ' ';
}
return hoganUtils.render(genericTemplatesPath, 'line',
{
type: type,
lineClass: lineClass,
contentClass: contentClass,
prefix: prefix,
content: lineWithoutPrefix,
lineNumber: number
});
};
SideBySidePrinter.prototype.generateEmptyDiff = function() {
var fileHtml = {};
fileHtml.right = '';
fileHtml.left = hoganUtils.render(genericTemplatesPath, 'empty-diff', {
contentClass: 'd2h-code-side-line',
diffParser: diffParser
});
return fileHtml;
};
module.exports.SideBySidePrinter = SideBySidePrinter;
})();
},{"./diff-parser.js":9,"./hoganjs-utils.js":12,"./printer-utils.js":15,"./rematch.js":16,"./utils.js":19}],18:[function(require,module,exports){
(function (global){
(function() {
if (!!!global.browserTemplates) global.browserTemplates = {};
var Hogan = require("hogan.js");global.browserTemplates["file-summary-line"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<li class=\"d2h-file-list-line\">");t.b("\n" + i);t.b(" <span class=\"d2h-file-name-wrapper\">");t.b("\n" + i);t.b(t.rp("<fileIcon0",c,p," "));t.b(" <a href=\"#");t.b(t.v(t.f("fileHtmlId",c,p,0)));t.b("\" class=\"d2h-file-name\">");t.b(t.v(t.f("fileName",c,p,0)));t.b("</a>");t.b("\n" + i);t.b(" <span class=\"d2h-file-stats\">");t.b("\n" + i);t.b(" <span class=\"d2h-lines-added\">");t.b(t.v(t.f("addedLines",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(" <span class=\"d2h-lines-deleted\">");t.b(t.v(t.f("deletedLines",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b(" </span>");t.b("\n" + i);t.b("</li>");return t.fl(); },partials: {"<fileIcon0":{name:"fileIcon", partials: {}, subs: { }}}, subs: { }});
global.browserTemplates["file-summary-wrapper"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"d2h-file-list-wrapper\">");t.b("\n" + i);t.b(" <div class=\"d2h-file-list-header\">");t.b("\n" + i);t.b(" <span class=\"d2h-file-list-title\">Files changed (");t.b(t.v(t.f("filesNumber",c,p,0)));t.b(")</span>");t.b("\n" + i);t.b(" <a class=\"d2h-file-switch d2h-hide\">hide</a>");t.b("\n" + i);t.b(" <a class=\"d2h-file-switch d2h-show\">show</a>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <ol class=\"d2h-file-list\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("files",c,p,0)));t.b("\n" + i);t.b(" </ol>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["generic-column-line-number"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.f("lineClass",c,p,0)));t.b(" ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\"></td>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"");t.b(t.v(t.f("contentClass",c,p,0)));t.b(" ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b(t.t(t.f("blockHeader",c,p,0)));t.b("</div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["generic-empty-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"");t.b(t.v(t.f("contentClass",c,p,0)));t.b(" ");t.b(t.v(t.d("diffParser.LINE_TYPE.INFO",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" File without changes");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["generic-file-path"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<span class=\"d2h-file-name-wrapper\">");t.b("\n" + i);t.b(t.rp("<fileIcon0",c,p," "));t.b(" <span class=\"d2h-file-name\">");t.b(t.v(t.f("fileDiffName",c,p,0)));t.b("</span>");t.b("\n" + i);t.b(t.rp("<fileTag1",c,p," "));t.b("</span>");return t.fl(); },partials: {"<fileIcon0":{name:"fileIcon", partials: {}, subs: { }},"<fileTag1":{name:"fileTag", partials: {}, subs: { }}}, subs: { }});
global.browserTemplates["generic-line"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<tr>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.f("lineClass",c,p,0)));t.b(" ");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("lineNumber",c,p,0)));t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b(" <td class=\"");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"");t.b(t.v(t.f("contentClass",c,p,0)));t.b(" ");t.b(t.v(t.f("type",c,p,0)));t.b("\">");t.b("\n" + i);if(t.s(t.f("prefix",c,p,1),c,p,0,171,247,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" <span class=\"d2h-code-line-prefix\">");t.b(t.t(t.f("prefix",c,p,0)));t.b("</span>");t.b("\n" + i);});c.pop();}if(t.s(t.f("content",c,p,1),c,p,0,279,353,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" <span class=\"d2h-code-line-ctn\">");t.b(t.t(t.f("content",c,p,0)));t.b("</span>");t.b("\n" + i);});c.pop();}t.b(" </div>");t.b("\n" + i);t.b(" </td>");t.b("\n" + i);t.b("</tr>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["generic-wrapper"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"d2h-wrapper\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("content",c,p,0)));t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["icon-file-added"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<svg aria-hidden=\"true\" class=\"d2h-icon d2h-added\" height=\"16\" title=\"added\" version=\"1.1\" viewBox=\"0 0 14 16\"");t.b("\n" + i);t.b(" width=\"14\">");t.b("\n" + i);t.b(" <path d=\"M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM6 9H3V7h3V4h2v3h3v2H8v3H6V9z\"></path>");t.b("\n" + i);t.b("</svg>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["icon-file-changed"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<svg aria-hidden=\"true\" class=\"d2h-icon d2h-changed\" height=\"16\" title=\"modified\" version=\"1.1\"");t.b("\n" + i);t.b(" viewBox=\"0 0 14 16\" width=\"14\">");t.b("\n" + i);t.b(" <path d=\"M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z\"></path>");t.b("\n" + i);t.b("</svg>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["icon-file-deleted"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<svg aria-hidden=\"true\" class=\"d2h-icon d2h-deleted\" height=\"16\" title=\"removed\" version=\"1.1\"");t.b("\n" + i);t.b(" viewBox=\"0 0 14 16\" width=\"14\">");t.b("\n" + i);t.b(" <path d=\"M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM11 9H3V7h8v2z\"></path>");t.b("\n" + i);t.b("</svg>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["icon-file-renamed"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<svg aria-hidden=\"true\" class=\"d2h-icon d2h-moved\" height=\"16\" title=\"renamed\" version=\"1.1\"");t.b("\n" + i);t.b(" viewBox=\"0 0 14 16\" width=\"14\">");t.b("\n" + i);t.b(" <path d=\"M6 9H3V7h3V4l5 4-5 4V9z m8-7v12c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h12c0.55 0 1 0.45 1 1z m-1 0H1v12h12V2z\"></path>");t.b("\n" + i);t.b("</svg>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["icon-file"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<svg aria-hidden=\"true\" class=\"d2h-icon\" height=\"16\" version=\"1.1\" viewBox=\"0 0 12 16\" width=\"12\">");t.b("\n" + i);t.b(" <path d=\"M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z\"></path>");t.b("\n" + i);t.b("</svg>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["line-by-line-file-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div id=\"");t.b(t.v(t.f("fileHtmlId",c,p,0)));t.b("\" class=\"d2h-file-wrapper\" data-lang=\"");t.b(t.v(t.d("file.language",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-file-header\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("filePath",c,p,0)));t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <div class=\"d2h-file-diff\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-wrapper\">");t.b("\n" + i);t.b(" <table class=\"d2h-diff-table\">");t.b("\n" + i);t.b(" <tbody class=\"d2h-diff-tbody\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("diffs",c,p,0)));t.b("\n" + i);t.b(" </tbody>");t.b("\n" + i);t.b(" </table>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["line-by-line-numbers"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div class=\"line-num1\">");t.b(t.v(t.f("oldNumber",c,p,0)));t.b("</div>");t.b("\n" + i);t.b("<div class=\"line-num2\">");t.b(t.v(t.f("newNumber",c,p,0)));t.b("</div>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["side-by-side-file-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<div id=\"");t.b(t.v(t.f("fileHtmlId",c,p,0)));t.b("\" class=\"d2h-file-wrapper\" data-lang=\"");t.b(t.v(t.d("file.language",c,p,0)));t.b("\">");t.b("\n" + i);t.b(" <div class=\"d2h-file-header\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("filePath",c,p,0)));t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <div class=\"d2h-files-diff\">");t.b("\n" + i);t.b(" <div class=\"d2h-file-side-diff\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-wrapper\">");t.b("\n" + i);t.b(" <table class=\"d2h-diff-table\">");t.b("\n" + i);t.b(" <tbody class=\"d2h-diff-tbody\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.d("diffs.left",c,p,0)));t.b("\n" + i);t.b(" </tbody>");t.b("\n" + i);t.b(" </table>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" <div class=\"d2h-file-side-diff\">");t.b("\n" + i);t.b(" <div class=\"d2h-code-wrapper\">");t.b("\n" + i);t.b(" <table class=\"d2h-diff-table\">");t.b("\n" + i);t.b(" <tbody class=\"d2h-diff-tbody\">");t.b("\n" + i);t.b(" ");t.b(t.t(t.d("diffs.right",c,p,0)));t.b("\n" + i);t.b(" </tbody>");t.b("\n" + i);t.b(" </table>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b(" </div>");t.b("\n" + i);t.b("</div>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["tag-file-added"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<span class=\"d2h-tag d2h-added d2h-added-tag\">ADDED</span>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["tag-file-changed"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<span class=\"d2h-tag d2h-changed d2h-changed-tag\">CHANGED</span>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["tag-file-deleted"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<span class=\"d2h-tag d2h-deleted d2h-deleted-tag\">DELETED</span>");return t.fl(); },partials: {}, subs: { }});
global.browserTemplates["tag-file-renamed"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("<span class=\"d2h-tag d2h-moved d2h-moved-tag\">RENAMED</span>");return t.fl(); },partials: {}, subs: { }});
module.exports = global.browserTemplates;
})();
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"hogan.js":4}],19:[function(require,module,exports){
/*
*
* Utils (utils.js)
* Author: rtfpessoa
*
*/
(function() {
var merge = require('merge');
function Utils() {
}
Utils.prototype.escape = function(str) {
return str.slice(0)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
};
Utils.prototype.startsWith = function(str, start) {
if (typeof start === 'object') {
var result = false;
start.forEach(function(s) {
if (str.indexOf(s) === 0) {
result = true;
}
});
return result;
}
return str && str.indexOf(start) === 0;
};
Utils.prototype.valueOrEmpty = function(value) {
return value || '';
};
Utils.prototype.safeConfig = function(cfg, defaultConfig) {
return merge.recursive(true, defaultConfig, cfg);
};
module.exports.Utils = new Utils();
})();
},{"merge":6}]},{},[10]);
//}}}
//{{{
(function() {
var css = store.getTiddlerText("diff2html.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}"));
setStylesheet(css, "diff2html.js-stylesheet");
})();
//}}}
/***
|Name|diff_match_patch_uncompressed.js|
|Source|https://github.com/google/diff-match-patch/blob/master/javascript/diff_match_patch_uncompressed.js|
|Documentation|https://github.com/google/diff-match-patch|
|Version|commit 62f2e689f498f9c92dbc588c58750addec9b1654 2019-07-25 https://raw.githubusercontent.com/google/diff-match-patch/master/javascript/diff_match_patch_uncompressed.js|
|License|[[Apache License 2.0|https://www.apache.org/licenses/LICENSE-2.0]]|
|Description|The Diff Match and Patch libraries offer robust algorithms to perform the operations required for synchronizing plain text|
!!!!!Code
***/
//{{{
config.macros.diff_match_patch = {
initialized: false,
init: function () {
if (!this.initialized) {
//}}}
//{{{
/**
* Diff Match and Patch
* Copyright 2018 The diff-match-patch Authors.
* https://github.com/google/diff-match-patch
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Computes the difference between two texts to create a patch.
* Applies the patch onto another text, allowing for errors.
* @author fraser@google.com (Neil Fraser)
*/
/**
* Class containing the diff, match and patch methods.
* @constructor
*/
var diff_match_patch = function() {
// Defaults.
// Redefine these in your program to override the defaults.
// Number of seconds to map a diff before giving up (0 for infinity).
this.Diff_Timeout = 1.0;
// Cost of an empty edit operation in terms of edit characters.
this.Diff_EditCost = 4;
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
this.Match_Threshold = 0.5;
// How far to search for a match (0 = exact location, 1000+ = broad match).
// A match this many characters away from the expected location will add
// 1.0 to the score (0.0 is a perfect match).
this.Match_Distance = 1000;
// When deleting a large block of text (over ~64 characters), how close do
// the contents have to be to match the expected contents. (0.0 = perfection,
// 1.0 = very loose). Note that Match_Threshold controls how closely the
// end points of a delete need to match.
this.Patch_DeleteThreshold = 0.5;
// Chunk size for context length.
this.Patch_Margin = 4;
// The number of bits in an int.
this.Match_MaxBits = 32;
};
// DIFF FUNCTIONS
/**
* The data structure representing a diff is an array of tuples:
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
* which means: delete 'Hello', add 'Goodbye' and keep ' world.'
*/
var DIFF_DELETE = -1;
var DIFF_INSERT = 1;
var DIFF_EQUAL = 0;
/**
* Class representing one diff tuple.
* Attempts to look like a two-element array (which is what this used to be).
* @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.
* @param {string} text Text to be deleted, inserted, or retained.
* @constructor
*/
diff_match_patch.Diff = function(op, text) {
this[0] = op;
this[1] = text;
};
diff_match_patch.Diff.prototype.length = 2;
/**
* Emulate the output of a two-element array.
* @return {string} Diff operation as a string.
*/
diff_match_patch.Diff.prototype.toString = function() {
return this[0] + ',' + this[1];
};
/**
* Find the differences between two texts. Simplifies the problem by stripping
* any common prefix or suffix off the texts before diffing.
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
* @param {boolean=} opt_checklines Optional speedup flag. If present and false,
* then don't run a line-level diff first to identify the changed areas.
* Defaults to true, which does a faster, slightly less optimal diff.
* @param {number=} opt_deadline Optional time when the diff should be complete
* by. Used internally for recursive calls. Users should set DiffTimeout
* instead.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
*/
diff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines,
opt_deadline) {
// Set a deadline by which time the diff must be complete.
if (typeof opt_deadline == 'undefined') {
if (this.Diff_Timeout <= 0) {
opt_deadline = Number.MAX_VALUE;
} else {
opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000;
}
}
var deadline = opt_deadline;
// Check for null inputs.
if (text1 == null || text2 == null) {
throw new Error('Null input. (diff_main)');
}
// Check for equality (speedup).
if (text1 == text2) {
if (text1) {
return [new diff_match_patch.Diff(DIFF_EQUAL, text1)];
}
return [];
}
if (typeof opt_checklines == 'undefined') {
opt_checklines = true;
}
var checklines = opt_checklines;
// Trim off common prefix (speedup).
var commonlength = this.diff_commonPrefix(text1, text2);
var commonprefix = text1.substring(0, commonlength);
text1 = text1.substring(commonlength);
text2 = text2.substring(commonlength);
// Trim off common suffix (speedup).
commonlength = this.diff_commonSuffix(text1, text2);
var commonsuffix = text1.substring(text1.length - commonlength);
text1 = text1.substring(0, text1.length - commonlength);
text2 = text2.substring(0, text2.length - commonlength);
// Compute the diff on the middle block.
var diffs = this.diff_compute_(text1, text2, checklines, deadline);
// Restore the prefix and suffix.
if (commonprefix) {
diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, commonprefix));
}
if (commonsuffix) {
diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, commonsuffix));
}
this.diff_cleanupMerge(diffs);
return diffs;
};
/**
* Find the differences between two texts. Assumes that the texts do not
* have any common prefix or suffix.
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
* @param {boolean} checklines Speedup flag. If false, then don't run a
* line-level diff first to identify the changed areas.
* If true, then run a faster, slightly less optimal diff.
* @param {number} deadline Time when the diff should be complete by.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
* @private
*/
diff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,
deadline) {
var diffs;
if (!text1) {
// Just add some text (speedup).
return [new diff_match_patch.Diff(DIFF_INSERT, text2)];
}
if (!text2) {
// Just delete some text (speedup).
return [new diff_match_patch.Diff(DIFF_DELETE, text1)];
}
var longtext = text1.length > text2.length ? text1 : text2;
var shorttext = text1.length > text2.length ? text2 : text1;
var i = longtext.indexOf(shorttext);
if (i != -1) {
// Shorter text is inside the longer text (speedup).
diffs = [new diff_match_patch.Diff(DIFF_INSERT, longtext.substring(0, i)),
new diff_match_patch.Diff(DIFF_EQUAL, shorttext),
new diff_match_patch.Diff(DIFF_INSERT,
longtext.substring(i + shorttext.length))];
// Swap insertions for deletions if diff is reversed.
if (text1.length > text2.length) {
diffs[0][0] = diffs[2][0] = DIFF_DELETE;
}
return diffs;
}
if (shorttext.length == 1) {
// Single character string.
// After the previous speedup, the character can't be an equality.
return [new diff_match_patch.Diff(DIFF_DELETE, text1),
new diff_match_patch.Diff(DIFF_INSERT, text2)];
}
// Check to see if the problem can be split in two.
var hm = this.diff_halfMatch_(text1, text2);
if (hm) {
// A half-match was found, sort out the return data.
var text1_a = hm[0];
var text1_b = hm[1];
var text2_a = hm[2];
var text2_b = hm[3];
var mid_common = hm[4];
// Send both pairs off for separate processing.
var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline);
var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline);
// Merge the results.
return diffs_a.concat([new diff_match_patch.Diff(DIFF_EQUAL, mid_common)],
diffs_b);
}
if (checklines && text1.length > 100 && text2.length > 100) {
return this.diff_lineMode_(text1, text2, deadline);
}
return this.diff_bisect_(text1, text2, deadline);
};
/**
* Do a quick line-level diff on both strings, then rediff the parts for
* greater accuracy.
* This speedup can produce non-minimal diffs.
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
* @param {number} deadline Time when the diff should be complete by.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
* @private
*/
diff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) {
// Scan the text on a line-by-line basis first.
var a = this.diff_linesToChars_(text1, text2);
text1 = a.chars1;
text2 = a.chars2;
var linearray = a.lineArray;
var diffs = this.diff_main(text1, text2, false, deadline);
// Convert the diff back to original text.
this.diff_charsToLines_(diffs, linearray);
// Eliminate freak matches (e.g. blank lines)
this.diff_cleanupSemantic(diffs);
// Rediff any replacement blocks, this time character-by-character.
// Add a dummy entry at the end.
diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));
var pointer = 0;
var count_delete = 0;
var count_insert = 0;
var text_delete = '';
var text_insert = '';
while (pointer < diffs.length) {
switch (diffs[pointer][0]) {
case DIFF_INSERT:
count_insert++;
text_insert += diffs[pointer][1];
break;
case DIFF_DELETE:
count_delete++;
text_delete += diffs[pointer][1];
break;
case DIFF_EQUAL:
// Upon reaching an equality, check for prior redundancies.
if (count_delete >= 1 && count_insert >= 1) {
// Delete the offending records and add the merged ones.
diffs.splice(pointer - count_delete - count_insert,
count_delete + count_insert);
pointer = pointer - count_delete - count_insert;
var subDiff =
this.diff_main(text_delete, text_insert, false, deadline);
for (var j = subDiff.length - 1; j >= 0; j--) {
diffs.splice(pointer, 0, subDiff[j]);
}
pointer = pointer + subDiff.length;
}
count_insert = 0;
count_delete = 0;
text_delete = '';
text_insert = '';
break;
}
pointer++;
}
diffs.pop(); // Remove the dummy entry at the end.
return diffs;
};
/**
* Find the 'middle snake' of a diff, split the problem in two
* and return the recursively constructed diff.
* See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
* @param {number} deadline Time at which to bail if not yet complete.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
* @private
*/
diff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) {
// Cache the text lengths to prevent multiple calls.
var text1_length = text1.length;
var text2_length = text2.length;
var max_d = Math.ceil((text1_length + text2_length) / 2);
var v_offset = max_d;
var v_length = 2 * max_d;
var v1 = new Array(v_length);
var v2 = new Array(v_length);
// Setting all elements to -1 is faster in Chrome & Firefox than mixing
// integers and undefined.
for (var x = 0; x < v_length; x++) {
v1[x] = -1;
v2[x] = -1;
}
v1[v_offset + 1] = 0;
v2[v_offset + 1] = 0;
var delta = text1_length - text2_length;
// If the total number of characters is odd, then the front path will collide
// with the reverse path.
var front = (delta % 2 != 0);
// Offsets for start and end of k loop.
// Prevents mapping of space beyond the grid.
var k1start = 0;
var k1end = 0;
var k2start = 0;
var k2end = 0;
for (var d = 0; d < max_d; d++) {
// Bail out if deadline is reached.
if ((new Date()).getTime() > deadline) {
break;
}
// Walk the front path one step.
for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
var k1_offset = v_offset + k1;
var x1;
if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {
x1 = v1[k1_offset + 1];
} else {
x1 = v1[k1_offset - 1] + 1;
}
var y1 = x1 - k1;
while (x1 < text1_length && y1 < text2_length &&
text1.charAt(x1) == text2.charAt(y1)) {
x1++;
y1++;
}
v1[k1_offset] = x1;
if (x1 > text1_length) {
// Ran off the right of the graph.
k1end += 2;
} else if (y1 > text2_length) {
// Ran off the bottom of the graph.
k1start += 2;
} else if (front) {
var k2_offset = v_offset + delta - k1;
if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {
// Mirror x2 onto top-left coordinate system.
var x2 = text1_length - v2[k2_offset];
if (x1 >= x2) {
// Overlap detected.
return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);
}
}
}
}
// Walk the reverse path one step.
for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
var k2_offset = v_offset + k2;
var x2;
if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {
x2 = v2[k2_offset + 1];
} else {
x2 = v2[k2_offset - 1] + 1;
}
var y2 = x2 - k2;
while (x2 < text1_length && y2 < text2_length &&
text1.charAt(text1_length - x2 - 1) ==
text2.charAt(text2_length - y2 - 1)) {
x2++;
y2++;
}
v2[k2_offset] = x2;
if (x2 > text1_length) {
// Ran off the left of the graph.
k2end += 2;
} else if (y2 > text2_length) {
// Ran off the top of the graph.
k2start += 2;
} else if (!front) {
var k1_offset = v_offset + delta - k2;
if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {
var x1 = v1[k1_offset];
var y1 = v_offset + x1 - k1_offset;
// Mirror x2 onto top-left coordinate system.
x2 = text1_length - x2;
if (x1 >= x2) {
// Overlap detected.
return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);
}
}
}
}
}
// Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
return [new diff_match_patch.Diff(DIFF_DELETE, text1),
new diff_match_patch.Diff(DIFF_INSERT, text2)];
};
/**
* Given the location of the 'middle snake', split the diff in two parts
* and recurse.
* @param {string} text1 Old string to be diffed.
* @param {string} text2 New string to be diffed.
* @param {number} x Index of split point in text1.
* @param {number} y Index of split point in text2.
* @param {number} deadline Time at which to bail if not yet complete.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
* @private
*/
diff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y,
deadline) {
var text1a = text1.substring(0, x);
var text2a = text2.substring(0, y);
var text1b = text1.substring(x);
var text2b = text2.substring(y);
// Compute both diffs serially.
var diffs = this.diff_main(text1a, text2a, false, deadline);
var diffsb = this.diff_main(text1b, text2b, false, deadline);
return diffs.concat(diffsb);
};
/**
* Split two texts into an array of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* @param {string} text1 First string.
* @param {string} text2 Second string.
* @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
* An object containing the encoded text1, the encoded text2 and
* the array of unique strings.
* The zeroth element of the array of unique strings is intentionally blank.
* @private
*/
diff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) {
var lineArray = []; // e.g. lineArray[4] == 'Hello\n'
var lineHash = {}; // e.g. lineHash['Hello\n'] == 4
// '\x00' is a valid character, but various debuggers don't like it.
// So we'll insert a junk entry to avoid generating a null character.
lineArray[0] = '';
/**
* Split a text into an array of strings. Reduce the texts to a string of
* hashes where each Unicode character represents one line.
* Modifies linearray and linehash through being a closure.
* @param {string} text String to encode.
* @return {string} Encoded string.
* @private
*/
function diff_linesToCharsMunge_(text) {
var chars = '';
// Walk the text, pulling out a substring for each line.
// text.split('\n') would would temporarily double our memory footprint.
// Modifying text would create many large strings to garbage collect.
var lineStart = 0;
var lineEnd = -1;
// Keeping our own length variable is faster than looking it up.
var lineArrayLength = lineArray.length;
while (lineEnd < text.length - 1) {
lineEnd = text.indexOf('\n', lineStart);
if (lineEnd == -1) {
lineEnd = text.length - 1;
}
var line = text.substring(lineStart, lineEnd + 1);
if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
(lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
} else {
if (lineArrayLength == maxLines) {
// Bail out at 65535 because
// String.fromCharCode(65536) == String.fromCharCode(0)
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
// Allocate 2/3rds of the space for text1, the rest for text2.
var maxLines = 40000;
var chars1 = diff_linesToCharsMunge_(text1);
maxLines = 65535;
var chars2 = diff_linesToCharsMunge_(text2);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
};
/**
* Rehydrate the text in a diff from a string of line hashes to real lines of
* text.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @param {!Array.<string>} lineArray Array of unique strings.
* @private
*/
diff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) {
for (var i = 0; i < diffs.length; i++) {
var chars = diffs[i][1];
var text = [];
for (var j = 0; j < chars.length; j++) {
text[j] = lineArray[chars.charCodeAt(j)];
}
diffs[i][1] = text.join('');
}
};
/**
* Determine the common prefix of two strings.
* @param {string} text1 First string.
* @param {string} text2 Second string.
* @return {number} The number of characters common to the start of each
* string.
*/
diff_match_patch.prototype.diff_commonPrefix = function(text1, text2) {
// Quick check for common null cases.
if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {
return 0;
}
// Binary search.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/
var pointermin = 0;
var pointermax = Math.min(text1.length, text2.length);
var pointermid = pointermax;
var pointerstart = 0;
while (pointermin < pointermid) {
if (text1.substring(pointerstart, pointermid) ==
text2.substring(pointerstart, pointermid)) {
pointermin = pointermid;
pointerstart = pointermin;
} else {
pointermax = pointermid;
}
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
}
return pointermid;
};
/**
* Determine the common suffix of two strings.
* @param {string} text1 First string.
* @param {string} text2 Second string.
* @return {number} The number of characters common to the end of each string.
*/
diff_match_patch.prototype.diff_commonSuffix = function(text1, text2) {
// Quick check for common null cases.
if (!text1 || !text2 ||
text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {
return 0;
}
// Binary search.
// Performance analysis: https://neil.fraser.name/news/2007/10/09/
var pointermin = 0;
var pointermax = Math.min(text1.length, text2.length);
var pointermid = pointermax;
var pointerend = 0;
while (pointermin < pointermid) {
if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==
text2.substring(text2.length - pointermid, text2.length - pointerend)) {
pointermin = pointermid;
pointerend = pointermin;
} else {
pointermax = pointermid;
}
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
}
return pointermid;
};
/**
* Determine if the suffix of one string is the prefix of another.
* @param {string} text1 First string.
* @param {string} text2 Second string.
* @return {number} The number of characters common to the end of the first
* string and the start of the second string.
* @private
*/
diff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) {
// Cache the text lengths to prevent multiple calls.
var text1_length = text1.length;
var text2_length = text2.length;
// Eliminate the null case.
if (text1_length == 0 || text2_length == 0) {
return 0;
}
// Truncate the longer string.
if (text1_length > text2_length) {
text1 = text1.substring(text1_length - text2_length);
} else if (text1_length < text2_length) {
text2 = text2.substring(0, text1_length);
}
var text_length = Math.min(text1_length, text2_length);
// Quick check for the worst case.
if (text1 == text2) {
return text_length;
}
// Start by looking for a single character match
// and increase length until no match is found.
// Performance analysis: https://neil.fraser.name/news/2010/11/04/
var best = 0;
var length = 1;
while (true) {
var pattern = text1.substring(text_length - length);
var found = text2.indexOf(pattern);
if (found == -1) {
return best;
}
length += found;
if (found == 0 || text1.substring(text_length - length) ==
text2.substring(0, length)) {
best = length;
length++;
}
}
};
/**
* Do the two texts share a substring which is at least half the length of the
* longer text?
* This speedup can produce non-minimal diffs.
* @param {string} text1 First string.
* @param {string} text2 Second string.
* @return {Array.<string>} Five element Array, containing the prefix of
* text1, the suffix of text1, the prefix of text2, the suffix of
* text2 and the common middle. Or null if there was no match.
* @private
*/
diff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) {
if (this.Diff_Timeout <= 0) {
// Don't risk returning a non-optimal diff if we have unlimited time.
return null;
}
var longtext = text1.length > text2.length ? text1 : text2;
var shorttext = text1.length > text2.length ? text2 : text1;
if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
return null; // Pointless.
}
var dmp = this; // 'this' becomes 'window' in a closure.
/**
* Does a substring of shorttext exist within longtext such that the substring
* is at least half the length of longtext?
* Closure, but does not reference any external variables.
* @param {string} longtext Longer string.
* @param {string} shorttext Shorter string.
* @param {number} i Start index of quarter length substring within longtext.
* @return {Array.<string>} Five element Array, containing the prefix of
* longtext, the suffix of longtext, the prefix of shorttext, the suffix
* of shorttext and the common middle. Or null if there was no match.
* @private
*/
function diff_halfMatchI_(longtext, shorttext, i) {
// Start with a 1/4 length substring at position i as a seed.
var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
var j = -1;
var best_common = '';
var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;
while ((j = shorttext.indexOf(seed, j + 1)) != -1) {
var prefixLength = dmp.diff_commonPrefix(longtext.substring(i),
shorttext.substring(j));
var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i),
shorttext.substring(0, j));
if (best_common.length < suffixLength + prefixLength) {
best_common = shorttext.substring(j - suffixLength, j) +
shorttext.substring(j, j + prefixLength);
best_longtext_a = longtext.substring(0, i - suffixLength);
best_longtext_b = longtext.substring(i + prefixLength);
best_shorttext_a = shorttext.substring(0, j - suffixLength);
best_shorttext_b = shorttext.substring(j + prefixLength);
}
}
if (best_common.length * 2 >= longtext.length) {
return [best_longtext_a, best_longtext_b,
best_shorttext_a, best_shorttext_b, best_common];
} else {
return null;
}
}
// First check if the second quarter is the seed for a half-match.
var hm1 = diff_halfMatchI_(longtext, shorttext,
Math.ceil(longtext.length / 4));
// Check again based on the third quarter.
var hm2 = diff_halfMatchI_(longtext, shorttext,
Math.ceil(longtext.length / 2));
var hm;
if (!hm1 && !hm2) {
return null;
} else if (!hm2) {
hm = hm1;
} else if (!hm1) {
hm = hm2;
} else {
// Both matched. Select the longest.
hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
}
// A half-match was found, sort out the return data.
var text1_a, text1_b, text2_a, text2_b;
if (text1.length > text2.length) {
text1_a = hm[0];
text1_b = hm[1];
text2_a = hm[2];
text2_b = hm[3];
} else {
text2_a = hm[0];
text2_b = hm[1];
text1_a = hm[2];
text1_b = hm[3];
}
var mid_common = hm[4];
return [text1_a, text1_b, text2_a, text2_b, mid_common];
};
/**
* Reduce the number of edits by eliminating semantically trivial equalities.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
*/
diff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {
var changes = false;
var equalities = []; // Stack of indices where equalities are found.
var equalitiesLength = 0; // Keeping our own length var is faster in JS.
/** @type {?string} */
var lastEquality = null;
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
var pointer = 0; // Index of current position.
// Number of characters that changed prior to the equality.
var length_insertions1 = 0;
var length_deletions1 = 0;
// Number of characters that changed after the equality.
var length_insertions2 = 0;
var length_deletions2 = 0;
while (pointer < diffs.length) {
if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found.
equalities[equalitiesLength++] = pointer;
length_insertions1 = length_insertions2;
length_deletions1 = length_deletions2;
length_insertions2 = 0;
length_deletions2 = 0;
lastEquality = diffs[pointer][1];
} else { // An insertion or deletion.
if (diffs[pointer][0] == DIFF_INSERT) {
length_insertions2 += diffs[pointer][1].length;
} else {
length_deletions2 += diffs[pointer][1].length;
}
// Eliminate an equality that is smaller or equal to the edits on both
// sides of it.
if (lastEquality && (lastEquality.length <=
Math.max(length_insertions1, length_deletions1)) &&
(lastEquality.length <= Math.max(length_insertions2,
length_deletions2))) {
// Duplicate record.
diffs.splice(equalities[equalitiesLength - 1], 0,
new diff_match_patch.Diff(DIFF_DELETE, lastEquality));
// Change second copy to insert.
diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
// Throw away the equality we just deleted.
equalitiesLength--;
// Throw away the previous equality (it needs to be reevaluated).
equalitiesLength--;
pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
length_insertions1 = 0; // Reset the counters.
length_deletions1 = 0;
length_insertions2 = 0;
length_deletions2 = 0;
lastEquality = null;
changes = true;
}
}
pointer++;
}
// Normalize the diff.
if (changes) {
this.diff_cleanupMerge(diffs);
}
this.diff_cleanupSemanticLossless(diffs);
// Find any overlaps between deletions and insertions.
// e.g: <del>abcxxx</del><ins>xxxdef</ins>
// -> <del>abc</del>xxx<ins>def</ins>
// e.g: <del>xxxabc</del><ins>defxxx</ins>
// -> <ins>def</ins>xxx<del>abc</del>
// Only extract an overlap if it is as big as the edit ahead or behind it.
pointer = 1;
while (pointer < diffs.length) {
if (diffs[pointer - 1][0] == DIFF_DELETE &&
diffs[pointer][0] == DIFF_INSERT) {
var deletion = diffs[pointer - 1][1];
var insertion = diffs[pointer][1];
var overlap_length1 = this.diff_commonOverlap_(deletion, insertion);
var overlap_length2 = this.diff_commonOverlap_(insertion, deletion);
if (overlap_length1 >= overlap_length2) {
if (overlap_length1 >= deletion.length / 2 ||
overlap_length1 >= insertion.length / 2) {
// Overlap found. Insert an equality and trim the surrounding edits.
diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,
insertion.substring(0, overlap_length1)));
diffs[pointer - 1][1] =
deletion.substring(0, deletion.length - overlap_length1);
diffs[pointer + 1][1] = insertion.substring(overlap_length1);
pointer++;
}
} else {
if (overlap_length2 >= deletion.length / 2 ||
overlap_length2 >= insertion.length / 2) {
// Reverse overlap found.
// Insert an equality and swap and trim the surrounding edits.
diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,
deletion.substring(0, overlap_length2)));
diffs[pointer - 1][0] = DIFF_INSERT;
diffs[pointer - 1][1] =
insertion.substring(0, insertion.length - overlap_length2);
diffs[pointer + 1][0] = DIFF_DELETE;
diffs[pointer + 1][1] =
deletion.substring(overlap_length2);
pointer++;
}
}
pointer++;
}
pointer++;
}
};
/**
* Look for single edits surrounded on both sides by equalities
* which can be shifted sideways to align the edit to a word boundary.
* e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
*/
diff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) {
/**
* Given two strings, compute a score representing whether the internal
* boundary falls on logical boundaries.
* Scores range from 6 (best) to 0 (worst).
* Closure, but does not reference any external variables.
* @param {string} one First string.
* @param {string} two Second string.
* @return {number} The score.
* @private
*/
function diff_cleanupSemanticScore_(one, two) {
if (!one || !two) {
// Edges are the best.
return 6;
}
// Each port of this function behaves slightly differently due to
// subtle differences in each language's definition of things like
// 'whitespace'. Since this function's purpose is largely cosmetic,
// the choice has been made to use each language's native features
// rather than force total conformity.
var char1 = one.charAt(one.length - 1);
var char2 = two.charAt(0);
var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_);
var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_);
var whitespace1 = nonAlphaNumeric1 &&
char1.match(diff_match_patch.whitespaceRegex_);
var whitespace2 = nonAlphaNumeric2 &&
char2.match(diff_match_patch.whitespaceRegex_);
var lineBreak1 = whitespace1 &&
char1.match(diff_match_patch.linebreakRegex_);
var lineBreak2 = whitespace2 &&
char2.match(diff_match_patch.linebreakRegex_);
var blankLine1 = lineBreak1 &&
one.match(diff_match_patch.blanklineEndRegex_);
var blankLine2 = lineBreak2 &&
two.match(diff_match_patch.blanklineStartRegex_);
if (blankLine1 || blankLine2) {
// Five points for blank lines.
return 5;
} else if (lineBreak1 || lineBreak2) {
// Four points for line breaks.
return 4;
} else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {
// Three points for end of sentences.
return 3;
} else if (whitespace1 || whitespace2) {
// Two points for whitespace.
return 2;
} else if (nonAlphaNumeric1 || nonAlphaNumeric2) {
// One point for non-alphanumeric.
return 1;
}
return 0;
}
var pointer = 1;
// Intentionally ignore the first and last element (don't need checking).
while (pointer < diffs.length - 1) {
if (diffs[pointer - 1][0] == DIFF_EQUAL &&
diffs[pointer + 1][0] == DIFF_EQUAL) {
// This is a single edit surrounded by equalities.
var equality1 = diffs[pointer - 1][1];
var edit = diffs[pointer][1];
var equality2 = diffs[pointer + 1][1];
// First, shift the edit as far left as possible.
var commonOffset = this.diff_commonSuffix(equality1, edit);
if (commonOffset) {
var commonString = edit.substring(edit.length - commonOffset);
equality1 = equality1.substring(0, equality1.length - commonOffset);
edit = commonString + edit.substring(0, edit.length - commonOffset);
equality2 = commonString + equality2;
}
// Second, step character by character right, looking for the best fit.
var bestEquality1 = equality1;
var bestEdit = edit;
var bestEquality2 = equality2;
var bestScore = diff_cleanupSemanticScore_(equality1, edit) +
diff_cleanupSemanticScore_(edit, equality2);
while (edit.charAt(0) === equality2.charAt(0)) {
equality1 += edit.charAt(0);
edit = edit.substring(1) + equality2.charAt(0);
equality2 = equality2.substring(1);
var score = diff_cleanupSemanticScore_(equality1, edit) +
diff_cleanupSemanticScore_(edit, equality2);
// The >= encourages trailing rather than leading whitespace on edits.
if (score >= bestScore) {
bestScore = score;
bestEquality1 = equality1;
bestEdit = edit;
bestEquality2 = equality2;
}
}
if (diffs[pointer - 1][1] != bestEquality1) {
// We have an improvement, save it back to the diff.
if (bestEquality1) {
diffs[pointer - 1][1] = bestEquality1;
} else {
diffs.splice(pointer - 1, 1);
pointer--;
}
diffs[pointer][1] = bestEdit;
if (bestEquality2) {
diffs[pointer + 1][1] = bestEquality2;
} else {
diffs.splice(pointer + 1, 1);
pointer--;
}
}
}
pointer++;
}
};
// Define some regex patterns for matching boundaries.
diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/;
diff_match_patch.whitespaceRegex_ = /\s/;
diff_match_patch.linebreakRegex_ = /[\r\n]/;
diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/;
diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/;
/**
* Reduce the number of edits by eliminating operationally trivial equalities.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
*/
diff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {
var changes = false;
var equalities = []; // Stack of indices where equalities are found.
var equalitiesLength = 0; // Keeping our own length var is faster in JS.
/** @type {?string} */
var lastEquality = null;
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
var pointer = 0; // Index of current position.
// Is there an insertion operation before the last equality.
var pre_ins = false;
// Is there a deletion operation before the last equality.
var pre_del = false;
// Is there an insertion operation after the last equality.
var post_ins = false;
// Is there a deletion operation after the last equality.
var post_del = false;
while (pointer < diffs.length) {
if (diffs[pointer][0] == DIFF_EQUAL) { // Equality found.
if (diffs[pointer][1].length < this.Diff_EditCost &&
(post_ins || post_del)) {
// Candidate found.
equalities[equalitiesLength++] = pointer;
pre_ins = post_ins;
pre_del = post_del;
lastEquality = diffs[pointer][1];
} else {
// Not a candidate, and can never become one.
equalitiesLength = 0;
lastEquality = null;
}
post_ins = post_del = false;
} else { // An insertion or deletion.
if (diffs[pointer][0] == DIFF_DELETE) {
post_del = true;
} else {
post_ins = true;
}
/*
* Five types to be split:
* <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
* <ins>A</ins>X<ins>C</ins><del>D</del>
* <ins>A</ins><del>B</del>X<ins>C</ins>
* <ins>A</del>X<ins>C</ins><del>D</del>
* <ins>A</ins><del>B</del>X<del>C</del>
*/
if (lastEquality && ((pre_ins && pre_del && post_ins && post_del) ||
((lastEquality.length < this.Diff_EditCost / 2) &&
(pre_ins + pre_del + post_ins + post_del) == 3))) {
// Duplicate record.
diffs.splice(equalities[equalitiesLength - 1], 0,
new diff_match_patch.Diff(DIFF_DELETE, lastEquality));
// Change second copy to insert.
diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
equalitiesLength--; // Throw away the equality we just deleted;
lastEquality = null;
if (pre_ins && pre_del) {
// No changes made which could affect previous entry, keep going.
post_ins = post_del = true;
equalitiesLength = 0;
} else {
equalitiesLength--; // Throw away the previous equality.
pointer = equalitiesLength > 0 ?
equalities[equalitiesLength - 1] : -1;
post_ins = post_del = false;
}
changes = true;
}
}
pointer++;
}
if (changes) {
this.diff_cleanupMerge(diffs);
}
};
/**
* Reorder and merge like edit sections. Merge equalities.
* Any edit section can move as long as it doesn't cross an equality.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
*/
diff_match_patch.prototype.diff_cleanupMerge = function(diffs) {
// Add a dummy entry at the end.
diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));
var pointer = 0;
var count_delete = 0;
var count_insert = 0;
var text_delete = '';
var text_insert = '';
var commonlength;
while (pointer < diffs.length) {
switch (diffs[pointer][0]) {
case DIFF_INSERT:
count_insert++;
text_insert += diffs[pointer][1];
pointer++;
break;
case DIFF_DELETE:
count_delete++;
text_delete += diffs[pointer][1];
pointer++;
break;
case DIFF_EQUAL:
// Upon reaching an equality, check for prior redundancies.
if (count_delete + count_insert > 1) {
if (count_delete !== 0 && count_insert !== 0) {
// Factor out any common prefixies.
commonlength = this.diff_commonPrefix(text_insert, text_delete);
if (commonlength !== 0) {
if ((pointer - count_delete - count_insert) > 0 &&
diffs[pointer - count_delete - count_insert - 1][0] ==
DIFF_EQUAL) {
diffs[pointer - count_delete - count_insert - 1][1] +=
text_insert.substring(0, commonlength);
} else {
diffs.splice(0, 0, new diff_match_patch.Diff(DIFF_EQUAL,
text_insert.substring(0, commonlength)));
pointer++;
}
text_insert = text_insert.substring(commonlength);
text_delete = text_delete.substring(commonlength);
}
// Factor out any common suffixies.
commonlength = this.diff_commonSuffix(text_insert, text_delete);
if (commonlength !== 0) {
diffs[pointer][1] = text_insert.substring(text_insert.length -
commonlength) + diffs[pointer][1];
text_insert = text_insert.substring(0, text_insert.length -
commonlength);
text_delete = text_delete.substring(0, text_delete.length -
commonlength);
}
}
// Delete the offending records and add the merged ones.
pointer -= count_delete + count_insert;
diffs.splice(pointer, count_delete + count_insert);
if (text_delete.length) {
diffs.splice(pointer, 0,
new diff_match_patch.Diff(DIFF_DELETE, text_delete));
pointer++;
}
if (text_insert.length) {
diffs.splice(pointer, 0,
new diff_match_patch.Diff(DIFF_INSERT, text_insert));
pointer++;
}
pointer++;
} else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {
// Merge this equality with the previous one.
diffs[pointer - 1][1] += diffs[pointer][1];
diffs.splice(pointer, 1);
} else {
pointer++;
}
count_insert = 0;
count_delete = 0;
text_delete = '';
text_insert = '';
break;
}
}
if (diffs[diffs.length - 1][1] === '') {
diffs.pop(); // Remove the dummy entry at the end.
}
// Second pass: look for single edits surrounded on both sides by equalities
// which can be shifted sideways to eliminate an equality.
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
var changes = false;
pointer = 1;
// Intentionally ignore the first and last element (don't need checking).
while (pointer < diffs.length - 1) {
if (diffs[pointer - 1][0] == DIFF_EQUAL &&
diffs[pointer + 1][0] == DIFF_EQUAL) {
// This is a single edit surrounded by equalities.
if (diffs[pointer][1].substring(diffs[pointer][1].length -
diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {
// Shift the edit over the previous equality.
diffs[pointer][1] = diffs[pointer - 1][1] +
diffs[pointer][1].substring(0, diffs[pointer][1].length -
diffs[pointer - 1][1].length);
diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
diffs.splice(pointer - 1, 1);
changes = true;
} else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==
diffs[pointer + 1][1]) {
// Shift the edit over the next equality.
diffs[pointer - 1][1] += diffs[pointer + 1][1];
diffs[pointer][1] =
diffs[pointer][1].substring(diffs[pointer + 1][1].length) +
diffs[pointer + 1][1];
diffs.splice(pointer + 1, 1);
changes = true;
}
}
pointer++;
}
// If shifts were made, the diff needs reordering and another shift sweep.
if (changes) {
this.diff_cleanupMerge(diffs);
}
};
/**
* loc is a location in text1, compute and return the equivalent location in
* text2.
* e.g. 'The cat' vs 'The big cat', 1->1, 5->8
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @param {number} loc Location within text1.
* @return {number} Location within text2.
*/
diff_match_patch.prototype.diff_xIndex = function(diffs, loc) {
var chars1 = 0;
var chars2 = 0;
var last_chars1 = 0;
var last_chars2 = 0;
var x;
for (x = 0; x < diffs.length; x++) {
if (diffs[x][0] !== DIFF_INSERT) { // Equality or deletion.
chars1 += diffs[x][1].length;
}
if (diffs[x][0] !== DIFF_DELETE) { // Equality or insertion.
chars2 += diffs[x][1].length;
}
if (chars1 > loc) { // Overshot the location.
break;
}
last_chars1 = chars1;
last_chars2 = chars2;
}
// Was the location was deleted?
if (diffs.length != x && diffs[x][0] === DIFF_DELETE) {
return last_chars2;
}
// Add the remaining character length.
return last_chars2 + (loc - last_chars1);
};
/**
* Convert a diff array into a pretty HTML report.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @return {string} HTML representation.
*/
diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
var html = [];
var pattern_amp = /&/g;
var pattern_lt = /</g;
var pattern_gt = />/g;
var pattern_para = /\n/g;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0]; // Operation (insert, delete, equal)
var data = diffs[x][1]; // Text of change.
var text = data.replace(pattern_amp, '&').replace(pattern_lt, '<')
.replace(pattern_gt, '>').replace(pattern_para, '¶<br>');
switch (op) {
case DIFF_INSERT:
html[x] = '<ins style="background:#e6ffe6;">' + text + '</ins>';
break;
case DIFF_DELETE:
html[x] = '<del style="background:#ffe6e6;">' + text + '</del>';
break;
case DIFF_EQUAL:
html[x] = '<span>' + text + '</span>';
break;
}
}
return html.join('');
};
/**
* Compute and return the source text (all equalities and deletions).
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @return {string} Source text.
*/
diff_match_patch.prototype.diff_text1 = function(diffs) {
var text = [];
for (var x = 0; x < diffs.length; x++) {
if (diffs[x][0] !== DIFF_INSERT) {
text[x] = diffs[x][1];
}
}
return text.join('');
};
/**
* Compute and return the destination text (all equalities and insertions).
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @return {string} Destination text.
*/
diff_match_patch.prototype.diff_text2 = function(diffs) {
var text = [];
for (var x = 0; x < diffs.length; x++) {
if (diffs[x][0] !== DIFF_DELETE) {
text[x] = diffs[x][1];
}
}
return text.join('');
};
/**
* Compute the Levenshtein distance; the number of inserted, deleted or
* substituted characters.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @return {number} Number of changes.
*/
diff_match_patch.prototype.diff_levenshtein = function(diffs) {
var levenshtein = 0;
var insertions = 0;
var deletions = 0;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0];
var data = diffs[x][1];
switch (op) {
case DIFF_INSERT:
insertions += data.length;
break;
case DIFF_DELETE:
deletions += data.length;
break;
case DIFF_EQUAL:
// A deletion and an insertion is one substitution.
levenshtein += Math.max(insertions, deletions);
insertions = 0;
deletions = 0;
break;
}
}
levenshtein += Math.max(insertions, deletions);
return levenshtein;
};
/**
* Crush the diff into an encoded string which describes the operations
* required to transform text1 into text2.
* E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
* Operations are tab-separated. Inserted text is escaped using %xx notation.
* @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
* @return {string} Delta text.
*/
diff_match_patch.prototype.diff_toDelta = function(diffs) {
var text = [];
for (var x = 0; x < diffs.length; x++) {
switch (diffs[x][0]) {
case DIFF_INSERT:
text[x] = '+' + encodeURI(diffs[x][1]);
break;
case DIFF_DELETE:
text[x] = '-' + diffs[x][1].length;
break;
case DIFF_EQUAL:
text[x] = '=' + diffs[x][1].length;
break;
}
}
return text.join('\t').replace(/%20/g, ' ');
};
/**
* Given the original text1, and an encoded string which describes the
* operations required to transform text1 into text2, compute the full diff.
* @param {string} text1 Source string for the diff.
* @param {string} delta Delta text.
* @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.
* @throws {!Error} If invalid input.
*/
diff_match_patch.prototype.diff_fromDelta = function(text1, delta) {
var diffs = [];
var diffsLength = 0; // Keeping our own length var is faster in JS.
var pointer = 0; // Cursor in text1
var tokens = delta.split(/\t/g);
for (var x = 0; x < tokens.length; x++) {
// Each token begins with a one character parameter which specifies the
// operation of this token (delete, insert, equality).
var param = tokens[x].substring(1);
switch (tokens[x].charAt(0)) {
case '+':
try {
diffs[diffsLength++] =
new diff_match_patch.Diff(DIFF_INSERT, decodeURI(param));
} catch (ex) {
// Malformed URI sequence.
throw new Error('Illegal escape in diff_fromDelta: ' + param);
}
break;
case '-':
// Fall through.
case '=':
var n = parseInt(param, 10);
if (isNaN(n) || n < 0) {
throw new Error('Invalid number in diff_fromDelta: ' + param);
}
var text = text1.substring(pointer, pointer += n);
if (tokens[x].charAt(0) == '=') {
diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_EQUAL, text);
} else {
diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_DELETE, text);
}
break;
default:
// Blank tokens are ok (from a trailing \t).
// Anything else is an error.
if (tokens[x]) {
throw new Error('Invalid diff operation in diff_fromDelta: ' +
tokens[x]);
}
}
}
if (pointer != text1.length) {
throw new Error('Delta length (' + pointer +
') does not equal source text length (' + text1.length + ').');
}
return diffs;
};
// MATCH FUNCTIONS
/**
* Locate the best instance of 'pattern' in 'text' near 'loc'.
* @param {string} text The text to search.
* @param {string} pattern The pattern to search for.
* @param {number} loc The location to search around.
* @return {number} Best match index or -1.
*/
diff_match_patch.prototype.match_main = function(text, pattern, loc) {
// Check for null inputs.
if (text == null || pattern == null || loc == null) {
throw new Error('Null input. (match_main)');
}
loc = Math.max(0, Math.min(loc, text.length));
if (text == pattern) {
// Shortcut (potentially not guaranteed by the algorithm)
return 0;
} else if (!text.length) {
// Nothing to match.
return -1;
} else if (text.substring(loc, loc + pattern.length) == pattern) {
// Perfect match at the perfect spot! (Includes case of null pattern)
return loc;
} else {
// Do a fuzzy compare.
return this.match_bitap_(text, pattern, loc);
}
};
/**
* Locate the best instance of 'pattern' in 'text' near 'loc' using the
* Bitap algorithm.
* @param {string} text The text to search.
* @param {string} pattern The pattern to search for.
* @param {number} loc The location to search around.
* @return {number} Best match index or -1.
* @private
*/
diff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) {
if (pattern.length > this.Match_MaxBits) {
throw new Error('Pattern too long for this browser.');
}
// Initialise the alphabet.
var s = this.match_alphabet_(pattern);
var dmp = this; // 'this' becomes 'window' in a closure.
/**
* Compute and return the score for a match with e errors and x location.
* Accesses loc and pattern through being a closure.
* @param {number} e Number of errors in match.
* @param {number} x Location of match.
* @return {number} Overall score for match (0.0 = good, 1.0 = bad).
* @private
*/
function match_bitapScore_(e, x) {
var accuracy = e / pattern.length;
var proximity = Math.abs(loc - x);
if (!dmp.Match_Distance) {
// Dodge divide by zero error.
return proximity ? 1.0 : accuracy;
}
return accuracy + (proximity / dmp.Match_Distance);
}
// Highest score beyond which we give up.
var score_threshold = this.Match_Threshold;
// Is there a nearby exact match? (speedup)
var best_loc = text.indexOf(pattern, loc);
if (best_loc != -1) {
score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold);
// What about in the other direction? (speedup)
best_loc = text.lastIndexOf(pattern, loc + pattern.length);
if (best_loc != -1) {
score_threshold =
Math.min(match_bitapScore_(0, best_loc), score_threshold);
}
}
// Initialise the bit arrays.
var matchmask = 1 << (pattern.length - 1);
best_loc = -1;
var bin_min, bin_mid;
var bin_max = pattern.length + text.length;
var last_rd;
for (var d = 0; d < pattern.length; d++) {
// Scan for the best match; each iteration allows for one more error.
// Run a binary search to determine how far from 'loc' we can stray at this
// error level.
bin_min = 0;
bin_mid = bin_max;
while (bin_min < bin_mid) {
if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) {
bin_min = bin_mid;
} else {
bin_max = bin_mid;
}
bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min);
}
// Use the result from this iteration as the maximum for the next.
bin_max = bin_mid;
var start = Math.max(1, loc - bin_mid + 1);
var finish = Math.min(loc + bin_mid, text.length) + pattern.length;
var rd = Array(finish + 2);
rd[finish + 1] = (1 << d) - 1;
for (var j = finish; j >= start; j--) {
// The alphabet (s) is a sparse hash, so the following line generates
// warnings.
var charMatch = s[text.charAt(j - 1)];
if (d === 0) { // First pass: exact match.
rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
} else { // Subsequent passes: fuzzy match.
rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) |
(((last_rd[j + 1] | last_rd[j]) << 1) | 1) |
last_rd[j + 1];
}
if (rd[j] & matchmask) {
var score = match_bitapScore_(d, j - 1);
// This match will almost certainly be better than any existing match.
// But check anyway.
if (score <= score_threshold) {
// Told you so.
score_threshold = score;
best_loc = j - 1;
if (best_loc > loc) {
// When passing loc, don't exceed our current distance from loc.
start = Math.max(1, 2 * loc - best_loc);
} else {
// Already passed loc, downhill from here on in.
break;
}
}
}
}
// No hope for a (better) match at greater error levels.
if (match_bitapScore_(d + 1, loc) > score_threshold) {
break;
}
last_rd = rd;
}
return best_loc;
};
/**
* Initialise the alphabet for the Bitap algorithm.
* @param {string} pattern The text to encode.
* @return {!Object} Hash of character locations.
* @private
*/
diff_match_patch.prototype.match_alphabet_ = function(pattern) {
var s = {};
for (var i = 0; i < pattern.length; i++) {
s[pattern.charAt(i)] = 0;
}
for (var i = 0; i < pattern.length; i++) {
s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1);
}
return s;
};
// PATCH FUNCTIONS
/**
* Increase the context until it is unique,
* but don't let the pattern expand beyond Match_MaxBits.
* @param {!diff_match_patch.patch_obj} patch The patch to grow.
* @param {string} text Source text.
* @private
*/
diff_match_patch.prototype.patch_addContext_ = function(patch, text) {
if (text.length == 0) {
return;
}
if (patch.start2 === null) {
throw Error('patch not initialized');
}
var pattern = text.substring(patch.start2, patch.start2 + patch.length1);
var padding = 0;
// Look for the first and last matches of pattern in text. If two different
// matches are found, increase the pattern length.
while (text.indexOf(pattern) != text.lastIndexOf(pattern) &&
pattern.length < this.Match_MaxBits - this.Patch_Margin -
this.Patch_Margin) {
padding += this.Patch_Margin;
pattern = text.substring(patch.start2 - padding,
patch.start2 + patch.length1 + padding);
}
// Add one chunk for good luck.
padding += this.Patch_Margin;
// Add the prefix.
var prefix = text.substring(patch.start2 - padding, patch.start2);
if (prefix) {
patch.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, prefix));
}
// Add the suffix.
var suffix = text.substring(patch.start2 + patch.length1,
patch.start2 + patch.length1 + padding);
if (suffix) {
patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, suffix));
}
// Roll back the start points.
patch.start1 -= prefix.length;
patch.start2 -= prefix.length;
// Extend the lengths.
patch.length1 += prefix.length + suffix.length;
patch.length2 += prefix.length + suffix.length;
};
/**
* Compute a list of patches to turn text1 into text2.
* Use diffs if provided, otherwise compute it ourselves.
* There are four ways to call this function, depending on what data is
* available to the caller:
* Method 1:
* a = text1, b = text2
* Method 2:
* a = diffs
* Method 3 (optimal):
* a = text1, b = diffs
* Method 4 (deprecated, use method 3):
* a = text1, b = text2, c = diffs
*
* @param {string|!Array.<!diff_match_patch.Diff>} a text1 (methods 1,3,4) or
* Array of diff tuples for text1 to text2 (method 2).
* @param {string|!Array.<!diff_match_patch.Diff>=} opt_b text2 (methods 1,4) or
* Array of diff tuples for text1 to text2 (method 3) or undefined (method 2).
* @param {string|!Array.<!diff_match_patch.Diff>=} opt_c Array of diff tuples
* for text1 to text2 (method 4) or undefined (methods 1,2,3).
* @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.
*/
diff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) {
var text1, diffs;
if (typeof a == 'string' && typeof opt_b == 'string' &&
typeof opt_c == 'undefined') {
// Method 1: text1, text2
// Compute diffs from text1 and text2.
text1 = /** @type {string} */(a);
diffs = this.diff_main(text1, /** @type {string} */(opt_b), true);
if (diffs.length > 2) {
this.diff_cleanupSemantic(diffs);
this.diff_cleanupEfficiency(diffs);
}
} else if (a && typeof a == 'object' && typeof opt_b == 'undefined' &&
typeof opt_c == 'undefined') {
// Method 2: diffs
// Compute text1 from diffs.
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(a);
text1 = this.diff_text1(diffs);
} else if (typeof a == 'string' && opt_b && typeof opt_b == 'object' &&
typeof opt_c == 'undefined') {
// Method 3: text1, diffs
text1 = /** @type {string} */(a);
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(opt_b);
} else if (typeof a == 'string' && typeof opt_b == 'string' &&
opt_c && typeof opt_c == 'object') {
// Method 4: text1, text2, diffs
// text2 is not used.
text1 = /** @type {string} */(a);
diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(opt_c);
} else {
throw new Error('Unknown call format to patch_make.');
}
if (diffs.length === 0) {
return []; // Get rid of the null case.
}
var patches = [];
var patch = new diff_match_patch.patch_obj();
var patchDiffLength = 0; // Keeping our own length var is faster in JS.
var char_count1 = 0; // Number of characters into the text1 string.
var char_count2 = 0; // Number of characters into the text2 string.
// Start with text1 (prepatch_text) and apply the diffs until we arrive at
// text2 (postpatch_text). We recreate the patches one by one to determine
// context info.
var prepatch_text = text1;
var postpatch_text = text1;
for (var x = 0; x < diffs.length; x++) {
var diff_type = diffs[x][0];
var diff_text = diffs[x][1];
if (!patchDiffLength && diff_type !== DIFF_EQUAL) {
// A new patch starts here.
patch.start1 = char_count1;
patch.start2 = char_count2;
}
switch (diff_type) {
case DIFF_INSERT:
patch.diffs[patchDiffLength++] = diffs[x];
patch.length2 += diff_text.length;
postpatch_text = postpatch_text.substring(0, char_count2) + diff_text +
postpatch_text.substring(char_count2);
break;
case DIFF_DELETE:
patch.length1 += diff_text.length;
patch.diffs[patchDiffLength++] = diffs[x];
postpatch_text = postpatch_text.substring(0, char_count2) +
postpatch_text.substring(char_count2 +
diff_text.length);
break;
case DIFF_EQUAL:
if (diff_text.length <= 2 * this.Patch_Margin &&
patchDiffLength && diffs.length != x + 1) {
// Small equality inside a patch.
patch.diffs[patchDiffLength++] = diffs[x];
patch.length1 += diff_text.length;
patch.length2 += diff_text.length;
} else if (diff_text.length >= 2 * this.Patch_Margin) {
// Time for a new patch.
if (patchDiffLength) {
this.patch_addContext_(patch, prepatch_text);
patches.push(patch);
patch = new diff_match_patch.patch_obj();
patchDiffLength = 0;
// Unlike Unidiff, our patch lists have a rolling context.
// https://github.com/google/diff-match-patch/wiki/Unidiff
// Update prepatch text & pos to reflect the application of the
// just completed patch.
prepatch_text = postpatch_text;
char_count1 = char_count2;
}
}
break;
}
// Update the current character count.
if (diff_type !== DIFF_INSERT) {
char_count1 += diff_text.length;
}
if (diff_type !== DIFF_DELETE) {
char_count2 += diff_text.length;
}
}
// Pick up the leftover patch if not empty.
if (patchDiffLength) {
this.patch_addContext_(patch, prepatch_text);
patches.push(patch);
}
return patches;
};
/**
* Given an array of patches, return another array that is identical.
* @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.
* @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.
*/
diff_match_patch.prototype.patch_deepCopy = function(patches) {
// Making deep copies is hard in JavaScript.
var patchesCopy = [];
for (var x = 0; x < patches.length; x++) {
var patch = patches[x];
var patchCopy = new diff_match_patch.patch_obj();
patchCopy.diffs = [];
for (var y = 0; y < patch.diffs.length; y++) {
patchCopy.diffs[y] =
new diff_match_patch.Diff(patch.diffs[y][0], patch.diffs[y][1]);
}
patchCopy.start1 = patch.start1;
patchCopy.start2 = patch.start2;
patchCopy.length1 = patch.length1;
patchCopy.length2 = patch.length2;
patchesCopy[x] = patchCopy;
}
return patchesCopy;
};
/**
* Merge a set of patches onto the text. Return a patched text, as well
* as a list of true/false values indicating which patches were applied.
* @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.
* @param {string} text Old text.
* @return {!Array.<string|!Array.<boolean>>} Two element Array, containing the
* new text and an array of boolean values.
*/
diff_match_patch.prototype.patch_apply = function(patches, text) {
if (patches.length == 0) {
return [text, []];
}
// Deep copy the patches so that no changes are made to originals.
patches = this.patch_deepCopy(patches);
var nullPadding = this.patch_addPadding(patches);
text = nullPadding + text + nullPadding;
this.patch_splitMax(patches);
// delta keeps track of the offset between the expected and actual location
// of the previous patch. If there are patches expected at positions 10 and
// 20, but the first patch was found at 12, delta is 2 and the second patch
// has an effective expected position of 22.
var delta = 0;
var results = [];
for (var x = 0; x < patches.length; x++) {
var expected_loc = patches[x].start2 + delta;
var text1 = this.diff_text1(patches[x].diffs);
var start_loc;
var end_loc = -1;
if (text1.length > this.Match_MaxBits) {
// patch_splitMax will only provide an oversized pattern in the case of
// a monster delete.
start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits),
expected_loc);
if (start_loc != -1) {
end_loc = this.match_main(text,
text1.substring(text1.length - this.Match_MaxBits),
expected_loc + text1.length - this.Match_MaxBits);
if (end_loc == -1 || start_loc >= end_loc) {
// Can't find valid trailing context. Drop this patch.
start_loc = -1;
}
}
} else {
start_loc = this.match_main(text, text1, expected_loc);
}
if (start_loc == -1) {
// No match found. :(
results[x] = false;
// Subtract the delta for this failed patch from subsequent patches.
delta -= patches[x].length2 - patches[x].length1;
} else {
// Found a match. :)
results[x] = true;
delta = start_loc - expected_loc;
var text2;
if (end_loc == -1) {
text2 = text.substring(start_loc, start_loc + text1.length);
} else {
text2 = text.substring(start_loc, end_loc + this.Match_MaxBits);
}
if (text1 == text2) {
// Perfect match, just shove the replacement text in.
text = text.substring(0, start_loc) +
this.diff_text2(patches[x].diffs) +
text.substring(start_loc + text1.length);
} else {
// Imperfect match. Run a diff to get a framework of equivalent
// indices.
var diffs = this.diff_main(text1, text2, false);
if (text1.length > this.Match_MaxBits &&
this.diff_levenshtein(diffs) / text1.length >
this.Patch_DeleteThreshold) {
// The end points match, but the content is unacceptably bad.
results[x] = false;
} else {
this.diff_cleanupSemanticLossless(diffs);
var index1 = 0;
var index2;
for (var y = 0; y < patches[x].diffs.length; y++) {
var mod = patches[x].diffs[y];
if (mod[0] !== DIFF_EQUAL) {
index2 = this.diff_xIndex(diffs, index1);
}
if (mod[0] === DIFF_INSERT) { // Insertion
text = text.substring(0, start_loc + index2) + mod[1] +
text.substring(start_loc + index2);
} else if (mod[0] === DIFF_DELETE) { // Deletion
text = text.substring(0, start_loc + index2) +
text.substring(start_loc + this.diff_xIndex(diffs,
index1 + mod[1].length));
}
if (mod[0] !== DIFF_DELETE) {
index1 += mod[1].length;
}
}
}
}
}
}
// Strip the padding off.
text = text.substring(nullPadding.length, text.length - nullPadding.length);
return [text, results];
};
/**
* Add some padding on text start and end so that edges can match something.
* Intended to be called only from within patch_apply.
* @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.
* @return {string} The padding string added to each side.
*/
diff_match_patch.prototype.patch_addPadding = function(patches) {
var paddingLength = this.Patch_Margin;
var nullPadding = '';
for (var x = 1; x <= paddingLength; x++) {
nullPadding += String.fromCharCode(x);
}
// Bump all the patches forward.
for (var x = 0; x < patches.length; x++) {
patches[x].start1 += paddingLength;
patches[x].start2 += paddingLength;
}
// Add some padding on start of first diff.
var patch = patches[0];
var diffs = patch.diffs;
if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) {
// Add nullPadding equality.
diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));
patch.start1 -= paddingLength; // Should be 0.
patch.start2 -= paddingLength; // Should be 0.
patch.length1 += paddingLength;
patch.length2 += paddingLength;
} else if (paddingLength > diffs[0][1].length) {
// Grow first equality.
var extraLength = paddingLength - diffs[0][1].length;
diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1];
patch.start1 -= extraLength;
patch.start2 -= extraLength;
patch.length1 += extraLength;
patch.length2 += extraLength;
}
// Add some padding on end of last diff.
patch = patches[patches.length - 1];
diffs = patch.diffs;
if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) {
// Add nullPadding equality.
diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));
patch.length1 += paddingLength;
patch.length2 += paddingLength;
} else if (paddingLength > diffs[diffs.length - 1][1].length) {
// Grow last equality.
var extraLength = paddingLength - diffs[diffs.length - 1][1].length;
diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength);
patch.length1 += extraLength;
patch.length2 += extraLength;
}
return nullPadding;
};
/**
* Look through the patches and break up any which are longer than the maximum
* limit of the match algorithm.
* Intended to be called only from within patch_apply.
* @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.
*/
diff_match_patch.prototype.patch_splitMax = function(patches) {
var patch_size = this.Match_MaxBits;
for (var x = 0; x < patches.length; x++) {
if (patches[x].length1 <= patch_size) {
continue;
}
var bigpatch = patches[x];
// Remove the big old patch.
patches.splice(x--, 1);
var start1 = bigpatch.start1;
var start2 = bigpatch.start2;
var precontext = '';
while (bigpatch.diffs.length !== 0) {
// Create one of several smaller patches.
var patch = new diff_match_patch.patch_obj();
var empty = true;
patch.start1 = start1 - precontext.length;
patch.start2 = start2 - precontext.length;
if (precontext !== '') {
patch.length1 = patch.length2 = precontext.length;
patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, precontext));
}
while (bigpatch.diffs.length !== 0 &&
patch.length1 < patch_size - this.Patch_Margin) {
var diff_type = bigpatch.diffs[0][0];
var diff_text = bigpatch.diffs[0][1];
if (diff_type === DIFF_INSERT) {
// Insertions are harmless.
patch.length2 += diff_text.length;
start2 += diff_text.length;
patch.diffs.push(bigpatch.diffs.shift());
empty = false;
} else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 &&
patch.diffs[0][0] == DIFF_EQUAL &&
diff_text.length > 2 * patch_size) {
// This is a large deletion. Let it pass in one chunk.
patch.length1 += diff_text.length;
start1 += diff_text.length;
empty = false;
patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));
bigpatch.diffs.shift();
} else {
// Deletion or equality. Only take as much as we can stomach.
diff_text = diff_text.substring(0,
patch_size - patch.length1 - this.Patch_Margin);
patch.length1 += diff_text.length;
start1 += diff_text.length;
if (diff_type === DIFF_EQUAL) {
patch.length2 += diff_text.length;
start2 += diff_text.length;
} else {
empty = false;
}
patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));
if (diff_text == bigpatch.diffs[0][1]) {
bigpatch.diffs.shift();
} else {
bigpatch.diffs[0][1] =
bigpatch.diffs[0][1].substring(diff_text.length);
}
}
}
// Compute the head context for the next patch.
precontext = this.diff_text2(patch.diffs);
precontext =
precontext.substring(precontext.length - this.Patch_Margin);
// Append the end context for this patch.
var postcontext = this.diff_text1(bigpatch.diffs)
.substring(0, this.Patch_Margin);
if (postcontext !== '') {
patch.length1 += postcontext.length;
patch.length2 += postcontext.length;
if (patch.diffs.length !== 0 &&
patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) {
patch.diffs[patch.diffs.length - 1][1] += postcontext;
} else {
patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, postcontext));
}
}
if (!empty) {
patches.splice(++x, 0, patch);
}
}
}
};
/**
* Take a list of patches and return a textual representation.
* @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.
* @return {string} Text representation of patches.
*/
diff_match_patch.prototype.patch_toText = function(patches) {
var text = [];
for (var x = 0; x < patches.length; x++) {
text[x] = patches[x];
}
return text.join('');
};
/**
* Parse a textual representation of patches and return a list of Patch objects.
* @param {string} textline Text representation of patches.
* @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.
* @throws {!Error} If invalid input.
*/
diff_match_patch.prototype.patch_fromText = function(textline) {
var patches = [];
if (!textline) {
return patches;
}
var text = textline.split('\n');
var textPointer = 0;
var patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;
while (textPointer < text.length) {
var m = text[textPointer].match(patchHeader);
if (!m) {
throw new Error('Invalid patch string: ' + text[textPointer]);
}
var patch = new diff_match_patch.patch_obj();
patches.push(patch);
patch.start1 = parseInt(m[1], 10);
if (m[2] === '') {
patch.start1--;
patch.length1 = 1;
} else if (m[2] == '0') {
patch.length1 = 0;
} else {
patch.start1--;
patch.length1 = parseInt(m[2], 10);
}
patch.start2 = parseInt(m[3], 10);
if (m[4] === '') {
patch.start2--;
patch.length2 = 1;
} else if (m[4] == '0') {
patch.length2 = 0;
} else {
patch.start2--;
patch.length2 = parseInt(m[4], 10);
}
textPointer++;
while (textPointer < text.length) {
var sign = text[textPointer].charAt(0);
try {
var line = decodeURI(text[textPointer].substring(1));
} catch (ex) {
// Malformed URI sequence.
throw new Error('Illegal escape in patch_fromText: ' + line);
}
if (sign == '-') {
// Deletion.
patch.diffs.push(new diff_match_patch.Diff(DIFF_DELETE, line));
} else if (sign == '+') {
// Insertion.
patch.diffs.push(new diff_match_patch.Diff(DIFF_INSERT, line));
} else if (sign == ' ') {
// Minor equality.
patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, line));
} else if (sign == '@') {
// Start of next patch.
break;
} else if (sign === '') {
// Blank line? Whatever.
} else {
// WTF?
throw new Error('Invalid patch mode "' + sign + '" in: ' + line);
}
textPointer++;
}
}
return patches;
};
/**
* Class representing one patch operation.
* @constructor
*/
diff_match_patch.patch_obj = function() {
/** @type {!Array.<!diff_match_patch.Diff>} */
this.diffs = [];
/** @type {?number} */
this.start1 = null;
/** @type {?number} */
this.start2 = null;
/** @type {number} */
this.length1 = 0;
/** @type {number} */
this.length2 = 0;
};
/**
* Emulate GNU diff's format.
* Header: @@ -382,8 +481,9 @@
* Indices are printed as 1-based, not 0-based.
* @return {string} The GNU diff string.
*/
diff_match_patch.patch_obj.prototype.toString = function() {
var coords1, coords2;
if (this.length1 === 0) {
coords1 = this.start1 + ',0';
} else if (this.length1 == 1) {
coords1 = this.start1 + 1;
} else {
coords1 = (this.start1 + 1) + ',' + this.length1;
}
if (this.length2 === 0) {
coords2 = this.start2 + ',0';
} else if (this.length2 == 1) {
coords2 = this.start2 + 1;
} else {
coords2 = (this.start2 + 1) + ',' + this.length2;
}
var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n'];
var op;
// Escape the body of the patch with %xx notation.
for (var x = 0; x < this.diffs.length; x++) {
switch (this.diffs[x][0]) {
case DIFF_INSERT:
op = '+';
break;
case DIFF_DELETE:
op = '-';
break;
case DIFF_EQUAL:
op = ' ';
break;
}
text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n';
}
return text.join('').replace(/%20/g, ' ');
};
// CLOSURE:begin_strip
// Lines below here will not be included in the Closure-compatible library.
// Export these global variables so that they survive Google's JS compiler.
// In a browser, 'this' will be 'window'.
// Users of node.js should 'require' the uncompressed version since Google's
// JS compiler may break the following exports for non-browser environments.
/** @suppress {globalThis} */
this['diff_match_patch'] = diff_match_patch;
/** @suppress {globalThis} */
this['DIFF_DELETE'] = DIFF_DELETE;
/** @suppress {globalThis} */
this['DIFF_INSERT'] = DIFF_INSERT;
/** @suppress {globalThis} */
this['DIFF_EQUAL'] = DIFF_EQUAL;
//}}}
//{{{
var code = eval("diff_match_patch.prototype.diff_prettyHtml").toString();
code = code.replace("¶<br>", "<br>");
eval("diff_match_patch.prototype.diff_prettyHtml = function diff_prettyHtml" + code.substring(code.indexOf("(")));
code = eval("diff_match_patch.patch_obj.prototype.toString").toString();
code = code.replace("encodeURI", "");
code = code.replace(".replace(/%20/g, ' ')", "");
eval("diff_match_patch.patch_obj.prototype.toString = function toString" + code.substring(code.indexOf("(")));
this.initialized = true;
}
},
//}}}
//{{{
diff: function (text1, text2, params) {
this.init();
var mode = "";
var cleanup = "";
var format = null;
var diff_obj = new this.diff_match_patch();
if (params) {
for (var p in params) {
if ((p === "mode") && params[p]) {
mode = params[p];
} else if ((p === "cleanup") && params[p]) {
cleanup = (params[p] === true) ? "efficiency" : params[p];
} else if (p === "format") {
format = (params[p] === true) ? "html" : params[p];
} else {
diff_obj[p] = params[p];
}
}
}
var diffs;
switch (mode) {
case "line":
var a = diff_obj.diff_linesToChars_(text1, text2);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
diffs = diff_obj.diff_main(lineText1, lineText2, false);
diff_obj.diff_charsToLines_(diffs, lineArray);
break;
default:
diffs = diff_obj.diff_main(text1, text2, true);
break;
}
if (cleanup.indexOf("semantic") > -1) {
diff_obj.diff_cleanupSemantic(diffs);
}
if (cleanup.indexOf("efficiency") > -1) {
diff_obj.diff_cleanupEfficiency(diffs);
}
if (format) {
switch (format) {
case "html":
diffs = diff_obj.diff_prettyHtml(diffs);
break;
case "patch":
if (diffs.length > 2) {
diff_obj.diff_cleanupSemantic(diffs);
}
var patch_list = diff_obj.patch_make(text1, text2, diffs);
diffs = diff_obj.patch_toText(patch_list);
break;
}
}
return diffs;
},
//}}}
//{{{
// https://www.agiliq.com/blog/2014/05/google-diff-match-patch-library
formatOldContent: function (diffs) {
this.init();
// Returns HTML representation of 'deletions'
var html = [];
for (var x = 0; x < diffs.length; x++) {
var flag = diffs[x][0];
var data = diffs[x][1];
var text = data.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\n/g, "<br>");
if (flag == this.DIFF_DELETE) {
html.push("<del style='background:#ffe6e6;'>" + text + "</del>");
} else if (flag == this.DIFF_EQUAL) {
html.push("<span>" + text + "</span>");
}
}
return html.join("");
},
formatNewContent: function (diffs) {
this.init();
// Returns HTML representation of 'insertions'
var html = [];
for (var x = 0; x < diffs.length; x++) {
var flag = diffs[x][0];
var data = diffs[x][1];
var text = data.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/\n/g, "<br>");
if (flag == this.DIFF_INSERT) {
html.push("<ins style='background:#e6ffe6;'>" + text + "</ins>");
} else if (flag == this.DIFF_EQUAL) {
html.push("<span>" + text + "</span>");
}
}
return html.join("");
}
//}}}
//{{{
}
//}}}
!usage
{{{[img[fractalveg.jpg]]}}}
[img[fractalveg.jpg]]
!notes
attached by FileDropPlugin
!type
image/jpeg
!file
F:/fractalveg.jpg
!url
!data
data:image/jpeg;base64,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
/***
|Name|iCalendarPlugin|
|License|[[TW Notes License]]|
|Requires|[[ical.js]]|
!!!!!Code
***/
//{{{
if (config.options.txtICalendarTiddlerName === undefined) {
config.options.txtICalendarTiddlerName = "iCalendarPlugin Reminders";
}
if (config.options.txtICalendarEventTransformer === undefined) {
config.options.txtICalendarEventTransformer = "";
}
merge(config.optionsDesc, {
txtICalendarTiddlerName: "Name of the tiddler in which to store iCalendarPlugin reminders",
txtICalendarEventTransformer: "Code used to transform or exclude events"
});
//}}}
//{{{
config.macros.ics = {
cache: {},
normalize: function (ics) {
if (ics) {
ics = ics.replace(/\n\n/g, "\n").replace(/X-LOTUS-CHILD_UID:/gi, "X-LOTUS-CHILD-UID:");
}
return ics;
},
determineStartEnd: function (event) {
var occurrenceDetails = event;
var occurrence = event.iterator().next();
if (occurrence) {
occurrenceDetails = event.getOccurrenceDetails(occurrence);
}
var dateStart = occurrenceDetails.startDate ? occurrenceDetails.startDate.toJSDate() : "";
var dateEnd = occurrenceDetails.endDate ? occurrenceDetails.endDate.toJSDate() : "";
return [dateStart, dateEnd];
},
parse: function (tiddlerNameOrTag, calendarTiddlerName, serializer) {
var delimiterStart = "\n{{{BEGIN " + tiddlerNameOrTag + "}}}";
var delimiterEnd = "\n{{{END " + tiddlerNameOrTag + "}}}";
if (!this.cache[calendarTiddlerName]) {
this.cache[calendarTiddlerName] = {};
}
var calendars = null;
var icsTiddler = store.getTiddler(tiddlerNameOrTag);
var icsTaggedTiddlers = store.getTaggedTiddlers(tiddlerNameOrTag, "title");
if (icsTiddler || (icsTaggedTiddlers && icsTaggedTiddlers.length > 0)) {
var tiddlers = [];
if (icsTiddler) {
tiddlers[tiddlers.length] = icsTiddler;
}
var tiddlerText = icsTiddler ? icsTiddler.text : "";
if (icsTaggedTiddlers && icsTaggedTiddlers.length > 0) {
for (var i = 0; i < icsTaggedTiddlers.length; i++) {
tiddlers[tiddlers.length] = icsTaggedTiddlers[i];
tiddlerText = tiddlerText + ((tiddlerText.length > 0) ? "\r\n" : "") + icsTaggedTiddlers[i].text;
}
}
var parsedIcs = this.cache[calendarTiddlerName][tiddlerNameOrTag];
if (parsedIcs && (parsedIcs.tiddlerText == tiddlerText)) {
calendars = parsedIcs.calendars;
}
if (calendars == null) {
var calendarTiddler = store.getTiddler(calendarTiddlerName);
if (calendarTiddler) {
var calendarTiddlerText = calendarTiddler.text;
var startIndex = calendarTiddlerText.indexOf(delimiterStart);
if (startIndex > -1) {
var endIndex = calendarTiddlerText.indexOf(delimiterEnd);
if (endIndex > -1) {
var calendarText = calendarTiddlerText.substring(startIndex + delimiterStart.length, endIndex).trim();
var tiddlerTextSha1 = jQuery.encoding.digests.hexSha1Str(tiddlerText);
var calendarIndex = calendarText.indexOf(tiddlerTextSha1);
if (calendarIndex == 0) {
var serializedCalendars = calendarText.substring(tiddlerTextSha1.length);
calendars = serializer.deserialize(serializedCalendars);
this.cache[calendarTiddlerName][tiddlerNameOrTag] = {
tiddlerText: tiddlerText,
calendars: calendars
};
} else {
calendarTiddlerText = calendarTiddlerText.substring(0, startIndex) + calendarTiddlerText.substring(endIndex + delimiterEnd.length);
calendarTiddler.text = calendarTiddlerText;
calendarTiddler.modifier = config.options.txtUserName;
calendarTiddler.modified = new Date();
store.setDirty(true);
}
}
}
}
}
if (calendars == null) {
calendarTiddlers = [];
calendars = [];
for (var i = 0; i < tiddlers.length; i++) {
var tiddlersText = tiddlers[i].text;
var icsStart;
var icsEnd = 0;
while ((icsStart = tiddlersText.indexOf("BEGIN:VCALENDAR", icsEnd)) > -1) {
icsEnd = tiddlersText.indexOf("END:VCALENDAR", icsStart);
if (icsEnd > -1) {
var ics = tiddlersText.substring(icsStart, icsEnd + "END:VCALENDAR".length);
ics = this.normalize(ics);
calendars[calendars.length] = ICAL.parse(ics);
calendarTiddlers[calendarTiddlers.length] = tiddlers[i];
} else {
break;
}
}
}
this.cache[calendarTiddlerName][tiddlerNameOrTag] = {
tiddlerText: tiddlerText,
calendars: calendars
};
var calendarTiddler = store.getTiddler(calendarTiddlerName);
if (!calendarTiddler) {
store.saveTiddler(calendarTiddlerName, calendarTiddlerName, "", config.options.txtUserName, new Date(), ["readOnlyReminder"]);
calendarTiddler = store.getTiddler(calendarTiddlerName);
calendarTiddler.text = "calendars\n";
} else {
calendarTiddler.modified = new Date();
if (!calendarTiddler.text.trim()) {
calendarTiddler.text = "calendars\n";
}
}
calendarTiddler.text = calendarTiddler.text + delimiterStart + jQuery.encoding.digests.hexSha1Str(tiddlerText) + "\n" + serializer.serialize(calendars, calendarTiddlers) + delimiterEnd;
calendarTiddler.modifier = config.options.txtUserName;
calendarTiddler.modified = new Date();
store.setDirty(true);
}
}
return calendars;
},
formatTitle: function (dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var tiddler = null;
if (dateHash["title"]) {
if (!dateHash["title-processed"]) {
dateHash["title"] = dateHash["title"]
.replace(/'/g, "'").replace(/"/g, '"').replace(/&/g, "&").replace(/\\"/g, '"')
.replace(/\\\[\\\[/g, "[[").replace(/\\\]\\\]/g, "]]")
.replace(/\\\[\\\[/g, "[[").replace(/\\\]\\\]/g, "]]")
.replace(/\\\</g, "<").replace(/\\\>/g, ">");
dateHash["title-processed"] = true;
}
var titles = [];
dateHash["title"].replace(/.*<<(goToTiddler|newTiddler).*title:\{\{(['|"](?:(?!['|"]\}\}).)*['|"])\}\}.*>>.*/, function () {
titles.push(Array.prototype.slice.call(arguments, 2, 3));
});
if (titles.length > 0) {
tiddler = store.getTiddler(eval("" + titles[0]));
}
if (tiddler) {
dateHash["title"] = dateHash["title"].replace(/(.*<<(goToTiddler|newTiddler).*)text:\{\{['|"].*['|"]\}\}(.*>>.*)/g, "$1$3");
}
}
return tiddler;
},
create: function (tiddlerNameOrTag, dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound) {
var cmi = this;
var escapeCharacters = function (text) {
text = text.replace(/\\;/g, ";").replace(/\\,/g, ",").replace(/\\/g, "\\\\").replace(/\$/g, "$$$$").replace(/\n/g, "\\n").replace(/>/g, "\\\\>").replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "\\'");
return text;
};
var processDateHashTitleForTiddlerName = function (dateHashTitle, dateStart, dateEnd, summary, dateToken) {
if (dateHashTitle) {
var startTime = jQuery.isFunction(dateStart.getHours) ? ((dateStart.getHours() < 10 ? "0" : "") + dateStart.getHours() + ":" + (dateStart.getMinutes() < 10 ? "0" : "") + dateStart.getMinutes()) : dateStart;
var endTime = jQuery.isFunction(dateEnd.getHours) ? ((dateEnd.getHours() < 10 ? "0" : "") + dateEnd.getHours() + ":" + (dateEnd.getMinutes() < 10 ? "0" : "") + dateEnd.getMinutes()) : dateEnd;
dateHashTitle = dateHashTitle.replace(/%DATE%/g, dateToken ? dateToken : dateStart.formatString("YYYY-0MM-0DD"));
dateHashTitle = dateHashTitle.replace(/%START%/g, startTime);
dateHashTitle = dateHashTitle.replace(/%END%/g, endTime);
dateHashTitle = dateHashTitle.replace(/\{\{(.*)'%SUMMARY%'(.*)\}\}/g, summary ? "{{$1'" + escapeCharacters(summary) + "'$2}}" : "{{$1'???'$2}}");
dateHashTitle = dateHashTitle.replace(/\{\{(.*)"%SUMMARY%"(.*)\}\}/g, summary ? "{{$1\"" + escapeCharacters(summary) + "\"$2}}" : "{{$1\"???\"$2}}");
dateHashTitle = dateHashTitle.replace(/%SUMMARY%/g, summary ? summary.replace(/\\;/g, ";").replace(/\\,/g, ",").replace(/([^\\])"/g, '$1\\"').replace(/>/g, "\\>") : "");
}
return dateHashTitle;
};
var serializer = {
serialize: function (calendars, calendarTiddlers) {
var reminders = {};
var events = [];
for (var i = 0; i < calendars.length; i++) {
var calendarTiddler = calendarTiddlers[i];
var vcalendar = new ICAL.Component(calendars[i]);
var vtimezones = vcalendar.getAllSubcomponents("vtimezone");
for (var j = 0; j < vtimezones.length; j++) {
var tzid = vtimezones[j].getFirstProperty("tzid");
if (!tzid || !ICAL.TimezoneService.has(tzid.getFirstValue())) {
ICAL.TimezoneService.register(vtimezones[j]);
}
}
var vevents = vcalendar.getAllSubcomponents("vevent");
for (var j = 0; j < vevents.length; j++) {
var vevent = vevents[j];
var event = new ICAL.Event(vevents[j], {strictExceptions: true});
var startEnd = cmi.determineStartEnd(event);
var dateStart = startEnd[0];
var dateEnd = startEnd[1];
dateStart = (dateStart) ? dateStart : (dateEnd ? dateEnd : new Date());
dateEnd = (dateEnd) ? dateEnd : dateStart;
var status = vevent.getFirstPropertyValue("status");
var isCancelled = status && (status.toUpperCase() === "CANCELLED");
if (config.options.txtICalendarEventTransformer) {
try {
eval(config.options.txtICalendarEventTransformer);
} catch (e) {}
}
if (!isCancelled) {
events.push({
calendarTiddler: calendarTiddler,
vevent: vevent,
event: event,
dateStart: dateStart,
dateEnd: dateEnd,
status: status,
rrule: vevent.getFirstProperty("rrule"),
uid: vevent.getFirstPropertyValue("uid")
});
}
}
}
var rruleEvents = {};
for (var i = 0; i < events.length; i++) {
if (events[i].uid && events[i].rrule) {
rruleEvents[events[i].uid] = events[i];
}
}
for (var i = 0; i < events.length; i++) {
if (events[i].uid && !events[i].rrule && rruleEvents[events[i].uid] && events[i].vevent.getFirstProperty("recurrence-id")) {
var exdate = rruleEvents[events[i].uid].vevent.getFirstProperty("exdate");
if (!exdate) {
exdate = ICAL.helpers.clone(events[i].vevent.getFirstProperty("recurrence-id").jCal, true);
exdate[0] = "exdate";
exdate = new ICAL.Property(exdate);
rruleEvents[events[i].uid].vevent.addProperty(exdate);
} else {
exdate.setValues(exdate.getValues().concat(events[i].vevent.getFirstProperty("recurrence-id").getValues()).sort());
}
}
}
for (var i = 0; i < events.length; i++) {
var calendarTiddler = events[i].calendarTiddler;
var vevent = events[i].vevent;
var event = events[i].event;
var dateStart = events[i].dateStart;
var dateEnd = events[i].dateEnd;
var status = events[i].status;
var rrule = null;
var rruleProperty = events[i].rrule;
if ((rruleProperty != null) && vevent.getFirstProperty("dtstart")) {
var recur = null;
var rrulePropertyJCal = rruleProperty.toJSON();
var recurIndex = 0;
for (; recurIndex < rrulePropertyJCal.length; recurIndex++) {
if ((rrulePropertyJCal[recurIndex] === "recur") && (rrulePropertyJCal.length > recurIndex + 1)) {
recur = new ICAL.Recur(rrulePropertyJCal[recurIndex + 1]);
break;
}
}
if ((recur != null) && recur.count) {
var last = null;
var iterator = event.iterator();
do {
var next = iterator.next();
last = (next == null) ? last : next;
} while (next != null);
if (last && event.getOccurrenceDetails(last) && event.getOccurrenceDetails(last).startDate) {
recur.until = event.getOccurrenceDetails(last).startDate.convertToZone(ICAL.Timezone.utcTimezone);
recur.count = null;
}
rrulePropertyJCal[recurIndex + 1] = recur.toJSON();
}
var dtend = vevent.getFirstProperty("dtend");
rrule = ICAL.stringify.property(vevent.getFirstProperty("dtstart").toJSON(), ICAL.design.defaultSet, true)
+ (dtend ? "\\n" + ICAL.stringify.property(dtend.toJSON(), ICAL.design.defaultSet, true) : "")
+ "\\n" + ICAL.stringify.property(rruleProperty.toJSON(), ICAL.design.defaultSet, true);
}
var rdateProperty = vevent.getFirstProperty("rdate");
if (rdateProperty != null) {
rrule = (rrule ? rrule + "\\n" : "") + ICAL.stringify.property(rdateProperty.toJSON(), ICAL.design.defaultSet, true);
}
var exdateProperty = vevent.getFirstProperty("exdate");
if (exdateProperty != null) {
rrule = (rrule ? rrule + "\\n" : "") + ICAL.stringify.property(exdateProperty.toJSON(), ICAL.design.defaultSet, true);
}
if (rrule) {
var timezones = "";
for (var k = 0; k < vtimezones.length; k++) {
timezones += ICAL.stringify.component(vtimezones[k].toJSON(), ICAL.design.defaultSet).replace(/\r/g, "").replace(/\n/g, "\\n") + "\\n";
}
rrule = timezones + rrule;
}
var summary = event.summary;
summary = summary ? summary.trim() : "???";
var description = event.description;
var uid = events[i].uid;
var location = event.location;
if (location) {
description = location + (description ? "\n\n" + description : "");
}
var replaceTokens = function (source) {
source = source.replace(/\{\{'(.*?)%DESCRIPTION%(.*?)'\}\}/g, description ? "{{'$1" + escapeCharacters(description) + "$2'}}" : "{{'$1$2'}}");
source = source.replace(/\{\{"(.*?)%DESCRIPTION%(.*?)"\}\}/g, description ? "{{\"$1" + escapeCharacters(description) + "$2\"}}" : "{{\"$1$2\"}}");
source = source.replace(/%DESCRIPTION%/g, description ? description : "");
source = source.replace(/\{\{'%UID%'\}\}/g, uid ? "{{'" + escapeCharacters(uid) + "'}}" : "{{''}}");
source = source.replace(/\{\{"%UID%"\}\}/g, uid ? "{{\"" + escapeCharacters(uid) + "\"}}" : "{{\"\"}}");
source = source.replace(/%UID%/g, uid ? uid : "");
source = source.replace(/\{\{'%ICS_TIDDLER_TITLE%'\}\}/g, calendarTiddler && calendarTiddler.title ? "{{'" + escapeCharacters(calendarTiddler.title) + "'}}" : "{{''}}");
source = source.replace(/\{\{"%ICS_TIDDLER_TITLE%"\}\}/g, calendarTiddler && calendarTiddler.title ? "{{\"" + escapeCharacters(calendarTiddler.title) + "\"}}" : "{{\"\"}}");
source = source.replace(/%ICS_TIDDLER_TITLE%/g, calendarTiddler && calendarTiddler.title ? calendarTiddler.title : "");
return source;
};
var title = "";
if (dateHash["title"]) {
title = processDateHashTitleForTiddlerName(dateHash["title"], rrule ? "REMINDERSTART" : dateStart, rrule ? "REMINDEREND" : dateEnd, summary, rrule ? "REMINDERDATE" : null);
title = replaceTokens(title);
} else {
title = startTime + " : " + summary;
}
var tag = null;
if (dateHash["tag"]) {
tag = replaceTokens(dateHash["tag"]);
}
var tiddler = cmi.formatTitle({title: title});
if (!tiddler || !tiddler.isTagged("cancelled")) {
var reminderKey = "" + (uid ? uid : event.summary) + "\n" + dateStart.toUTCString();
var reminder = reminders[reminderKey];
if (reminder) {
if (!(reminder.vevent.getFirstProperty("rrule") || reminder.vevent.getFirstProperty("rdate"))
&& (vevent.getFirstProperty("rrule") || vevent.getFirstProperty("rdate"))) {
reminderKey += "\nrrule";
reminder = null;
} else if ((reminder.vevent.getFirstProperty("rrule") || reminder.vevent.getFirstProperty("rdate"))
&& !(vevent.getFirstProperty("rrule") || vevent.getFirstProperty("rdate"))
&& !reminders[reminderKey + "\nrrule"]) {
reminders[reminderKey + "\nrrule"] = reminder;
reminder = null;
}
}
if (!reminder) {
reminder = {
vevent: vevent,
reminder: '<' + '<reminder' + (rrule ? ' "messagefunction:config.macros.reminder.replaceReminderDateTime()"' : '') + ' "function:config.macros.ics.formatTitle()" "messagefunction:config.macros.reminder.replaceTiddlerFormatting()" year:' + dateStart.getFullYear() + ' month:' + (dateStart.getMonth() + 1) + ' day:' + dateStart.getDate() + (dateHash['leadtime'] ? ' leadtime:' + dateHash['leadtime'][0] + '...' + dateHash['leadtime'][1] : '') + (rrule ? ' rrule:"' + rrule + '"' : '') + (tag ? ' tag:"' + tag + '"' : '') + ' "title:' + title + '">>'
};
}
reminders[reminderKey] = reminder;
}
}
var reminderList = "";
for (var k in reminders) {
reminderList += reminders[k].reminder + "\n";
}
return reminderList;
},
deserialize: function (serializedCalendars) {
return serializedCalendars;
}
};
this.parse(tiddlerNameOrTag, config.options.txtICalendarTiddlerName, serializer);
},
format: function (calendars) {
var formatEmail = function (name, email) {
if (email) {
email = (email.toLowerCase().indexOf("mailto:") == 0) ? email.substring("mailto:".length) : email;
var emailLink = "";
var webmailURLTemplate = config.options.txtWebmailURLTemplate;
if (webmailURLTemplate) {
emailLink = webmailURLTemplate.replace(/\{name\}/gi, name).replace(/\{email\}/gi, email);
} else {
emailLink = "mailto:" + (name ? name + " <" + email + ">" : email);
}
name = name ? name : email;
return "[[" + name + "|" + emailLink + "]]";
} else {
return name;
}
};
var escapeNewlines = function (element) {
element.html(element.html().replace(/\n/g, "<br>"));
return element;
};
var formatInfo = function (title, text, isWikiText) {
var info = jQuery("<td align='left'></td>");
if (isWikiText) {
wikify(text, info[0]);
} else {
escapeNewlines(info.text(text || ""))
}
return jQuery("<tr><th align='left'>" + title + "</th></tr>").append(info);
};
var addInfo = function (table, title, text, isWikiText) {
if (text && text.trim()) {
table.append(formatInfo(title, text, isWikiText));
}
};
var result = jQuery("<div></div>");
var icsEnd = 0;
while ((icsStart = calendars.indexOf("BEGIN:VCALENDAR", icsEnd)) > -1) {
icsEnd = calendars.indexOf("END:VCALENDAR", icsStart);
if (icsEnd > -1) {
var ics = calendars.substring(icsStart, icsEnd + "END:VCALENDAR".length);
ics = this.normalize(ics);
var vcalendar = new ICAL.Component(ICAL.parse(ics));
var vtimezones = vcalendar.getAllSubcomponents("vtimezone");
for (var j = 0; j < vtimezones.length; j++) {
var tzid = vtimezones[j].getFirstProperty("tzid");
if (!tzid || !ICAL.TimezoneService.has(tzid.getFirstValue())) {
ICAL.TimezoneService.register(vtimezones[j]);
}
}
var vevents = vcalendar.getAllSubcomponents("vevent");
for (var i = 0; i < vevents.length; i++) {
var vevent = vevents[i];
var event = new ICAL.Event(vevent, {strictExceptions: true});
var startEnd = this.determineStartEnd(event);
var dateStart = startEnd[0];
var dateEnd = startEnd[1];
var table = jQuery("<table></table>");
result.append(table);
var status = vevent.getFirstPropertyValue("status");
var isCancelled = status && (status.toUpperCase() === "CANCELLED");
if (isCancelled) {
table.append("<tr><th colspan='2' align='center'>Canceled</th></tr>");
}
addInfo(table, "Title", event.summary);
addInfo(table, "Location", event.location);
var rrule = vevent.getFirstPropertyValue("rrule");
addInfo(table, "When", dateStart + ((dateStart && dateEnd) ? "\n" : "") + dateEnd + (((dateStart || dateEnd) && rrule) ? "\n" : "") + (rrule ? rrule.toString() : ""));
var organizer = vevent.getFirstProperty("organizer");
addInfo(table, "Organizer", formatEmail(organizer ? organizer.getParameter("cn") : "", organizer ? organizer.getFirstValue() : ""), true);
addInfo(table, "Description", event.description);
addInfo(table, "Comment", vevent.getFirstPropertyValue("comment"));
var attendees = "";
if (event.attendees) {
for (var j = 0; j < event.attendees.length; j++) {
var isOptional = event.attendees[j].getParameter("role") && event.attendees[j].getParameter("role").toUpperCase() === "OPT-PARTICIPANT";
attendees += ((j > 0) ? "<br>" : "") + (isOptional ? "//" : "") + formatEmail(event.attendees[j].getParameter("cn"), event.attendees[j].getFirstValue()) + (isOptional ? "//" : "");
}
}
addInfo(table, "Attendees", attendees, true);
}
} else {
break;
}
}
return result;
}
}
//}}}
/***
|Name|ical.js|
|Source|https://github.com/kewisch/ical.js/releases/download/v1.5.0/ical.js|
|Documentation|https://github.com/mozilla-comm/ical.js|
|Version|1.5.0|
|License|[[Mozilla Public License|https://www.mozilla.org/MPL/2.0/]], version 2.0|
|Description|ical.js - Javascript parser for rfc5545|
!!!!!Code
***/
//{{{
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2021 */
/* jshint ignore:start */
var ICAL;
(function() {
/* istanbul ignore next */
if (typeof module === 'object') {
// CommonJS, where exports may be different each time.
ICAL = module.exports;
} else if (typeof HTMLScriptElement !== 'undefined' && 'noModule' in HTMLScriptElement.prototype) {
// Until we use ES6 exports, using <script type="module"> we define ICAL on the window global.
window.ICAL = ICAL = {};
} else if (typeof ICAL !== 'object') {
ICAL = {};
}
})();
/* jshint ignore:end */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* The number of characters before iCalendar line folding should occur
* @type {Number}
* @default 75
*/
ICAL.foldLength = 75;
/**
* The character(s) to be used for a newline. The default value is provided by
* rfc5545.
* @type {String}
* @default "\r\n"
*/
ICAL.newLineChar = '\r\n';
/**
* Helper functions used in various places within ical.js
* @namespace
*/
ICAL.helpers = {
/**
* Compiles a list of all referenced TZIDs in all subcomponents and
* removes any extra VTIMEZONE subcomponents. In addition, if any TZIDs
* are referenced by a component, but a VTIMEZONE does not exist,
* an attempt will be made to generate a VTIMEZONE using ICAL.TimezoneService.
*
* @param {ICAL.Component} vcal The top-level VCALENDAR component.
* @return {ICAL.Component} The ICAL.Component that was passed in.
*/
updateTimezones: function(vcal) {
var allsubs, properties, vtimezones, reqTzid, i, tzid;
if (!vcal || vcal.name !== "vcalendar") {
//not a top-level vcalendar component
return vcal;
}
//Store vtimezone subcomponents in an object reference by tzid.
//Store properties from everything else in another array
allsubs = vcal.getAllSubcomponents();
properties = [];
vtimezones = {};
for (i = 0; i < allsubs.length; i++) {
if (allsubs[i].name === "vtimezone") {
tzid = allsubs[i].getFirstProperty("tzid").getFirstValue();
vtimezones[tzid] = allsubs[i];
} else {
properties = properties.concat(allsubs[i].getAllProperties());
}
}
//create an object with one entry for each required tz
reqTzid = {};
for (i = 0; i < properties.length; i++) {
if ((tzid = properties[i].getParameter("tzid"))) {
reqTzid[tzid] = true;
}
}
//delete any vtimezones that are not on the reqTzid list.
for (i in vtimezones) {
if (vtimezones.hasOwnProperty(i) && !reqTzid[i]) {
vcal.removeSubcomponent(vtimezones[i]);
}
}
//create any missing, but registered timezones
for (i in reqTzid) {
if (
reqTzid.hasOwnProperty(i) &&
!vtimezones[i] &&
ICAL.TimezoneService.has(i)
) {
vcal.addSubcomponent(ICAL.TimezoneService.get(i).component);
}
}
return vcal;
},
/**
* Checks if the given type is of the number type and also NaN.
*
* @param {Number} number The number to check
* @return {Boolean} True, if the number is strictly NaN
*/
isStrictlyNaN: function(number) {
return typeof(number) === 'number' && isNaN(number);
},
/**
* Parses a string value that is expected to be an integer, when the valid is
* not an integer throws a decoration error.
*
* @param {String} string Raw string input
* @return {Number} Parsed integer
*/
strictParseInt: function(string) {
var result = parseInt(string, 10);
if (ICAL.helpers.isStrictlyNaN(result)) {
throw new Error(
'Could not extract integer from "' + string + '"'
);
}
return result;
},
/**
* Creates or returns a class instance of a given type with the initialization
* data if the data is not already an instance of the given type.
*
* @example
* var time = new ICAL.Time(...);
* var result = ICAL.helpers.formatClassType(time, ICAL.Time);
*
* (result instanceof ICAL.Time)
* // => true
*
* result = ICAL.helpers.formatClassType({}, ICAL.Time);
* (result isntanceof ICAL.Time)
* // => true
*
*
* @param {Object} data object initialization data
* @param {Object} type object type (like ICAL.Time)
* @return {?} An instance of the found type.
*/
formatClassType: function formatClassType(data, type) {
if (typeof(data) === 'undefined') {
return undefined;
}
if (data instanceof type) {
return data;
}
return new type(data);
},
/**
* Identical to indexOf but will only match values when they are not preceded
* by a backslash character.
*
* @param {String} buffer String to search
* @param {String} search Value to look for
* @param {Number} pos Start position
* @return {Number} The position, or -1 if not found
*/
unescapedIndexOf: function(buffer, search, pos) {
while ((pos = buffer.indexOf(search, pos)) !== -1) {
if (pos > 0 && buffer[pos - 1] === '\\') {
pos += 1;
} else {
return pos;
}
}
return -1;
},
/**
* Find the index for insertion using binary search.
*
* @param {Array} list The list to search
* @param {?} seekVal The value to insert
* @param {function(?,?)} cmpfunc The comparison func, that can
* compare two seekVals
* @return {Number} The insert position
*/
binsearchInsert: function(list, seekVal, cmpfunc) {
if (!list.length)
return 0;
var low = 0, high = list.length - 1,
mid, cmpval;
while (low <= high) {
mid = low + Math.floor((high - low) / 2);
cmpval = cmpfunc(seekVal, list[mid]);
if (cmpval < 0)
high = mid - 1;
else if (cmpval > 0)
low = mid + 1;
else
break;
}
if (cmpval < 0)
return mid; // insertion is displacing, so use mid outright.
else if (cmpval > 0)
return mid + 1;
else
return mid;
},
/**
* Convenience function for debug output
* @private
*/
dumpn: /* istanbul ignore next */ function() {
if (!ICAL.debug) {
return;
}
if (typeof (console) !== 'undefined' && 'log' in console) {
ICAL.helpers.dumpn = function consoleDumpn(input) {
console.log(input);
};
} else {
ICAL.helpers.dumpn = function geckoDumpn(input) {
dump(input + '\n');
};
}
ICAL.helpers.dumpn(arguments[0]);
},
/**
* Clone the passed object or primitive. By default a shallow clone will be
* executed.
*
* @param {*} aSrc The thing to clone
* @param {Boolean=} aDeep If true, a deep clone will be performed
* @return {*} The copy of the thing
*/
clone: function(aSrc, aDeep) {
if (!aSrc || typeof aSrc != "object") {
return aSrc;
} else if (aSrc instanceof Date) {
return new Date(aSrc.getTime());
} else if ("clone" in aSrc) {
return aSrc.clone();
} else if (Array.isArray(aSrc)) {
var arr = [];
for (var i = 0; i < aSrc.length; i++) {
arr.push(aDeep ? ICAL.helpers.clone(aSrc[i], true) : aSrc[i]);
}
return arr;
} else {
var obj = {};
for (var name in aSrc) {
// uses prototype method to allow use of Object.create(null);
/* istanbul ignore else */
if (Object.prototype.hasOwnProperty.call(aSrc, name)) {
if (aDeep) {
obj[name] = ICAL.helpers.clone(aSrc[name], true);
} else {
obj[name] = aSrc[name];
}
}
}
return obj;
}
},
/**
* Performs iCalendar line folding. A line ending character is inserted and
* the next line begins with a whitespace.
*
* @example
* SUMMARY:This line will be fold
* ed right in the middle of a word.
*
* @param {String} aLine The line to fold
* @return {String} The folded line
*/
foldline: function foldline(aLine) {
var result = "";
var line = aLine || "", pos = 0, line_length = 0;
//pos counts position in line for the UTF-16 presentation
//line_length counts the bytes for the UTF-8 presentation
while (line.length) {
var cp = line.codePointAt(pos);
if (cp < 128) ++line_length;
else if (cp < 2048) line_length += 2;//needs 2 UTF-8 bytes
else if (cp < 65536) line_length += 3;
else line_length += 4; //cp is less than 1114112
if (line_length < ICAL.foldLength + 1)
pos += cp > 65535 ? 2 : 1;
else {
result += ICAL.newLineChar + " " + line.substring(0, pos);
line = line.substring(pos);
pos = line_length = 0;
}
}
return result.substr(ICAL.newLineChar.length + 1);
},
/**
* Pads the given string or number with zeros so it will have at least two
* characters.
*
* @param {String|Number} data The string or number to pad
* @return {String} The number padded as a string
*/
pad2: function pad(data) {
if (typeof(data) !== 'string') {
// handle fractions.
if (typeof(data) === 'number') {
data = parseInt(data);
}
data = String(data);
}
var len = data.length;
switch (len) {
case 0:
return '00';
case 1:
return '0' + data;
default:
return data;
}
},
/**
* Truncates the given number, correctly handling negative numbers.
*
* @param {Number} number The number to truncate
* @return {Number} The truncated number
*/
trunc: function trunc(number) {
return (number < 0 ? Math.ceil(number) : Math.floor(number));
},
/**
* Poor-man's cross-browser inheritance for JavaScript. Doesn't support all
* the features, but enough for our usage.
*
* @param {Function} base The base class constructor function.
* @param {Function} child The child class constructor function.
* @param {Object} extra Extends the prototype with extra properties
* and methods
*/
inherits: function(base, child, extra) {
function F() {}
F.prototype = base.prototype;
child.prototype = new F();
if (extra) {
ICAL.helpers.extend(extra, child.prototype);
}
},
/**
* Poor-man's cross-browser object extension. Doesn't support all the
* features, but enough for our usage. Note that the target's properties are
* not overwritten with the source properties.
*
* @example
* var child = ICAL.helpers.extend(parent, {
* "bar": 123
* });
*
* @param {Object} source The object to extend
* @param {Object} target The object to extend with
* @return {Object} Returns the target.
*/
extend: function(source, target) {
for (var key in source) {
var descr = Object.getOwnPropertyDescriptor(source, key);
if (descr && !Object.getOwnPropertyDescriptor(target, key)) {
Object.defineProperty(target, key, descr);
}
}
return target;
}
};
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/** @namespace ICAL */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.design = (function() {
'use strict';
var FROM_ICAL_NEWLINE = /\\\\|\\;|\\,|\\[Nn]/g;
var TO_ICAL_NEWLINE = /\\|;|,|\n/g;
var FROM_VCARD_NEWLINE = /\\\\|\\,|\\[Nn]/g;
var TO_VCARD_NEWLINE = /\\|,|\n/g;
function createTextType(fromNewline, toNewline) {
var result = {
matches: /.*/,
fromICAL: function(aValue, structuredEscape) {
return replaceNewline(aValue, fromNewline, structuredEscape);
},
toICAL: function(aValue, structuredEscape) {
var regEx = toNewline;
if (structuredEscape)
regEx = new RegExp(regEx.source + '|' + structuredEscape);
return aValue.replace(regEx, function(str) {
switch (str) {
case "\\":
return "\\\\";
case ";":
return "\\;";
case ",":
return "\\,";
case "\n":
return "\\n";
/* istanbul ignore next */
default:
return str;
}
});
}
};
return result;
}
// default types used multiple times
var DEFAULT_TYPE_TEXT = { defaultType: "text" };
var DEFAULT_TYPE_TEXT_MULTI = { defaultType: "text", multiValue: "," };
var DEFAULT_TYPE_TEXT_STRUCTURED = { defaultType: "text", structuredValue: ";" };
var DEFAULT_TYPE_INTEGER = { defaultType: "integer" };
var DEFAULT_TYPE_DATETIME_DATE = { defaultType: "date-time", allowedTypes: ["date-time", "date"] };
var DEFAULT_TYPE_DATETIME = { defaultType: "date-time" };
var DEFAULT_TYPE_URI = { defaultType: "uri" };
var DEFAULT_TYPE_UTCOFFSET = { defaultType: "utc-offset" };
var DEFAULT_TYPE_RECUR = { defaultType: "recur" };
var DEFAULT_TYPE_DATE_ANDOR_TIME = { defaultType: "date-and-or-time", allowedTypes: ["date-time", "date", "text"] };
function replaceNewlineReplace(string) {
switch (string) {
case "\\\\":
return "\\";
case "\\;":
return ";";
case "\\,":
return ",";
case "\\n":
case "\\N":
return "\n";
/* istanbul ignore next */
default:
return string;
}
}
function replaceNewline(value, newline, structuredEscape) {
// avoid regex when possible.
if (value.indexOf('\\') === -1) {
return value;
}
if (structuredEscape)
newline = new RegExp(newline.source + '|\\\\' + structuredEscape);
return value.replace(newline, replaceNewlineReplace);
}
var commonProperties = {
"categories": DEFAULT_TYPE_TEXT_MULTI,
"url": DEFAULT_TYPE_URI,
"version": DEFAULT_TYPE_TEXT,
"uid": DEFAULT_TYPE_TEXT
};
var commonValues = {
"boolean": {
values: ["TRUE", "FALSE"],
fromICAL: function(aValue) {
switch (aValue) {
case 'TRUE':
return true;
case 'FALSE':
return false;
default:
//TODO: parser warning
return false;
}
},
toICAL: function(aValue) {
if (aValue) {
return 'TRUE';
}
return 'FALSE';
}
},
float: {
matches: /^[+-]?\d+\.\d+$/,
fromICAL: function(aValue) {
var parsed = parseFloat(aValue);
if (ICAL.helpers.isStrictlyNaN(parsed)) {
// TODO: parser warning
return 0.0;
}
return parsed;
},
toICAL: function(aValue) {
return String(aValue);
}
},
integer: {
fromICAL: function(aValue) {
var parsed = parseInt(aValue);
if (ICAL.helpers.isStrictlyNaN(parsed)) {
return 0;
}
return parsed;
},
toICAL: function(aValue) {
return String(aValue);
}
},
"utc-offset": {
toICAL: function(aValue) {
if (aValue.length < 7) {
// no seconds
// -0500
return aValue.substr(0, 3) +
aValue.substr(4, 2);
} else {
// seconds
// -050000
return aValue.substr(0, 3) +
aValue.substr(4, 2) +
aValue.substr(7, 2);
}
},
fromICAL: function(aValue) {
if (aValue.length < 6) {
// no seconds
// -05:00
return aValue.substr(0, 3) + ':' +
aValue.substr(3, 2);
} else {
// seconds
// -05:00:00
return aValue.substr(0, 3) + ':' +
aValue.substr(3, 2) + ':' +
aValue.substr(5, 2);
}
},
decorate: function(aValue) {
return ICAL.UtcOffset.fromString(aValue);
},
undecorate: function(aValue) {
return aValue.toString();
}
}
};
var icalParams = {
// Although the syntax is DQUOTE uri DQUOTE, I don't think we should
// enfoce anything aside from it being a valid content line.
//
// At least some params require - if multi values are used - DQUOTEs
// for each of its values - e.g. delegated-from="uri1","uri2"
// To indicate this, I introduced the new k/v pair
// multiValueSeparateDQuote: true
//
// "ALTREP": { ... },
// CN just wants a param-value
// "CN": { ... }
"cutype": {
values: ["INDIVIDUAL", "GROUP", "RESOURCE", "ROOM", "UNKNOWN"],
allowXName: true,
allowIanaToken: true
},
"delegated-from": {
valueType: "cal-address",
multiValue: ",",
multiValueSeparateDQuote: true
},
"delegated-to": {
valueType: "cal-address",
multiValue: ",",
multiValueSeparateDQuote: true
},
// "DIR": { ... }, // See ALTREP
"encoding": {
values: ["8BIT", "BASE64"]
},
// "FMTTYPE": { ... }, // See ALTREP
"fbtype": {
values: ["FREE", "BUSY", "BUSY-UNAVAILABLE", "BUSY-TENTATIVE"],
allowXName: true,
allowIanaToken: true
},
// "LANGUAGE": { ... }, // See ALTREP
"member": {
valueType: "cal-address",
multiValue: ",",
multiValueSeparateDQuote: true
},
"partstat": {
// TODO These values are actually different per-component
values: ["NEEDS-ACTION", "ACCEPTED", "DECLINED", "TENTATIVE",
"DELEGATED", "COMPLETED", "IN-PROCESS"],
allowXName: true,
allowIanaToken: true
},
"range": {
values: ["THISANDFUTURE"]
},
"related": {
values: ["START", "END"]
},
"reltype": {
values: ["PARENT", "CHILD", "SIBLING"],
allowXName: true,
allowIanaToken: true
},
"role": {
values: ["REQ-PARTICIPANT", "CHAIR",
"OPT-PARTICIPANT", "NON-PARTICIPANT"],
allowXName: true,
allowIanaToken: true
},
"rsvp": {
values: ["TRUE", "FALSE"]
},
"sent-by": {
valueType: "cal-address"
},
"tzid": {
matches: /^\//
},
"value": {
// since the value here is a 'type' lowercase is used.
values: ["binary", "boolean", "cal-address", "date", "date-time",
"duration", "float", "integer", "period", "recur", "text",
"time", "uri", "utc-offset"],
allowXName: true,
allowIanaToken: true
}
};
// When adding a value here, be sure to add it to the parameter types!
var icalValues = ICAL.helpers.extend(commonValues, {
text: createTextType(FROM_ICAL_NEWLINE, TO_ICAL_NEWLINE),
uri: {
// TODO
/* ... */
},
"binary": {
decorate: function(aString) {
return ICAL.Binary.fromString(aString);
},
undecorate: function(aBinary) {
return aBinary.toString();
}
},
"cal-address": {
// needs to be an uri
},
"date": {
decorate: function(aValue, aProp) {
if (design.strict) {
return ICAL.Time.fromDateString(aValue, aProp);
} else {
return ICAL.Time.fromString(aValue, aProp);
}
},
/**
* undecorates a time object.
*/
undecorate: function(aValue) {
return aValue.toString();
},
fromICAL: function(aValue) {
// from: 20120901
// to: 2012-09-01
if (!design.strict && aValue.length >= 15) {
// This is probably a date-time, e.g. 20120901T130000Z
return icalValues["date-time"].fromICAL(aValue);
} else {
return aValue.substr(0, 4) + '-' +
aValue.substr(4, 2) + '-' +
aValue.substr(6, 2);
}
},
toICAL: function(aValue) {
// from: 2012-09-01
// to: 20120901
var len = aValue.length;
if (len == 10) {
return aValue.substr(0, 4) +
aValue.substr(5, 2) +
aValue.substr(8, 2);
} else if (len >= 19) {
return icalValues["date-time"].toICAL(aValue);
} else {
//TODO: serialize warning?
return aValue;
}
}
},
"date-time": {
fromICAL: function(aValue) {
// from: 20120901T130000
// to: 2012-09-01T13:00:00
if (!design.strict && aValue.length == 8) {
// This is probably a date, e.g. 20120901
return icalValues.date.fromICAL(aValue);
} else {
var result = aValue.substr(0, 4) + '-' +
aValue.substr(4, 2) + '-' +
aValue.substr(6, 2) + 'T' +
aValue.substr(9, 2) + ':' +
aValue.substr(11, 2) + ':' +
aValue.substr(13, 2);
if (aValue[15] && aValue[15] === 'Z') {
result += 'Z';
}
return result;
}
},
toICAL: function(aValue) {
// from: 2012-09-01T13:00:00
// to: 20120901T130000
var len = aValue.length;
if (len == 10 && !design.strict) {
return icalValues.date.toICAL(aValue);
} else if (len >= 19) {
var result = aValue.substr(0, 4) +
aValue.substr(5, 2) +
// grab the (DDTHH) segment
aValue.substr(8, 5) +
// MM
aValue.substr(14, 2) +
// SS
aValue.substr(17, 2);
if (aValue[19] && aValue[19] === 'Z') {
result += 'Z';
}
return result;
} else {
// TODO: error
return aValue;
}
},
decorate: function(aValue, aProp) {
if (design.strict) {
return ICAL.Time.fromDateTimeString(aValue, aProp);
} else {
return ICAL.Time.fromString(aValue, aProp);
}
},
undecorate: function(aValue) {
return aValue.toString();
}
},
duration: {
decorate: function(aValue) {
return ICAL.Duration.fromString(aValue);
},
undecorate: function(aValue) {
return aValue.toString();
}
},
period: {
fromICAL: function(string) {
var parts = string.split('/');
parts[0] = icalValues['date-time'].fromICAL(parts[0]);
if (!ICAL.Duration.isValueString(parts[1])) {
parts[1] = icalValues['date-time'].fromICAL(parts[1]);
}
return parts;
},
toICAL: function(parts) {
if (!design.strict && parts[0].length == 10) {
parts[0] = icalValues.date.toICAL(parts[0]);
} else {
parts[0] = icalValues['date-time'].toICAL(parts[0]);
}
if (!ICAL.Duration.isValueString(parts[1])) {
if (!design.strict && parts[1].length == 10) {
parts[1] = icalValues.date.toICAL(parts[1]);
} else {
parts[1] = icalValues['date-time'].toICAL(parts[1]);
}
}
return parts.join("/");
},
decorate: function(aValue, aProp) {
return ICAL.Period.fromJSON(aValue, aProp, !design.strict);
},
undecorate: function(aValue) {
return aValue.toJSON();
}
},
recur: {
fromICAL: function(string) {
return ICAL.Recur._stringToData(string, true);
},
toICAL: function(data) {
var str = "";
for (var k in data) {
/* istanbul ignore if */
if (!Object.prototype.hasOwnProperty.call(data, k)) {
continue;
}
var val = data[k];
if (k == "until") {
if (val.length > 10) {
val = icalValues['date-time'].toICAL(val);
} else {
val = icalValues.date.toICAL(val);
}
} else if (k == "wkst") {
if (typeof val === 'number') {
val = ICAL.Recur.numericDayToIcalDay(val);
}
} else if (Array.isArray(val)) {
val = val.join(",");
}
str += k.toUpperCase() + "=" + val + ";";
}
return str.substr(0, str.length - 1);
},
decorate: function decorate(aValue) {
return ICAL.Recur.fromData(aValue);
},
undecorate: function(aRecur) {
return aRecur.toJSON();
}
},
time: {
fromICAL: function(aValue) {
// from: MMHHSS(Z)?
// to: HH:MM:SS(Z)?
if (aValue.length < 6) {
// TODO: parser exception?
return aValue;
}
// HH::MM::SSZ?
var result = aValue.substr(0, 2) + ':' +
aValue.substr(2, 2) + ':' +
aValue.substr(4, 2);
if (aValue[6] === 'Z') {
result += 'Z';
}
return result;
},
toICAL: function(aValue) {
// from: HH:MM:SS(Z)?
// to: MMHHSS(Z)?
if (aValue.length < 8) {
//TODO: error
return aValue;
}
var result = aValue.substr(0, 2) +
aValue.substr(3, 2) +
aValue.substr(6, 2);
if (aValue[8] === 'Z') {
result += 'Z';
}
return result;
}
}
});
var icalProperties = ICAL.helpers.extend(commonProperties, {
"action": DEFAULT_TYPE_TEXT,
"attach": { defaultType: "uri" },
"attendee": { defaultType: "cal-address" },
"calscale": DEFAULT_TYPE_TEXT,
"class": DEFAULT_TYPE_TEXT,
"comment": DEFAULT_TYPE_TEXT,
"completed": DEFAULT_TYPE_DATETIME,
"contact": DEFAULT_TYPE_TEXT,
"created": DEFAULT_TYPE_DATETIME,
"description": DEFAULT_TYPE_TEXT,
"dtend": DEFAULT_TYPE_DATETIME_DATE,
"dtstamp": DEFAULT_TYPE_DATETIME,
"dtstart": DEFAULT_TYPE_DATETIME_DATE,
"due": DEFAULT_TYPE_DATETIME_DATE,
"duration": { defaultType: "duration" },
"exdate": {
defaultType: "date-time",
allowedTypes: ["date-time", "date"],
multiValue: ','
},
"exrule": DEFAULT_TYPE_RECUR,
"freebusy": { defaultType: "period", multiValue: "," },
"geo": { defaultType: "float", structuredValue: ";" },
"last-modified": DEFAULT_TYPE_DATETIME,
"location": DEFAULT_TYPE_TEXT,
"method": DEFAULT_TYPE_TEXT,
"organizer": { defaultType: "cal-address" },
"percent-complete": DEFAULT_TYPE_INTEGER,
"priority": DEFAULT_TYPE_INTEGER,
"prodid": DEFAULT_TYPE_TEXT,
"related-to": DEFAULT_TYPE_TEXT,
"repeat": DEFAULT_TYPE_INTEGER,
"rdate": {
defaultType: "date-time",
allowedTypes: ["date-time", "date", "period"],
multiValue: ',',
detectType: function(string) {
if (string.indexOf('/') !== -1) {
return 'period';
}
return (string.indexOf('T') === -1) ? 'date' : 'date-time';
}
},
"recurrence-id": DEFAULT_TYPE_DATETIME_DATE,
"resources": DEFAULT_TYPE_TEXT_MULTI,
"request-status": DEFAULT_TYPE_TEXT_STRUCTURED,
"rrule": DEFAULT_TYPE_RECUR,
"sequence": DEFAULT_TYPE_INTEGER,
"status": DEFAULT_TYPE_TEXT,
"summary": DEFAULT_TYPE_TEXT,
"transp": DEFAULT_TYPE_TEXT,
"trigger": { defaultType: "duration", allowedTypes: ["duration", "date-time"] },
"tzoffsetfrom": DEFAULT_TYPE_UTCOFFSET,
"tzoffsetto": DEFAULT_TYPE_UTCOFFSET,
"tzurl": DEFAULT_TYPE_URI,
"tzid": DEFAULT_TYPE_TEXT,
"tzname": DEFAULT_TYPE_TEXT
});
// When adding a value here, be sure to add it to the parameter types!
var vcardValues = ICAL.helpers.extend(commonValues, {
text: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE),
uri: createTextType(FROM_VCARD_NEWLINE, TO_VCARD_NEWLINE),
date: {
decorate: function(aValue) {
return ICAL.VCardTime.fromDateAndOrTimeString(aValue, "date");
},
undecorate: function(aValue) {
return aValue.toString();
},
fromICAL: function(aValue) {
if (aValue.length == 8) {
return icalValues.date.fromICAL(aValue);
} else if (aValue[0] == '-' && aValue.length == 6) {
return aValue.substr(0, 4) + '-' + aValue.substr(4);
} else {
return aValue;
}
},
toICAL: function(aValue) {
if (aValue.length == 10) {
return icalValues.date.toICAL(aValue);
} else if (aValue[0] == '-' && aValue.length == 7) {
return aValue.substr(0, 4) + aValue.substr(5);
} else {
return aValue;
}
}
},
time: {
decorate: function(aValue) {
return ICAL.VCardTime.fromDateAndOrTimeString("T" + aValue, "time");
},
undecorate: function(aValue) {
return aValue.toString();
},
fromICAL: function(aValue) {
var splitzone = vcardValues.time._splitZone(aValue, true);
var zone = splitzone[0], value = splitzone[1];
//console.log("SPLIT: ",splitzone);
if (value.length == 6) {
value = value.substr(0, 2) + ':' +
value.substr(2, 2) + ':' +
value.substr(4, 2);
} else if (value.length == 4 && value[0] != '-') {
value = value.substr(0, 2) + ':' + value.substr(2, 2);
} else if (value.length == 5) {
value = value.substr(0, 3) + ':' + value.substr(3, 2);
}
if (zone.length == 5 && (zone[0] == '-' || zone[0] == '+')) {
zone = zone.substr(0, 3) + ':' + zone.substr(3);
}
return value + zone;
},
toICAL: function(aValue) {
var splitzone = vcardValues.time._splitZone(aValue);
var zone = splitzone[0], value = splitzone[1];
if (value.length == 8) {
value = value.substr(0, 2) +
value.substr(3, 2) +
value.substr(6, 2);
} else if (value.length == 5 && value[0] != '-') {
value = value.substr(0, 2) + value.substr(3, 2);
} else if (value.length == 6) {
value = value.substr(0, 3) + value.substr(4, 2);
}
if (zone.length == 6 && (zone[0] == '-' || zone[0] == '+')) {
zone = zone.substr(0, 3) + zone.substr(4);
}
return value + zone;
},
_splitZone: function(aValue, isFromIcal) {
var lastChar = aValue.length - 1;
var signChar = aValue.length - (isFromIcal ? 5 : 6);
var sign = aValue[signChar];
var zone, value;
if (aValue[lastChar] == 'Z') {
zone = aValue[lastChar];
value = aValue.substr(0, lastChar);
} else if (aValue.length > 6 && (sign == '-' || sign == '+')) {
zone = aValue.substr(signChar);
value = aValue.substr(0, signChar);
} else {
zone = "";
value = aValue;
}
return [zone, value];
}
},
"date-time": {
decorate: function(aValue) {
return ICAL.VCardTime.fromDateAndOrTimeString(aValue, "date-time");
},
undecorate: function(aValue) {
return aValue.toString();
},
fromICAL: function(aValue) {
return vcardValues['date-and-or-time'].fromICAL(aValue);
},
toICAL: function(aValue) {
return vcardValues['date-and-or-time'].toICAL(aValue);
}
},
"date-and-or-time": {
decorate: function(aValue) {
return ICAL.VCardTime.fromDateAndOrTimeString(aValue, "date-and-or-time");
},
undecorate: function(aValue) {
return aValue.toString();
},
fromICAL: function(aValue) {
var parts = aValue.split('T');
return (parts[0] ? vcardValues.date.fromICAL(parts[0]) : '') +
(parts[1] ? 'T' + vcardValues.time.fromICAL(parts[1]) : '');
},
toICAL: function(aValue) {
var parts = aValue.split('T');
return vcardValues.date.toICAL(parts[0]) +
(parts[1] ? 'T' + vcardValues.time.toICAL(parts[1]) : '');
}
},
timestamp: icalValues['date-time'],
"language-tag": {
matches: /^[a-zA-Z0-9-]+$/ // Could go with a more strict regex here
}
});
var vcardParams = {
"type": {
valueType: "text",
multiValue: ","
},
"value": {
// since the value here is a 'type' lowercase is used.
values: ["text", "uri", "date", "time", "date-time", "date-and-or-time",
"timestamp", "boolean", "integer", "float", "utc-offset",
"language-tag"],
allowXName: true,
allowIanaToken: true
}
};
var vcardProperties = ICAL.helpers.extend(commonProperties, {
"adr": { defaultType: "text", structuredValue: ";", multiValue: "," },
"anniversary": DEFAULT_TYPE_DATE_ANDOR_TIME,
"bday": DEFAULT_TYPE_DATE_ANDOR_TIME,
"caladruri": DEFAULT_TYPE_URI,
"caluri": DEFAULT_TYPE_URI,
"clientpidmap": DEFAULT_TYPE_TEXT_STRUCTURED,
"email": DEFAULT_TYPE_TEXT,
"fburl": DEFAULT_TYPE_URI,
"fn": DEFAULT_TYPE_TEXT,
"gender": DEFAULT_TYPE_TEXT_STRUCTURED,
"geo": DEFAULT_TYPE_URI,
"impp": DEFAULT_TYPE_URI,
"key": DEFAULT_TYPE_URI,
"kind": DEFAULT_TYPE_TEXT,
"lang": { defaultType: "language-tag" },
"logo": DEFAULT_TYPE_URI,
"member": DEFAULT_TYPE_URI,
"n": { defaultType: "text", structuredValue: ";", multiValue: "," },
"nickname": DEFAULT_TYPE_TEXT_MULTI,
"note": DEFAULT_TYPE_TEXT,
"org": { defaultType: "text", structuredValue: ";" },
"photo": DEFAULT_TYPE_URI,
"related": DEFAULT_TYPE_URI,
"rev": { defaultType: "timestamp" },
"role": DEFAULT_TYPE_TEXT,
"sound": DEFAULT_TYPE_URI,
"source": DEFAULT_TYPE_URI,
"tel": { defaultType: "uri", allowedTypes: ["uri", "text"] },
"title": DEFAULT_TYPE_TEXT,
"tz": { defaultType: "text", allowedTypes: ["text", "utc-offset", "uri"] },
"xml": DEFAULT_TYPE_TEXT
});
var vcard3Values = ICAL.helpers.extend(commonValues, {
binary: icalValues.binary,
date: vcardValues.date,
"date-time": vcardValues["date-time"],
"phone-number": {
// TODO
/* ... */
},
uri: icalValues.uri,
text: icalValues.text,
time: icalValues.time,
vcard: icalValues.text,
"utc-offset": {
toICAL: function(aValue) {
return aValue.substr(0, 7);
},
fromICAL: function(aValue) {
return aValue.substr(0, 7);
},
decorate: function(aValue) {
return ICAL.UtcOffset.fromString(aValue);
},
undecorate: function(aValue) {
return aValue.toString();
}
}
});
var vcard3Params = {
"type": {
valueType: "text",
multiValue: ","
},
"value": {
// since the value here is a 'type' lowercase is used.
values: ["text", "uri", "date", "date-time", "phone-number", "time",
"boolean", "integer", "float", "utc-offset", "vcard", "binary"],
allowXName: true,
allowIanaToken: true
}
};
var vcard3Properties = ICAL.helpers.extend(commonProperties, {
fn: DEFAULT_TYPE_TEXT,
n: { defaultType: "text", structuredValue: ";", multiValue: "," },
nickname: DEFAULT_TYPE_TEXT_MULTI,
photo: { defaultType: "binary", allowedTypes: ["binary", "uri"] },
bday: {
defaultType: "date-time",
allowedTypes: ["date-time", "date"],
detectType: function(string) {
return (string.indexOf('T') === -1) ? 'date' : 'date-time';
}
},
adr: { defaultType: "text", structuredValue: ";", multiValue: "," },
label: DEFAULT_TYPE_TEXT,
tel: { defaultType: "phone-number" },
email: DEFAULT_TYPE_TEXT,
mailer: DEFAULT_TYPE_TEXT,
tz: { defaultType: "utc-offset", allowedTypes: ["utc-offset", "text"] },
geo: { defaultType: "float", structuredValue: ";" },
title: DEFAULT_TYPE_TEXT,
role: DEFAULT_TYPE_TEXT,
logo: { defaultType: "binary", allowedTypes: ["binary", "uri"] },
agent: { defaultType: "vcard", allowedTypes: ["vcard", "text", "uri"] },
org: DEFAULT_TYPE_TEXT_STRUCTURED,
note: DEFAULT_TYPE_TEXT_MULTI,
prodid: DEFAULT_TYPE_TEXT,
rev: {
defaultType: "date-time",
allowedTypes: ["date-time", "date"],
detectType: function(string) {
return (string.indexOf('T') === -1) ? 'date' : 'date-time';
}
},
"sort-string": DEFAULT_TYPE_TEXT,
sound: { defaultType: "binary", allowedTypes: ["binary", "uri"] },
class: DEFAULT_TYPE_TEXT,
key: { defaultType: "binary", allowedTypes: ["binary", "text"] }
});
/**
* iCalendar design set
* @type {ICAL.design.designSet}
*/
var icalSet = {
value: icalValues,
param: icalParams,
property: icalProperties
};
/**
* vCard 4.0 design set
* @type {ICAL.design.designSet}
*/
var vcardSet = {
value: vcardValues,
param: vcardParams,
property: vcardProperties
};
/**
* vCard 3.0 design set
* @type {ICAL.design.designSet}
*/
var vcard3Set = {
value: vcard3Values,
param: vcard3Params,
property: vcard3Properties
};
/**
* The design data, used by the parser to determine types for properties and
* other metadata needed to produce correct jCard/jCal data.
*
* @alias ICAL.design
* @namespace
*/
var design = {
/**
* A designSet describes value, parameter and property data. It is used by
* ther parser and stringifier in components and properties to determine they
* should be represented.
*
* @typedef {Object} designSet
* @memberOf ICAL.design
* @property {Object} value Definitions for value types, keys are type names
* @property {Object} param Definitions for params, keys are param names
* @property {Object} property Defintions for properties, keys are property names
*/
/**
* Can be set to false to make the parser more lenient.
*/
strict: true,
/**
* The default set for new properties and components if none is specified.
* @type {ICAL.design.designSet}
*/
defaultSet: icalSet,
/**
* The default type for unknown properties
* @type {String}
*/
defaultType: 'unknown',
/**
* Holds the design set for known top-level components
*
* @type {Object}
* @property {ICAL.design.designSet} vcard vCard VCARD
* @property {ICAL.design.designSet} vevent iCalendar VEVENT
* @property {ICAL.design.designSet} vtodo iCalendar VTODO
* @property {ICAL.design.designSet} vjournal iCalendar VJOURNAL
* @property {ICAL.design.designSet} valarm iCalendar VALARM
* @property {ICAL.design.designSet} vtimezone iCalendar VTIMEZONE
* @property {ICAL.design.designSet} daylight iCalendar DAYLIGHT
* @property {ICAL.design.designSet} standard iCalendar STANDARD
*
* @example
* var propertyName = 'fn';
* var componentDesign = ICAL.design.components.vcard;
* var propertyDetails = componentDesign.property[propertyName];
* if (propertyDetails.defaultType == 'text') {
* // Yep, sure is...
* }
*/
components: {
vcard: vcardSet,
vcard3: vcard3Set,
vevent: icalSet,
vtodo: icalSet,
vjournal: icalSet,
valarm: icalSet,
vtimezone: icalSet,
daylight: icalSet,
standard: icalSet
},
/**
* The design set for iCalendar (rfc5545/rfc7265) components.
* @type {ICAL.design.designSet}
*/
icalendar: icalSet,
/**
* The design set for vCard (rfc6350/rfc7095) components.
* @type {ICAL.design.designSet}
*/
vcard: vcardSet,
/**
* The design set for vCard (rfc2425/rfc2426/rfc7095) components.
* @type {ICAL.design.designSet}
*/
vcard3: vcard3Set,
/**
* Gets the design set for the given component name.
*
* @param {String} componentName The name of the component
* @return {ICAL.design.designSet} The design set for the component
*/
getDesignSet: function(componentName) {
var isInDesign = componentName && componentName in design.components;
return isInDesign ? design.components[componentName] : design.defaultSet;
}
};
return design;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* Contains various functions to convert jCal and jCard data back into
* iCalendar and vCard.
* @namespace
*/
ICAL.stringify = (function() {
'use strict';
var LINE_ENDING = '\r\n';
var DEFAULT_VALUE_TYPE = 'unknown';
var design = ICAL.design;
var helpers = ICAL.helpers;
/**
* Convert a full jCal/jCard array into a iCalendar/vCard string.
*
* @function ICAL.stringify
* @variation function
* @param {Array} jCal The jCal/jCard document
* @return {String} The stringified iCalendar/vCard document
*/
function stringify(jCal) {
if (typeof jCal[0] == "string") {
// This is a single component
jCal = [jCal];
}
var i = 0;
var len = jCal.length;
var result = '';
for (; i < len; i++) {
result += stringify.component(jCal[i]) + LINE_ENDING;
}
return result;
}
/**
* Converts an jCal component array into a ICAL string.
* Recursive will resolve sub-components.
*
* Exact component/property order is not saved all
* properties will come before subcomponents.
*
* @function ICAL.stringify.component
* @param {Array} component
* jCal/jCard fragment of a component
* @param {ICAL.design.designSet} designSet
* The design data to use for this component
* @return {String} The iCalendar/vCard string
*/
stringify.component = function(component, designSet) {
var name = component[0].toUpperCase();
var result = 'BEGIN:' + name + LINE_ENDING;
var props = component[1];
var propIdx = 0;
var propLen = props.length;
var designSetName = component[0];
// rfc6350 requires that in vCard 4.0 the first component is the VERSION
// component with as value 4.0, note that 3.0 does not have this requirement.
if (designSetName === 'vcard' && component[1].length > 0 &&
!(component[1][0][0] === "version" && component[1][0][3] === "4.0")) {
designSetName = "vcard3";
}
designSet = designSet || design.getDesignSet(designSetName);
for (; propIdx < propLen; propIdx++) {
result += stringify.property(props[propIdx], designSet) + LINE_ENDING;
}
// Ignore subcomponents if none exist, e.g. in vCard.
var comps = component[2] || [];
var compIdx = 0;
var compLen = comps.length;
for (; compIdx < compLen; compIdx++) {
result += stringify.component(comps[compIdx], designSet) + LINE_ENDING;
}
result += 'END:' + name;
return result;
};
/**
* Converts a single jCal/jCard property to a iCalendar/vCard string.
*
* @function ICAL.stringify.property
* @param {Array} property
* jCal/jCard property array
* @param {ICAL.design.designSet} designSet
* The design data to use for this property
* @param {Boolean} noFold
* If true, the line is not folded
* @return {String} The iCalendar/vCard string
*/
stringify.property = function(property, designSet, noFold) {
var name = property[0].toUpperCase();
var jsName = property[0];
var params = property[1];
var line = name;
var paramName;
for (paramName in params) {
var value = params[paramName];
/* istanbul ignore else */
if (params.hasOwnProperty(paramName)) {
var multiValue = (paramName in designSet.param) && designSet.param[paramName].multiValue;
if (multiValue && Array.isArray(value)) {
if (designSet.param[paramName].multiValueSeparateDQuote) {
multiValue = '"' + multiValue + '"';
}
value = value.map(stringify._rfc6868Unescape);
value = stringify.multiValue(value, multiValue, "unknown", null, designSet);
} else {
value = stringify._rfc6868Unescape(value);
}
line += ';' + paramName.toUpperCase();
line += '=' + stringify.propertyValue(value);
}
}
if (property.length === 3) {
// If there are no values, we must assume a blank value
return line + ':';
}
var valueType = property[2];
if (!designSet) {
designSet = design.defaultSet;
}
var propDetails;
var multiValue = false;
var structuredValue = false;
var isDefault = false;
if (jsName in designSet.property) {
propDetails = designSet.property[jsName];
if ('multiValue' in propDetails) {
multiValue = propDetails.multiValue;
}
if (('structuredValue' in propDetails) && Array.isArray(property[3])) {
structuredValue = propDetails.structuredValue;
}
if ('defaultType' in propDetails) {
if (valueType === propDetails.defaultType) {
isDefault = true;
}
} else {
if (valueType === DEFAULT_VALUE_TYPE) {
isDefault = true;
}
}
} else {
if (valueType === DEFAULT_VALUE_TYPE) {
isDefault = true;
}
}
// push the VALUE property if type is not the default
// for the current property.
if (!isDefault) {
// value will never contain ;/:/, so we don't escape it here.
line += ';VALUE=' + valueType.toUpperCase();
}
line += ':';
if (multiValue && structuredValue) {
line += stringify.multiValue(
property[3], structuredValue, valueType, multiValue, designSet, structuredValue
);
} else if (multiValue) {
line += stringify.multiValue(
property.slice(3), multiValue, valueType, null, designSet, false
);
} else if (structuredValue) {
line += stringify.multiValue(
property[3], structuredValue, valueType, null, designSet, structuredValue
);
} else {
line += stringify.value(property[3], valueType, designSet, false);
}
return noFold ? line : ICAL.helpers.foldline(line);
};
/**
* Handles escaping of property values that may contain:
*
* COLON (:), SEMICOLON (;), or COMMA (,)
*
* If any of the above are present the result is wrapped
* in double quotes.
*
* @function ICAL.stringify.propertyValue
* @param {String} value Raw property value
* @return {String} Given or escaped value when needed
*/
stringify.propertyValue = function(value) {
if ((helpers.unescapedIndexOf(value, ',') === -1) &&
(helpers.unescapedIndexOf(value, ':') === -1) &&
(helpers.unescapedIndexOf(value, ';') === -1)) {
return value;
}
return '"' + value + '"';
};
/**
* Converts an array of ical values into a single
* string based on a type and a delimiter value (like ",").
*
* @function ICAL.stringify.multiValue
* @param {Array} values List of values to convert
* @param {String} delim Used to join the values (",", ";", ":")
* @param {String} type Lowecase ical value type
* (like boolean, date-time, etc..)
* @param {?String} innerMulti If set, each value will again be processed
* Used for structured values
* @param {ICAL.design.designSet} designSet
* The design data to use for this property
*
* @return {String} iCalendar/vCard string for value
*/
stringify.multiValue = function(values, delim, type, innerMulti, designSet, structuredValue) {
var result = '';
var len = values.length;
var i = 0;
for (; i < len; i++) {
if (innerMulti && Array.isArray(values[i])) {
result += stringify.multiValue(values[i], innerMulti, type, null, designSet, structuredValue);
} else {
result += stringify.value(values[i], type, designSet, structuredValue);
}
if (i !== (len - 1)) {
result += delim;
}
}
return result;
};
/**
* Processes a single ical value runs the associated "toICAL" method from the
* design value type if available to convert the value.
*
* @function ICAL.stringify.value
* @param {String|Number} value A formatted value
* @param {String} type Lowercase iCalendar/vCard value type
* (like boolean, date-time, etc..)
* @return {String} iCalendar/vCard value for single value
*/
stringify.value = function(value, type, designSet, structuredValue) {
if (type in designSet.value && 'toICAL' in designSet.value[type]) {
return designSet.value[type].toICAL(value, structuredValue);
}
return value;
};
/**
* Internal helper for rfc6868. Exposing this on ICAL.stringify so that
* hackers can disable the rfc6868 parsing if the really need to.
*
* @param {String} val The value to unescape
* @return {String} The escaped value
*/
stringify._rfc6868Unescape = function(val) {
return val.replace(/[\n^"]/g, function(x) {
return RFC6868_REPLACE_MAP[x];
});
};
var RFC6868_REPLACE_MAP = { '"': "^'", "\n": "^n", "^": "^^" };
return stringify;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* Contains various functions to parse iCalendar and vCard data.
* @namespace
*/
ICAL.parse = (function() {
'use strict';
var CHAR = /[^ \t]/;
var MULTIVALUE_DELIMITER = ',';
var VALUE_DELIMITER = ':';
var PARAM_DELIMITER = ';';
var PARAM_NAME_DELIMITER = '=';
var DEFAULT_VALUE_TYPE = 'unknown';
var DEFAULT_PARAM_TYPE = 'text';
var design = ICAL.design;
var helpers = ICAL.helpers;
/**
* An error that occurred during parsing.
*
* @param {String} message The error message
* @memberof ICAL.parse
* @extends {Error}
* @class
*/
function ParserError(message) {
this.message = message;
this.name = 'ParserError';
try {
throw new Error();
} catch (e) {
if (e.stack) {
var split = e.stack.split('\n');
split.shift();
this.stack = split.join('\n');
}
}
}
ParserError.prototype = Error.prototype;
/**
* Parses iCalendar or vCard data into a raw jCal object. Consult
* documentation on the {@tutorial layers|layers of parsing} for more
* details.
*
* @function ICAL.parse
* @variation function
* @todo Fix the API to be more clear on the return type
* @param {String} input The string data to parse
* @return {Object|Object[]} A single jCal object, or an array thereof
*/
function parser(input) {
var state = {};
var root = state.component = [];
state.stack = [root];
parser._eachLine(input, function(err, line) {
parser._handleContentLine(line, state);
});
// when there are still items on the stack
// throw a fatal error, a component was not closed
// correctly in that case.
if (state.stack.length > 1) {
throw new ParserError(
'invalid ical body. component began but did not end'
);
}
state = null;
return (root.length == 1 ? root[0] : root);
}
/**
* Parse an iCalendar property value into the jCal for a single property
*
* @function ICAL.parse.property
* @param {String} str
* The iCalendar property string to parse
* @param {ICAL.design.designSet=} designSet
* The design data to use for this property
* @return {Object}
* The jCal Object containing the property
*/
parser.property = function(str, designSet) {
var state = {
component: [[], []],
designSet: designSet || design.defaultSet
};
parser._handleContentLine(str, state);
return state.component[1][0];
};
/**
* Convenience method to parse a component. You can use ICAL.parse() directly
* instead.
*
* @function ICAL.parse.component
* @see ICAL.parse(function)
* @param {String} str The iCalendar component string to parse
* @return {Object} The jCal Object containing the component
*/
parser.component = function(str) {
return parser(str);
};
// classes & constants
parser.ParserError = ParserError;
/**
* The state for parsing content lines from an iCalendar/vCard string.
*
* @private
* @memberof ICAL.parse
* @typedef {Object} parserState
* @property {ICAL.design.designSet} designSet The design set to use for parsing
* @property {ICAL.Component[]} stack The stack of components being processed
* @property {ICAL.Component} component The currently active component
*/
/**
* Handles a single line of iCalendar/vCard, updating the state.
*
* @private
* @function ICAL.parse._handleContentLine
* @param {String} line The content line to process
* @param {ICAL.parse.parserState} The current state of the line parsing
*/
parser._handleContentLine = function(line, state) {
// break up the parts of the line
var valuePos = line.indexOf(VALUE_DELIMITER);
var paramPos = line.indexOf(PARAM_DELIMITER);
var lastParamIndex;
var lastValuePos;
// name of property or begin/end
var name;
var value;
// params is only overridden if paramPos !== -1.
// we can't do params = params || {} later on
// because it sacrifices ops.
var params = {};
/**
* Different property cases
*
*
* 1. RRULE:FREQ=foo
* // FREQ= is not a param but the value
*
* 2. ATTENDEE;ROLE=REQ-PARTICIPANT;
* // ROLE= is a param because : has not happened yet
*/
// when the parameter delimiter is after the
// value delimiter then it is not a parameter.
if ((paramPos !== -1 && valuePos !== -1)) {
// when the parameter delimiter is after the
// value delimiter then it is not a parameter.
if (paramPos > valuePos) {
paramPos = -1;
}
}
var parsedParams;
if (paramPos !== -1) {
name = line.substring(0, paramPos).toLowerCase();
parsedParams = parser._parseParameters(line.substring(paramPos), 0, state.designSet);
if (parsedParams[2] == -1) {
throw new ParserError("Invalid parameters in '" + line + "'");
}
params = parsedParams[0];
lastParamIndex = parsedParams[1].length + parsedParams[2] + paramPos;
if ((lastValuePos =
line.substring(lastParamIndex).indexOf(VALUE_DELIMITER)) !== -1) {
value = line.substring(lastParamIndex + lastValuePos + 1);
} else {
throw new ParserError("Missing parameter value in '" + line + "'");
}
} else if (valuePos !== -1) {
// without parmeters (BEGIN:VCAENDAR, CLASS:PUBLIC)
name = line.substring(0, valuePos).toLowerCase();
value = line.substring(valuePos + 1);
if (name === 'begin') {
var newComponent = [value.toLowerCase(), [], []];
if (state.stack.length === 1) {
state.component.push(newComponent);
} else {
state.component[2].push(newComponent);
}
state.stack.push(state.component);
state.component = newComponent;
if (!state.designSet) {
state.designSet = design.getDesignSet(state.component[0]);
}
return;
} else if (name === 'end') {
state.component = state.stack.pop();
return;
}
// If it is not begin/end, then this is a property with an empty value,
// which should be considered valid.
} else {
/**
* Invalid line.
* The rational to throw an error is we will
* never be certain that the rest of the file
* is sane and it is unlikely that we can serialize
* the result correctly either.
*/
throw new ParserError(
'invalid line (no token ";" or ":") "' + line + '"'
);
}
var valueType;
var multiValue = false;
var structuredValue = false;
var propertyDetails;
if (name in state.designSet.property) {
propertyDetails = state.designSet.property[name];
if ('multiValue' in propertyDetails) {
multiValue = propertyDetails.multiValue;
}
if ('structuredValue' in propertyDetails) {
structuredValue = propertyDetails.structuredValue;
}
if (value && 'detectType' in propertyDetails) {
valueType = propertyDetails.detectType(value);
}
}
// attempt to determine value
if (!valueType) {
if (!('value' in params)) {
if (propertyDetails) {
valueType = propertyDetails.defaultType;
} else {
valueType = DEFAULT_VALUE_TYPE;
}
} else {
// possible to avoid this?
valueType = params.value.toLowerCase();
}
}
delete params.value;
/**
* Note on `var result` juggling:
*
* I observed that building the array in pieces has adverse
* effects on performance, so where possible we inline the creation.
* It is a little ugly but resulted in ~2000 additional ops/sec.
*/
var result;
if (multiValue && structuredValue) {
value = parser._parseMultiValue(value, structuredValue, valueType, [], multiValue, state.designSet, structuredValue);
result = [name, params, valueType, value];
} else if (multiValue) {
result = [name, params, valueType];
parser._parseMultiValue(value, multiValue, valueType, result, null, state.designSet, false);
} else if (structuredValue) {
value = parser._parseMultiValue(value, structuredValue, valueType, [], null, state.designSet, structuredValue);
result = [name, params, valueType, value];
} else {
value = parser._parseValue(value, valueType, state.designSet, false);
result = [name, params, valueType, value];
}
// rfc6350 requires that in vCard 4.0 the first component is the VERSION
// component with as value 4.0, note that 3.0 does not have this requirement.
if (state.component[0] === 'vcard' && state.component[1].length === 0 &&
!(name === 'version' && value === '4.0')) {
state.designSet = design.getDesignSet("vcard3");
}
state.component[1].push(result);
};
/**
* Parse a value from the raw value into the jCard/jCal value.
*
* @private
* @function ICAL.parse._parseValue
* @param {String} value Original value
* @param {String} type Type of value
* @param {Object} designSet The design data to use for this value
* @return {Object} varies on type
*/
parser._parseValue = function(value, type, designSet, structuredValue) {
if (type in designSet.value && 'fromICAL' in designSet.value[type]) {
return designSet.value[type].fromICAL(value, structuredValue);
}
return value;
};
/**
* Parse parameters from a string to object.
*
* @function ICAL.parse._parseParameters
* @private
* @param {String} line A single unfolded line
* @param {Numeric} start Position to start looking for properties
* @param {Object} designSet The design data to use for this property
* @return {Object} key/value pairs
*/
parser._parseParameters = function(line, start, designSet) {
var lastParam = start;
var pos = 0;
var delim = PARAM_NAME_DELIMITER;
var result = {};
var name, lcname;
var value, valuePos = -1;
var type, multiValue, mvdelim;
// find the next '=' sign
// use lastParam and pos to find name
// check if " is used if so get value from "->"
// then increment pos to find next ;
while ((pos !== false) &&
(pos = helpers.unescapedIndexOf(line, delim, pos + 1)) !== -1) {
name = line.substr(lastParam + 1, pos - lastParam - 1);
if (name.length == 0) {
throw new ParserError("Empty parameter name in '" + line + "'");
}
lcname = name.toLowerCase();
mvdelim = false;
multiValue = false;
if (lcname in designSet.param && designSet.param[lcname].valueType) {
type = designSet.param[lcname].valueType;
} else {
type = DEFAULT_PARAM_TYPE;
}
if (lcname in designSet.param) {
multiValue = designSet.param[lcname].multiValue;
if (designSet.param[lcname].multiValueSeparateDQuote) {
mvdelim = parser._rfc6868Escape('"' + multiValue + '"');
}
}
var nextChar = line[pos + 1];
if (nextChar === '"') {
valuePos = pos + 2;
pos = helpers.unescapedIndexOf(line, '"', valuePos);
if (multiValue && pos != -1) {
var extendedValue = true;
while (extendedValue) {
if (line[pos + 1] == multiValue && line[pos + 2] == '"') {
pos = helpers.unescapedIndexOf(line, '"', pos + 3);
} else {
extendedValue = false;
}
}
}
if (pos === -1) {
throw new ParserError(
'invalid line (no matching double quote) "' + line + '"'
);
}
value = line.substr(valuePos, pos - valuePos);
lastParam = helpers.unescapedIndexOf(line, PARAM_DELIMITER, pos);
if (lastParam === -1) {
pos = false;
}
} else {
valuePos = pos + 1;
// move to next ";"
var nextPos = helpers.unescapedIndexOf(line, PARAM_DELIMITER, valuePos);
var propValuePos = helpers.unescapedIndexOf(line, VALUE_DELIMITER, valuePos);
if (propValuePos !== -1 && nextPos > propValuePos) {
// this is a delimiter in the property value, let's stop here
nextPos = propValuePos;
pos = false;
} else if (nextPos === -1) {
// no ";"
if (propValuePos === -1) {
nextPos = line.length;
} else {
nextPos = propValuePos;
}
pos = false;
} else {
lastParam = nextPos;
pos = nextPos;
}
value = line.substr(valuePos, nextPos - valuePos);
}
value = parser._rfc6868Escape(value);
if (multiValue) {
var delimiter = mvdelim || multiValue;
value = parser._parseMultiValue(value, delimiter, type, [], null, designSet);
} else {
value = parser._parseValue(value, type, designSet);
}
if (multiValue && (lcname in result)) {
if (Array.isArray(result[lcname])) {
result[lcname].push(value);
} else {
result[lcname] = [
result[lcname],
value
];
}
} else {
result[lcname] = value;
}
}
return [result, value, valuePos];
};
/**
* Internal helper for rfc6868. Exposing this on ICAL.parse so that
* hackers can disable the rfc6868 parsing if the really need to.
*
* @function ICAL.parse._rfc6868Escape
* @param {String} val The value to escape
* @return {String} The escaped value
*/
parser._rfc6868Escape = function(val) {
return val.replace(/\^['n^]/g, function(x) {
return RFC6868_REPLACE_MAP[x];
});
};
var RFC6868_REPLACE_MAP = { "^'": '"', "^n": "\n", "^^": "^" };
/**
* Parse a multi value string. This function is used either for parsing
* actual multi-value property's values, or for handling parameter values. It
* can be used for both multi-value properties and structured value properties.
*
* @private
* @function ICAL.parse._parseMultiValue
* @param {String} buffer The buffer containing the full value
* @param {String} delim The multi-value delimiter
* @param {String} type The value type to be parsed
* @param {Array.<?>} result The array to append results to, varies on value type
* @param {String} innerMulti The inner delimiter to split each value with
* @param {ICAL.design.designSet} designSet The design data for this value
* @return {?|Array.<?>} Either an array of results, or the first result
*/
parser._parseMultiValue = function(buffer, delim, type, result, innerMulti, designSet, structuredValue) {
var pos = 0;
var lastPos = 0;
var value;
if (delim.length === 0) {
return buffer;
}
// split each piece
while ((pos = helpers.unescapedIndexOf(buffer, delim, lastPos)) !== -1) {
value = buffer.substr(lastPos, pos - lastPos);
if (innerMulti) {
value = parser._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
} else {
value = parser._parseValue(value, type, designSet, structuredValue);
}
result.push(value);
lastPos = pos + delim.length;
}
// on the last piece take the rest of string
value = buffer.substr(lastPos);
if (innerMulti) {
value = parser._parseMultiValue(value, innerMulti, type, [], null, designSet, structuredValue);
} else {
value = parser._parseValue(value, type, designSet, structuredValue);
}
result.push(value);
return result.length == 1 ? result[0] : result;
};
/**
* Process a complete buffer of iCalendar/vCard data line by line, correctly
* unfolding content. Each line will be processed with the given callback
*
* @private
* @function ICAL.parse._eachLine
* @param {String} buffer The buffer to process
* @param {function(?String, String)} callback The callback for each line
*/
parser._eachLine = function(buffer, callback) {
var len = buffer.length;
var lastPos = buffer.search(CHAR);
var pos = lastPos;
var line;
var firstChar;
var newlineOffset;
do {
pos = buffer.indexOf('\n', lastPos) + 1;
if (pos > 1 && buffer[pos - 2] === '\r') {
newlineOffset = 2;
} else {
newlineOffset = 1;
}
if (pos === 0) {
pos = len;
newlineOffset = 0;
}
firstChar = buffer[lastPos];
if (firstChar === ' ' || firstChar === '\t') {
// add to line
line += buffer.substr(
lastPos + 1,
pos - lastPos - (newlineOffset + 1)
);
} else {
if (line)
callback(null, line);
// push line
line = buffer.substr(
lastPos,
pos - lastPos - newlineOffset
);
}
lastPos = pos;
} while (pos !== len);
// extra ending line
line = line.trim();
if (line.length)
callback(null, line);
};
return parser;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.Component = (function() {
'use strict';
var PROPERTY_INDEX = 1;
var COMPONENT_INDEX = 2;
var NAME_INDEX = 0;
/**
* @classdesc
* Wraps a jCal component, adding convenience methods to add, remove and
* update subcomponents and properties.
*
* @class
* @alias ICAL.Component
* @param {Array|String} jCal Raw jCal component data OR name of new
* component
* @param {ICAL.Component} parent Parent component to associate
*/
function Component(jCal, parent) {
if (typeof(jCal) === 'string') {
// jCal spec (name, properties, components)
jCal = [jCal, [], []];
}
// mostly for legacy reasons.
this.jCal = jCal;
this.parent = parent || null;
}
Component.prototype = {
/**
* Hydrated properties are inserted into the _properties array at the same
* position as in the jCal array, so it is possible that the array contains
* undefined values for unhydrdated properties. To avoid iterating the
* array when checking if all properties have been hydrated, we save the
* count here.
*
* @type {Number}
* @private
*/
_hydratedPropertyCount: 0,
/**
* The same count as for _hydratedPropertyCount, but for subcomponents
*
* @type {Number}
* @private
*/
_hydratedComponentCount: 0,
/**
* The name of this component
* @readonly
*/
get name() {
return this.jCal[NAME_INDEX];
},
/**
* The design set for this component, e.g. icalendar vs vcard
*
* @type {ICAL.design.designSet}
* @private
*/
get _designSet() {
var parentDesign = this.parent && this.parent._designSet;
return parentDesign || ICAL.design.getDesignSet(this.name);
},
_hydrateComponent: function(index) {
if (!this._components) {
this._components = [];
this._hydratedComponentCount = 0;
}
if (this._components[index]) {
return this._components[index];
}
var comp = new Component(
this.jCal[COMPONENT_INDEX][index],
this
);
this._hydratedComponentCount++;
return (this._components[index] = comp);
},
_hydrateProperty: function(index) {
if (!this._properties) {
this._properties = [];
this._hydratedPropertyCount = 0;
}
if (this._properties[index]) {
return this._properties[index];
}
var prop = new ICAL.Property(
this.jCal[PROPERTY_INDEX][index],
this
);
this._hydratedPropertyCount++;
return (this._properties[index] = prop);
},
/**
* Finds first sub component, optionally filtered by name.
*
* @param {String=} name Optional name to filter by
* @return {?ICAL.Component} The found subcomponent
*/
getFirstSubcomponent: function(name) {
if (name) {
var i = 0;
var comps = this.jCal[COMPONENT_INDEX];
var len = comps.length;
for (; i < len; i++) {
if (comps[i][NAME_INDEX] === name) {
var result = this._hydrateComponent(i);
return result;
}
}
} else {
if (this.jCal[COMPONENT_INDEX].length) {
return this._hydrateComponent(0);
}
}
// ensure we return a value (strict mode)
return null;
},
/**
* Finds all sub components, optionally filtering by name.
*
* @param {String=} name Optional name to filter by
* @return {ICAL.Component[]} The found sub components
*/
getAllSubcomponents: function(name) {
var jCalLen = this.jCal[COMPONENT_INDEX].length;
var i = 0;
if (name) {
var comps = this.jCal[COMPONENT_INDEX];
var result = [];
for (; i < jCalLen; i++) {
if (name === comps[i][NAME_INDEX]) {
result.push(
this._hydrateComponent(i)
);
}
}
return result;
} else {
if (!this._components ||
(this._hydratedComponentCount !== jCalLen)) {
for (; i < jCalLen; i++) {
this._hydrateComponent(i);
}
}
return this._components || [];
}
},
/**
* Returns true when a named property exists.
*
* @param {String} name The property name
* @return {Boolean} True, when property is found
*/
hasProperty: function(name) {
var props = this.jCal[PROPERTY_INDEX];
var len = props.length;
var i = 0;
for (; i < len; i++) {
// 0 is property name
if (props[i][NAME_INDEX] === name) {
return true;
}
}
return false;
},
/**
* Finds the first property, optionally with the given name.
*
* @param {String=} name Lowercase property name
* @return {?ICAL.Property} The found property
*/
getFirstProperty: function(name) {
if (name) {
var i = 0;
var props = this.jCal[PROPERTY_INDEX];
var len = props.length;
for (; i < len; i++) {
if (props[i][NAME_INDEX] === name) {
var result = this._hydrateProperty(i);
return result;
}
}
} else {
if (this.jCal[PROPERTY_INDEX].length) {
return this._hydrateProperty(0);
}
}
return null;
},
/**
* Returns first property's value, if available.
*
* @param {String=} name Lowercase property name
* @return {?String} The found property value.
*/
getFirstPropertyValue: function(name) {
var prop = this.getFirstProperty(name);
if (prop) {
return prop.getFirstValue();
}
return null;
},
/**
* Get all properties in the component, optionally filtered by name.
*
* @param {String=} name Lowercase property name
* @return {ICAL.Property[]} List of properties
*/
getAllProperties: function(name) {
var jCalLen = this.jCal[PROPERTY_INDEX].length;
var i = 0;
if (name) {
var props = this.jCal[PROPERTY_INDEX];
var result = [];
for (; i < jCalLen; i++) {
if (name === props[i][NAME_INDEX]) {
result.push(
this._hydrateProperty(i)
);
}
}
return result;
} else {
if (!this._properties ||
(this._hydratedPropertyCount !== jCalLen)) {
for (; i < jCalLen; i++) {
this._hydrateProperty(i);
}
}
return this._properties || [];
}
},
_removeObjectByIndex: function(jCalIndex, cache, index) {
cache = cache || [];
// remove cached version
if (cache[index]) {
var obj = cache[index];
if ("parent" in obj) {
obj.parent = null;
}
}
cache.splice(index, 1);
// remove it from the jCal
this.jCal[jCalIndex].splice(index, 1);
},
_removeObject: function(jCalIndex, cache, nameOrObject) {
var i = 0;
var objects = this.jCal[jCalIndex];
var len = objects.length;
var cached = this[cache];
if (typeof(nameOrObject) === 'string') {
for (; i < len; i++) {
if (objects[i][NAME_INDEX] === nameOrObject) {
this._removeObjectByIndex(jCalIndex, cached, i);
return true;
}
}
} else if (cached) {
for (; i < len; i++) {
if (cached[i] && cached[i] === nameOrObject) {
this._removeObjectByIndex(jCalIndex, cached, i);
return true;
}
}
}
return false;
},
_removeAllObjects: function(jCalIndex, cache, name) {
var cached = this[cache];
// Unfortunately we have to run through all children to reset their
// parent property.
var objects = this.jCal[jCalIndex];
var i = objects.length - 1;
// descending search required because splice
// is used and will effect the indices.
for (; i >= 0; i--) {
if (!name || objects[i][NAME_INDEX] === name) {
this._removeObjectByIndex(jCalIndex, cached, i);
}
}
},
/**
* Adds a single sub component.
*
* @param {ICAL.Component} component The component to add
* @return {ICAL.Component} The passed in component
*/
addSubcomponent: function(component) {
if (!this._components) {
this._components = [];
this._hydratedComponentCount = 0;
}
if (component.parent) {
component.parent.removeSubcomponent(component);
}
var idx = this.jCal[COMPONENT_INDEX].push(component.jCal);
this._components[idx - 1] = component;
this._hydratedComponentCount++;
component.parent = this;
return component;
},
/**
* Removes a single component by name or the instance of a specific
* component.
*
* @param {ICAL.Component|String} nameOrComp Name of component, or component
* @return {Boolean} True when comp is removed
*/
removeSubcomponent: function(nameOrComp) {
var removed = this._removeObject(COMPONENT_INDEX, '_components', nameOrComp);
if (removed) {
this._hydratedComponentCount--;
}
return removed;
},
/**
* Removes all components or (if given) all components by a particular
* name.
*
* @param {String=} name Lowercase component name
*/
removeAllSubcomponents: function(name) {
var removed = this._removeAllObjects(COMPONENT_INDEX, '_components', name);
this._hydratedComponentCount = 0;
return removed;
},
/**
* Adds an {@link ICAL.Property} to the component.
*
* @param {ICAL.Property} property The property to add
* @return {ICAL.Property} The passed in property
*/
addProperty: function(property) {
if (!(property instanceof ICAL.Property)) {
throw new TypeError('must instance of ICAL.Property');
}
if (!this._properties) {
this._properties = [];
this._hydratedPropertyCount = 0;
}
if (property.parent) {
property.parent.removeProperty(property);
}
var idx = this.jCal[PROPERTY_INDEX].push(property.jCal);
this._properties[idx - 1] = property;
this._hydratedPropertyCount++;
property.parent = this;
return property;
},
/**
* Helper method to add a property with a value to the component.
*
* @param {String} name Property name to add
* @param {String|Number|Object} value Property value
* @return {ICAL.Property} The created property
*/
addPropertyWithValue: function(name, value) {
var prop = new ICAL.Property(name);
prop.setValue(value);
this.addProperty(prop);
return prop;
},
/**
* Helper method that will update or create a property of the given name
* and sets its value. If multiple properties with the given name exist,
* only the first is updated.
*
* @param {String} name Property name to update
* @param {String|Number|Object} value Property value
* @return {ICAL.Property} The created property
*/
updatePropertyWithValue: function(name, value) {
var prop = this.getFirstProperty(name);
if (prop) {
prop.setValue(value);
} else {
prop = this.addPropertyWithValue(name, value);
}
return prop;
},
/**
* Removes a single property by name or the instance of the specific
* property.
*
* @param {String|ICAL.Property} nameOrProp Property name or instance to remove
* @return {Boolean} True, when deleted
*/
removeProperty: function(nameOrProp) {
var removed = this._removeObject(PROPERTY_INDEX, '_properties', nameOrProp);
if (removed) {
this._hydratedPropertyCount--;
}
return removed;
},
/**
* Removes all properties associated with this component, optionally
* filtered by name.
*
* @param {String=} name Lowercase property name
* @return {Boolean} True, when deleted
*/
removeAllProperties: function(name) {
var removed = this._removeAllObjects(PROPERTY_INDEX, '_properties', name);
this._hydratedPropertyCount = 0;
return removed;
},
/**
* Returns the Object representation of this component. The returned object
* is a live jCal object and should be cloned if modified.
* @return {Object}
*/
toJSON: function() {
return this.jCal;
},
/**
* The string representation of this component.
* @return {String}
*/
toString: function() {
return ICAL.stringify.component(
this.jCal, this._designSet
);
}
};
/**
* Create an {@link ICAL.Component} by parsing the passed iCalendar string.
*
* @param {String} str The iCalendar string to parse
*/
Component.fromString = function(str) {
return new Component(ICAL.parse.component(str));
};
return Component;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.Property = (function() {
'use strict';
var NAME_INDEX = 0;
var PROP_INDEX = 1;
var TYPE_INDEX = 2;
var VALUE_INDEX = 3;
var design = ICAL.design;
/**
* @classdesc
* Provides a layer on top of the raw jCal object for manipulating a single
* property, with its parameters and value.
*
* @description
* It is important to note that mutations done in the wrapper
* directly mutate the jCal object used to initialize.
*
* Can also be used to create new properties by passing
* the name of the property (as a String).
*
* @class
* @alias ICAL.Property
* @param {Array|String} jCal Raw jCal representation OR
* the new name of the property
*
* @param {ICAL.Component=} parent Parent component
*/
function Property(jCal, parent) {
this._parent = parent || null;
if (typeof(jCal) === 'string') {
// We are creating the property by name and need to detect the type
this.jCal = [jCal, {}, design.defaultType];
this.jCal[TYPE_INDEX] = this.getDefaultType();
} else {
this.jCal = jCal;
}
this._updateType();
}
Property.prototype = {
/**
* The value type for this property
* @readonly
* @type {String}
*/
get type() {
return this.jCal[TYPE_INDEX];
},
/**
* The name of this property, in lowercase.
* @readonly
* @type {String}
*/
get name() {
return this.jCal[NAME_INDEX];
},
/**
* The parent component for this property.
* @type {ICAL.Component}
*/
get parent() {
return this._parent;
},
set parent(p) {
// Before setting the parent, check if the design set has changed. If it
// has, we later need to update the type if it was unknown before.
var designSetChanged = !this._parent || (p && p._designSet != this._parent._designSet);
this._parent = p;
if (this.type == design.defaultType && designSetChanged) {
this.jCal[TYPE_INDEX] = this.getDefaultType();
this._updateType();
}
return p;
},
/**
* The design set for this property, e.g. icalendar vs vcard
*
* @type {ICAL.design.designSet}
* @private
*/
get _designSet() {
return this.parent ? this.parent._designSet : design.defaultSet;
},
/**
* Updates the type metadata from the current jCal type and design set.
*
* @private
*/
_updateType: function() {
var designSet = this._designSet;
if (this.type in designSet.value) {
var designType = designSet.value[this.type];
if ('decorate' in designSet.value[this.type]) {
this.isDecorated = true;
} else {
this.isDecorated = false;
}
if (this.name in designSet.property) {
this.isMultiValue = ('multiValue' in designSet.property[this.name]);
this.isStructuredValue = ('structuredValue' in designSet.property[this.name]);
}
}
},
/**
* Hydrate a single value. The act of hydrating means turning the raw jCal
* value into a potentially wrapped object, for example {@link ICAL.Time}.
*
* @private
* @param {Number} index The index of the value to hydrate
* @return {Object} The decorated value.
*/
_hydrateValue: function(index) {
if (this._values && this._values[index]) {
return this._values[index];
}
// for the case where there is no value.
if (this.jCal.length <= (VALUE_INDEX + index)) {
return null;
}
if (this.isDecorated) {
if (!this._values) {
this._values = [];
}
return (this._values[index] = this._decorate(
this.jCal[VALUE_INDEX + index]
));
} else {
return this.jCal[VALUE_INDEX + index];
}
},
/**
* Decorate a single value, returning its wrapped object. This is used by
* the hydrate function to actually wrap the value.
*
* @private
* @param {?} value The value to decorate
* @return {Object} The decorated value
*/
_decorate: function(value) {
return this._designSet.value[this.type].decorate(value, this);
},
/**
* Undecorate a single value, returning its raw jCal data.
*
* @private
* @param {Object} value The value to undecorate
* @return {?} The undecorated value
*/
_undecorate: function(value) {
return this._designSet.value[this.type].undecorate(value, this);
},
/**
* Sets the value at the given index while also hydrating it. The passed
* value can either be a decorated or undecorated value.
*
* @private
* @param {?} value The value to set
* @param {Number} index The index to set it at
*/
_setDecoratedValue: function(value, index) {
if (!this._values) {
this._values = [];
}
if (typeof(value) === 'object' && 'icaltype' in value) {
// decorated value
this.jCal[VALUE_INDEX + index] = this._undecorate(value);
this._values[index] = value;
} else {
// undecorated value
this.jCal[VALUE_INDEX + index] = value;
this._values[index] = this._decorate(value);
}
},
/**
* Gets a parameter on the property.
*
* @param {String} name Parameter name (lowercase)
* @return {Array|String} Parameter value
*/
getParameter: function(name) {
if (name in this.jCal[PROP_INDEX]) {
return this.jCal[PROP_INDEX][name];
} else {
return undefined;
}
},
/**
* Gets first parameter on the property.
*
* @param {String} name Parameter name (lowercase)
* @return {String} Parameter value
*/
getFirstParameter: function(name) {
var parameters = this.getParameter(name);
if (Array.isArray(parameters)) {
return parameters[0];
}
return parameters;
},
/**
* Sets a parameter on the property.
*
* @param {String} name The parameter name
* @param {Array|String} value The parameter value
*/
setParameter: function(name, value) {
var lcname = name.toLowerCase();
if (typeof value === "string" &&
lcname in this._designSet.param &&
'multiValue' in this._designSet.param[lcname]) {
value = [value];
}
this.jCal[PROP_INDEX][name] = value;
},
/**
* Removes a parameter
*
* @param {String} name The parameter name
*/
removeParameter: function(name) {
delete this.jCal[PROP_INDEX][name];
},
/**
* Get the default type based on this property's name.
*
* @return {String} The default type for this property
*/
getDefaultType: function() {
var name = this.jCal[NAME_INDEX];
var designSet = this._designSet;
if (name in designSet.property) {
var details = designSet.property[name];
if ('defaultType' in details) {
return details.defaultType;
}
}
return design.defaultType;
},
/**
* Sets type of property and clears out any existing values of the current
* type.
*
* @param {String} type New iCAL type (see design.*.values)
*/
resetType: function(type) {
this.removeAllValues();
this.jCal[TYPE_INDEX] = type;
this._updateType();
},
/**
* Finds the first property value.
*
* @return {String} First property value
*/
getFirstValue: function() {
return this._hydrateValue(0);
},
/**
* Gets all values on the property.
*
* NOTE: this creates an array during each call.
*
* @return {Array} List of values
*/
getValues: function() {
var len = this.jCal.length - VALUE_INDEX;
if (len < 1) {
// it is possible for a property to have no value.
return [];
}
var i = 0;
var result = [];
for (; i < len; i++) {
result[i] = this._hydrateValue(i);
}
return result;
},
/**
* Removes all values from this property
*/
removeAllValues: function() {
if (this._values) {
this._values.length = 0;
}
this.jCal.length = 3;
},
/**
* Sets the values of the property. Will overwrite the existing values.
* This can only be used for multi-value properties.
*
* @param {Array} values An array of values
*/
setValues: function(values) {
if (!this.isMultiValue) {
throw new Error(
this.name + ': does not not support mulitValue.\n' +
'override isMultiValue'
);
}
var len = values.length;
var i = 0;
this.removeAllValues();
if (len > 0 &&
typeof(values[0]) === 'object' &&
'icaltype' in values[0]) {
this.resetType(values[0].icaltype);
}
if (this.isDecorated) {
for (; i < len; i++) {
this._setDecoratedValue(values[i], i);
}
} else {
for (; i < len; i++) {
this.jCal[VALUE_INDEX + i] = values[i];
}
}
},
/**
* Sets the current value of the property. If this is a multi-value
* property, all other values will be removed.
*
* @param {String|Object} value New property value.
*/
setValue: function(value) {
this.removeAllValues();
if (typeof(value) === 'object' && 'icaltype' in value) {
this.resetType(value.icaltype);
}
if (this.isDecorated) {
this._setDecoratedValue(value, 0);
} else {
this.jCal[VALUE_INDEX] = value;
}
},
/**
* Returns the Object representation of this component. The returned object
* is a live jCal object and should be cloned if modified.
* @return {Object}
*/
toJSON: function() {
return this.jCal;
},
/**
* The string representation of this component.
* @return {String}
*/
toICALString: function() {
return ICAL.stringify.property(
this.jCal, this._designSet, true
);
}
};
/**
* Create an {@link ICAL.Property} by parsing the passed iCalendar string.
*
* @param {String} str The iCalendar string to parse
* @param {ICAL.design.designSet=} designSet The design data to use for this property
* @return {ICAL.Property} The created iCalendar property
*/
Property.fromString = function(str, designSet) {
return new Property(ICAL.parse.property(str, designSet));
};
return Property;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.UtcOffset = (function() {
/**
* @classdesc
* This class represents the "duration" value type, with various calculation
* and manipulation methods.
*
* @class
* @alias ICAL.UtcOffset
* @param {Object} aData An object with members of the utc offset
* @param {Number=} aData.hours The hours for the utc offset
* @param {Number=} aData.minutes The minutes in the utc offset
* @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
*/
function UtcOffset(aData) {
this.fromData(aData);
}
UtcOffset.prototype = {
/**
* The hours in the utc-offset
* @type {Number}
*/
hours: 0,
/**
* The minutes in the utc-offset
* @type {Number}
*/
minutes: 0,
/**
* The sign of the utc offset, 1 for positive offset, -1 for negative
* offsets.
* @type {Number}
*/
factor: 1,
/**
* The type name, to be used in the jCal object.
* @constant
* @type {String}
* @default "utc-offset"
*/
icaltype: "utc-offset",
/**
* Returns a clone of the utc offset object.
*
* @return {ICAL.UtcOffset} The cloned object
*/
clone: function() {
return ICAL.UtcOffset.fromSeconds(this.toSeconds());
},
/**
* Sets up the current instance using members from the passed data object.
*
* @param {Object} aData An object with members of the utc offset
* @param {Number=} aData.hours The hours for the utc offset
* @param {Number=} aData.minutes The minutes in the utc offset
* @param {Number=} aData.factor The factor for the utc-offset, either -1 or 1
*/
fromData: function(aData) {
if (aData) {
for (var key in aData) {
/* istanbul ignore else */
if (aData.hasOwnProperty(key)) {
this[key] = aData[key];
}
}
}
this._normalize();
},
/**
* Sets up the current instance from the given seconds value. The seconds
* value is truncated to the minute. Offsets are wrapped when the world
* ends, the hour after UTC+14:00 is UTC-12:00.
*
* @param {Number} aSeconds The seconds to convert into an offset
*/
fromSeconds: function(aSeconds) {
var secs = Math.abs(aSeconds);
this.factor = aSeconds < 0 ? -1 : 1;
this.hours = ICAL.helpers.trunc(secs / 3600);
secs -= (this.hours * 3600);
this.minutes = ICAL.helpers.trunc(secs / 60);
return this;
},
/**
* Convert the current offset to a value in seconds
*
* @return {Number} The offset in seconds
*/
toSeconds: function() {
return this.factor * (60 * this.minutes + 3600 * this.hours);
},
/**
* Compare this utc offset with another one.
*
* @param {ICAL.UtcOffset} other The other offset to compare with
* @return {Number} -1, 0 or 1 for less/equal/greater
*/
compare: function icaltime_compare(other) {
var a = this.toSeconds();
var b = other.toSeconds();
return (a > b) - (b > a);
},
_normalize: function() {
// Range: 97200 seconds (with 1 hour inbetween)
var secs = this.toSeconds();
var factor = this.factor;
while (secs < -43200) { // = UTC-12:00
secs += 97200;
}
while (secs > 50400) { // = UTC+14:00
secs -= 97200;
}
this.fromSeconds(secs);
// Avoid changing the factor when on zero seconds
if (secs == 0) {
this.factor = factor;
}
},
/**
* The iCalendar string representation of this utc-offset.
* @return {String}
*/
toICALString: function() {
return ICAL.design.icalendar.value['utc-offset'].toICAL(this.toString());
},
/**
* The string representation of this utc-offset.
* @return {String}
*/
toString: function toString() {
return (this.factor == 1 ? "+" : "-") +
ICAL.helpers.pad2(this.hours) + ':' +
ICAL.helpers.pad2(this.minutes);
}
};
/**
* Creates a new {@link ICAL.UtcOffset} instance from the passed string.
*
* @param {String} aString The string to parse
* @return {ICAL.Duration} The created utc-offset instance
*/
UtcOffset.fromString = function(aString) {
// -05:00
var options = {};
//TODO: support seconds per rfc5545 ?
options.factor = (aString[0] === '+') ? 1 : -1;
options.hours = ICAL.helpers.strictParseInt(aString.substr(1, 2));
options.minutes = ICAL.helpers.strictParseInt(aString.substr(4, 2));
return new ICAL.UtcOffset(options);
};
/**
* Creates a new {@link ICAL.UtcOffset} instance from the passed seconds
* value.
*
* @param {Number} aSeconds The number of seconds to convert
*/
UtcOffset.fromSeconds = function(aSeconds) {
var instance = new UtcOffset();
instance.fromSeconds(aSeconds);
return instance;
};
return UtcOffset;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.Binary = (function() {
/**
* @classdesc
* Represents the BINARY value type, which contains extra methods for
* encoding and decoding.
*
* @class
* @alias ICAL.Binary
* @param {String} aValue The binary data for this value
*/
function Binary(aValue) {
this.value = aValue;
}
Binary.prototype = {
/**
* The type name, to be used in the jCal object.
* @default "binary"
* @constant
*/
icaltype: "binary",
/**
* Base64 decode the current value
*
* @return {String} The base64-decoded value
*/
decodeValue: function decodeValue() {
return this._b64_decode(this.value);
},
/**
* Encodes the passed parameter with base64 and sets the internal
* value to the result.
*
* @param {String} aValue The raw binary value to encode
*/
setEncodedValue: function setEncodedValue(aValue) {
this.value = this._b64_encode(aValue);
},
_b64_encode: function base64_encode(data) {
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Bayron Guevara
// + improved by: Thunder.m
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: Rafał Kukawski (http://kukawski.pl)
// * example 1: base64_encode('Kevin van Zonneveld');
// * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['atob'] == 'function') {
// return atob(data);
//}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
enc = "",
tmp_arr = [];
if (!data) {
return data;
}
do { // pack three octets into four hexets
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = o1 << 16 | o2 << 8 | o3;
h1 = bits >> 18 & 0x3f;
h2 = bits >> 12 & 0x3f;
h3 = bits >> 6 & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join('');
var r = data.length % 3;
return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
},
_b64_decode: function base64_decode(data) {
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Thunder.m
// + input by: Aman Gupta
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
// * returns 1: 'Kevin van Zonneveld'
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['btoa'] == 'function') {
// return btoa(data);
//}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"abcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
dec = "",
tmp_arr = [];
if (!data) {
return data;
}
data += '';
do { // unpack four hexets into three octets using index points in b64
h1 = b64.indexOf(data.charAt(i++));
h2 = b64.indexOf(data.charAt(i++));
h3 = b64.indexOf(data.charAt(i++));
h4 = b64.indexOf(data.charAt(i++));
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
o1 = bits >> 16 & 0xff;
o2 = bits >> 8 & 0xff;
o3 = bits & 0xff;
if (h3 == 64) {
tmp_arr[ac++] = String.fromCharCode(o1);
} else if (h4 == 64) {
tmp_arr[ac++] = String.fromCharCode(o1, o2);
} else {
tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
}
} while (i < data.length);
dec = tmp_arr.join('');
return dec;
},
/**
* The string representation of this value
* @return {String}
*/
toString: function() {
return this.value;
}
};
/**
* Creates a binary value from the given string.
*
* @param {String} aString The binary value string
* @return {ICAL.Binary} The binary value instance
*/
Binary.fromString = function(aString) {
return new Binary(aString);
};
return Binary;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
(function() {
/**
* @classdesc
* This class represents the "period" value type, with various calculation
* and manipulation methods.
*
* @description
* The passed data object cannot contain both and end date and a duration.
*
* @class
* @param {Object} aData An object with members of the period
* @param {ICAL.Time=} aData.start The start of the period
* @param {ICAL.Time=} aData.end The end of the period
* @param {ICAL.Duration=} aData.duration The duration of the period
*/
ICAL.Period = function icalperiod(aData) {
this.wrappedJSObject = this;
if (aData && 'start' in aData) {
if (aData.start && !(aData.start instanceof ICAL.Time)) {
throw new TypeError('.start must be an instance of ICAL.Time');
}
this.start = aData.start;
}
if (aData && aData.end && aData.duration) {
throw new Error('cannot accept both end and duration');
}
if (aData && 'end' in aData) {
if (aData.end && !(aData.end instanceof ICAL.Time)) {
throw new TypeError('.end must be an instance of ICAL.Time');
}
this.end = aData.end;
}
if (aData && 'duration' in aData) {
if (aData.duration && !(aData.duration instanceof ICAL.Duration)) {
throw new TypeError('.duration must be an instance of ICAL.Duration');
}
this.duration = aData.duration;
}
};
ICAL.Period.prototype = {
/**
* The start of the period
* @type {ICAL.Time}
*/
start: null,
/**
* The end of the period
* @type {ICAL.Time}
*/
end: null,
/**
* The duration of the period
* @type {ICAL.Duration}
*/
duration: null,
/**
* The class identifier.
* @constant
* @type {String}
* @default "icalperiod"
*/
icalclass: "icalperiod",
/**
* The type name, to be used in the jCal object.
* @constant
* @type {String}
* @default "period"
*/
icaltype: "period",
/**
* Returns a clone of the duration object.
*
* @return {ICAL.Period} The cloned object
*/
clone: function() {
return ICAL.Period.fromData({
start: this.start ? this.start.clone() : null,
end: this.end ? this.end.clone() : null,
duration: this.duration ? this.duration.clone() : null
});
},
/**
* Calculates the duration of the period, either directly or by subtracting
* start from end date.
*
* @return {ICAL.Duration} The calculated duration
*/
getDuration: function duration() {
if (this.duration) {
return this.duration;
} else {
return this.end.subtractDate(this.start);
}
},
/**
* Calculates the end date of the period, either directly or by adding
* duration to start date.
*
* @return {ICAL.Time} The calculated end date
*/
getEnd: function() {
if (this.end) {
return this.end;
} else {
var end = this.start.clone();
end.addDuration(this.duration);
return end;
}
},
/**
* The string representation of this period.
* @return {String}
*/
toString: function toString() {
return this.start + "/" + (this.end || this.duration);
},
/**
* The jCal representation of this period type.
* @return {Object}
*/
toJSON: function() {
return [this.start.toString(), (this.end || this.duration).toString()];
},
/**
* The iCalendar string representation of this period.
* @return {String}
*/
toICALString: function() {
return this.start.toICALString() + "/" +
(this.end || this.duration).toICALString();
}
};
/**
* Creates a new {@link ICAL.Period} instance from the passed string.
*
* @param {String} str The string to parse
* @param {ICAL.Property} prop The property this period will be on
* @return {ICAL.Period} The created period instance
*/
ICAL.Period.fromString = function fromString(str, prop) {
var parts = str.split('/');
if (parts.length !== 2) {
throw new Error(
'Invalid string value: "' + str + '" must contain a "/" char.'
);
}
var options = {
start: ICAL.Time.fromDateTimeString(parts[0], prop)
};
var end = parts[1];
if (ICAL.Duration.isValueString(end)) {
options.duration = ICAL.Duration.fromString(end);
} else {
options.end = ICAL.Time.fromDateTimeString(end, prop);
}
return new ICAL.Period(options);
};
/**
* Creates a new {@link ICAL.Period} instance from the given data object.
* The passed data object cannot contain both and end date and a duration.
*
* @param {Object} aData An object with members of the period
* @param {ICAL.Time=} aData.start The start of the period
* @param {ICAL.Time=} aData.end The end of the period
* @param {ICAL.Duration=} aData.duration The duration of the period
* @return {ICAL.Period} The period instance
*/
ICAL.Period.fromData = function fromData(aData) {
return new ICAL.Period(aData);
};
/**
* Returns a new period instance from the given jCal data array. The first
* member is always the start date string, the second member is either a
* duration or end date string.
*
* @param {Array<String,String>} aData The jCal data array
* @param {ICAL.Property} aProp The property this jCal data is on
* @param {Boolean} aLenient If true, data value can be both date and date-time
* @return {ICAL.Period} The period instance
*/
ICAL.Period.fromJSON = function(aData, aProp, aLenient) {
function fromDateOrDateTimeString(aValue, aProp) {
if (aLenient) {
return ICAL.Time.fromString(aValue, aProp);
} else {
return ICAL.Time.fromDateTimeString(aValue, aProp);
}
}
if (ICAL.Duration.isValueString(aData[1])) {
return ICAL.Period.fromData({
start: fromDateOrDateTimeString(aData[0], aProp),
duration: ICAL.Duration.fromString(aData[1])
});
} else {
return ICAL.Period.fromData({
start: fromDateOrDateTimeString(aData[0], aProp),
end: fromDateOrDateTimeString(aData[1], aProp)
});
}
};
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
(function() {
var DURATION_LETTERS = /([PDWHMTS]{1,1})/;
/**
* @classdesc
* This class represents the "duration" value type, with various calculation
* and manipulation methods.
*
* @class
* @alias ICAL.Duration
* @param {Object} data An object with members of the duration
* @param {Number} data.weeks Duration in weeks
* @param {Number} data.days Duration in days
* @param {Number} data.hours Duration in hours
* @param {Number} data.minutes Duration in minutes
* @param {Number} data.seconds Duration in seconds
* @param {Boolean} data.isNegative If true, the duration is negative
*/
ICAL.Duration = function icalduration(data) {
this.wrappedJSObject = this;
this.fromData(data);
};
ICAL.Duration.prototype = {
/**
* The weeks in this duration
* @type {Number}
* @default 0
*/
weeks: 0,
/**
* The days in this duration
* @type {Number}
* @default 0
*/
days: 0,
/**
* The days in this duration
* @type {Number}
* @default 0
*/
hours: 0,
/**
* The minutes in this duration
* @type {Number}
* @default 0
*/
minutes: 0,
/**
* The seconds in this duration
* @type {Number}
* @default 0
*/
seconds: 0,
/**
* The seconds in this duration
* @type {Boolean}
* @default false
*/
isNegative: false,
/**
* The class identifier.
* @constant
* @type {String}
* @default "icalduration"
*/
icalclass: "icalduration",
/**
* The type name, to be used in the jCal object.
* @constant
* @type {String}
* @default "duration"
*/
icaltype: "duration",
/**
* Returns a clone of the duration object.
*
* @return {ICAL.Duration} The cloned object
*/
clone: function clone() {
return ICAL.Duration.fromData(this);
},
/**
* The duration value expressed as a number of seconds.
*
* @return {Number} The duration value in seconds
*/
toSeconds: function toSeconds() {
var seconds = this.seconds + 60 * this.minutes + 3600 * this.hours +
86400 * this.days + 7 * 86400 * this.weeks;
return (this.isNegative ? -seconds : seconds);
},
/**
* Reads the passed seconds value into this duration object. Afterwards,
* members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
* accordingly.
*
* @param {Number} aSeconds The duration value in seconds
* @return {ICAL.Duration} Returns this instance
*/
fromSeconds: function fromSeconds(aSeconds) {
var secs = Math.abs(aSeconds);
this.isNegative = (aSeconds < 0);
this.days = ICAL.helpers.trunc(secs / 86400);
// If we have a flat number of weeks, use them.
if (this.days % 7 == 0) {
this.weeks = this.days / 7;
this.days = 0;
} else {
this.weeks = 0;
}
secs -= (this.days + 7 * this.weeks) * 86400;
this.hours = ICAL.helpers.trunc(secs / 3600);
secs -= this.hours * 3600;
this.minutes = ICAL.helpers.trunc(secs / 60);
secs -= this.minutes * 60;
this.seconds = secs;
return this;
},
/**
* Sets up the current instance using members from the passed data object.
*
* @param {Object} aData An object with members of the duration
* @param {Number} aData.weeks Duration in weeks
* @param {Number} aData.days Duration in days
* @param {Number} aData.hours Duration in hours
* @param {Number} aData.minutes Duration in minutes
* @param {Number} aData.seconds Duration in seconds
* @param {Boolean} aData.isNegative If true, the duration is negative
*/
fromData: function fromData(aData) {
var propsToCopy = ["weeks", "days", "hours",
"minutes", "seconds", "isNegative"];
for (var key in propsToCopy) {
/* istanbul ignore if */
if (!propsToCopy.hasOwnProperty(key)) {
continue;
}
var prop = propsToCopy[key];
if (aData && prop in aData) {
this[prop] = aData[prop];
} else {
this[prop] = 0;
}
}
},
/**
* Resets the duration instance to the default values, i.e. PT0S
*/
reset: function reset() {
this.isNegative = false;
this.weeks = 0;
this.days = 0;
this.hours = 0;
this.minutes = 0;
this.seconds = 0;
},
/**
* Compares the duration instance with another one.
*
* @param {ICAL.Duration} aOther The instance to compare with
* @return {Number} -1, 0 or 1 for less/equal/greater
*/
compare: function compare(aOther) {
var thisSeconds = this.toSeconds();
var otherSeconds = aOther.toSeconds();
return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds);
},
/**
* Normalizes the duration instance. For example, a duration with a value
* of 61 seconds will be normalized to 1 minute and 1 second.
*/
normalize: function normalize() {
this.fromSeconds(this.toSeconds());
},
/**
* The string representation of this duration.
* @return {String}
*/
toString: function toString() {
if (this.toSeconds() == 0) {
return "PT0S";
} else {
var str = "";
if (this.isNegative) str += "-";
str += "P";
if (this.weeks) str += this.weeks + "W";
if (this.days) str += this.days + "D";
if (this.hours || this.minutes || this.seconds) {
str += "T";
if (this.hours) str += this.hours + "H";
if (this.minutes) str += this.minutes + "M";
if (this.seconds) str += this.seconds + "S";
}
return str;
}
},
/**
* The iCalendar string representation of this duration.
* @return {String}
*/
toICALString: function() {
return this.toString();
}
};
/**
* Returns a new ICAL.Duration instance from the passed seconds value.
*
* @param {Number} aSeconds The seconds to create the instance from
* @return {ICAL.Duration} The newly created duration instance
*/
ICAL.Duration.fromSeconds = function icalduration_from_seconds(aSeconds) {
return (new ICAL.Duration()).fromSeconds(aSeconds);
};
/**
* Internal helper function to handle a chunk of a duration.
*
* @param {String} letter type of duration chunk
* @param {String} number numeric value or -/+
* @param {Object} dict target to assign values to
*/
function parseDurationChunk(letter, number, object) {
var type;
switch (letter) {
case 'P':
if (number && number === '-') {
object.isNegative = true;
} else {
object.isNegative = false;
}
// period
break;
case 'D':
type = 'days';
break;
case 'W':
type = 'weeks';
break;
case 'H':
type = 'hours';
break;
case 'M':
type = 'minutes';
break;
case 'S':
type = 'seconds';
break;
default:
// Not a valid chunk
return 0;
}
if (type) {
if (!number && number !== 0) {
throw new Error(
'invalid duration value: Missing number before "' + letter + '"'
);
}
var num = parseInt(number, 10);
if (ICAL.helpers.isStrictlyNaN(num)) {
throw new Error(
'invalid duration value: Invalid number "' + number + '" before "' + letter + '"'
);
}
object[type] = num;
}
return 1;
}
/**
* Checks if the given string is an iCalendar duration value.
*
* @param {String} value The raw ical value
* @return {Boolean} True, if the given value is of the
* duration ical type
*/
ICAL.Duration.isValueString = function(string) {
return (string[0] === 'P' || string[1] === 'P');
};
/**
* Creates a new {@link ICAL.Duration} instance from the passed string.
*
* @param {String} aStr The string to parse
* @return {ICAL.Duration} The created duration instance
*/
ICAL.Duration.fromString = function icalduration_from_string(aStr) {
var pos = 0;
var dict = Object.create(null);
var chunks = 0;
while ((pos = aStr.search(DURATION_LETTERS)) !== -1) {
var type = aStr[pos];
var numeric = aStr.substr(0, pos);
aStr = aStr.substr(pos + 1);
chunks += parseDurationChunk(type, numeric, dict);
}
if (chunks < 2) {
// There must be at least a chunk with "P" and some unit chunk
throw new Error(
'invalid duration value: Not enough duration components in "' + aStr + '"'
);
}
return new ICAL.Duration(dict);
};
/**
* Creates a new ICAL.Duration instance from the given data object.
*
* @param {Object} aData An object with members of the duration
* @param {Number} aData.weeks Duration in weeks
* @param {Number} aData.days Duration in days
* @param {Number} aData.hours Duration in hours
* @param {Number} aData.minutes Duration in minutes
* @param {Number} aData.seconds Duration in seconds
* @param {Boolean} aData.isNegative If true, the duration is negative
* @return {ICAL.Duration} The createad duration instance
*/
ICAL.Duration.fromData = function icalduration_from_data(aData) {
return new ICAL.Duration(aData);
};
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2012 */
(function() {
var OPTIONS = ["tzid", "location", "tznames",
"latitude", "longitude"];
/**
* @classdesc
* Timezone representation, created by passing in a tzid and component.
*
* @example
* var vcalendar;
* var timezoneComp = vcalendar.getFirstSubcomponent('vtimezone');
* var tzid = timezoneComp.getFirstPropertyValue('tzid');
*
* var timezone = new ICAL.Timezone({
* component: timezoneComp,
* tzid
* });
*
* @class
* @param {ICAL.Component|Object} data options for class
* @param {String|ICAL.Component} data.component
* If data is a simple object, then this member can be set to either a
* string containing the component data, or an already parsed
* ICAL.Component
* @param {String} data.tzid The timezone identifier
* @param {String} data.location The timezone locationw
* @param {String} data.tznames An alternative string representation of the
* timezone
* @param {Number} data.latitude The latitude of the timezone
* @param {Number} data.longitude The longitude of the timezone
*/
ICAL.Timezone = function icaltimezone(data) {
this.wrappedJSObject = this;
this.fromData(data);
};
ICAL.Timezone.prototype = {
/**
* Timezone identifier
* @type {String}
*/
tzid: "",
/**
* Timezone location
* @type {String}
*/
location: "",
/**
* Alternative timezone name, for the string representation
* @type {String}
*/
tznames: "",
/**
* The primary latitude for the timezone.
* @type {Number}
*/
latitude: 0.0,
/**
* The primary longitude for the timezone.
* @type {Number}
*/
longitude: 0.0,
/**
* The vtimezone component for this timezone.
* @type {ICAL.Component}
*/
component: null,
/**
* The year this timezone has been expanded to. All timezone transition
* dates until this year are known and can be used for calculation
*
* @private
* @type {Number}
*/
expandedUntilYear: 0,
/**
* The class identifier.
* @constant
* @type {String}
* @default "icaltimezone"
*/
icalclass: "icaltimezone",
/**
* Sets up the current instance using members from the passed data object.
*
* @param {ICAL.Component|Object} aData options for class
* @param {String|ICAL.Component} aData.component
* If aData is a simple object, then this member can be set to either a
* string containing the component data, or an already parsed
* ICAL.Component
* @param {String} aData.tzid The timezone identifier
* @param {String} aData.location The timezone locationw
* @param {String} aData.tznames An alternative string representation of the
* timezone
* @param {Number} aData.latitude The latitude of the timezone
* @param {Number} aData.longitude The longitude of the timezone
*/
fromData: function fromData(aData) {
this.expandedUntilYear = 0;
this.changes = [];
if (aData instanceof ICAL.Component) {
// Either a component is passed directly
this.component = aData;
} else {
// Otherwise the component may be in the data object
if (aData && "component" in aData) {
if (typeof aData.component == "string") {
// If a string was passed, parse it as a component
var jCal = ICAL.parse(aData.component);
this.component = new ICAL.Component(jCal);
} else if (aData.component instanceof ICAL.Component) {
// If it was a component already, then just set it
this.component = aData.component;
} else {
// Otherwise just null out the component
this.component = null;
}
}
// Copy remaining passed properties
for (var key in OPTIONS) {
/* istanbul ignore else */
if (OPTIONS.hasOwnProperty(key)) {
var prop = OPTIONS[key];
if (aData && prop in aData) {
this[prop] = aData[prop];
}
}
}
}
// If we have a component but no TZID, attempt to get it from the
// component's properties.
if (this.component instanceof ICAL.Component && !this.tzid) {
this.tzid = this.component.getFirstPropertyValue('tzid');
}
return this;
},
/**
* Finds the utcOffset the given time would occur in this timezone.
*
* @param {ICAL.Time} tt The time to check for
* @return {Number} utc offset in seconds
*/
utcOffset: function utcOffset(tt) {
if (this == ICAL.Timezone.utcTimezone || this == ICAL.Timezone.localTimezone) {
return 0;
}
this._ensureCoverage(tt.year);
if (!this.changes.length) {
return 0;
}
var tt_change = {
year: tt.year,
month: tt.month,
day: tt.day,
hour: tt.hour,
minute: tt.minute,
second: tt.second
};
var change_num = this._findNearbyChange(tt_change);
var change_num_to_use = -1;
var step = 1;
// TODO: replace with bin search?
for (;;) {
var change = ICAL.helpers.clone(this.changes[change_num], true);
if (change.utcOffset < change.prevUtcOffset) {
ICAL.Timezone.adjust_change(change, 0, 0, 0, change.utcOffset);
} else {
ICAL.Timezone.adjust_change(change, 0, 0, 0,
change.prevUtcOffset);
}
var cmp = ICAL.Timezone._compare_change_fn(tt_change, change);
if (cmp >= 0) {
change_num_to_use = change_num;
} else {
step = -1;
}
if (step == -1 && change_num_to_use != -1) {
break;
}
change_num += step;
if (change_num < 0) {
return 0;
}
if (change_num >= this.changes.length) {
break;
}
}
var zone_change = this.changes[change_num_to_use];
var utcOffset_change = zone_change.utcOffset - zone_change.prevUtcOffset;
if (utcOffset_change < 0 && change_num_to_use > 0) {
var tmp_change = ICAL.helpers.clone(zone_change, true);
ICAL.Timezone.adjust_change(tmp_change, 0, 0, 0,
tmp_change.prevUtcOffset);
if (ICAL.Timezone._compare_change_fn(tt_change, tmp_change) < 0) {
var prev_zone_change = this.changes[change_num_to_use - 1];
var want_daylight = false; // TODO
if (zone_change.is_daylight != want_daylight &&
prev_zone_change.is_daylight == want_daylight) {
zone_change = prev_zone_change;
}
}
}
// TODO return is_daylight?
return zone_change.utcOffset;
},
_findNearbyChange: function icaltimezone_find_nearby_change(change) {
// find the closest match
var idx = ICAL.helpers.binsearchInsert(
this.changes,
change,
ICAL.Timezone._compare_change_fn
);
if (idx >= this.changes.length) {
return this.changes.length - 1;
}
return idx;
},
_ensureCoverage: function(aYear) {
if (ICAL.Timezone._minimumExpansionYear == -1) {
var today = ICAL.Time.now();
ICAL.Timezone._minimumExpansionYear = today.year;
}
var changesEndYear = aYear;
if (changesEndYear < ICAL.Timezone._minimumExpansionYear) {
changesEndYear = ICAL.Timezone._minimumExpansionYear;
}
changesEndYear += ICAL.Timezone.EXTRA_COVERAGE;
if (changesEndYear > ICAL.Timezone.MAX_YEAR) {
changesEndYear = ICAL.Timezone.MAX_YEAR;
}
if (!this.changes.length || this.expandedUntilYear < aYear) {
var subcomps = this.component.getAllSubcomponents();
var compLen = subcomps.length;
var compIdx = 0;
for (; compIdx < compLen; compIdx++) {
this._expandComponent(
subcomps[compIdx], changesEndYear, this.changes
);
}
this.changes.sort(ICAL.Timezone._compare_change_fn);
this.expandedUntilYear = changesEndYear;
}
},
_expandComponent: function(aComponent, aYear, changes) {
if (!aComponent.hasProperty("dtstart") ||
!aComponent.hasProperty("tzoffsetto") ||
!aComponent.hasProperty("tzoffsetfrom")) {
return null;
}
var dtstart = aComponent.getFirstProperty("dtstart").getFirstValue();
var change;
function convert_tzoffset(offset) {
return offset.factor * (offset.hours * 3600 + offset.minutes * 60);
}
function init_changes() {
var changebase = {};
changebase.is_daylight = (aComponent.name == "daylight");
changebase.utcOffset = convert_tzoffset(
aComponent.getFirstProperty("tzoffsetto").getFirstValue()
);
changebase.prevUtcOffset = convert_tzoffset(
aComponent.getFirstProperty("tzoffsetfrom").getFirstValue()
);
return changebase;
}
if (!aComponent.hasProperty("rrule") && !aComponent.hasProperty("rdate")) {
change = init_changes();
change.year = dtstart.year;
change.month = dtstart.month;
change.day = dtstart.day;
change.hour = dtstart.hour;
change.minute = dtstart.minute;
change.second = dtstart.second;
ICAL.Timezone.adjust_change(change, 0, 0, 0,
-change.prevUtcOffset);
changes.push(change);
} else {
var props = aComponent.getAllProperties("rdate");
for (var rdatekey in props) {
/* istanbul ignore if */
if (!props.hasOwnProperty(rdatekey)) {
continue;
}
var rdate = props[rdatekey];
var time = rdate.getFirstValue();
change = init_changes();
change.year = time.year;
change.month = time.month;
change.day = time.day;
if (time.isDate) {
change.hour = dtstart.hour;
change.minute = dtstart.minute;
change.second = dtstart.second;
if (dtstart.zone != ICAL.Timezone.utcTimezone) {
ICAL.Timezone.adjust_change(change, 0, 0, 0,
-change.prevUtcOffset);
}
} else {
change.hour = time.hour;
change.minute = time.minute;
change.second = time.second;
if (time.zone != ICAL.Timezone.utcTimezone) {
ICAL.Timezone.adjust_change(change, 0, 0, 0,
-change.prevUtcOffset);
}
}
changes.push(change);
}
var rrule = aComponent.getFirstProperty("rrule");
if (rrule) {
rrule = rrule.getFirstValue();
change = init_changes();
if (rrule.until && rrule.until.zone == ICAL.Timezone.utcTimezone) {
rrule.until.adjust(0, 0, 0, change.prevUtcOffset);
rrule.until.zone = ICAL.Timezone.localTimezone;
}
var iterator = rrule.iterator(dtstart);
var occ;
while ((occ = iterator.next())) {
change = init_changes();
if (occ.year > aYear || !occ) {
break;
}
change.year = occ.year;
change.month = occ.month;
change.day = occ.day;
change.hour = occ.hour;
change.minute = occ.minute;
change.second = occ.second;
change.isDate = occ.isDate;
ICAL.Timezone.adjust_change(change, 0, 0, 0,
-change.prevUtcOffset);
changes.push(change);
}
}
}
return changes;
},
/**
* The string representation of this timezone.
* @return {String}
*/
toString: function toString() {
return (this.tznames ? this.tznames : this.tzid);
}
};
ICAL.Timezone._compare_change_fn = function icaltimezone_compare_change_fn(a, b) {
if (a.year < b.year) return -1;
else if (a.year > b.year) return 1;
if (a.month < b.month) return -1;
else if (a.month > b.month) return 1;
if (a.day < b.day) return -1;
else if (a.day > b.day) return 1;
if (a.hour < b.hour) return -1;
else if (a.hour > b.hour) return 1;
if (a.minute < b.minute) return -1;
else if (a.minute > b.minute) return 1;
if (a.second < b.second) return -1;
else if (a.second > b.second) return 1;
return 0;
};
/**
* Convert the date/time from one zone to the next.
*
* @param {ICAL.Time} tt The time to convert
* @param {ICAL.Timezone} from_zone The source zone to convert from
* @param {ICAL.Timezone} to_zone The target zone to convert to
* @return {ICAL.Time} The converted date/time object
*/
ICAL.Timezone.convert_time = function icaltimezone_convert_time(tt, from_zone, to_zone) {
if (tt.isDate ||
from_zone.tzid == to_zone.tzid ||
from_zone == ICAL.Timezone.localTimezone ||
to_zone == ICAL.Timezone.localTimezone) {
tt.zone = to_zone;
return tt;
}
var utcOffset = from_zone.utcOffset(tt);
tt.adjust(0, 0, 0, - utcOffset);
utcOffset = to_zone.utcOffset(tt);
tt.adjust(0, 0, 0, utcOffset);
return null;
};
/**
* Creates a new ICAL.Timezone instance from the passed data object.
*
* @param {ICAL.Component|Object} aData options for class
* @param {String|ICAL.Component} aData.component
* If aData is a simple object, then this member can be set to either a
* string containing the component data, or an already parsed
* ICAL.Component
* @param {String} aData.tzid The timezone identifier
* @param {String} aData.location The timezone locationw
* @param {String} aData.tznames An alternative string representation of the
* timezone
* @param {Number} aData.latitude The latitude of the timezone
* @param {Number} aData.longitude The longitude of the timezone
*/
ICAL.Timezone.fromData = function icaltimezone_fromData(aData) {
var tt = new ICAL.Timezone();
return tt.fromData(aData);
};
/**
* The instance describing the UTC timezone
* @type {ICAL.Timezone}
* @constant
* @instance
*/
ICAL.Timezone.utcTimezone = ICAL.Timezone.fromData({
tzid: "UTC"
});
/**
* The instance describing the local timezone
* @type {ICAL.Timezone}
* @constant
* @instance
*/
ICAL.Timezone.localTimezone = ICAL.Timezone.fromData({
tzid: "floating"
});
/**
* Adjust a timezone change object.
* @private
* @param {Object} change The timezone change object
* @param {Number} days The extra amount of days
* @param {Number} hours The extra amount of hours
* @param {Number} minutes The extra amount of minutes
* @param {Number} seconds The extra amount of seconds
*/
ICAL.Timezone.adjust_change = function icaltimezone_adjust_change(change, days, hours, minutes, seconds) {
return ICAL.Time.prototype.adjust.call(
change,
days,
hours,
minutes,
seconds,
change
);
};
ICAL.Timezone._minimumExpansionYear = -1;
ICAL.Timezone.MAX_YEAR = 2035; // TODO this is because of time_t, which we don't need. Still usefull?
ICAL.Timezone.EXTRA_COVERAGE = 5;
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.TimezoneService = (function() {
var zones;
/**
* @classdesc
* Singleton class to contain timezones. Right now it is all manual registry in
* the future we may use this class to download timezone information or handle
* loading pre-expanded timezones.
*
* @namespace
* @alias ICAL.TimezoneService
*/
var TimezoneService = {
get count() {
return Object.keys(zones).length;
},
reset: function() {
zones = Object.create(null);
var utc = ICAL.Timezone.utcTimezone;
zones.Z = utc;
zones.UTC = utc;
zones.GMT = utc;
},
/**
* Checks if timezone id has been registered.
*
* @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
* @return {Boolean} False, when not present
*/
has: function(tzid) {
return !!zones[tzid];
},
/**
* Returns a timezone by its tzid if present.
*
* @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
* @return {?ICAL.Timezone} The timezone, or null if not found
*/
get: function(tzid) {
return zones[tzid];
},
/**
* Registers a timezone object or component.
*
* @param {String=} name
* The name of the timezone. Defaults to the component's TZID if not
* passed.
* @param {ICAL.Component|ICAL.Timezone} zone
* The initialized zone or vtimezone.
*/
register: function(name, timezone) {
if (name instanceof ICAL.Component) {
if (name.name === 'vtimezone') {
timezone = new ICAL.Timezone(name);
name = timezone.tzid;
}
}
if (timezone instanceof ICAL.Timezone) {
zones[name] = timezone;
} else {
throw new TypeError('timezone must be ICAL.Timezone or ICAL.Component');
}
},
/**
* Removes a timezone by its tzid from the list.
*
* @param {String} tzid Timezone identifier (e.g. America/Los_Angeles)
* @return {?ICAL.Timezone} The removed timezone, or null if not registered
*/
remove: function(tzid) {
return (delete zones[tzid]);
}
};
// initialize defaults
TimezoneService.reset();
return TimezoneService;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
(function() {
/**
* @classdesc
* iCalendar Time representation (similar to JS Date object). Fully
* independent of system (OS) timezone / time. Unlike JS Date, the month
* January is 1, not zero.
*
* @example
* var time = new ICAL.Time({
* year: 2012,
* month: 10,
* day: 11
* minute: 0,
* second: 0,
* isDate: false
* });
*
*
* @alias ICAL.Time
* @class
* @param {Object} data Time initialization
* @param {Number=} data.year The year for this date
* @param {Number=} data.month The month for this date
* @param {Number=} data.day The day for this date
* @param {Number=} data.hour The hour for this date
* @param {Number=} data.minute The minute for this date
* @param {Number=} data.second The second for this date
* @param {Boolean=} data.isDate If true, the instance represents a date (as
* opposed to a date-time)
* @param {ICAL.Timezone} zone timezone this position occurs in
*/
ICAL.Time = function icaltime(data, zone) {
this.wrappedJSObject = this;
var time = this._time = Object.create(null);
/* time defaults */
time.year = 0;
time.month = 1;
time.day = 1;
time.hour = 0;
time.minute = 0;
time.second = 0;
time.isDate = false;
this.fromData(data, zone);
};
ICAL.Time._dowCache = {};
ICAL.Time._wnCache = {};
ICAL.Time.prototype = {
/**
* The class identifier.
* @constant
* @type {String}
* @default "icaltime"
*/
icalclass: "icaltime",
_cachedUnixTime: null,
/**
* The type name, to be used in the jCal object. This value may change and
* is strictly defined by the {@link ICAL.Time#isDate isDate} member.
* @readonly
* @type {String}
* @default "date-time"
*/
get icaltype() {
return this.isDate ? 'date' : 'date-time';
},
/**
* The timezone for this time.
* @type {ICAL.Timezone}
*/
zone: null,
/**
* Internal uses to indicate that a change has been made and the next read
* operation must attempt to normalize the value (for example changing the
* day to 33).
*
* @type {Boolean}
* @private
*/
_pendingNormalization: false,
/**
* Returns a clone of the time object.
*
* @return {ICAL.Time} The cloned object
*/
clone: function() {
return new ICAL.Time(this._time, this.zone);
},
/**
* Reset the time instance to epoch time
*/
reset: function icaltime_reset() {
this.fromData(ICAL.Time.epochTime);
this.zone = ICAL.Timezone.utcTimezone;
},
/**
* Reset the time instance to the given date/time values.
*
* @param {Number} year The year to set
* @param {Number} month The month to set
* @param {Number} day The day to set
* @param {Number} hour The hour to set
* @param {Number} minute The minute to set
* @param {Number} second The second to set
* @param {ICAL.Timezone} timezone The timezone to set
*/
resetTo: function icaltime_resetTo(year, month, day,
hour, minute, second, timezone) {
this.fromData({
year: year,
month: month,
day: day,
hour: hour,
minute: minute,
second: second,
zone: timezone
});
},
/**
* Set up the current instance from the Javascript date value.
*
* @param {?Date} aDate The Javascript Date to read, or null to reset
* @param {Boolean} useUTC If true, the UTC values of the date will be used
*/
fromJSDate: function icaltime_fromJSDate(aDate, useUTC) {
if (!aDate) {
this.reset();
} else {
if (useUTC) {
this.zone = ICAL.Timezone.utcTimezone;
this.year = aDate.getUTCFullYear();
this.month = aDate.getUTCMonth() + 1;
this.day = aDate.getUTCDate();
this.hour = aDate.getUTCHours();
this.minute = aDate.getUTCMinutes();
this.second = aDate.getUTCSeconds();
} else {
this.zone = ICAL.Timezone.localTimezone;
this.year = aDate.getFullYear();
this.month = aDate.getMonth() + 1;
this.day = aDate.getDate();
this.hour = aDate.getHours();
this.minute = aDate.getMinutes();
this.second = aDate.getSeconds();
}
}
this._cachedUnixTime = null;
return this;
},
/**
* Sets up the current instance using members from the passed data object.
*
* @param {Object} aData Time initialization
* @param {Number=} aData.year The year for this date
* @param {Number=} aData.month The month for this date
* @param {Number=} aData.day The day for this date
* @param {Number=} aData.hour The hour for this date
* @param {Number=} aData.minute The minute for this date
* @param {Number=} aData.second The second for this date
* @param {Boolean=} aData.isDate If true, the instance represents a date
* (as opposed to a date-time)
* @param {ICAL.Timezone=} aZone Timezone this position occurs in
*/
fromData: function fromData(aData, aZone) {
if (aData) {
for (var key in aData) {
/* istanbul ignore else */
if (Object.prototype.hasOwnProperty.call(aData, key)) {
// ical type cannot be set
if (key === 'icaltype') continue;
this[key] = aData[key];
}
}
}
if (aZone) {
this.zone = aZone;
}
if (aData && !("isDate" in aData)) {
this.isDate = !("hour" in aData);
} else if (aData && ("isDate" in aData)) {
this.isDate = aData.isDate;
}
if (aData && "timezone" in aData) {
var zone = ICAL.TimezoneService.get(
aData.timezone
);
this.zone = zone || ICAL.Timezone.localTimezone;
}
if (aData && "zone" in aData) {
this.zone = aData.zone;
}
if (!this.zone) {
this.zone = ICAL.Timezone.localTimezone;
}
this._cachedUnixTime = null;
return this;
},
/**
* Calculate the day of week.
* @param {ICAL.Time.weekDay=} aWeekStart
* The week start weekday, defaults to SUNDAY
* @return {ICAL.Time.weekDay}
*/
dayOfWeek: function icaltime_dayOfWeek(aWeekStart) {
var firstDow = aWeekStart || ICAL.Time.SUNDAY;
var dowCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + firstDow;
if (dowCacheKey in ICAL.Time._dowCache) {
return ICAL.Time._dowCache[dowCacheKey];
}
// Using Zeller's algorithm
var q = this.day;
var m = this.month + (this.month < 3 ? 12 : 0);
var Y = this.year - (this.month < 3 ? 1 : 0);
var h = (q + Y + ICAL.helpers.trunc(((m + 1) * 26) / 10) + ICAL.helpers.trunc(Y / 4));
/* istanbul ignore else */
if (true /* gregorian */) {
h += ICAL.helpers.trunc(Y / 100) * 6 + ICAL.helpers.trunc(Y / 400);
} else {
h += 5;
}
// Normalize to 1 = wkst
h = ((h + 7 - firstDow) % 7) + 1;
ICAL.Time._dowCache[dowCacheKey] = h;
return h;
},
/**
* Calculate the day of year.
* @return {Number}
*/
dayOfYear: function dayOfYear() {
var is_leap = (ICAL.Time.isLeapYear(this.year) ? 1 : 0);
var diypm = ICAL.Time.daysInYearPassedMonth;
return diypm[is_leap][this.month - 1] + this.day;
},
/**
* Returns a copy of the current date/time, rewound to the start of the
* week. The resulting ICAL.Time instance is of icaltype date, even if this
* is a date-time.
*
* @param {ICAL.Time.weekDay=} aWeekStart
* The week start weekday, defaults to SUNDAY
* @return {ICAL.Time} The start of the week (cloned)
*/
startOfWeek: function startOfWeek(aWeekStart) {
var firstDow = aWeekStart || ICAL.Time.SUNDAY;
var result = this.clone();
result.day -= ((this.dayOfWeek() + 7 - firstDow) % 7);
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* Returns a copy of the current date/time, shifted to the end of the week.
* The resulting ICAL.Time instance is of icaltype date, even if this is a
* date-time.
*
* @param {ICAL.Time.weekDay=} aWeekStart
* The week start weekday, defaults to SUNDAY
* @return {ICAL.Time} The end of the week (cloned)
*/
endOfWeek: function endOfWeek(aWeekStart) {
var firstDow = aWeekStart || ICAL.Time.SUNDAY;
var result = this.clone();
result.day += (7 - this.dayOfWeek() + firstDow - ICAL.Time.SUNDAY) % 7;
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* Returns a copy of the current date/time, rewound to the start of the
* month. The resulting ICAL.Time instance is of icaltype date, even if
* this is a date-time.
*
* @return {ICAL.Time} The start of the month (cloned)
*/
startOfMonth: function startOfMonth() {
var result = this.clone();
result.day = 1;
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* Returns a copy of the current date/time, shifted to the end of the
* month. The resulting ICAL.Time instance is of icaltype date, even if
* this is a date-time.
*
* @return {ICAL.Time} The end of the month (cloned)
*/
endOfMonth: function endOfMonth() {
var result = this.clone();
result.day = ICAL.Time.daysInMonth(result.month, result.year);
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* Returns a copy of the current date/time, rewound to the start of the
* year. The resulting ICAL.Time instance is of icaltype date, even if
* this is a date-time.
*
* @return {ICAL.Time} The start of the year (cloned)
*/
startOfYear: function startOfYear() {
var result = this.clone();
result.day = 1;
result.month = 1;
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* Returns a copy of the current date/time, shifted to the end of the
* year. The resulting ICAL.Time instance is of icaltype date, even if
* this is a date-time.
*
* @return {ICAL.Time} The end of the year (cloned)
*/
endOfYear: function endOfYear() {
var result = this.clone();
result.day = 31;
result.month = 12;
result.isDate = true;
result.hour = 0;
result.minute = 0;
result.second = 0;
return result;
},
/**
* First calculates the start of the week, then returns the day of year for
* this date. If the day falls into the previous year, the day is zero or negative.
*
* @param {ICAL.Time.weekDay=} aFirstDayOfWeek
* The week start weekday, defaults to SUNDAY
* @return {Number} The calculated day of year
*/
startDoyWeek: function startDoyWeek(aFirstDayOfWeek) {
var firstDow = aFirstDayOfWeek || ICAL.Time.SUNDAY;
var delta = this.dayOfWeek() - firstDow;
if (delta < 0) delta += 7;
return this.dayOfYear() - delta;
},
/**
* Get the dominical letter for the current year. Letters range from A - G
* for common years, and AG to GF for leap years.
*
* @param {Number} yr The year to retrieve the letter for
* @return {String} The dominical letter.
*/
getDominicalLetter: function() {
return ICAL.Time.getDominicalLetter(this.year);
},
/**
* Finds the nthWeekDay relative to the current month (not day). The
* returned value is a day relative the month that this month belongs to so
* 1 would indicate the first of the month and 40 would indicate a day in
* the following month.
*
* @param {Number} aDayOfWeek Day of the week see the day name constants
* @param {Number} aPos Nth occurrence of a given week day values
* of 1 and 0 both indicate the first weekday of that type. aPos may
* be either positive or negative
*
* @return {Number} numeric value indicating a day relative
* to the current month of this time object
*/
nthWeekDay: function icaltime_nthWeekDay(aDayOfWeek, aPos) {
var daysInMonth = ICAL.Time.daysInMonth(this.month, this.year);
var weekday;
var pos = aPos;
var start = 0;
var otherDay = this.clone();
if (pos >= 0) {
otherDay.day = 1;
// because 0 means no position has been given
// 1 and 0 indicate the same day.
if (pos != 0) {
// remove the extra numeric value
pos--;
}
// set current start offset to current day.
start = otherDay.day;
// find the current day of week
var startDow = otherDay.dayOfWeek();
// calculate the difference between current
// day of the week and desired day of the week
var offset = aDayOfWeek - startDow;
// if the offset goes into the past
// week we add 7 so it goes into the next
// week. We only want to go forward in time here.
if (offset < 0)
// this is really important otherwise we would
// end up with dates from in the past.
offset += 7;
// add offset to start so start is the same
// day of the week as the desired day of week.
start += offset;
// because we are going to add (and multiply)
// the numeric value of the day we subtract it
// from the start position so not to add it twice.
start -= aDayOfWeek;
// set week day
weekday = aDayOfWeek;
} else {
// then we set it to the last day in the current month
otherDay.day = daysInMonth;
// find the ends weekday
var endDow = otherDay.dayOfWeek();
pos++;
weekday = (endDow - aDayOfWeek);
if (weekday < 0) {
weekday += 7;
}
weekday = daysInMonth - weekday;
}
weekday += pos * 7;
return start + weekday;
},
/**
* Checks if current time is the nth weekday, relative to the current
* month. Will always return false when rule resolves outside of current
* month.
*
* @param {ICAL.Time.weekDay} aDayOfWeek Day of week to check
* @param {Number} aPos Relative position
* @return {Boolean} True, if it is the nth weekday
*/
isNthWeekDay: function(aDayOfWeek, aPos) {
var dow = this.dayOfWeek();
if (aPos === 0 && dow === aDayOfWeek) {
return true;
}
// get pos
var day = this.nthWeekDay(aDayOfWeek, aPos);
if (day === this.day) {
return true;
}
return false;
},
/**
* Calculates the ISO 8601 week number. The first week of a year is the
* week that contains the first Thursday. The year can have 53 weeks, if
* January 1st is a Friday.
*
* Note there are regions where the first week of the year is the one that
* starts on January 1st, which may offset the week number. Also, if a
* different week start is specified, this will also affect the week
* number.
*
* @see ICAL.Time.weekOneStarts
* @param {ICAL.Time.weekDay} aWeekStart The weekday the week starts with
* @return {Number} The ISO week number
*/
weekNumber: function weekNumber(aWeekStart) {
var wnCacheKey = (this.year << 12) + (this.month << 8) + (this.day << 3) + aWeekStart;
if (wnCacheKey in ICAL.Time._wnCache) {
return ICAL.Time._wnCache[wnCacheKey];
}
// This function courtesty of Julian Bucknall, published under the MIT license
// http://www.boyet.com/articles/publishedarticles/calculatingtheisoweeknumb.html
// plus some fixes to be able to use different week starts.
var week1;
var dt = this.clone();
dt.isDate = true;
var isoyear = this.year;
if (dt.month == 12 && dt.day > 25) {
week1 = ICAL.Time.weekOneStarts(isoyear + 1, aWeekStart);
if (dt.compare(week1) < 0) {
week1 = ICAL.Time.weekOneStarts(isoyear, aWeekStart);
} else {
isoyear++;
}
} else {
week1 = ICAL.Time.weekOneStarts(isoyear, aWeekStart);
if (dt.compare(week1) < 0) {
week1 = ICAL.Time.weekOneStarts(--isoyear, aWeekStart);
}
}
var daysBetween = (dt.subtractDate(week1).toSeconds() / 86400);
var answer = ICAL.helpers.trunc(daysBetween / 7) + 1;
ICAL.Time._wnCache[wnCacheKey] = answer;
return answer;
},
/**
* Adds the duration to the current time. The instance is modified in
* place.
*
* @param {ICAL.Duration} aDuration The duration to add
*/
addDuration: function icaltime_add(aDuration) {
var mult = (aDuration.isNegative ? -1 : 1);
// because of the duration optimizations it is much
// more efficient to grab all the values up front
// then set them directly (which will avoid a normalization call).
// So we don't actually normalize until we need it.
var second = this.second;
var minute = this.minute;
var hour = this.hour;
var day = this.day;
second += mult * aDuration.seconds;
minute += mult * aDuration.minutes;
hour += mult * aDuration.hours;
day += mult * aDuration.days;
day += mult * 7 * aDuration.weeks;
this.second = second;
this.minute = minute;
this.hour = hour;
this.day = day;
this._cachedUnixTime = null;
},
/**
* Subtract the date details (_excluding_ timezone). Useful for finding
* the relative difference between two time objects excluding their
* timezone differences.
*
* @param {ICAL.Time} aDate The date to substract
* @return {ICAL.Duration} The difference as a duration
*/
subtractDate: function icaltime_subtract(aDate) {
var unixTime = this.toUnixTime() + this.utcOffset();
var other = aDate.toUnixTime() + aDate.utcOffset();
return ICAL.Duration.fromSeconds(unixTime - other);
},
/**
* Subtract the date details, taking timezones into account.
*
* @param {ICAL.Time} aDate The date to subtract
* @return {ICAL.Duration} The difference in duration
*/
subtractDateTz: function icaltime_subtract_abs(aDate) {
var unixTime = this.toUnixTime();
var other = aDate.toUnixTime();
return ICAL.Duration.fromSeconds(unixTime - other);
},
/**
* Compares the ICAL.Time instance with another one.
*
* @param {ICAL.Duration} aOther The instance to compare with
* @return {Number} -1, 0 or 1 for less/equal/greater
*/
compare: function icaltime_compare(other) {
var a = this.toUnixTime();
var b = other.toUnixTime();
if (a > b) return 1;
if (b > a) return -1;
return 0;
},
/**
* Compares only the date part of this instance with another one.
*
* @param {ICAL.Duration} other The instance to compare with
* @param {ICAL.Timezone} tz The timezone to compare in
* @return {Number} -1, 0 or 1 for less/equal/greater
*/
compareDateOnlyTz: function icaltime_compareDateOnlyTz(other, tz) {
function cmp(attr) {
return ICAL.Time._cmp_attr(a, b, attr);
}
var a = this.convertToZone(tz);
var b = other.convertToZone(tz);
var rc = 0;
if ((rc = cmp("year")) != 0) return rc;
if ((rc = cmp("month")) != 0) return rc;
if ((rc = cmp("day")) != 0) return rc;
return rc;
},
/**
* Convert the instance into another timezone. The returned ICAL.Time
* instance is always a copy.
*
* @param {ICAL.Timezone} zone The zone to convert to
* @return {ICAL.Time} The copy, converted to the zone
*/
convertToZone: function convertToZone(zone) {
var copy = this.clone();
var zone_equals = (this.zone.tzid == zone.tzid);
if (!this.isDate && !zone_equals) {
ICAL.Timezone.convert_time(copy, this.zone, zone);
}
copy.zone = zone;
return copy;
},
/**
* Calculates the UTC offset of the current date/time in the timezone it is
* in.
*
* @return {Number} UTC offset in seconds
*/
utcOffset: function utc_offset() {
if (this.zone == ICAL.Timezone.localTimezone ||
this.zone == ICAL.Timezone.utcTimezone) {
return 0;
} else {
return this.zone.utcOffset(this);
}
},
/**
* Returns an RFC 5545 compliant ical representation of this object.
*
* @return {String} ical date/date-time
*/
toICALString: function() {
var string = this.toString();
if (string.length > 10) {
return ICAL.design.icalendar.value['date-time'].toICAL(string);
} else {
return ICAL.design.icalendar.value.date.toICAL(string);
}
},
/**
* The string representation of this date/time, in jCal form
* (including : and - separators).
* @return {String}
*/
toString: function toString() {
var result = this.year + '-' +
ICAL.helpers.pad2(this.month) + '-' +
ICAL.helpers.pad2(this.day);
if (!this.isDate) {
result += 'T' + ICAL.helpers.pad2(this.hour) + ':' +
ICAL.helpers.pad2(this.minute) + ':' +
ICAL.helpers.pad2(this.second);
if (this.zone === ICAL.Timezone.utcTimezone) {
result += 'Z';
}
}
return result;
},
/**
* Converts the current instance to a Javascript date
* @return {Date}
*/
toJSDate: function toJSDate() {
if (this.zone == ICAL.Timezone.localTimezone) {
if (this.isDate) {
return new Date(this.year, this.month - 1, this.day);
} else {
return new Date(this.year, this.month - 1, this.day,
this.hour, this.minute, this.second, 0);
}
} else {
return new Date(this.toUnixTime() * 1000);
}
},
_normalize: function icaltime_normalize() {
var isDate = this._time.isDate;
if (this._time.isDate) {
this._time.hour = 0;
this._time.minute = 0;
this._time.second = 0;
}
this.adjust(0, 0, 0, 0);
return this;
},
/**
* Adjust the date/time by the given offset
*
* @param {Number} aExtraDays The extra amount of days
* @param {Number} aExtraHours The extra amount of hours
* @param {Number} aExtraMinutes The extra amount of minutes
* @param {Number} aExtraSeconds The extra amount of seconds
* @param {Number=} aTime The time to adjust, defaults to the
* current instance.
*/
adjust: function icaltime_adjust(aExtraDays, aExtraHours,
aExtraMinutes, aExtraSeconds, aTime) {
var minutesOverflow, hoursOverflow,
daysOverflow = 0, yearsOverflow = 0;
var second, minute, hour, day;
var daysInMonth;
var time = aTime || this._time;
if (!time.isDate) {
second = time.second + aExtraSeconds;
time.second = second % 60;
minutesOverflow = ICAL.helpers.trunc(second / 60);
if (time.second < 0) {
time.second += 60;
minutesOverflow--;
}
minute = time.minute + aExtraMinutes + minutesOverflow;
time.minute = minute % 60;
hoursOverflow = ICAL.helpers.trunc(minute / 60);
if (time.minute < 0) {
time.minute += 60;
hoursOverflow--;
}
hour = time.hour + aExtraHours + hoursOverflow;
time.hour = hour % 24;
daysOverflow = ICAL.helpers.trunc(hour / 24);
if (time.hour < 0) {
time.hour += 24;
daysOverflow--;
}
}
// Adjust month and year first, because we need to know what month the day
// is in before adjusting it.
if (time.month > 12) {
yearsOverflow = ICAL.helpers.trunc((time.month - 1) / 12);
} else if (time.month < 1) {
yearsOverflow = ICAL.helpers.trunc(time.month / 12) - 1;
}
time.year += yearsOverflow;
time.month -= 12 * yearsOverflow;
// Now take care of the days (and adjust month if needed)
day = time.day + aExtraDays + daysOverflow;
if (day > 0) {
for (;;) {
daysInMonth = ICAL.Time.daysInMonth(time.month, time.year);
if (day <= daysInMonth) {
break;
}
time.month++;
if (time.month > 12) {
time.year++;
time.month = 1;
}
day -= daysInMonth;
}
} else {
while (day <= 0) {
if (time.month == 1) {
time.year--;
time.month = 12;
} else {
time.month--;
}
day += ICAL.Time.daysInMonth(time.month, time.year);
}
}
time.day = day;
this._cachedUnixTime = null;
return this;
},
/**
* Sets up the current instance from unix time, the number of seconds since
* January 1st, 1970.
*
* @param {Number} seconds The seconds to set up with
*/
fromUnixTime: function fromUnixTime(seconds) {
this.zone = ICAL.Timezone.utcTimezone;
var epoch = ICAL.Time.epochTime.clone();
epoch.adjust(0, 0, 0, seconds);
this.year = epoch.year;
this.month = epoch.month;
this.day = epoch.day;
this.hour = epoch.hour;
this.minute = epoch.minute;
this.second = Math.floor(epoch.second);
this._cachedUnixTime = null;
},
/**
* Converts the current instance to seconds since January 1st 1970.
*
* @return {Number} Seconds since 1970
*/
toUnixTime: function toUnixTime() {
if (this._cachedUnixTime !== null) {
return this._cachedUnixTime;
}
var offset = this.utcOffset();
// we use the offset trick to ensure
// that we are getting the actual UTC time
var ms = Date.UTC(
this.year,
this.month - 1,
this.day,
this.hour,
this.minute,
this.second - offset
);
// seconds
this._cachedUnixTime = ms / 1000;
return this._cachedUnixTime;
},
/**
* Converts time to into Object which can be serialized then re-created
* using the constructor.
*
* @example
* // toJSON will automatically be called
* var json = JSON.stringify(mytime);
*
* var deserialized = JSON.parse(json);
*
* var time = new ICAL.Time(deserialized);
*
* @return {Object}
*/
toJSON: function() {
var copy = [
'year',
'month',
'day',
'hour',
'minute',
'second',
'isDate'
];
var result = Object.create(null);
var i = 0;
var len = copy.length;
var prop;
for (; i < len; i++) {
prop = copy[i];
result[prop] = this[prop];
}
if (this.zone) {
result.timezone = this.zone.tzid;
}
return result;
}
};
(function setupNormalizeAttributes() {
// This needs to run before any instances are created!
function defineAttr(attr) {
Object.defineProperty(ICAL.Time.prototype, attr, {
get: function getTimeAttr() {
if (this._pendingNormalization) {
this._normalize();
this._pendingNormalization = false;
}
return this._time[attr];
},
set: function setTimeAttr(val) {
// Check if isDate will be set and if was not set to normalize date.
// This avoids losing days when seconds, minutes and hours are zeroed
// what normalize will do when time is a date.
if (attr === "isDate" && val && !this._time.isDate) {
this.adjust(0, 0, 0, 0);
}
this._cachedUnixTime = null;
this._pendingNormalization = true;
this._time[attr] = val;
return val;
}
});
}
/* istanbul ignore else */
if ("defineProperty" in Object) {
defineAttr("year");
defineAttr("month");
defineAttr("day");
defineAttr("hour");
defineAttr("minute");
defineAttr("second");
defineAttr("isDate");
}
})();
/**
* Returns the days in the given month
*
* @param {Number} month The month to check
* @param {Number} year The year to check
* @return {Number} The number of days in the month
*/
ICAL.Time.daysInMonth = function icaltime_daysInMonth(month, year) {
var _daysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var days = 30;
if (month < 1 || month > 12) return days;
days = _daysInMonth[month];
if (month == 2) {
days += ICAL.Time.isLeapYear(year);
}
return days;
};
/**
* Checks if the year is a leap year
*
* @param {Number} year The year to check
* @return {Boolean} True, if the year is a leap year
*/
ICAL.Time.isLeapYear = function isLeapYear(year) {
if (year <= 1752) {
return ((year % 4) == 0);
} else {
return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
}
};
/**
* Create a new ICAL.Time from the day of year and year. The date is returned
* in floating timezone.
*
* @param {Number} aDayOfYear The day of year
* @param {Number} aYear The year to create the instance in
* @return {ICAL.Time} The created instance with the calculated date
*/
ICAL.Time.fromDayOfYear = function icaltime_fromDayOfYear(aDayOfYear, aYear) {
var year = aYear;
var doy = aDayOfYear;
var tt = new ICAL.Time();
tt.auto_normalize = false;
var is_leap = (ICAL.Time.isLeapYear(year) ? 1 : 0);
if (doy < 1) {
year--;
is_leap = (ICAL.Time.isLeapYear(year) ? 1 : 0);
doy += ICAL.Time.daysInYearPassedMonth[is_leap][12];
return ICAL.Time.fromDayOfYear(doy, year);
} else if (doy > ICAL.Time.daysInYearPassedMonth[is_leap][12]) {
is_leap = (ICAL.Time.isLeapYear(year) ? 1 : 0);
doy -= ICAL.Time.daysInYearPassedMonth[is_leap][12];
year++;
return ICAL.Time.fromDayOfYear(doy, year);
}
tt.year = year;
tt.isDate = true;
for (var month = 11; month >= 0; month--) {
if (doy > ICAL.Time.daysInYearPassedMonth[is_leap][month]) {
tt.month = month + 1;
tt.day = doy - ICAL.Time.daysInYearPassedMonth[is_leap][month];
break;
}
}
tt.auto_normalize = true;
return tt;
};
/**
* Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
*
* @deprecated Use {@link ICAL.Time.fromDateString} instead
* @param {String} str The string to create from
* @return {ICAL.Time} The date/time instance
*/
ICAL.Time.fromStringv2 = function fromString(str) {
return new ICAL.Time({
year: parseInt(str.substr(0, 4), 10),
month: parseInt(str.substr(5, 2), 10),
day: parseInt(str.substr(8, 2), 10),
isDate: true
});
};
/**
* Returns a new ICAL.Time instance from a date string, e.g 2015-01-02.
*
* @param {String} aValue The string to create from
* @return {ICAL.Time} The date/time instance
*/
ICAL.Time.fromDateString = function(aValue) {
// Dates should have no timezone.
// Google likes to sometimes specify Z on dates
// we specifically ignore that to avoid issues.
// YYYY-MM-DD
// 2012-10-10
return new ICAL.Time({
year: ICAL.helpers.strictParseInt(aValue.substr(0, 4)),
month: ICAL.helpers.strictParseInt(aValue.substr(5, 2)),
day: ICAL.helpers.strictParseInt(aValue.substr(8, 2)),
isDate: true
});
};
/**
* Returns a new ICAL.Time instance from a date-time string, e.g
* 2015-01-02T03:04:05. If a property is specified, the timezone is set up
* from the property's TZID parameter.
*
* @param {String} aValue The string to create from
* @param {ICAL.Property=} prop The property the date belongs to
* @return {ICAL.Time} The date/time instance
*/
ICAL.Time.fromDateTimeString = function(aValue, prop) {
if (aValue.length < 19) {
throw new Error(
'invalid date-time value: "' + aValue + '"'
);
}
var zone;
if (aValue[19] && aValue[19] === 'Z') {
zone = 'Z';
} else if (prop) {
zone = prop.getParameter('tzid');
}
// 2012-10-10T10:10:10(Z)?
var time = new ICAL.Time({
year: ICAL.helpers.strictParseInt(aValue.substr(0, 4)),
month: ICAL.helpers.strictParseInt(aValue.substr(5, 2)),
day: ICAL.helpers.strictParseInt(aValue.substr(8, 2)),
hour: ICAL.helpers.strictParseInt(aValue.substr(11, 2)),
minute: ICAL.helpers.strictParseInt(aValue.substr(14, 2)),
second: ICAL.helpers.strictParseInt(aValue.substr(17, 2)),
timezone: zone
});
return time;
};
/**
* Returns a new ICAL.Time instance from a date or date-time string,
*
* @param {String} aValue The string to create from
* @param {ICAL.Property=} prop The property the date belongs to
* @return {ICAL.Time} The date/time instance
*/
ICAL.Time.fromString = function fromString(aValue, aProperty) {
if (aValue.length > 10) {
return ICAL.Time.fromDateTimeString(aValue, aProperty);
} else {
return ICAL.Time.fromDateString(aValue);
}
};
/**
* Creates a new ICAL.Time instance from the given Javascript Date.
*
* @param {?Date} aDate The Javascript Date to read, or null to reset
* @param {Boolean} useUTC If true, the UTC values of the date will be used
*/
ICAL.Time.fromJSDate = function fromJSDate(aDate, useUTC) {
var tt = new ICAL.Time();
return tt.fromJSDate(aDate, useUTC);
};
/**
* Creates a new ICAL.Time instance from the the passed data object.
*
* @param {Object} aData Time initialization
* @param {Number=} aData.year The year for this date
* @param {Number=} aData.month The month for this date
* @param {Number=} aData.day The day for this date
* @param {Number=} aData.hour The hour for this date
* @param {Number=} aData.minute The minute for this date
* @param {Number=} aData.second The second for this date
* @param {Boolean=} aData.isDate If true, the instance represents a date
* (as opposed to a date-time)
* @param {ICAL.Timezone=} aZone Timezone this position occurs in
*/
ICAL.Time.fromData = function fromData(aData, aZone) {
var t = new ICAL.Time();
return t.fromData(aData, aZone);
};
/**
* Creates a new ICAL.Time instance from the current moment.
* The instance is “floating” - has no timezone relation.
* To create an instance considering the time zone, call
* ICAL.Time.fromJSDate(new Date(), true)
* @return {ICAL.Time}
*/
ICAL.Time.now = function icaltime_now() {
return ICAL.Time.fromJSDate(new Date(), false);
};
/**
* Returns the date on which ISO week number 1 starts.
*
* @see ICAL.Time#weekNumber
* @param {Number} aYear The year to search in
* @param {ICAL.Time.weekDay=} aWeekStart The week start weekday, used for calculation.
* @return {ICAL.Time} The date on which week number 1 starts
*/
ICAL.Time.weekOneStarts = function weekOneStarts(aYear, aWeekStart) {
var t = ICAL.Time.fromData({
year: aYear,
month: 1,
day: 1,
isDate: true
});
var dow = t.dayOfWeek();
var wkst = aWeekStart || ICAL.Time.DEFAULT_WEEK_START;
if (dow > ICAL.Time.THURSDAY) {
t.day += 7;
}
if (wkst > ICAL.Time.THURSDAY) {
t.day -= 7;
}
t.day -= dow - wkst;
return t;
};
/**
* Get the dominical letter for the given year. Letters range from A - G for
* common years, and AG to GF for leap years.
*
* @param {Number} yr The year to retrieve the letter for
* @return {String} The dominical letter.
*/
ICAL.Time.getDominicalLetter = function(yr) {
var LTRS = "GFEDCBA";
var dom = (yr + (yr / 4 | 0) + (yr / 400 | 0) - (yr / 100 | 0) - 1) % 7;
var isLeap = ICAL.Time.isLeapYear(yr);
if (isLeap) {
return LTRS[(dom + 6) % 7] + LTRS[dom];
} else {
return LTRS[dom];
}
};
/**
* January 1st, 1970 as an ICAL.Time.
* @type {ICAL.Time}
* @constant
* @instance
*/
ICAL.Time.epochTime = ICAL.Time.fromData({
year: 1970,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
isDate: false,
timezone: "Z"
});
ICAL.Time._cmp_attr = function _cmp_attr(a, b, attr) {
if (a[attr] > b[attr]) return 1;
if (a[attr] < b[attr]) return -1;
return 0;
};
/**
* The days that have passed in the year after a given month. The array has
* two members, one being an array of passed days for non-leap years, the
* other analog for leap years.
* @example
* var isLeapYear = ICAL.Time.isLeapYear(year);
* var passedDays = ICAL.Time.daysInYearPassedMonth[isLeapYear][month];
* @type {Array.<Array.<Number>>}
*/
ICAL.Time.daysInYearPassedMonth = [
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365],
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
];
/**
* The weekday, 1 = SUNDAY, 7 = SATURDAY. Access via
* ICAL.Time.MONDAY, ICAL.Time.TUESDAY, ...
*
* @typedef {Number} weekDay
* @memberof ICAL.Time
*/
ICAL.Time.SUNDAY = 1;
ICAL.Time.MONDAY = 2;
ICAL.Time.TUESDAY = 3;
ICAL.Time.WEDNESDAY = 4;
ICAL.Time.THURSDAY = 5;
ICAL.Time.FRIDAY = 6;
ICAL.Time.SATURDAY = 7;
/**
* The default weekday for the WKST part.
* @constant
* @default ICAL.Time.MONDAY
*/
ICAL.Time.DEFAULT_WEEK_START = ICAL.Time.MONDAY;
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2015 */
(function() {
/**
* Describes a vCard time, which has slight differences to the ICAL.Time.
* Properties can be null if not specified, for example for dates with
* reduced accuracy or truncation.
*
* Note that currently not all methods are correctly re-implemented for
* VCardTime. For example, comparison will have undefined results when some
* members are null.
*
* Also, normalization is not yet implemented for this class!
*
* @alias ICAL.VCardTime
* @class
* @extends {ICAL.Time}
* @param {Object} data The data for the time instance
* @param {Number=} data.year The year for this date
* @param {Number=} data.month The month for this date
* @param {Number=} data.day The day for this date
* @param {Number=} data.hour The hour for this date
* @param {Number=} data.minute The minute for this date
* @param {Number=} data.second The second for this date
* @param {ICAL.Timezone|ICAL.UtcOffset} zone The timezone to use
* @param {String} icaltype The type for this date/time object
*/
ICAL.VCardTime = function(data, zone, icaltype) {
this.wrappedJSObject = this;
var time = this._time = Object.create(null);
time.year = null;
time.month = null;
time.day = null;
time.hour = null;
time.minute = null;
time.second = null;
this.icaltype = icaltype || "date-and-or-time";
this.fromData(data, zone);
};
ICAL.helpers.inherits(ICAL.Time, ICAL.VCardTime, /** @lends ICAL.VCardTime */ {
/**
* The class identifier.
* @constant
* @type {String}
* @default "vcardtime"
*/
icalclass: "vcardtime",
/**
* The type name, to be used in the jCal object.
* @type {String}
* @default "date-and-or-time"
*/
icaltype: "date-and-or-time",
/**
* The timezone. This can either be floating, UTC, or an instance of
* ICAL.UtcOffset.
* @type {ICAL.Timezone|ICAL.UtcOFfset}
*/
zone: null,
/**
* Returns a clone of the vcard date/time object.
*
* @return {ICAL.VCardTime} The cloned object
*/
clone: function() {
return new ICAL.VCardTime(this._time, this.zone, this.icaltype);
},
_normalize: function() {
return this;
},
/**
* @inheritdoc
*/
utcOffset: function() {
if (this.zone instanceof ICAL.UtcOffset) {
return this.zone.toSeconds();
} else {
return ICAL.Time.prototype.utcOffset.apply(this, arguments);
}
},
/**
* Returns an RFC 6350 compliant representation of this object.
*
* @return {String} vcard date/time string
*/
toICALString: function() {
return ICAL.design.vcard.value[this.icaltype].toICAL(this.toString());
},
/**
* The string representation of this date/time, in jCard form
* (including : and - separators).
* @return {String}
*/
toString: function toString() {
var p2 = ICAL.helpers.pad2;
var y = this.year, m = this.month, d = this.day;
var h = this.hour, mm = this.minute, s = this.second;
var hasYear = y !== null, hasMonth = m !== null, hasDay = d !== null;
var hasHour = h !== null, hasMinute = mm !== null, hasSecond = s !== null;
var datepart = (hasYear ? p2(y) + (hasMonth || hasDay ? '-' : '') : (hasMonth || hasDay ? '--' : '')) +
(hasMonth ? p2(m) : '') +
(hasDay ? '-' + p2(d) : '');
var timepart = (hasHour ? p2(h) : '-') + (hasHour && hasMinute ? ':' : '') +
(hasMinute ? p2(mm) : '') + (!hasHour && !hasMinute ? '-' : '') +
(hasMinute && hasSecond ? ':' : '') +
(hasSecond ? p2(s) : '');
var zone;
if (this.zone === ICAL.Timezone.utcTimezone) {
zone = 'Z';
} else if (this.zone instanceof ICAL.UtcOffset) {
zone = this.zone.toString();
} else if (this.zone === ICAL.Timezone.localTimezone) {
zone = '';
} else if (this.zone instanceof ICAL.Timezone) {
var offset = ICAL.UtcOffset.fromSeconds(this.zone.utcOffset(this));
zone = offset.toString();
} else {
zone = '';
}
switch (this.icaltype) {
case "time":
return timepart + zone;
case "date-and-or-time":
case "date-time":
return datepart + (timepart == '--' ? '' : 'T' + timepart + zone);
case "date":
return datepart;
}
return null;
}
});
/**
* Returns a new ICAL.VCardTime instance from a date and/or time string.
*
* @param {String} aValue The string to create from
* @param {String} aIcalType The type for this instance, e.g. date-and-or-time
* @return {ICAL.VCardTime} The date/time instance
*/
ICAL.VCardTime.fromDateAndOrTimeString = function(aValue, aIcalType) {
function part(v, s, e) {
return v ? ICAL.helpers.strictParseInt(v.substr(s, e)) : null;
}
var parts = aValue.split('T');
var dt = parts[0], tmz = parts[1];
var splitzone = tmz ? ICAL.design.vcard.value.time._splitZone(tmz) : [];
var zone = splitzone[0], tm = splitzone[1];
var stoi = ICAL.helpers.strictParseInt;
var dtlen = dt ? dt.length : 0;
var tmlen = tm ? tm.length : 0;
var hasDashDate = dt && dt[0] == '-' && dt[1] == '-';
var hasDashTime = tm && tm[0] == '-';
var o = {
year: hasDashDate ? null : part(dt, 0, 4),
month: hasDashDate && (dtlen == 4 || dtlen == 7) ? part(dt, 2, 2) : dtlen == 7 ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 5, 2) : null,
day: dtlen == 5 ? part(dt, 3, 2) : dtlen == 7 && hasDashDate ? part(dt, 5, 2) : dtlen == 10 ? part(dt, 8, 2) : null,
hour: hasDashTime ? null : part(tm, 0, 2),
minute: hasDashTime && tmlen == 3 ? part(tm, 1, 2) : tmlen > 4 ? hasDashTime ? part(tm, 1, 2) : part(tm, 3, 2) : null,
second: tmlen == 4 ? part(tm, 2, 2) : tmlen == 6 ? part(tm, 4, 2) : tmlen == 8 ? part(tm, 6, 2) : null
};
if (zone == 'Z') {
zone = ICAL.Timezone.utcTimezone;
} else if (zone && zone[3] == ':') {
zone = ICAL.UtcOffset.fromString(zone);
} else {
zone = null;
}
return new ICAL.VCardTime(o, zone, aIcalType);
};
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
(function() {
var DOW_MAP = {
SU: ICAL.Time.SUNDAY,
MO: ICAL.Time.MONDAY,
TU: ICAL.Time.TUESDAY,
WE: ICAL.Time.WEDNESDAY,
TH: ICAL.Time.THURSDAY,
FR: ICAL.Time.FRIDAY,
SA: ICAL.Time.SATURDAY
};
var REVERSE_DOW_MAP = {};
for (var key in DOW_MAP) {
/* istanbul ignore else */
if (DOW_MAP.hasOwnProperty(key)) {
REVERSE_DOW_MAP[DOW_MAP[key]] = key;
}
}
var COPY_PARTS = ["BYSECOND", "BYMINUTE", "BYHOUR", "BYDAY",
"BYMONTHDAY", "BYYEARDAY", "BYWEEKNO",
"BYMONTH", "BYSETPOS"];
/**
* @classdesc
* This class represents the "recur" value type, with various calculation
* and manipulation methods.
*
* @class
* @alias ICAL.Recur
* @param {Object} data An object with members of the recurrence
* @param {ICAL.Recur.frequencyValues=} data.freq The frequency value
* @param {Number=} data.interval The INTERVAL value
* @param {ICAL.Time.weekDay=} data.wkst The week start value
* @param {ICAL.Time=} data.until The end of the recurrence set
* @param {Number=} data.count The number of occurrences
* @param {Array.<Number>=} data.bysecond The seconds for the BYSECOND part
* @param {Array.<Number>=} data.byminute The minutes for the BYMINUTE part
* @param {Array.<Number>=} data.byhour The hours for the BYHOUR part
* @param {Array.<String>=} data.byday The BYDAY values
* @param {Array.<Number>=} data.bymonthday The days for the BYMONTHDAY part
* @param {Array.<Number>=} data.byyearday The days for the BYYEARDAY part
* @param {Array.<Number>=} data.byweekno The weeks for the BYWEEKNO part
* @param {Array.<Number>=} data.bymonth The month for the BYMONTH part
* @param {Array.<Number>=} data.bysetpos The positionals for the BYSETPOS part
*/
ICAL.Recur = function icalrecur(data) {
this.wrappedJSObject = this;
this.parts = {};
if (data && typeof(data) === 'object') {
this.fromData(data);
}
};
ICAL.Recur.prototype = {
/**
* An object holding the BY-parts of the recurrence rule
* @type {Object}
*/
parts: null,
/**
* The interval value for the recurrence rule.
* @type {Number}
*/
interval: 1,
/**
* The week start day
*
* @type {ICAL.Time.weekDay}
* @default ICAL.Time.MONDAY
*/
wkst: ICAL.Time.MONDAY,
/**
* The end of the recurrence
* @type {?ICAL.Time}
*/
until: null,
/**
* The maximum number of occurrences
* @type {?Number}
*/
count: null,
/**
* The frequency value.
* @type {ICAL.Recur.frequencyValues}
*/
freq: null,
/**
* The class identifier.
* @constant
* @type {String}
* @default "icalrecur"
*/
icalclass: "icalrecur",
/**
* The type name, to be used in the jCal object.
* @constant
* @type {String}
* @default "recur"
*/
icaltype: "recur",
/**
* Create a new iterator for this recurrence rule. The passed start date
* must be the start date of the event, not the start of the range to
* search in.
*
* @example
* var recur = comp.getFirstPropertyValue('rrule');
* var dtstart = comp.getFirstPropertyValue('dtstart');
* var iter = recur.iterator(dtstart);
* for (var next = iter.next(); next; next = iter.next()) {
* if (next.compare(rangeStart) < 0) {
* continue;
* }
* console.log(next.toString());
* }
*
* @param {ICAL.Time} aStart The item's start date
* @return {ICAL.RecurIterator} The recurrence iterator
*/
iterator: function(aStart) {
return new ICAL.RecurIterator({
rule: this,
dtstart: aStart
});
},
/**
* Returns a clone of the recurrence object.
*
* @return {ICAL.Recur} The cloned object
*/
clone: function clone() {
return new ICAL.Recur(this.toJSON());
},
/**
* Checks if the current rule is finite, i.e. has a count or until part.
*
* @return {Boolean} True, if the rule is finite
*/
isFinite: function isfinite() {
return !!(this.count || this.until);
},
/**
* Checks if the current rule has a count part, and not limited by an until
* part.
*
* @return {Boolean} True, if the rule is by count
*/
isByCount: function isbycount() {
return !!(this.count && !this.until);
},
/**
* Adds a component (part) to the recurrence rule. This is not a component
* in the sense of {@link ICAL.Component}, but a part of the recurrence
* rule, i.e. BYMONTH.
*
* @param {String} aType The name of the component part
* @param {Array|String} aValue The component value
*/
addComponent: function addPart(aType, aValue) {
var ucname = aType.toUpperCase();
if (ucname in this.parts) {
this.parts[ucname].push(aValue);
} else {
this.parts[ucname] = [aValue];
}
},
/**
* Sets the component value for the given by-part.
*
* @param {String} aType The component part name
* @param {Array} aValues The component values
*/
setComponent: function setComponent(aType, aValues) {
this.parts[aType.toUpperCase()] = aValues.slice();
},
/**
* Gets (a copy) of the requested component value.
*
* @param {String} aType The component part name
* @return {Array} The component part value
*/
getComponent: function getComponent(aType) {
var ucname = aType.toUpperCase();
return (ucname in this.parts ? this.parts[ucname].slice() : []);
},
/**
* Retrieves the next occurrence after the given recurrence id. See the
* guide on {@tutorial terminology} for more details.
*
* NOTE: Currently, this method iterates all occurrences from the start
* date. It should not be called in a loop for performance reasons. If you
* would like to get more than one occurrence, you can iterate the
* occurrences manually, see the example on the
* {@link ICAL.Recur#iterator iterator} method.
*
* @param {ICAL.Time} aStartTime The start of the event series
* @param {ICAL.Time} aRecurrenceId The date of the last occurrence
* @return {ICAL.Time} The next occurrence after
*/
getNextOccurrence: function getNextOccurrence(aStartTime, aRecurrenceId) {
var iter = this.iterator(aStartTime);
var next, cdt;
do {
next = iter.next();
} while (next && next.compare(aRecurrenceId) <= 0);
if (next && aRecurrenceId.zone) {
next.zone = aRecurrenceId.zone;
}
return next;
},
/**
* Sets up the current instance using members from the passed data object.
*
* @param {Object} data An object with members of the recurrence
* @param {ICAL.Recur.frequencyValues=} data.freq The frequency value
* @param {Number=} data.interval The INTERVAL value
* @param {ICAL.Time.weekDay=} data.wkst The week start value
* @param {ICAL.Time=} data.until The end of the recurrence set
* @param {Number=} data.count The number of occurrences
* @param {Array.<Number>=} data.bysecond The seconds for the BYSECOND part
* @param {Array.<Number>=} data.byminute The minutes for the BYMINUTE part
* @param {Array.<Number>=} data.byhour The hours for the BYHOUR part
* @param {Array.<String>=} data.byday The BYDAY values
* @param {Array.<Number>=} data.bymonthday The days for the BYMONTHDAY part
* @param {Array.<Number>=} data.byyearday The days for the BYYEARDAY part
* @param {Array.<Number>=} data.byweekno The weeks for the BYWEEKNO part
* @param {Array.<Number>=} data.bymonth The month for the BYMONTH part
* @param {Array.<Number>=} data.bysetpos The positionals for the BYSETPOS part
*/
fromData: function(data) {
for (var key in data) {
var uckey = key.toUpperCase();
if (uckey in partDesign) {
if (Array.isArray(data[key])) {
this.parts[uckey] = data[key];
} else {
this.parts[uckey] = [data[key]];
}
} else {
this[key] = data[key];
}
}
if (this.interval && typeof this.interval != "number") {
optionDesign.INTERVAL(this.interval, this);
}
if (this.wkst && typeof this.wkst != "number") {
this.wkst = ICAL.Recur.icalDayToNumericDay(this.wkst);
}
if (this.until && !(this.until instanceof ICAL.Time)) {
this.until = ICAL.Time.fromString(this.until);
}
},
/**
* The jCal representation of this recurrence type.
* @return {Object}
*/
toJSON: function() {
var res = Object.create(null);
res.freq = this.freq;
if (this.count) {
res.count = this.count;
}
if (this.interval > 1) {
res.interval = this.interval;
}
for (var k in this.parts) {
/* istanbul ignore if */
if (!this.parts.hasOwnProperty(k)) {
continue;
}
var kparts = this.parts[k];
if (Array.isArray(kparts) && kparts.length == 1) {
res[k.toLowerCase()] = kparts[0];
} else {
res[k.toLowerCase()] = ICAL.helpers.clone(this.parts[k]);
}
}
if (this.until) {
res.until = this.until.toString();
}
if ('wkst' in this && this.wkst !== ICAL.Time.DEFAULT_WEEK_START) {
res.wkst = ICAL.Recur.numericDayToIcalDay(this.wkst);
}
return res;
},
/**
* The string representation of this recurrence rule.
* @return {String}
*/
toString: function icalrecur_toString() {
// TODO retain order
var str = "FREQ=" + this.freq;
if (this.count) {
str += ";COUNT=" + this.count;
}
if (this.interval > 1) {
str += ";INTERVAL=" + this.interval;
}
for (var k in this.parts) {
/* istanbul ignore else */
if (this.parts.hasOwnProperty(k)) {
str += ";" + k + "=" + this.parts[k];
}
}
if (this.until) {
str += ';UNTIL=' + this.until.toICALString();
}
if ('wkst' in this && this.wkst !== ICAL.Time.DEFAULT_WEEK_START) {
str += ';WKST=' + ICAL.Recur.numericDayToIcalDay(this.wkst);
}
return str;
}
};
function parseNumericValue(type, min, max, value) {
var result = value;
if (value[0] === '+') {
result = value.substr(1);
}
result = ICAL.helpers.strictParseInt(result);
if (min !== undefined && value < min) {
throw new Error(
type + ': invalid value "' + value + '" must be > ' + min
);
}
if (max !== undefined && value > max) {
throw new Error(
type + ': invalid value "' + value + '" must be < ' + min
);
}
return result;
}
/**
* Convert an ical representation of a day (SU, MO, etc..)
* into a numeric value of that day.
*
* @param {String} string The iCalendar day name
* @param {ICAL.Time.weekDay=} aWeekStart
* The week start weekday, defaults to SUNDAY
* @return {Number} Numeric value of given day
*/
ICAL.Recur.icalDayToNumericDay = function toNumericDay(string, aWeekStart) {
//XXX: this is here so we can deal
// with possibly invalid string values.
var firstDow = aWeekStart || ICAL.Time.SUNDAY;
return ((DOW_MAP[string] - firstDow + 7) % 7) + 1;
};
/**
* Convert a numeric day value into its ical representation (SU, MO, etc..)
*
* @param {Number} num Numeric value of given day
* @param {ICAL.Time.weekDay=} aWeekStart
* The week start weekday, defaults to SUNDAY
* @return {String} The ICAL day value, e.g SU,MO,...
*/
ICAL.Recur.numericDayToIcalDay = function toIcalDay(num, aWeekStart) {
//XXX: this is here so we can deal with possibly invalid number values.
// Also, this allows consistent mapping between day numbers and day
// names for external users.
var firstDow = aWeekStart || ICAL.Time.SUNDAY;
var dow = (num + firstDow - ICAL.Time.SUNDAY);
if (dow > 7) {
dow -= 7;
}
return REVERSE_DOW_MAP[dow];
};
var VALID_DAY_NAMES = /^(SU|MO|TU|WE|TH|FR|SA)$/;
var VALID_BYDAY_PART = /^([+-])?(5[0-3]|[1-4][0-9]|[1-9])?(SU|MO|TU|WE|TH|FR|SA)$/;
/**
* Possible frequency values for the FREQ part
* (YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY)
*
* @typedef {String} frequencyValues
* @memberof ICAL.Recur
*/
var ALLOWED_FREQ = ['SECONDLY', 'MINUTELY', 'HOURLY',
'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'];
var optionDesign = {
FREQ: function(value, dict, fmtIcal) {
// yes this is actually equal or faster then regex.
// upside here is we can enumerate the valid values.
if (ALLOWED_FREQ.indexOf(value) !== -1) {
dict.freq = value;
} else {
throw new Error(
'invalid frequency "' + value + '" expected: "' +
ALLOWED_FREQ.join(', ') + '"'
);
}
},
COUNT: function(value, dict, fmtIcal) {
dict.count = ICAL.helpers.strictParseInt(value);
},
INTERVAL: function(value, dict, fmtIcal) {
dict.interval = ICAL.helpers.strictParseInt(value);
if (dict.interval < 1) {
// 0 or negative values are not allowed, some engines seem to generate
// it though. Assume 1 instead.
dict.interval = 1;
}
},
UNTIL: function(value, dict, fmtIcal) {
if (value.length > 10) {
dict.until = ICAL.design.icalendar.value['date-time'].fromICAL(value);
} else {
dict.until = ICAL.design.icalendar.value.date.fromICAL(value);
}
if (!fmtIcal) {
dict.until = ICAL.Time.fromString(dict.until);
}
},
WKST: function(value, dict, fmtIcal) {
if (VALID_DAY_NAMES.test(value)) {
dict.wkst = ICAL.Recur.icalDayToNumericDay(value);
} else {
throw new Error('invalid WKST value "' + value + '"');
}
}
};
var partDesign = {
BYSECOND: parseNumericValue.bind(this, 'BYSECOND', 0, 60),
BYMINUTE: parseNumericValue.bind(this, 'BYMINUTE', 0, 59),
BYHOUR: parseNumericValue.bind(this, 'BYHOUR', 0, 23),
BYDAY: function(value) {
if (VALID_BYDAY_PART.test(value)) {
return value;
} else {
throw new Error('invalid BYDAY value "' + value + '"');
}
},
BYMONTHDAY: parseNumericValue.bind(this, 'BYMONTHDAY', -31, 31),
BYYEARDAY: parseNumericValue.bind(this, 'BYYEARDAY', -366, 366),
BYWEEKNO: parseNumericValue.bind(this, 'BYWEEKNO', -53, 53),
BYMONTH: parseNumericValue.bind(this, 'BYMONTH', 1, 12),
BYSETPOS: parseNumericValue.bind(this, 'BYSETPOS', -366, 366)
};
/**
* Creates a new {@link ICAL.Recur} instance from the passed string.
*
* @param {String} string The string to parse
* @return {ICAL.Recur} The created recurrence instance
*/
ICAL.Recur.fromString = function(string) {
var data = ICAL.Recur._stringToData(string, false);
return new ICAL.Recur(data);
};
/**
* Creates a new {@link ICAL.Recur} instance using members from the passed
* data object.
*
* @param {Object} aData An object with members of the recurrence
* @param {ICAL.Recur.frequencyValues=} aData.freq The frequency value
* @param {Number=} aData.interval The INTERVAL value
* @param {ICAL.Time.weekDay=} aData.wkst The week start value
* @param {ICAL.Time=} aData.until The end of the recurrence set
* @param {Number=} aData.count The number of occurrences
* @param {Array.<Number>=} aData.bysecond The seconds for the BYSECOND part
* @param {Array.<Number>=} aData.byminute The minutes for the BYMINUTE part
* @param {Array.<Number>=} aData.byhour The hours for the BYHOUR part
* @param {Array.<String>=} aData.byday The BYDAY values
* @param {Array.<Number>=} aData.bymonthday The days for the BYMONTHDAY part
* @param {Array.<Number>=} aData.byyearday The days for the BYYEARDAY part
* @param {Array.<Number>=} aData.byweekno The weeks for the BYWEEKNO part
* @param {Array.<Number>=} aData.bymonth The month for the BYMONTH part
* @param {Array.<Number>=} aData.bysetpos The positionals for the BYSETPOS part
*/
ICAL.Recur.fromData = function(aData) {
return new ICAL.Recur(aData);
};
/**
* Converts a recurrence string to a data object, suitable for the fromData
* method.
*
* @param {String} string The string to parse
* @param {Boolean} fmtIcal If true, the string is considered to be an
* iCalendar string
* @return {ICAL.Recur} The recurrence instance
*/
ICAL.Recur._stringToData = function(string, fmtIcal) {
var dict = Object.create(null);
// split is slower in FF but fast enough.
// v8 however this is faster then manual split?
var values = string.split(';');
var len = values.length;
for (var i = 0; i < len; i++) {
var parts = values[i].split('=');
var ucname = parts[0].toUpperCase();
var lcname = parts[0].toLowerCase();
var name = (fmtIcal ? lcname : ucname);
var value = parts[1];
if (ucname in partDesign) {
var partArr = value.split(',');
var partArrIdx = 0;
var partArrLen = partArr.length;
for (; partArrIdx < partArrLen; partArrIdx++) {
partArr[partArrIdx] = partDesign[ucname](partArr[partArrIdx]);
}
dict[name] = (partArr.length == 1 ? partArr[0] : partArr);
} else if (ucname in optionDesign) {
optionDesign[ucname](value, dict, fmtIcal);
} else {
// Don't swallow unknown values. Just set them as they are.
dict[lcname] = value;
}
}
return dict;
};
})();
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.RecurIterator = (function() {
/**
* @classdesc
* An iterator for a single recurrence rule. This class usually doesn't have
* to be instanciated directly, the convenience method
* {@link ICAL.Recur#iterator} can be used.
*
* @description
* The options object may contain additional members when resuming iteration from a previous run
*
* @description
* The options object may contain additional members when resuming iteration
* from a previous run.
*
* @class
* @alias ICAL.RecurIterator
* @param {Object} options The iterator options
* @param {ICAL.Recur} options.rule The rule to iterate.
* @param {ICAL.Time} options.dtstart The start date of the event.
* @param {Boolean=} options.initialized When true, assume that options are
* from a previously constructed iterator. Initialization will not be
* repeated.
*/
function icalrecur_iterator(options) {
this.fromData(options);
}
icalrecur_iterator.prototype = {
/**
* True when iteration is finished.
* @type {Boolean}
*/
completed: false,
/**
* The rule that is being iterated
* @type {ICAL.Recur}
*/
rule: null,
/**
* The start date of the event being iterated.
* @type {ICAL.Time}
*/
dtstart: null,
/**
* The last occurrence that was returned from the
* {@link ICAL.RecurIterator#next} method.
* @type {ICAL.Time}
*/
last: null,
/**
* The sequence number from the occurrence
* @type {Number}
*/
occurrence_number: 0,
/**
* The indices used for the {@link ICAL.RecurIterator#by_data} object.
* @type {Object}
* @private
*/
by_indices: null,
/**
* If true, the iterator has already been initialized
* @type {Boolean}
* @private
*/
initialized: false,
/**
* The initializd by-data.
* @type {Object}
* @private
*/
by_data: null,
/**
* The expanded yeardays
* @type {Array}
* @private
*/
days: null,
/**
* The index in the {@link ICAL.RecurIterator#days} array.
* @type {Number}
* @private
*/
days_index: 0,
/**
* Initialize the recurrence iterator from the passed data object. This
* method is usually not called directly, you can initialize the iterator
* through the constructor.
*
* @param {Object} options The iterator options
* @param {ICAL.Recur} options.rule The rule to iterate.
* @param {ICAL.Time} options.dtstart The start date of the event.
* @param {Boolean=} options.initialized When true, assume that options are
* from a previously constructed iterator. Initialization will not be
* repeated.
*/
fromData: function(options) {
this.rule = ICAL.helpers.formatClassType(options.rule, ICAL.Recur);
if (!this.rule) {
throw new Error('iterator requires a (ICAL.Recur) rule');
}
this.dtstart = ICAL.helpers.formatClassType(options.dtstart, ICAL.Time);
if (!this.dtstart) {
throw new Error('iterator requires a (ICAL.Time) dtstart');
}
if (options.by_data) {
this.by_data = options.by_data;
} else {
this.by_data = ICAL.helpers.clone(this.rule.parts, true);
}
if (options.occurrence_number)
this.occurrence_number = options.occurrence_number;
this.days = options.days || [];
if (options.last) {
this.last = ICAL.helpers.formatClassType(options.last, ICAL.Time);
}
this.by_indices = options.by_indices;
if (!this.by_indices) {
this.by_indices = {
"BYSECOND": 0,
"BYMINUTE": 0,
"BYHOUR": 0,
"BYDAY": 0,
"BYMONTH": 0,
"BYWEEKNO": 0,
"BYMONTHDAY": 0
};
}
this.initialized = options.initialized || false;
if (!this.initialized) {
this.init();
}
},
/**
* Intialize the iterator
* @private
*/
init: function icalrecur_iterator_init() {
this.initialized = true;
this.last = this.dtstart.clone();
var parts = this.by_data;
if ("BYDAY" in parts) {
// libical does this earlier when the rule is loaded, but we postpone to
// now so we can preserve the original order.
this.sort_byday_rules(parts.BYDAY);
}
// If the BYYEARDAY appares, no other date rule part may appear
if ("BYYEARDAY" in parts) {
if ("BYMONTH" in parts || "BYWEEKNO" in parts ||
"BYMONTHDAY" in parts || "BYDAY" in parts) {
throw new Error("Invalid BYYEARDAY rule");
}
}
// BYWEEKNO and BYMONTHDAY rule parts may not both appear
if ("BYWEEKNO" in parts && "BYMONTHDAY" in parts) {
throw new Error("BYWEEKNO does not fit to BYMONTHDAY");
}
// For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
// BYWEEKNO may appear.
if (this.rule.freq == "MONTHLY" &&
("BYYEARDAY" in parts || "BYWEEKNO" in parts)) {
throw new Error("For MONTHLY recurrences neither BYYEARDAY nor BYWEEKNO may appear");
}
// For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
// BYYEARDAY may appear.
if (this.rule.freq == "WEEKLY" &&
("BYYEARDAY" in parts || "BYMONTHDAY" in parts)) {
throw new Error("For WEEKLY recurrences neither BYMONTHDAY nor BYYEARDAY may appear");
}
// BYYEARDAY may only appear in YEARLY rules
if (this.rule.freq != "YEARLY" && "BYYEARDAY" in parts) {
throw new Error("BYYEARDAY may only appear in YEARLY rules");
}
this.last.second = this.setup_defaults("BYSECOND", "SECONDLY", this.dtstart.second);
this.last.minute = this.setup_defaults("BYMINUTE", "MINUTELY", this.dtstart.minute);
this.last.hour = this.setup_defaults("BYHOUR", "HOURLY", this.dtstart.hour);
this.last.day = this.setup_defaults("BYMONTHDAY", "DAILY", this.dtstart.day);
this.last.month = this.setup_defaults("BYMONTH", "MONTHLY", this.dtstart.month);
if (this.rule.freq == "WEEKLY") {
if ("BYDAY" in parts) {
var bydayParts = this.ruleDayOfWeek(parts.BYDAY[0], this.rule.wkst);
var pos = bydayParts[0];
var dow = bydayParts[1];
var wkdy = dow - this.last.dayOfWeek(this.rule.wkst);
if ((this.last.dayOfWeek(this.rule.wkst) < dow && wkdy >= 0) || wkdy < 0) {
// Initial time is after first day of BYDAY data
this.last.day += wkdy;
}
} else {
var dayName = ICAL.Recur.numericDayToIcalDay(this.dtstart.dayOfWeek());
parts.BYDAY = [dayName];
}
}
if (this.rule.freq == "YEARLY") {
for (;;) {
this.expand_year_days(this.last.year);
if (this.days.length > 0) {
break;
}
this.increment_year(this.rule.interval);
}
this._nextByYearDay();
}
if (this.rule.freq == "MONTHLY" && this.has_by_data("BYDAY")) {
var tempLast = null;
var initLast = this.last.clone();
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
// Check every weekday in BYDAY with relative dow and pos.
for (var i in this.by_data.BYDAY) {
/* istanbul ignore if */
if (!this.by_data.BYDAY.hasOwnProperty(i)) {
continue;
}
this.last = initLast.clone();
var bydayParts = this.ruleDayOfWeek(this.by_data.BYDAY[i]);
var pos = bydayParts[0];
var dow = bydayParts[1];
var dayOfMonth = this.last.nthWeekDay(dow, pos);
// If |pos| >= 6, the byday is invalid for a monthly rule.
if (pos >= 6 || pos <= -6) {
throw new Error("Malformed values in BYDAY part");
}
// If a Byday with pos=+/-5 is not in the current month it
// must be searched in the next months.
if (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
// Skip if we have already found a "last" in this month.
if (tempLast && tempLast.month == initLast.month) {
continue;
}
while (dayOfMonth > daysInMonth || dayOfMonth <= 0) {
this.increment_month();
daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
dayOfMonth = this.last.nthWeekDay(dow, pos);
}
}
this.last.day = dayOfMonth;
if (!tempLast || this.last.compare(tempLast) < 0) {
tempLast = this.last.clone();
}
}
this.last = tempLast.clone();
//XXX: This feels like a hack, but we need to initialize
// the BYMONTHDAY case correctly and byDayAndMonthDay handles
// this case. It accepts a special flag which will avoid incrementing
// the initial value without the flag days that match the start time
// would be missed.
if (this.has_by_data('BYMONTHDAY')) {
this._byDayAndMonthDay(true);
}
if (this.last.day > daysInMonth || this.last.day == 0) {
throw new Error("Malformed values in BYDAY part");
}
} else if (this.has_by_data("BYMONTHDAY")) {
if (this.last.day < 0) {
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
this.last.day = daysInMonth + this.last.day + 1;
}
}
},
/**
* Retrieve the next occurrence from the iterator.
* @return {ICAL.Time}
*/
next: function icalrecur_iterator_next() {
var before = (this.last ? this.last.clone() : null);
if ((this.rule.count && this.occurrence_number >= this.rule.count) ||
(this.rule.until && this.last.compare(this.rule.until) > 0)) {
//XXX: right now this is just a flag and has no impact
// we can simplify the above case to check for completed later.
this.completed = true;
return null;
}
if (this.occurrence_number == 0 && this.last.compare(this.dtstart) >= 0) {
// First of all, give the instance that was initialized
this.occurrence_number++;
return this.last;
}
var valid;
do {
valid = 1;
switch (this.rule.freq) {
case "SECONDLY":
this.next_second();
break;
case "MINUTELY":
this.next_minute();
break;
case "HOURLY":
this.next_hour();
break;
case "DAILY":
this.next_day();
break;
case "WEEKLY":
this.next_week();
break;
case "MONTHLY":
valid = this.next_month();
break;
case "YEARLY":
this.next_year();
break;
default:
return null;
}
} while (!this.check_contracting_rules() ||
this.last.compare(this.dtstart) < 0 ||
!valid);
// TODO is this valid?
if (this.last.compare(before) == 0) {
throw new Error("Same occurrence found twice, protecting " +
"you from death by recursion");
}
if (this.rule.until && this.last.compare(this.rule.until) > 0) {
this.completed = true;
return null;
} else {
this.occurrence_number++;
return this.last;
}
},
next_second: function next_second() {
return this.next_generic("BYSECOND", "SECONDLY", "second", "minute");
},
increment_second: function increment_second(inc) {
return this.increment_generic(inc, "second", 60, "minute");
},
next_minute: function next_minute() {
return this.next_generic("BYMINUTE", "MINUTELY",
"minute", "hour", "next_second");
},
increment_minute: function increment_minute(inc) {
return this.increment_generic(inc, "minute", 60, "hour");
},
next_hour: function next_hour() {
return this.next_generic("BYHOUR", "HOURLY", "hour",
"monthday", "next_minute");
},
increment_hour: function increment_hour(inc) {
this.increment_generic(inc, "hour", 24, "monthday");
},
next_day: function next_day() {
var has_by_day = ("BYDAY" in this.by_data);
var this_freq = (this.rule.freq == "DAILY");
if (this.next_hour() == 0) {
return 0;
}
if (this_freq) {
this.increment_monthday(this.rule.interval);
} else {
this.increment_monthday(1);
}
return 0;
},
next_week: function next_week() {
var end_of_data = 0;
if (this.next_weekday_by_week() == 0) {
return end_of_data;
}
if (this.has_by_data("BYWEEKNO")) {
var idx = ++this.by_indices.BYWEEKNO;
if (this.by_indices.BYWEEKNO == this.by_data.BYWEEKNO.length) {
this.by_indices.BYWEEKNO = 0;
end_of_data = 1;
}
// HACK should be first month of the year
this.last.month = 1;
this.last.day = 1;
var week_no = this.by_data.BYWEEKNO[this.by_indices.BYWEEKNO];
this.last.day += 7 * week_no;
if (end_of_data) {
this.increment_year(1);
}
} else {
// Jump to the next week
this.increment_monthday(7 * this.rule.interval);
}
return end_of_data;
},
/**
* Normalize each by day rule for a given year/month.
* Takes into account ordering and negative rules
*
* @private
* @param {Number} year Current year.
* @param {Number} month Current month.
* @param {Array} rules Array of rules.
*
* @return {Array} sorted and normalized rules.
* Negative rules will be expanded to their
* correct positive values for easier processing.
*/
normalizeByMonthDayRules: function(year, month, rules) {
var daysInMonth = ICAL.Time.daysInMonth(month, year);
// XXX: This is probably bad for performance to allocate
// a new array for each month we scan, if possible
// we should try to optimize this...
var newRules = [];
var ruleIdx = 0;
var len = rules.length;
var rule;
for (; ruleIdx < len; ruleIdx++) {
rule = rules[ruleIdx];
// if this rule falls outside of given
// month discard it.
if (Math.abs(rule) > daysInMonth) {
continue;
}
// negative case
if (rule < 0) {
// we add (not subtract it is a negative number)
// one from the rule because 1 === last day of month
rule = daysInMonth + (rule + 1);
} else if (rule === 0) {
// skip zero: it is invalid.
continue;
}
// only add unique items...
if (newRules.indexOf(rule) === -1) {
newRules.push(rule);
}
}
// unique and sort
return newRules.sort(function(a, b) { return a - b; });
},
/**
* NOTES:
* We are given a list of dates in the month (BYMONTHDAY) (23, etc..)
* Also we are given a list of days (BYDAY) (MO, 2SU, etc..) when
* both conditions match a given date (this.last.day) iteration stops.
*
* @private
* @param {Boolean=} isInit When given true will not increment the
* current day (this.last).
*/
_byDayAndMonthDay: function(isInit) {
var byMonthDay; // setup in initMonth
var byDay = this.by_data.BYDAY;
var date;
var dateIdx = 0;
var dateLen; // setup in initMonth
var dayLen = byDay.length;
// we are not valid by default
var dataIsValid = 0;
var daysInMonth;
var self = this;
// we need a copy of this, because a DateTime gets normalized
// automatically if the day is out of range. At some points we
// set the last day to 0 to start counting.
var lastDay = this.last.day;
function initMonth() {
daysInMonth = ICAL.Time.daysInMonth(
self.last.month, self.last.year
);
byMonthDay = self.normalizeByMonthDayRules(
self.last.year,
self.last.month,
self.by_data.BYMONTHDAY
);
dateLen = byMonthDay.length;
// For the case of more than one occurrence in one month
// we have to be sure to start searching after the last
// found date or at the last BYMONTHDAY, unless we are
// initializing the iterator because in this case we have
// to consider the last found date too.
while (byMonthDay[dateIdx] <= lastDay &&
!(isInit && byMonthDay[dateIdx] == lastDay) &&
dateIdx < dateLen - 1) {
dateIdx++;
}
}
function nextMonth() {
// since the day is incremented at the start
// of the loop below, we need to start at 0
lastDay = 0;
self.increment_month();
dateIdx = 0;
initMonth();
}
initMonth();
// should come after initMonth
if (isInit) {
lastDay -= 1;
}
// Use a counter to avoid an infinite loop with malformed rules.
// Stop checking after 4 years so we consider also a leap year.
var monthsCounter = 48;
while (!dataIsValid && monthsCounter) {
monthsCounter--;
// increment the current date. This is really
// important otherwise we may fall into the infinite
// loop trap. The initial date takes care of the case
// where the current date is the date we are looking
// for.
date = lastDay + 1;
if (date > daysInMonth) {
nextMonth();
continue;
}
// find next date
var next = byMonthDay[dateIdx++];
// this logic is dependant on the BYMONTHDAYS
// being in order (which is done by #normalizeByMonthDayRules)
if (next >= date) {
// if the next month day is in the future jump to it.
lastDay = next;
} else {
// in this case the 'next' monthday has past
// we must move to the month.
nextMonth();
continue;
}
// Now we can loop through the day rules to see
// if one matches the current month date.
for (var dayIdx = 0; dayIdx < dayLen; dayIdx++) {
var parts = this.ruleDayOfWeek(byDay[dayIdx]);
var pos = parts[0];
var dow = parts[1];
this.last.day = lastDay;
if (this.last.isNthWeekDay(dow, pos)) {
// when we find the valid one we can mark
// the conditions as met and break the loop.
// (Because we have this condition above
// it will also break the parent loop).
dataIsValid = 1;
break;
}
}
// It is completely possible that the combination
// cannot be matched in the current month.
// When we reach the end of possible combinations
// in the current month we iterate to the next one.
// since dateIdx is incremented right after getting
// "next", we don't need dateLen -1 here.
if (!dataIsValid && dateIdx === dateLen) {
nextMonth();
continue;
}
}
if (monthsCounter <= 0) {
// Checked 4 years without finding a Byday that matches
// a Bymonthday. Maybe the rule is not correct.
throw new Error("Malformed values in BYDAY combined with BYMONTHDAY parts");
}
return dataIsValid;
},
next_month: function next_month() {
var this_freq = (this.rule.freq == "MONTHLY");
var data_valid = 1;
if (this.next_hour() == 0) {
return data_valid;
}
if (this.has_by_data("BYDAY") && this.has_by_data("BYMONTHDAY")) {
data_valid = this._byDayAndMonthDay();
} else if (this.has_by_data("BYDAY")) {
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
var setpos = 0;
var setpos_total = 0;
if (this.has_by_data("BYSETPOS")) {
var last_day = this.last.day;
for (var day = 1; day <= daysInMonth; day++) {
this.last.day = day;
if (this.is_day_in_byday(this.last)) {
setpos_total++;
if (day <= last_day) {
setpos++;
}
}
}
this.last.day = last_day;
}
data_valid = 0;
for (var day = this.last.day + 1; day <= daysInMonth; day++) {
this.last.day = day;
if (this.is_day_in_byday(this.last)) {
if (!this.has_by_data("BYSETPOS") ||
this.check_set_position(++setpos) ||
this.check_set_position(setpos - setpos_total - 1)) {
data_valid = 1;
break;
}
}
}
if (day > daysInMonth) {
this.last.day = 1;
this.increment_month();
if (this.is_day_in_byday(this.last)) {
if (!this.has_by_data("BYSETPOS") || this.check_set_position(1)) {
data_valid = 1;
}
} else {
data_valid = 0;
}
}
} else if (this.has_by_data("BYMONTHDAY")) {
this.by_indices.BYMONTHDAY++;
if (this.by_indices.BYMONTHDAY >= this.by_data.BYMONTHDAY.length) {
this.by_indices.BYMONTHDAY = 0;
this.increment_month();
}
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
var day = this.by_data.BYMONTHDAY[this.by_indices.BYMONTHDAY];
if (day < 0) {
day = daysInMonth + day + 1;
}
if (day > daysInMonth) {
this.last.day = 1;
data_valid = this.is_day_in_byday(this.last);
} else {
this.last.day = day;
}
} else {
this.increment_month();
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
if (this.by_data.BYMONTHDAY[0] > daysInMonth) {
data_valid = 0;
} else {
this.last.day = this.by_data.BYMONTHDAY[0];
}
}
return data_valid;
},
next_weekday_by_week: function next_weekday_by_week() {
var end_of_data = 0;
if (this.next_hour() == 0) {
return end_of_data;
}
if (!this.has_by_data("BYDAY")) {
return 1;
}
for (;;) {
var tt = new ICAL.Time();
this.by_indices.BYDAY++;
if (this.by_indices.BYDAY == Object.keys(this.by_data.BYDAY).length) {
this.by_indices.BYDAY = 0;
end_of_data = 1;
}
var coded_day = this.by_data.BYDAY[this.by_indices.BYDAY];
var parts = this.ruleDayOfWeek(coded_day);
var dow = parts[1];
dow -= this.rule.wkst;
if (dow < 0) {
dow += 7;
}
tt.year = this.last.year;
tt.month = this.last.month;
tt.day = this.last.day;
var startOfWeek = tt.startDoyWeek(this.rule.wkst);
if (dow + startOfWeek < 1) {
// The selected date is in the previous year
if (!end_of_data) {
continue;
}
}
var next = ICAL.Time.fromDayOfYear(startOfWeek + dow,
this.last.year);
/**
* The normalization horrors below are due to
* the fact that when the year/month/day changes
* it can effect the other operations that come after.
*/
this.last.year = next.year;
this.last.month = next.month;
this.last.day = next.day;
return end_of_data;
}
},
next_year: function next_year() {
if (this.next_hour() == 0) {
return 0;
}
if (++this.days_index == this.days.length) {
this.days_index = 0;
do {
this.increment_year(this.rule.interval);
this.expand_year_days(this.last.year);
} while (this.days.length == 0);
}
this._nextByYearDay();
return 1;
},
_nextByYearDay: function _nextByYearDay() {
var doy = this.days[this.days_index];
var year = this.last.year;
if (doy < 1) {
// Time.fromDayOfYear(doy, year) indexes relative to the
// start of the given year. That is different from the
// semantics of BYYEARDAY where negative indexes are an
// offset from the end of the given year.
doy += 1;
year += 1;
}
var next = ICAL.Time.fromDayOfYear(doy, year);
this.last.day = next.day;
this.last.month = next.month;
},
/**
* @param dow (eg: '1TU', '-1MO')
* @param {ICAL.Time.weekDay=} aWeekStart The week start weekday
* @return [pos, numericDow] (eg: [1, 3]) numericDow is relative to aWeekStart
*/
ruleDayOfWeek: function ruleDayOfWeek(dow, aWeekStart) {
var matches = dow.match(/([+-]?[0-9])?(MO|TU|WE|TH|FR|SA|SU)/);
if (matches) {
var pos = parseInt(matches[1] || 0, 10);
dow = ICAL.Recur.icalDayToNumericDay(matches[2], aWeekStart);
return [pos, dow];
} else {
return [0, 0];
}
},
next_generic: function next_generic(aRuleType, aInterval, aDateAttr,
aFollowingAttr, aPreviousIncr) {
var has_by_rule = (aRuleType in this.by_data);
var this_freq = (this.rule.freq == aInterval);
var end_of_data = 0;
if (aPreviousIncr && this[aPreviousIncr]() == 0) {
return end_of_data;
}
if (has_by_rule) {
this.by_indices[aRuleType]++;
var idx = this.by_indices[aRuleType];
var dta = this.by_data[aRuleType];
if (this.by_indices[aRuleType] == dta.length) {
this.by_indices[aRuleType] = 0;
end_of_data = 1;
}
this.last[aDateAttr] = dta[this.by_indices[aRuleType]];
} else if (this_freq) {
this["increment_" + aDateAttr](this.rule.interval);
}
if (has_by_rule && end_of_data && this_freq) {
this["increment_" + aFollowingAttr](1);
}
return end_of_data;
},
increment_monthday: function increment_monthday(inc) {
for (var i = 0; i < inc; i++) {
var daysInMonth = ICAL.Time.daysInMonth(this.last.month, this.last.year);
this.last.day++;
if (this.last.day > daysInMonth) {
this.last.day -= daysInMonth;
this.increment_month();
}
}
},
increment_month: function increment_month() {
this.last.day = 1;
if (this.has_by_data("BYMONTH")) {
this.by_indices.BYMONTH++;
if (this.by_indices.BYMONTH == this.by_data.BYMONTH.length) {
this.by_indices.BYMONTH = 0;
this.increment_year(1);
}
this.last.month = this.by_data.BYMONTH[this.by_indices.BYMONTH];
} else {
if (this.rule.freq == "MONTHLY") {
this.last.month += this.rule.interval;
} else {
this.last.month++;
}
this.last.month--;
var years = ICAL.helpers.trunc(this.last.month / 12);
this.last.month %= 12;
this.last.month++;
if (years != 0) {
this.increment_year(years);
}
}
},
increment_year: function increment_year(inc) {
this.last.year += inc;
},
increment_generic: function increment_generic(inc, aDateAttr,
aFactor, aNextIncrement) {
this.last[aDateAttr] += inc;
var nextunit = ICAL.helpers.trunc(this.last[aDateAttr] / aFactor);
this.last[aDateAttr] %= aFactor;
if (nextunit != 0) {
this["increment_" + aNextIncrement](nextunit);
}
},
has_by_data: function has_by_data(aRuleType) {
return (aRuleType in this.rule.parts);
},
expand_year_days: function expand_year_days(aYear) {
var t = new ICAL.Time();
this.days = [];
// We need our own copy with a few keys set
var parts = {};
var rules = ["BYDAY", "BYWEEKNO", "BYMONTHDAY", "BYMONTH", "BYYEARDAY"];
for (var p in rules) {
/* istanbul ignore else */
if (rules.hasOwnProperty(p)) {
var part = rules[p];
if (part in this.rule.parts) {
parts[part] = this.rule.parts[part];
}
}
}
if ("BYMONTH" in parts && "BYWEEKNO" in parts) {
var valid = 1;
var validWeeks = {};
t.year = aYear;
t.isDate = true;
for (var monthIdx = 0; monthIdx < this.by_data.BYMONTH.length; monthIdx++) {
var month = this.by_data.BYMONTH[monthIdx];
t.month = month;
t.day = 1;
var first_week = t.weekNumber(this.rule.wkst);
t.day = ICAL.Time.daysInMonth(month, aYear);
var last_week = t.weekNumber(this.rule.wkst);
for (monthIdx = first_week; monthIdx < last_week; monthIdx++) {
validWeeks[monthIdx] = 1;
}
}
for (var weekIdx = 0; weekIdx < this.by_data.BYWEEKNO.length && valid; weekIdx++) {
var weekno = this.by_data.BYWEEKNO[weekIdx];
if (weekno < 52) {
valid &= validWeeks[weekIdx];
} else {
valid = 0;
}
}
if (valid) {
delete parts.BYMONTH;
} else {
delete parts.BYWEEKNO;
}
}
var partCount = Object.keys(parts).length;
if (partCount == 0) {
var t1 = this.dtstart.clone();
t1.year = this.last.year;
this.days.push(t1.dayOfYear());
} else if (partCount == 1 && "BYMONTH" in parts) {
for (var monthkey in this.by_data.BYMONTH) {
/* istanbul ignore if */
if (!this.by_data.BYMONTH.hasOwnProperty(monthkey)) {
continue;
}
var t2 = this.dtstart.clone();
t2.year = aYear;
t2.month = this.by_data.BYMONTH[monthkey];
t2.isDate = true;
this.days.push(t2.dayOfYear());
}
} else if (partCount == 1 && "BYMONTHDAY" in parts) {
for (var monthdaykey in this.by_data.BYMONTHDAY) {
/* istanbul ignore if */
if (!this.by_data.BYMONTHDAY.hasOwnProperty(monthdaykey)) {
continue;
}
var t3 = this.dtstart.clone();
var day_ = this.by_data.BYMONTHDAY[monthdaykey];
if (day_ < 0) {
var daysInMonth = ICAL.Time.daysInMonth(t3.month, aYear);
day_ = day_ + daysInMonth + 1;
}
t3.day = day_;
t3.year = aYear;
t3.isDate = true;
this.days.push(t3.dayOfYear());
}
} else if (partCount == 2 &&
"BYMONTHDAY" in parts &&
"BYMONTH" in parts) {
for (var monthkey in this.by_data.BYMONTH) {
/* istanbul ignore if */
if (!this.by_data.BYMONTH.hasOwnProperty(monthkey)) {
continue;
}
var month_ = this.by_data.BYMONTH[monthkey];
var daysInMonth = ICAL.Time.daysInMonth(month_, aYear);
for (var monthdaykey in this.by_data.BYMONTHDAY) {
/* istanbul ignore if */
if (!this.by_data.BYMONTHDAY.hasOwnProperty(monthdaykey)) {
continue;
}
var day_ = this.by_data.BYMONTHDAY[monthdaykey];
if (day_ < 0) {
day_ = day_ + daysInMonth + 1;
}
t.day = day_;
t.month = month_;
t.year = aYear;
t.isDate = true;
this.days.push(t.dayOfYear());
}
}
} else if (partCount == 1 && "BYWEEKNO" in parts) {
// TODO unimplemented in libical
} else if (partCount == 2 &&
"BYWEEKNO" in parts &&
"BYMONTHDAY" in parts) {
// TODO unimplemented in libical
} else if (partCount == 1 && "BYDAY" in parts) {
this.days = this.days.concat(this.expand_by_day(aYear));
} else if (partCount == 2 && "BYDAY" in parts && "BYMONTH" in parts) {
for (var monthkey in this.by_data.BYMONTH) {
/* istanbul ignore if */
if (!this.by_data.BYMONTH.hasOwnProperty(monthkey)) {
continue;
}
var month = this.by_data.BYMONTH[monthkey];
var daysInMonth = ICAL.Time.daysInMonth(month, aYear);
t.year = aYear;
t.month = this.by_data.BYMONTH[monthkey];
t.day = 1;
t.isDate = true;
var first_dow = t.dayOfWeek();
var doy_offset = t.dayOfYear() - 1;
t.day = daysInMonth;
var last_dow = t.dayOfWeek();
if (this.has_by_data("BYSETPOS")) {
var set_pos_counter = 0;
var by_month_day = [];
for (var day = 1; day <= daysInMonth; day++) {
t.day = day;
if (this.is_day_in_byday(t)) {
by_month_day.push(day);
}
}
for (var spIndex = 0; spIndex < by_month_day.length; spIndex++) {
if (this.check_set_position(spIndex + 1) ||
this.check_set_position(spIndex - by_month_day.length)) {
this.days.push(doy_offset + by_month_day[spIndex]);
}
}
} else {
for (var daycodedkey in this.by_data.BYDAY) {
/* istanbul ignore if */
if (!this.by_data.BYDAY.hasOwnProperty(daycodedkey)) {
continue;
}
var coded_day = this.by_data.BYDAY[daycodedkey];
var bydayParts = this.ruleDayOfWeek(coded_day);
var pos = bydayParts[0];
var dow = bydayParts[1];
var month_day;
var first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
var last_matching_day = daysInMonth - ((last_dow + 7 - dow) % 7);
if (pos == 0) {
for (var day = first_matching_day; day <= daysInMonth; day += 7) {
this.days.push(doy_offset + day);
}
} else if (pos > 0) {
month_day = first_matching_day + (pos - 1) * 7;
if (month_day <= daysInMonth) {
this.days.push(doy_offset + month_day);
}
} else {
month_day = last_matching_day + (pos + 1) * 7;
if (month_day > 0) {
this.days.push(doy_offset + month_day);
}
}
}
}
}
// Return dates in order of occurrence (1,2,3,...) instead
// of by groups of weekdays (1,8,15,...,2,9,16,...).
this.days.sort(function(a, b) { return a - b; }); // Comparator function allows to sort numbers.
} else if (partCount == 2 && "BYDAY" in parts && "BYMONTHDAY" in parts) {
var expandedDays = this.expand_by_day(aYear);
for (var daykey in expandedDays) {
/* istanbul ignore if */
if (!expandedDays.hasOwnProperty(daykey)) {
continue;
}
var day = expandedDays[daykey];
var tt = ICAL.Time.fromDayOfYear(day, aYear);
if (this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) {
this.days.push(day);
}
}
} else if (partCount == 3 &&
"BYDAY" in parts &&
"BYMONTHDAY" in parts &&
"BYMONTH" in parts) {
var expandedDays = this.expand_by_day(aYear);
for (var daykey in expandedDays) {
/* istanbul ignore if */
if (!expandedDays.hasOwnProperty(daykey)) {
continue;
}
var day = expandedDays[daykey];
var tt = ICAL.Time.fromDayOfYear(day, aYear);
if (this.by_data.BYMONTH.indexOf(tt.month) >= 0 &&
this.by_data.BYMONTHDAY.indexOf(tt.day) >= 0) {
this.days.push(day);
}
}
} else if (partCount == 2 && "BYDAY" in parts && "BYWEEKNO" in parts) {
var expandedDays = this.expand_by_day(aYear);
for (var daykey in expandedDays) {
/* istanbul ignore if */
if (!expandedDays.hasOwnProperty(daykey)) {
continue;
}
var day = expandedDays[daykey];
var tt = ICAL.Time.fromDayOfYear(day, aYear);
var weekno = tt.weekNumber(this.rule.wkst);
if (this.by_data.BYWEEKNO.indexOf(weekno)) {
this.days.push(day);
}
}
} else if (partCount == 3 &&
"BYDAY" in parts &&
"BYWEEKNO" in parts &&
"BYMONTHDAY" in parts) {
// TODO unimplemted in libical
} else if (partCount == 1 && "BYYEARDAY" in parts) {
this.days = this.days.concat(this.by_data.BYYEARDAY);
} else {
this.days = [];
}
return 0;
},
expand_by_day: function expand_by_day(aYear) {
var days_list = [];
var tmp = this.last.clone();
tmp.year = aYear;
tmp.month = 1;
tmp.day = 1;
tmp.isDate = true;
var start_dow = tmp.dayOfWeek();
tmp.month = 12;
tmp.day = 31;
tmp.isDate = true;
var end_dow = tmp.dayOfWeek();
var end_year_day = tmp.dayOfYear();
for (var daykey in this.by_data.BYDAY) {
/* istanbul ignore if */
if (!this.by_data.BYDAY.hasOwnProperty(daykey)) {
continue;
}
var day = this.by_data.BYDAY[daykey];
var parts = this.ruleDayOfWeek(day);
var pos = parts[0];
var dow = parts[1];
if (pos == 0) {
var tmp_start_doy = ((dow + 7 - start_dow) % 7) + 1;
for (var doy = tmp_start_doy; doy <= end_year_day; doy += 7) {
days_list.push(doy);
}
} else if (pos > 0) {
var first;
if (dow >= start_dow) {
first = dow - start_dow + 1;
} else {
first = dow - start_dow + 8;
}
days_list.push(first + (pos - 1) * 7);
} else {
var last;
pos = -pos;
if (dow <= end_dow) {
last = end_year_day - end_dow + dow;
} else {
last = end_year_day - end_dow + dow - 7;
}
days_list.push(last - (pos - 1) * 7);
}
}
return days_list;
},
is_day_in_byday: function is_day_in_byday(tt) {
for (var daykey in this.by_data.BYDAY) {
/* istanbul ignore if */
if (!this.by_data.BYDAY.hasOwnProperty(daykey)) {
continue;
}
var day = this.by_data.BYDAY[daykey];
var parts = this.ruleDayOfWeek(day);
var pos = parts[0];
var dow = parts[1];
var this_dow = tt.dayOfWeek();
if ((pos == 0 && dow == this_dow) ||
(tt.nthWeekDay(dow, pos) == tt.day)) {
return 1;
}
}
return 0;
},
/**
* Checks if given value is in BYSETPOS.
*
* @private
* @param {Numeric} aPos position to check for.
* @return {Boolean} false unless BYSETPOS rules exist
* and the given value is present in rules.
*/
check_set_position: function check_set_position(aPos) {
if (this.has_by_data('BYSETPOS')) {
var idx = this.by_data.BYSETPOS.indexOf(aPos);
// negative numbers are not false-y
return idx !== -1;
}
return false;
},
sort_byday_rules: function icalrecur_sort_byday_rules(aRules) {
for (var i = 0; i < aRules.length; i++) {
for (var j = 0; j < i; j++) {
var one = this.ruleDayOfWeek(aRules[j], this.rule.wkst)[1];
var two = this.ruleDayOfWeek(aRules[i], this.rule.wkst)[1];
if (one > two) {
var tmp = aRules[i];
aRules[i] = aRules[j];
aRules[j] = tmp;
}
}
}
},
check_contract_restriction: function check_contract_restriction(aRuleType, v) {
var indexMapValue = icalrecur_iterator._indexMap[aRuleType];
var ruleMapValue = icalrecur_iterator._expandMap[this.rule.freq][indexMapValue];
var pass = false;
if (aRuleType in this.by_data &&
ruleMapValue == icalrecur_iterator.CONTRACT) {
var ruleType = this.by_data[aRuleType];
for (var bydatakey in ruleType) {
/* istanbul ignore else */
if (ruleType.hasOwnProperty(bydatakey)) {
if (ruleType[bydatakey] == v) {
pass = true;
break;
}
}
}
} else {
// Not a contracting byrule or has no data, test passes
pass = true;
}
return pass;
},
check_contracting_rules: function check_contracting_rules() {
var dow = this.last.dayOfWeek();
var weekNo = this.last.weekNumber(this.rule.wkst);
var doy = this.last.dayOfYear();
return (this.check_contract_restriction("BYSECOND", this.last.second) &&
this.check_contract_restriction("BYMINUTE", this.last.minute) &&
this.check_contract_restriction("BYHOUR", this.last.hour) &&
this.check_contract_restriction("BYDAY", ICAL.Recur.numericDayToIcalDay(dow)) &&
this.check_contract_restriction("BYWEEKNO", weekNo) &&
this.check_contract_restriction("BYMONTHDAY", this.last.day) &&
this.check_contract_restriction("BYMONTH", this.last.month) &&
this.check_contract_restriction("BYYEARDAY", doy));
},
setup_defaults: function setup_defaults(aRuleType, req, deftime) {
var indexMapValue = icalrecur_iterator._indexMap[aRuleType];
var ruleMapValue = icalrecur_iterator._expandMap[this.rule.freq][indexMapValue];
if (ruleMapValue != icalrecur_iterator.CONTRACT) {
if (!(aRuleType in this.by_data)) {
this.by_data[aRuleType] = [deftime];
}
if (this.rule.freq != req) {
return this.by_data[aRuleType][0];
}
}
return deftime;
},
/**
* Convert iterator into a serialize-able object. Will preserve current
* iteration sequence to ensure the seamless continuation of the recurrence
* rule.
* @return {Object}
*/
toJSON: function() {
var result = Object.create(null);
result.initialized = this.initialized;
result.rule = this.rule.toJSON();
result.dtstart = this.dtstart.toJSON();
result.by_data = this.by_data;
result.days = this.days;
result.last = this.last.toJSON();
result.by_indices = this.by_indices;
result.occurrence_number = this.occurrence_number;
return result;
}
};
icalrecur_iterator._indexMap = {
"BYSECOND": 0,
"BYMINUTE": 1,
"BYHOUR": 2,
"BYDAY": 3,
"BYMONTHDAY": 4,
"BYYEARDAY": 5,
"BYWEEKNO": 6,
"BYMONTH": 7,
"BYSETPOS": 8
};
icalrecur_iterator._expandMap = {
"SECONDLY": [1, 1, 1, 1, 1, 1, 1, 1],
"MINUTELY": [2, 1, 1, 1, 1, 1, 1, 1],
"HOURLY": [2, 2, 1, 1, 1, 1, 1, 1],
"DAILY": [2, 2, 2, 1, 1, 1, 1, 1],
"WEEKLY": [2, 2, 2, 2, 3, 3, 1, 1],
"MONTHLY": [2, 2, 2, 2, 2, 3, 3, 1],
"YEARLY": [2, 2, 2, 2, 2, 2, 2, 2]
};
icalrecur_iterator.UNKNOWN = 0;
icalrecur_iterator.CONTRACT = 1;
icalrecur_iterator.EXPAND = 2;
icalrecur_iterator.ILLEGAL = 3;
return icalrecur_iterator;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.RecurExpansion = (function() {
function formatTime(item) {
return ICAL.helpers.formatClassType(item, ICAL.Time);
}
function compareTime(a, b) {
return a.compare(b);
}
function isRecurringComponent(comp) {
return comp.hasProperty('rdate') ||
comp.hasProperty('rrule') ||
comp.hasProperty('recurrence-id');
}
/**
* @classdesc
* Primary class for expanding recurring rules. Can take multiple rrules,
* rdates, exdate(s) and iterate (in order) over each next occurrence.
*
* Once initialized this class can also be serialized saved and continue
* iteration from the last point.
*
* NOTE: it is intended that this class is to be used
* with ICAL.Event which handles recurrence exceptions.
*
* @example
* // assuming event is a parsed ical component
* var event;
*
* var expand = new ICAL.RecurExpansion({
* component: event,
* dtstart: event.getFirstPropertyValue('dtstart')
* });
*
* // remember there are infinite rules
* // so it is a good idea to limit the scope
* // of the iterations then resume later on.
*
* // next is always an ICAL.Time or null
* var next;
*
* while (someCondition && (next = expand.next())) {
* // do something with next
* }
*
* // save instance for later
* var json = JSON.stringify(expand);
*
* //...
*
* // NOTE: if the component's properties have
* // changed you will need to rebuild the
* // class and start over. This only works
* // when the component's recurrence info is the same.
* var expand = new ICAL.RecurExpansion(JSON.parse(json));
*
* @description
* The options object can be filled with the specified initial values. It can
* also contain additional members, as a result of serializing a previous
* expansion state, as shown in the example.
*
* @class
* @alias ICAL.RecurExpansion
* @param {Object} options
* Recurrence expansion options
* @param {ICAL.Time} options.dtstart
* Start time of the event
* @param {ICAL.Component=} options.component
* Component for expansion, required if not resuming.
*/
function RecurExpansion(options) {
this.ruleDates = [];
this.exDates = [];
this.fromData(options);
}
RecurExpansion.prototype = {
/**
* True when iteration is fully completed.
* @type {Boolean}
*/
complete: false,
/**
* Array of rrule iterators.
*
* @type {ICAL.RecurIterator[]}
* @private
*/
ruleIterators: null,
/**
* Array of rdate instances.
*
* @type {ICAL.Time[]}
* @private
*/
ruleDates: null,
/**
* Array of exdate instances.
*
* @type {ICAL.Time[]}
* @private
*/
exDates: null,
/**
* Current position in ruleDates array.
* @type {Number}
* @private
*/
ruleDateInc: 0,
/**
* Current position in exDates array
* @type {Number}
* @private
*/
exDateInc: 0,
/**
* Current negative date.
*
* @type {ICAL.Time}
* @private
*/
exDate: null,
/**
* Current additional date.
*
* @type {ICAL.Time}
* @private
*/
ruleDate: null,
/**
* Start date of recurring rules.
*
* @type {ICAL.Time}
*/
dtstart: null,
/**
* Last expanded time
*
* @type {ICAL.Time}
*/
last: null,
/**
* Initialize the recurrence expansion from the data object. The options
* object may also contain additional members, see the
* {@link ICAL.RecurExpansion constructor} for more details.
*
* @param {Object} options
* Recurrence expansion options
* @param {ICAL.Time} options.dtstart
* Start time of the event
* @param {ICAL.Component=} options.component
* Component for expansion, required if not resuming.
*/
fromData: function(options) {
var start = ICAL.helpers.formatClassType(options.dtstart, ICAL.Time);
if (!start) {
throw new Error('.dtstart (ICAL.Time) must be given');
} else {
this.dtstart = start;
}
if (options.component) {
this._init(options.component);
} else {
this.last = formatTime(options.last) || start.clone();
if (!options.ruleIterators) {
throw new Error('.ruleIterators or .component must be given');
}
this.ruleIterators = options.ruleIterators.map(function(item) {
return ICAL.helpers.formatClassType(item, ICAL.RecurIterator);
});
this.ruleDateInc = options.ruleDateInc;
this.exDateInc = options.exDateInc;
if (options.ruleDates) {
this.ruleDates = options.ruleDates.map(formatTime);
this.ruleDate = this.ruleDates[this.ruleDateInc];
}
if (options.exDates) {
this.exDates = options.exDates.map(formatTime);
this.exDate = this.exDates[this.exDateInc];
}
if (typeof(options.complete) !== 'undefined') {
this.complete = options.complete;
}
}
},
/**
* Retrieve the next occurrence in the series.
* @return {ICAL.Time}
*/
next: function() {
var iter;
var ruleOfDay;
var next;
var compare;
var maxTries = 500;
var currentTry = 0;
while (true) {
if (currentTry++ > maxTries) {
throw new Error(
'max tries have occured, rule may be impossible to forfill.'
);
}
next = this.ruleDate;
iter = this._nextRecurrenceIter(this.last);
// no more matches
// because we increment the rule day or rule
// _after_ we choose a value this should be
// the only spot where we need to worry about the
// end of events.
if (!next && !iter) {
// there are no more iterators or rdates
this.complete = true;
break;
}
// no next rule day or recurrence rule is first.
if (!next || (iter && next.compare(iter.last) > 0)) {
// must be cloned, recur will reuse the time element.
next = iter.last.clone();
// move to next so we can continue
iter.next();
}
// if the ruleDate is still next increment it.
if (this.ruleDate === next) {
this._nextRuleDay();
}
this.last = next;
// check the negative rules
if (this.exDate) {
compare = this.exDate.compare(this.last);
if (compare < 0) {
this._nextExDay();
}
// if the current rule is excluded skip it.
if (compare === 0) {
this._nextExDay();
continue;
}
}
//XXX: The spec states that after we resolve the final
// list of dates we execute exdate this seems somewhat counter
// intuitive to what I have seen most servers do so for now
// I exclude based on the original date not the one that may
// have been modified by the exception.
return this.last;
}
},
/**
* Converts object into a serialize-able format. This format can be passed
* back into the expansion to resume iteration.
* @return {Object}
*/
toJSON: function() {
function toJSON(item) {
return item.toJSON();
}
var result = Object.create(null);
result.ruleIterators = this.ruleIterators.map(toJSON);
if (this.ruleDates) {
result.ruleDates = this.ruleDates.map(toJSON);
}
if (this.exDates) {
result.exDates = this.exDates.map(toJSON);
}
result.ruleDateInc = this.ruleDateInc;
result.exDateInc = this.exDateInc;
result.last = this.last.toJSON();
result.dtstart = this.dtstart.toJSON();
result.complete = this.complete;
return result;
},
/**
* Extract all dates from the properties in the given component. The
* properties will be filtered by the property name.
*
* @private
* @param {ICAL.Component} component The component to search in
* @param {String} propertyName The property name to search for
* @return {ICAL.Time[]} The extracted dates.
*/
_extractDates: function(component, propertyName) {
function handleProp(prop) {
idx = ICAL.helpers.binsearchInsert(
result,
prop,
compareTime
);
// ordered insert
result.splice(idx, 0, prop);
}
var result = [];
var props = component.getAllProperties(propertyName);
var len = props.length;
var i = 0;
var prop;
var idx;
for (; i < len; i++) {
props[i].getValues().forEach(handleProp);
}
return result;
},
/**
* Initialize the recurrence expansion.
*
* @private
* @param {ICAL.Component} component The component to initialize from.
*/
_init: function(component) {
this.ruleIterators = [];
this.last = this.dtstart.clone();
// to provide api consistency non-recurring
// events can also use the iterator though it will
// only return a single time.
if (!isRecurringComponent(component)) {
this.ruleDate = this.last.clone();
this.complete = true;
return;
}
if (component.hasProperty('rdate')) {
this.ruleDates = this._extractDates(component, 'rdate');
// special hack for cases where first rdate is prior
// to the start date. We only check for the first rdate.
// This is mostly for google's crazy recurring date logic
// (contacts birthdays).
if ((this.ruleDates[0]) &&
(this.ruleDates[0].compare(this.dtstart) < 0)) {
this.ruleDateInc = 0;
this.last = this.ruleDates[0].clone();
} else {
this.ruleDateInc = ICAL.helpers.binsearchInsert(
this.ruleDates,
this.last,
compareTime
);
}
this.ruleDate = this.ruleDates[this.ruleDateInc];
}
if (component.hasProperty('rrule')) {
var rules = component.getAllProperties('rrule');
var i = 0;
var len = rules.length;
var rule;
var iter;
for (; i < len; i++) {
rule = rules[i].getFirstValue();
iter = rule.iterator(this.dtstart);
this.ruleIterators.push(iter);
// increment to the next occurrence so future
// calls to next return times beyond the initial iteration.
// XXX: I find this suspicious might be a bug?
iter.next();
}
}
if (component.hasProperty('exdate')) {
this.exDates = this._extractDates(component, 'exdate');
// if we have a .last day we increment the index to beyond it.
this.exDateInc = ICAL.helpers.binsearchInsert(
this.exDates,
this.last,
compareTime
);
this.exDate = this.exDates[this.exDateInc];
}
},
/**
* Advance to the next exdate
* @private
*/
_nextExDay: function() {
this.exDate = this.exDates[++this.exDateInc];
},
/**
* Advance to the next rule date
* @private
*/
_nextRuleDay: function() {
this.ruleDate = this.ruleDates[++this.ruleDateInc];
},
/**
* Find and return the recurrence rule with the most recent event and
* return it.
*
* @private
* @return {?ICAL.RecurIterator} Found iterator.
*/
_nextRecurrenceIter: function() {
var iters = this.ruleIterators;
if (iters.length === 0) {
return null;
}
var len = iters.length;
var iter;
var iterTime;
var iterIdx = 0;
var chosenIter;
// loop through each iterator
for (; iterIdx < len; iterIdx++) {
iter = iters[iterIdx];
iterTime = iter.last;
// if iteration is complete
// then we must exclude it from
// the search and remove it.
if (iter.completed) {
len--;
if (iterIdx !== 0) {
iterIdx--;
}
iters.splice(iterIdx, 1);
continue;
}
// find the most recent possible choice
if (!chosenIter || chosenIter.last.compare(iterTime) > 0) {
// that iterator is saved
chosenIter = iter;
}
}
// the chosen iterator is returned but not mutated
// this iterator contains the most recent event.
return chosenIter;
}
};
return RecurExpansion;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.Event = (function() {
/**
* @classdesc
* ICAL.js is organized into multiple layers. The bottom layer is a raw jCal
* object, followed by the component/property layer. The highest level is the
* event representation, which this class is part of. See the
* {@tutorial layers} guide for more details.
*
* @class
* @alias ICAL.Event
* @param {ICAL.Component=} component The ICAL.Component to base this event on
* @param {Object} options Options for this event
* @param {Boolean} options.strictExceptions
* When true, will verify exceptions are related by their UUID
* @param {Array<ICAL.Component|ICAL.Event>} options.exceptions
* Exceptions to this event, either as components or events. If not
* specified exceptions will automatically be set in relation of
* component's parent
*/
function Event(component, options) {
if (!(component instanceof ICAL.Component)) {
options = component;
component = null;
}
if (component) {
this.component = component;
} else {
this.component = new ICAL.Component('vevent');
}
this._rangeExceptionCache = Object.create(null);
this.exceptions = Object.create(null);
this.rangeExceptions = [];
if (options && options.strictExceptions) {
this.strictExceptions = options.strictExceptions;
}
if (options && options.exceptions) {
options.exceptions.forEach(this.relateException, this);
} else if (this.component.parent && !this.isRecurrenceException()) {
this.component.parent.getAllSubcomponents('vevent').forEach(function(event) {
if (event.hasProperty('recurrence-id')) {
this.relateException(event);
}
}, this);
}
}
Event.prototype = {
THISANDFUTURE: 'THISANDFUTURE',
/**
* List of related event exceptions.
*
* @type {ICAL.Event[]}
*/
exceptions: null,
/**
* When true, will verify exceptions are related by their UUID.
*
* @type {Boolean}
*/
strictExceptions: false,
/**
* Relates a given event exception to this object. If the given component
* does not share the UID of this event it cannot be related and will throw
* an exception.
*
* If this component is an exception it cannot have other exceptions
* related to it.
*
* @param {ICAL.Component|ICAL.Event} obj Component or event
*/
relateException: function(obj) {
if (this.isRecurrenceException()) {
throw new Error('cannot relate exception to exceptions');
}
if (obj instanceof ICAL.Component) {
obj = new ICAL.Event(obj);
}
if (this.strictExceptions && obj.uid !== this.uid) {
throw new Error('attempted to relate unrelated exception');
}
var id = obj.recurrenceId.toString();
// we don't sort or manage exceptions directly
// here the recurrence expander handles that.
this.exceptions[id] = obj;
// index RANGE=THISANDFUTURE exceptions so we can
// look them up later in getOccurrenceDetails.
if (obj.modifiesFuture()) {
var item = [
obj.recurrenceId.toUnixTime(), id
];
// we keep them sorted so we can find the nearest
// value later on...
var idx = ICAL.helpers.binsearchInsert(
this.rangeExceptions,
item,
compareRangeException
);
this.rangeExceptions.splice(idx, 0, item);
}
},
/**
* Checks if this record is an exception and has the RANGE=THISANDFUTURE
* value.
*
* @return {Boolean} True, when exception is within range
*/
modifiesFuture: function() {
if (!this.component.hasProperty('recurrence-id')) {
return false;
}
var range = this.component.getFirstProperty('recurrence-id').getParameter('range');
return range === this.THISANDFUTURE;
},
/**
* Finds the range exception nearest to the given date.
*
* @param {ICAL.Time} time usually an occurrence time of an event
* @return {?ICAL.Event} the related event/exception or null
*/
findRangeException: function(time) {
if (!this.rangeExceptions.length) {
return null;
}
var utc = time.toUnixTime();
var idx = ICAL.helpers.binsearchInsert(
this.rangeExceptions,
[utc],
compareRangeException
);
idx -= 1;
// occurs before
if (idx < 0) {
return null;
}
var rangeItem = this.rangeExceptions[idx];
/* istanbul ignore next: sanity check only */
if (utc < rangeItem[0]) {
return null;
}
return rangeItem[1];
},
/**
* This object is returned by {@link ICAL.Event#getOccurrenceDetails getOccurrenceDetails}
*
* @typedef {Object} occurrenceDetails
* @memberof ICAL.Event
* @property {ICAL.Time} recurrenceId The passed in recurrence id
* @property {ICAL.Event} item The occurrence
* @property {ICAL.Time} startDate The start of the occurrence
* @property {ICAL.Time} endDate The end of the occurrence
*/
/**
* Returns the occurrence details based on its start time. If the
* occurrence has an exception will return the details for that exception.
*
* NOTE: this method is intend to be used in conjunction
* with the {@link ICAL.Event#iterator iterator} method.
*
* @param {ICAL.Time} occurrence time occurrence
* @return {ICAL.Event.occurrenceDetails} Information about the occurrence
*/
getOccurrenceDetails: function(occurrence) {
var id = occurrence.toString();
var utcId = occurrence.convertToZone(ICAL.Timezone.utcTimezone).toString();
var item;
var result = {
//XXX: Clone?
recurrenceId: occurrence
};
if (id in this.exceptions) {
item = result.item = this.exceptions[id];
result.startDate = item.startDate;
result.endDate = item.endDate;
result.item = item;
} else if (utcId in this.exceptions) {
item = this.exceptions[utcId];
result.startDate = item.startDate;
result.endDate = item.endDate;
result.item = item;
} else {
// range exceptions (RANGE=THISANDFUTURE) have a
// lower priority then direct exceptions but
// must be accounted for first. Their item is
// always the first exception with the range prop.
var rangeExceptionId = this.findRangeException(
occurrence
);
var end;
if (rangeExceptionId) {
var exception = this.exceptions[rangeExceptionId];
// range exception must modify standard time
// by the difference (if any) in start/end times.
result.item = exception;
var startDiff = this._rangeExceptionCache[rangeExceptionId];
if (!startDiff) {
var original = exception.recurrenceId.clone();
var newStart = exception.startDate.clone();
// zones must be same otherwise subtract may be incorrect.
original.zone = newStart.zone;
startDiff = newStart.subtractDate(original);
this._rangeExceptionCache[rangeExceptionId] = startDiff;
}
var start = occurrence.clone();
start.zone = exception.startDate.zone;
start.addDuration(startDiff);
end = start.clone();
end.addDuration(exception.duration);
result.startDate = start;
result.endDate = end;
} else {
// no range exception standard expansion
end = occurrence.clone();
end.addDuration(this.duration);
result.endDate = end;
result.startDate = occurrence;
result.item = this;
}
}
return result;
},
/**
* Builds a recur expansion instance for a specific point in time (defaults
* to startDate).
*
* @param {ICAL.Time} startTime Starting point for expansion
* @return {ICAL.RecurExpansion} Expansion object
*/
iterator: function(startTime) {
return new ICAL.RecurExpansion({
component: this.component,
dtstart: startTime || this.startDate
});
},
/**
* Checks if the event is recurring
*
* @return {Boolean} True, if event is recurring
*/
isRecurring: function() {
var comp = this.component;
return comp.hasProperty('rrule') || comp.hasProperty('rdate');
},
/**
* Checks if the event describes a recurrence exception. See
* {@tutorial terminology} for details.
*
* @return {Boolean} True, if the event describes a recurrence exception
*/
isRecurrenceException: function() {
return this.component.hasProperty('recurrence-id');
},
/**
* Returns the types of recurrences this event may have.
*
* Returned as an object with the following possible keys:
*
* - YEARLY
* - MONTHLY
* - WEEKLY
* - DAILY
* - MINUTELY
* - SECONDLY
*
* @return {Object.<ICAL.Recur.frequencyValues, Boolean>}
* Object of recurrence flags
*/
getRecurrenceTypes: function() {
var rules = this.component.getAllProperties('rrule');
var i = 0;
var len = rules.length;
var result = Object.create(null);
for (; i < len; i++) {
var value = rules[i].getFirstValue();
result[value.freq] = true;
}
return result;
},
/**
* The uid of this event
* @type {String}
*/
get uid() {
return this._firstProp('uid');
},
set uid(value) {
this._setProp('uid', value);
},
/**
* The start date
* @type {ICAL.Time}
*/
get startDate() {
return this._firstProp('dtstart');
},
set startDate(value) {
this._setTime('dtstart', value);
},
/**
* The end date. This can be the result directly from the property, or the
* end date calculated from start date and duration. Setting the property
* will remove any duration properties.
* @type {ICAL.Time}
*/
get endDate() {
var endDate = this._firstProp('dtend');
if (!endDate) {
var duration = this._firstProp('duration');
endDate = this.startDate.clone();
if (duration) {
endDate.addDuration(duration);
} else if (endDate.isDate) {
endDate.day += 1;
}
}
return endDate;
},
set endDate(value) {
if (this.component.hasProperty('duration')) {
this.component.removeProperty('duration');
}
this._setTime('dtend', value);
},
/**
* The duration. This can be the result directly from the property, or the
* duration calculated from start date and end date. Setting the property
* will remove any `dtend` properties.
* @type {ICAL.Duration}
*/
get duration() {
var duration = this._firstProp('duration');
if (!duration) {
return this.endDate.subtractDateTz(this.startDate);
}
return duration;
},
set duration(value) {
if (this.component.hasProperty('dtend')) {
this.component.removeProperty('dtend');
}
this._setProp('duration', value);
},
/**
* The location of the event.
* @type {String}
*/
get location() {
return this._firstProp('location');
},
set location(value) {
return this._setProp('location', value);
},
/**
* The attendees in the event
* @type {ICAL.Property[]}
* @readonly
*/
get attendees() {
//XXX: This is way lame we should have a better
// data structure for this later.
return this.component.getAllProperties('attendee');
},
/**
* The event summary
* @type {String}
*/
get summary() {
return this._firstProp('summary');
},
set summary(value) {
this._setProp('summary', value);
},
/**
* The event description.
* @type {String}
*/
get description() {
return this._firstProp('description');
},
set description(value) {
this._setProp('description', value);
},
/**
* The event color from [rfc7986](https://datatracker.ietf.org/doc/html/rfc7986)
* @type {String}
*/
get color() {
return this._firstProp('color');
},
set color(value) {
this._setProp('color', value);
},
/**
* The organizer value as an uri. In most cases this is a mailto: uri, but
* it can also be something else, like urn:uuid:...
* @type {String}
*/
get organizer() {
return this._firstProp('organizer');
},
set organizer(value) {
this._setProp('organizer', value);
},
/**
* The sequence value for this event. Used for scheduling
* see {@tutorial terminology}.
* @type {Number}
*/
get sequence() {
return this._firstProp('sequence');
},
set sequence(value) {
this._setProp('sequence', value);
},
/**
* The recurrence id for this event. See {@tutorial terminology} for details.
* @type {ICAL.Time}
*/
get recurrenceId() {
return this._firstProp('recurrence-id');
},
set recurrenceId(value) {
this._setTime('recurrence-id', value);
},
/**
* Set/update a time property's value.
* This will also update the TZID of the property.
*
* TODO: this method handles the case where we are switching
* from a known timezone to an implied timezone (one without TZID).
* This does _not_ handle the case of moving between a known
* (by TimezoneService) timezone to an unknown timezone...
*
* We will not add/remove/update the VTIMEZONE subcomponents
* leading to invalid ICAL data...
* @private
* @param {String} propName The property name
* @param {ICAL.Time} time The time to set
*/
_setTime: function(propName, time) {
var prop = this.component.getFirstProperty(propName);
if (!prop) {
prop = new ICAL.Property(propName);
this.component.addProperty(prop);
}
// utc and local don't get a tzid
if (
time.zone === ICAL.Timezone.localTimezone ||
time.zone === ICAL.Timezone.utcTimezone
) {
// remove the tzid
prop.removeParameter('tzid');
} else {
prop.setParameter('tzid', time.zone.tzid);
}
prop.setValue(time);
},
_setProp: function(name, value) {
this.component.updatePropertyWithValue(name, value);
},
_firstProp: function(name) {
return this.component.getFirstPropertyValue(name);
},
/**
* The string representation of this event.
* @return {String}
*/
toString: function() {
return this.component.toString();
}
};
function compareRangeException(a, b) {
if (a[0] > b[0]) return 1;
if (b[0] > a[0]) return -1;
return 0;
}
return Event;
}());
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Portions Copyright (C) Philipp Kewisch, 2011-2015 */
/**
* This symbol is further described later on
* @ignore
*/
ICAL.ComponentParser = (function() {
/**
* @classdesc
* The ComponentParser is used to process a String or jCal Object,
* firing callbacks for various found components, as well as completion.
*
* @example
* var options = {
* // when false no events will be emitted for type
* parseEvent: true,
* parseTimezone: true
* };
*
* var parser = new ICAL.ComponentParser(options);
*
* parser.onevent(eventComponent) {
* //...
* }
*
* // ontimezone, etc...
*
* parser.oncomplete = function() {
*
* };
*
* parser.process(stringOrComponent);
*
* @class
* @alias ICAL.ComponentParser
* @param {Object=} options Component parser options
* @param {Boolean} options.parseEvent Whether events should be parsed
* @param {Boolean} options.parseTimezeone Whether timezones should be parsed
*/
function ComponentParser(options) {
if (typeof(options) === 'undefined') {
options = {};
}
var key;
for (key in options) {
/* istanbul ignore else */
if (options.hasOwnProperty(key)) {
this[key] = options[key];
}
}
}
ComponentParser.prototype = {
/**
* When true, parse events
*
* @type {Boolean}
*/
parseEvent: true,
/**
* When true, parse timezones
*
* @type {Boolean}
*/
parseTimezone: true,
/* SAX like events here for reference */
/**
* Fired when parsing is complete
* @callback
*/
oncomplete: /* istanbul ignore next */ function() {},
/**
* Fired if an error occurs during parsing.
*
* @callback
* @param {Error} err details of error
*/
onerror: /* istanbul ignore next */ function(err) {},
/**
* Fired when a top level component (VTIMEZONE) is found
*
* @callback
* @param {ICAL.Timezone} component Timezone object
*/
ontimezone: /* istanbul ignore next */ function(component) {},
/**
* Fired when a top level component (VEVENT) is found.
*
* @callback
* @param {ICAL.Event} component Top level component
*/
onevent: /* istanbul ignore next */ function(component) {},
/**
* Process a string or parse ical object. This function itself will return
* nothing but will start the parsing process.
*
* Events must be registered prior to calling this method.
*
* @param {ICAL.Component|String|Object} ical The component to process,
* either in its final form, as a jCal Object, or string representation
*/
process: function(ical) {
//TODO: this is sync now in the future we will have a incremental parser.
if (typeof(ical) === 'string') {
ical = ICAL.parse(ical);
}
if (!(ical instanceof ICAL.Component)) {
ical = new ICAL.Component(ical);
}
var components = ical.getAllSubcomponents();
var i = 0;
var len = components.length;
var component;
for (; i < len; i++) {
component = components[i];
switch (component.name) {
case 'vtimezone':
if (this.parseTimezone) {
var tzid = component.getFirstPropertyValue('tzid');
if (tzid) {
this.ontimezone(new ICAL.Timezone({
tzid: tzid,
component: component
}));
}
}
break;
case 'vevent':
if (this.parseEvent) {
this.onevent(new ICAL.Event(component));
}
break;
default:
continue;
}
}
//XXX: ideally we should do a "nextTick" here
// so in all cases this is actually async.
this.oncomplete();
}
};
return ComponentParser;
}());
//}}}
//{{{
(function () {
var code = eval("ICAL.Event.prototype.relateException").toString();
code = code.replace("throw new Error('attempted to relate unrelated exception')", "return");
eval("ICAL.Event.prototype.relateException = function " + code.substring(code.indexOf("(")));
})();
//}}}
{{{
<?php
$language = getDefaultLanguage();
if (strpos($language, 'fr') === 0) {
header("Location: http://SOME.SERVER/PUBLISHEDCALENDAR_fr.html?t=" . time());
} else {
header("Location: http://SOME.SERVER/PUBLISHEDCALENDAR.html?t=" . time());
}
#########################################################
# Copyright © 2008 Darrin Yeager #
# http://www.dyeager.org/ #
# Licensed under BSD license. #
# http://www.dyeager.org/downloads/license-bsd.php #
#########################################################
function getDefaultLanguage() {
if (isset($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
return parseDefaultLanguage($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
else
return parseDefaultLanguage(NULL);
}
function parseDefaultLanguage($http_accept, $deflang = "en") {
if(isset($http_accept) && strlen($http_accept) > 1) {
# Split possible languages into array
$x = explode(",",$http_accept);
foreach ($x as $val) {
#check for q-value and create associative array. No q-value means 1 by rule
if(preg_match("/(.*);q=([0-1]{0,1}\.\d{0,4})/i",$val,$matches))
$lang[$matches[1]] = (float)$matches[2];
else
$lang[$val] = 1.0;
}
#return default language (highest q-value)
$qval = 0.0;
foreach ($lang as $key => $value) {
if ($value > $qval) {
$qval = (float)$value;
$deflang = $key;
}
}
}
return strtolower($deflang);
}
// http://www.dyeager.org/blog/2008/10/getting-browser-default-language-php.html
?>
}}}
/***
|Name|jQuery Calculator|
|Source|http://keith-wood.name/zip/jquery.calculator.package-2.0.1.zip|
|Documentation|http://keith-wood.name/calculator.html|
|Version|2.0.1|
|License|[[MIT|http://keith-wood.name/licence.html]]|
|Description|A [[jQuery plugin|https://jquery.com]] that attaches a popup calculator to an input field|
***/
* [[init|##init]]
* [[jquery.calculator.css|##jquery.calculator.css]]
* [[jquery.calculator.js|##jquery.calculator.js]]
* [[jquery.plugin.js|##jquery.plugin.js]]
* [[jquery.calculator-fr.js|##jquery.calculator-fr.js]]
!init
//{{{
if (!jQuery.JQPlugin) {
eval(store.getTiddlerText('jQueryCalculator##jquery.plugin.js'));
}
if (!jQuery.calculator) {
var css = store.getTiddlerText('jQueryCalculator##jquery.calculator.css.start');
var highlightColour = store.getTiddlerSlice("ColorPalette", "SecondaryLight") || "#fe8";
css += "\n.calculator-button-highlight { background: " + highlightColour + " !important }";
setStylesheet(css, 'jquery.calculator.css');
var jQueryCalculator = store.getTiddlerText('jQueryCalculator##jquery.calculator.js');
var index;
index = jQueryCalculator.indexOf("var styles = ", jQueryCalculator.indexOf("_generateHTML: function(inst) {"));
jQueryCalculator = jQueryCalculator.substring(0, index) + "var styles = []; try { styles = def[3] ? (!!( def[3].constructor && def[3].call && def[3].apply) ? ('' + def[3](inst)).split(' ') : def[3].split(' ')) : styles; } catch (e) {}" + jQueryCalculator.substring(jQueryCalculator.indexOf(";", index));
var calculateHandler = "\
_calculate: function (inst, expression) {\
if (expression) {\
var categorizedTokens = [];\
for (var code in this._keyDefs) {\
var keyDef = this._keyDefs[code];\
\
var label = keyDef[0].charAt(0) == '#' ? inst.options[keyDef[0].substr(1) + 'Text'] : keyDef[0];\
label = label == '.' ? inst.options.decimalChar : label;\
if (label) {\
var tokens = categorizedTokens[label.length];\
if (!tokens) {\
tokens = [];\
categorizedTokens[label.length] = tokens;\
}\
tokens[tokens.length] = {token: label.toLowerCase(), label: label, keyDef: keyDef};\
}\
\
var keyName = keyDef[6] ? keyDef[6] : keyDef[5];\
if (keyName) {\
var tokens = categorizedTokens[keyName.length];\
if (!tokens) {\
tokens = [];\
categorizedTokens[keyName.length] = tokens;\
}\
tokens[tokens.length] = {token: keyName, label: label, keyDef: keyDef};\
}\
}\
\
var tokens = [];\
for (var i = categorizedTokens.length - 1; i >= 0; i--) {\
if (categorizedTokens[i]) {\
tokens = tokens.concat(categorizedTokens[i]);\
}\
}\
\
expression = ('' + expression).toLowerCase();\
\
for (var i = 0; i < expression.length; i++) {\
for (var j = 0; j < tokens.length; j++) {\
if (expression.indexOf(tokens[j].token, i) == i) {\
var keyDef = tokens[j].keyDef;\
var label = tokens[j].label;\
\
inst._stacks = inst._stacks ? inst._stacks : {sequence: [], operatorSequence: [], calculation: [{number: [], operator: []}]};\
inst._stacks.sequence[inst._stacks.sequence.length] = keyDef;\
if ((keyDef[1] == this.binary) || (keyDef[1] == this.unary)) {\
inst._stacks.operatorSequence[inst._stacks.operatorSequence.length] = keyDef;\
}\
\
switch (keyDef[1]) {\
case this.control:\
keyDef[2].apply(this, [inst, label]);\
break;\
case this.digit:\
this._digit(inst, label);\
break;\
case this.binary:\
this._binaryOp(inst, keyDef[2], label);\
break;\
case this.unary:\
this._unaryOp(inst, keyDef[2], label);\
break;\
}\
\
i += tokens[j].token.length - 1;\
\
break;\
}\
}\
}\
}\
},\n";
index = jQueryCalculator.indexOf("},", jQueryCalculator.indexOf("_handleButton: function(inst, button) {")) + "},".length + 1;
jQueryCalculator = jQueryCalculator.substring(0, index) + calculateHandler + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf("_doKeyPress: function(e) {");
jQueryCalculator = jQueryCalculator.substring(0, index + "_doKeyPress: function(e) {".length) + "\nif (plugin._ctrlPressed) return true;\n" + jQueryCalculator.substring(index + "_doKeyPress: function(e) {".length);
var pasteHandler = "\
_doPaste: function(e) {\
setTimeout(function() {\
var inst = plugin._getInst(e.target);\
var inputVal = inst._input.val();\
inst._input.val('');\
plugin._calculate(inst, inputVal);\
}, 100);\
},\n";
jQueryCalculator = jQueryCalculator.substring(0, index) + pasteHandler + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf("inst._input.on('keydown.' + inst.name, this._doKeyDown).") + "inst._input.on('keydown.' + inst.name, this._doKeyDown).".length;
jQueryCalculator = jQueryCalculator.substring(0, index) + "on('paste.' + inst.name, this._doPaste)." + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf("if (e.keyCode === 18)");
jQueryCalculator = jQueryCalculator.substring(0, index) + " if (e.keyCode === 17) { plugin._ctrlPressed = true; handled = true; } else " + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf("_doKeyUp: function(e) {") + "_doKeyUp: function(e) {".length;
jQueryCalculator = jQueryCalculator.substring(0, index) + "\nif (e.keyCode === 17) plugin._ctrlPressed = false;\n" + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf("switch ", jQueryCalculator.indexOf("_handleButton: function(inst, button) {"));
jQueryCalculator = jQueryCalculator.substring(0, index) + "inst._stacks = inst._stacks ? inst._stacks : {sequence: [], operatorSequence: [], calculation: [{number: [], operator: []}]}; inst._stacks.sequence[inst._stacks.sequence.length] = keyDef; if ((keyDef[1] == this.binary) || (keyDef[1] == this.unary)) { inst._stacks.operatorSequence[inst._stacks.operatorSequence.length] = keyDef; }\n" + jQueryCalculator.substring(index);
var binaryOpHandler = "\
_binaryOp: function(inst, op, label) {\
var stacks = inst._stacks;\
var calculation = stacks.calculation[stacks.calculation.length - 1];\
var keyDef = stacks.sequence[stacks.sequence.length - 1];\
\
var performOperation = false;\
for (var i = stacks.sequence.length - 2; i >= 0; i--) {\
if ((stacks.sequence[i][0] == '=') || (stacks.sequence[i][1] == this.binary)) {\
break;\
} else if (stacks.sequence[i][1] != this.control) {\
performOperation = true;\
break;\
}\
}\
\
if (performOperation) {\
calculation.number[calculation.number.length] = inst.curValue;\
\
if (calculation.operator.length) {\
var precedence = keyDef[7];\
var previousPrecedence = calculation.operator[calculation.operator.length - 1][7];\
\
while ((precedence === undefined) || (precedence === null) || (previousPrecedence === undefined) || (previousPrecedence === null) || (precedence <= previousPrecedence)) {\
inst.curValue = calculation.number.pop();\
inst.prevValue = calculation.number.pop();\
inst._pendingOp = calculation.operator.pop()[2];\
inst._pendingOp(inst);\
inst.curValue = (inst.options.base == 10 ? inst.curValue : Math.floor(inst.curValue));\
calculation.number[calculation.number.length] = inst.curValue;\
if (!calculation.number.length || !calculation.operator.length) {\
break;\
}\
previousPrecedence = calculation.operator[calculation.operator.length - 1][7];\
}\
}\
\
calculation.operator[calculation.operator.length] = keyDef;\
inst.dispValue = this._setDisplay(inst);\
} else {\
if (calculation.number.length) {\
calculation.operator[calculation.operator.length - (calculation.operator.length ? 1 : 0)] = keyDef;\
}\
calculation.number[calculation.number.length - (calculation.number.length ? 1 : 0)] = inst.curValue;\
}\
\
inst.prevValue = inst.curValue;\
inst._newValue = true;\
inst._pendingOp = op;\
if (!inst._disableFormulaUpdate) {\
inst._formula = inst._formula.replace(/\D$/, '') + label;\
}\
inst._disableFormulaUpdate = false;\
this._sendButton(inst, label);\
this._updateCalculator(inst);\
},\n";
index = jQueryCalculator.indexOf("_binaryOp: function(inst, op, label) {");
jQueryCalculator = jQueryCalculator.substring(0, index) + binaryOpHandler + jQueryCalculator.substring(jQueryCalculator.indexOf("},", index) + "},".length);
index = jQueryCalculator.indexOf("inst._formula += (label === '=' ? '' : ' ' + label + ' ');", jQueryCalculator.indexOf("_unaryOp: function(inst, op, label) {"));
jQueryCalculator = jQueryCalculator.substring(0, index) + " if (!inst._disableFormulaUpdate) " + jQueryCalculator.substring(index);
index = jQueryCalculator.indexOf(";", index) + 1;
jQueryCalculator = jQueryCalculator.substring(0, index) + " inst._disableFormulaUpdate = false; " + jQueryCalculator.substring(index);
var percentHandler = "\
_percent: function(inst) {\
if ((inst._pendingOp == this._add) || (inst._pendingOp == this._subtract)) {\
inst.curValue = inst.prevValue * inst.curValue / 100;\
} else {\
inst.curValue = inst.curValue / 100;\
}\
},\n";
index = jQueryCalculator.indexOf("_percent: function(inst) {");
jQueryCalculator = jQueryCalculator.substring(0, index) + percentHandler + jQueryCalculator.substring(jQueryCalculator.indexOf("},", index) + "},".length);
var equalsHandler = "\
_equals: function(inst) {\
var stacks = inst._stacks;\
\
var lastCalculationIndex = 0;\
for (var i = 0; i < stacks.calculation.length; i++) {\
if (stacks.calculation[i].operator.length) {\
lastCalculationIndex = i;\
break;\
}\
}\
\
if (!stacks.calculation[lastCalculationIndex].operator.length) {\
if ((inst._stacks.operatorSequence.length > 1) && (inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 2][1] == this.binary)) {\
stacks.calculation[lastCalculationIndex].operator[stacks.calculation[lastCalculationIndex].operator.length] = inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 2];\
} else if ((inst._stacks.operatorSequence.length > 2) && (inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 2][0] == '=') && (inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 3][1] == this.unary)) {\
inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 3][2](inst);\
inst.curValue = (inst.options.base == 10 ? inst.curValue : Math.floor(inst.curValue));\
inst._stacks.operatorSequence = [inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 3], inst._stacks.operatorSequence[inst._stacks.operatorSequence.length - 1]];\
}\
}\
\
if (stacks.calculation[lastCalculationIndex].operator.length) {\
var calculation;\
\
var operator = stacks.calculation[lastCalculationIndex].operator[stacks.calculation[lastCalculationIndex].operator.length - 1];\
var value;\
\
if ((stacks.sequence.length < 2) || (stacks.sequence[stacks.sequence.length - 2][0] == '=')) {\
value = stacks.calculation[lastCalculationIndex].number.length ? stacks.calculation[lastCalculationIndex].number[stacks.calculation[lastCalculationIndex].number.length - 1] : inst.curValue;\
stacks.calculation = [{number: [inst.curValue, value], operator: stacks.calculation[lastCalculationIndex].operator}];\
calculation = stacks.calculation[stacks.calculation.length - 1];\
} else {\
value = inst.curValue;\
calculation = stacks.calculation[stacks.calculation.length - 1];\
calculation.number[calculation.number.length] = inst.curValue;\
}\
\
while (calculation) {\
while (calculation.operator.length) {\
inst.curValue = calculation.number.pop();\
inst.prevValue = calculation.number.pop();\
inst._pendingOp = calculation.operator.pop()[2];\
inst._pendingOp(inst);\
inst.curValue = (inst.options.base == 10 ? inst.curValue : Math.floor(inst.curValue));\
calculation.number[calculation.number.length] = inst.curValue;\
}\
\
if (stacks.calculation.length < 2) {\
calculation = null;\
} else {\
stacks.calculation.pop();\
calculation = stacks.calculation[stacks.calculation.length - 1];\
calculation.number[calculation.number.length] = inst.curValue;\
}\
}\
\
inst._stacks.operatorSequence = [operator];\
stacks.calculation = [{number: [value], operator: []}];\
}\
},\n";
index = jQueryCalculator.indexOf("_equals: function(inst) {");
jQueryCalculator = jQueryCalculator.substring(0, index) + equalsHandler + jQueryCalculator.substring(jQueryCalculator.indexOf("},", index) + "},".length);
index = jQueryCalculator.indexOf("_clear: function(inst, label) {") + "_clear: function(inst, label) {".length;
jQueryCalculator = jQueryCalculator.substring(0, index) + "\ninst._stacks = null;" + jQueryCalculator.substring(index);
jQueryCalculator = jQueryCalculator.replace(/\s[^\s]*\.focus\(\);/g, "");
jQueryCalculator = jQueryCalculator.substring(0, jQueryCalculator.indexOf("this._reset(inst, '0');")) + " $(document).on('click', '.' + this._resultClass, function(e) { $.calculator._getInst($(e.target).parents('.is-calculator'))._input.focus(); }); " + jQueryCalculator.substring(jQueryCalculator.indexOf("this._reset(inst, '0');"));
var highlight = "\
inst._highlight = inst._highlight || {};\
inst._highlight.keys = html.find('button');\
if (inst._highlight.timeout) {\
inst._highlight.keys.each(function () {\
var key = $(this);\
if (key.attr('data-keystroke') === inst._highlight.keystroke) {\
key.addClass('calculator-button-highlight');\
}\
});\
}\n";
index = jQueryCalculator.indexOf("html = $(html);") + "html = $(html);".length + 1;
jQueryCalculator = jQueryCalculator.substring(0, index) + highlight + jQueryCalculator.substring(index);
var mousedownHighlightVibrate = "\
var keyHighlightDelay = inst.options.keyHighlightDelay;\
if (jQuery.isNumeric(keyHighlightDelay)) {\
if (inst._highlight.timeout) {\
clearTimeout(inst._highlight.timeout);\
inst._highlight.timeout = null;\
}\
inst._highlight.keys.removeClass('calculator-button-highlight');\
var key = $(this);\
key.addClass('calculator-button-highlight');\
inst._highlight.timeout = setTimeout(function () {\
inst._highlight.timeout = null;\
inst._highlight.keys.removeClass('calculator-button-highlight');\
}, parseInt(keyHighlightDelay));\
inst._highlight.keystroke = key.attr('data-keystroke');\
}\
var vibrate = inst.options.vibrate;\
if (jQuery.isNumeric(vibrate)) {\
try {\
window.navigator.vibrate(parseInt(vibrate));\
} catch (e) {}\
}\n";
index = jQueryCalculator.indexOf("$(this).addClass(this._keyDownClass + (inst.options.useThemeRoller ? ' ui-state-active' : ''));") + "$(this).addClass(this._keyDownClass + (inst.options.useThemeRoller ? ' ui-state-active' : ''));".length + 1;
jQueryCalculator = jQueryCalculator.substring(0, index) + mousedownHighlightVibrate + jQueryCalculator.substring(index);
jQueryCalculator = jQueryCalculator.replace(/\$\(this\)\.removeClass\(\)\.addClass\(plugin\._saveClasses\);/g, "$(this).removeClass(this._keyDownClass + (inst.options.useThemeRoller ? ' ui-state-active' : ''));");
eval(jQueryCalculator);
jQuery.calculator._keyDefs["_+"][7] = 10;
jQuery.calculator._keyDefs["_-"][7] = 10;
jQuery.calculator._keyDefs["_*"][7] = 20;
jQuery.calculator._keyDefs["_/"][7] = 20;
jQuery.calculator._keyDefs["XY"][7] = 30;
// eval(store.getTiddlerText('jQueryCalculator##jquery.calculator-fr.js'));
}
//}}}
!jquery.calculator.css
//{{{
!jquery.calculator.css.start
/* Main style sheet for jQuery Calculator v2.0.1 */
div.is-calculator, span.is-calculator {
position: relative;
}
button.calculator-trigger {
width: 25px;
padding: 0px;
}
img.calculator-trigger {
margin: 2px;
vertical-align: middle;
}
.calculator-popup {
display: none;
z-index: 10;
margin: 0;
padding: 0;
border: 1px solid #888;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
color: #000;
background-color: #f4f4f4;
font-family: Arial,Helvetica,sans-serif;
}
.calculator-keyentry {
position: absolute;
top: 3px;
right: 3px;
width: 0px;
}
.calculator-inline {
position: relative;
border: 1px solid #888;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
background-color: #f4f4f4;
}
.calculator-inline .calculator-close {
display: none;
}
.calculator-disabled {
position: absolute;
z-index: 100;
background-color: white;
opacity: 0.5;
filter: alpha(opacity=50);
}
.calculator-rtl {
direction: rtl;
}
.calculator-prompt {
clear: both;
text-align: center;
}
.calculator-prompt.ui-widget-header {
margin: 2px;
}
.calculator-result {
clear: both;
margin: 2px;
padding: 0px 2px;
text-align: right;
background-color: #fff;
border: 1px inset #000;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
font-size: 110%;
}
.calculator-result span {
display: inline-block;
width: 100%;
}
.calculator-result .calculator-formula {
font-size: 60%;
}
.calculator-focussed {
background-color: #ffc;
}
.calculator-row {
clear: both;
width: 100%;
}
.calculator-space {
float: left;
margin: 2px;
width: 28px;
}
.calculator-half-space {
float: left;
margin: 1px;
width: 14px;
}
.calculator-row button {
position: relative;
float: left;
margin: 2px;
padding: 0px;
height: 22px;
background-color: #f4f4f4;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
text-align: center;
cursor: pointer;
}
.calculator-row .calculator-ctrl {
width: 60px;
background-color: #e8e8e8;
}
.calculator-row .calculator-undo, .calculator-row .calculator-clear-error, .calculator-row .calculator-clear {
width: 28px;
}
.calculator-row .calculator-base, .calculator-row .calculator-angle {
width: 28px;
font-size: 70%;
}
.calculator-row .calculator-base-active, .calculator-row .calculator-angle-active {
border: 2px inset #fff;
}
.calculator-digit, .calculator-oper {
width: 28px;
}
.calculator-mem-empty, .calculator-digit[disabled] {
color: #888;
}
.calculator-row .calculator-trig {
font-size: 70%;
}
@-moz-document url-prefix() { // Firefox
.calculator-trig, .calculator-base {
text-indent: -3px;
}
}
.calculator-key-down {
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.calculator-keystroke {
display: none;
width: 16px;
height: 14px;
position: absolute;
left: -1px;
top: -1px;
color: #000;
background-color: #fff;
border: 1px solid #888;
font-size: 80%;
}
.calculator-angle .calculator-keystroke, .calculator-base .calculator-keystroke, .calculator-trig .calculator-keystroke {
top: -2px;
font-size: 95%;
}
.calculator-keyname {
width: 22px;
font-size: 70%;
}
!jquery.calculator.css.end
//}}}
!jquery.calculator.js
//{{{
/* http://keith-wood.name/calculator.html
Calculator field entry extension for jQuery v2.0.1.
Written by Keith Wood (kbwood{at}iinet.com.au) October 2008.
Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) licence.
Please attribute the author if you use it. */
(function($) { // hide the namespace
var pluginName = 'calculator';
var layoutStandard = [' BSCECA', '_1_2_3_+@X', '_4_5_6_-@U', '_7_8_9_*@E', '_0_._=_/'];
var digit = 'd';
var binary = 'b';
var unary = 'u';
var control = 'c';
var space = 's';
/** Create the calculator plugin.
<p>Sets an input field to popup a calculator for arithmetic computations.</p>
<p>Expects HTML like:</p>
<pre><input type="text"></pre>
<p>Provide inline configuration like:</p>
<pre><input type="text" data-calculator="name: 'value'"/></pre>
@module Calculator
@augments JQPlugin
@example $(selector).calculator() */
$.JQPlugin.createPlugin({
/** The name of the plugin. */
name: pluginName,
/** Calculator is operator callback.
Triggered when determining which keystrokes popup the calculator.
@callback isOperatorCallback
@param ch {string} The character just type.
@param event {Event} The keystroke event.
@param value {string} The current field value.
@param base {number} The number base currently in use.
@param decimalChar {string} The character used to denote the decimal point.
@return {boolean} <code>true</code> if this character is an operator, <code>false</code> if not.
@example isOperator: function(ch, event, value, base, decimalChar) {
return '+-/*'.indexOf(ch) > -1;
} */
/** Calculator open callback.
Triggered when the popup calculator opens.
@callback openCallback
@param value {string} The current field value.
@param inst {object} The current instance settings. */
/** Calculator button callback.
Triggered when a button in the calculator is clicked.
@callback buttonCallback
@param label {string} The label from the clicked button.
@param value {string} The current field value.
@param inst {object} The current instance settings. */
/** Calculator close callback.
Triggered when the popup calculator closes.
@callback closeCallback
@param value {string} The current field value.
@param inst {object} The current instance settings. */
/** Calculator math callback.
Triggered when a button is clicked to activate the underlying maths.
@callback mathCallback
@param inst {object} The current instance settings.
@param label {string} The button label.
@example function add(inst) {
inst.curValue = inst.prevValue + inst.curValue;
} */
/** Default settings for the plugin.
@property [showOn='focus'] {string} When to display the calculator:
'focus' for popup on focus, 'button' for trigger button,
'both' for either, 'operator' for non-numeric character entered,
'opbutton' for operator/button combination.
@property [buttonImage=''] {string} The URL for the trigger button image.
@property [buttonImageOnly=false] {boolean} <code>true</code> if the image appears alone, <code>false</code> if it appears on a button.
@property [isOperator=$.calculator.isOperator] {operatorCallback} Call back function to determine if a keystroke opens the calculator.
@property [showAnim='show'] {string} The name of jQuery animation for popup.
@property [showOptions={}] {object} Any options for enhanced animations.
@property [duration='normal'] {string|number} The duration of display/closure (ms).
@property [appendText=''] {string} Any display text following the input box, e.g. showing the format.
@property [useThemeRoller=false] {boolean} <code>true</code> to add ThemeRoller classes.
@property [calculatorClass=''] {string} Any additional CSS class for the calculator for an instance.
@property [showFormula=false] {boolean} <code>true</code> to display formula as it's entered, <code>false</code> to hide it.
@property [prompt=''] {string} Text across the top of the calculator.
@property [layout=[' BSCECA', '_1_2_3_+@X', '_4_5_6_-@U', '_7_8_9_*@E', '_0_._=_/']] {string[]} The layout of keys by row.
@property [value=0] {number} The initial value for inline calculators.
@property [base=10] {number} The numeric base for calculations.
@property [precision=10] {number} The number of digits of precision to use in rounding for display.
@property [memoryAsCookie=false] {boolean} <code>true</code> to save memory into cookie, <code>false</code> for memory only.
@property [cookieName='calculatorMemory'] {string} The name of cookie for memory.
@property [cookieExpires=24 * 60 * 60] {Date|number} The time that the memory cookie expires as a Date or number of seconds.
@property [cookiePath=''] {string} The path for the memory cookie.
@property [useDegrees=false] {boolean} <code>true</code> to use degress for trigonometric functions, <code>false</code> for radians.
@property [constrainInput=true] {boolean} <code>true</code> to restrict typed characters to numerics, <code>false</code> to allow anything.
@property [onOpen=null] {openCallback} Define a callback function before the panel is opened.
@property [onButton=null] {buttonCallback} Define a callback function when a button is activated.
@property [onClose=null] {closeCallback} Define a callback function when the panel is closed. */
defaultOptions: {
showOn: 'focus',
buttonImage: '',
buttonImageOnly: false,
isOperator: null,
showAnim: 'show',
showOptions: {},
duration: 'normal',
appendText: '',
useThemeRoller: false,
calculatorClass: '',
showFormula: false,
prompt: '',
layout: layoutStandard,
value: 0,
base: 10,
precision: 10,
memoryAsCookie: false,
cookieName: 'calculatorMemory',
cookieExpires: 24 * 60 * 60,
cookiePath: '',
useDegrees: false,
constrainInput: true,
onOpen: null,
onButton: null,
onClose: null
},
/** Localisations for the plugin.
Entries are objects indexed by the language code ('' being the default US/English).
Each object has the following attributes.
@property [decimalChar='.'] {string} Character for the decimal point.
@property [buttonText='...'] {string} Display text for trigger button.
@property [buttonStatus='Open the calculator'] {string} Status text for trigger button.
@property [closeText='Close'] {string} Display text for close link.
@property [closeStatus='Close the calculator'] {string} Status text for close link.
@property [useText='Use'] {string} Display text for use link.
@property [useStatus='Use the current value'] {string} Status text for use link.
@property [eraseText='Erase'] {string} Display text for erase link.
@property [eraseStatus='Erase the value from the field'] {string} Status text for erase link.
@property [backspaceText='BS'] {string} Display text for backspace link.
@property [backspaceStatus='Erase the last digit'] {string} Status text for backspace link.
@property [clearErrorText='CE'] {string} Display text for clear error link.
@property [clearErrorStatus='Erase the last number'] {string} Status text for clear error link.
@property [clearText='CA'] {string} Display text for clear link.
@property [clearStatus='Reset the calculator'] {string} Status text for clear link.
@property [memClearText='MC'] {string} Display text for memory clear link.
@property [memClearStatus='Clear the memory'] {string} Status text for memory clear link.
@property [memRecallText='MR'] {string} Display text for memory recall link.
@property [memRecallStatus='Recall the value from memory'] {string} Status text for memory recall link.
@property [memStoreText='MS'] {string} Display text for memory store link.
@property [memStoreStatus='Store the value in memory'] {string} Status text for memory store link.
@property [memAddText='M+'] {string} Display text for memory add link.
@property [memAddStatus='Add to memory'] {string} Status text for memory add link.
@property [memSubtractText='M-'] {string} Display text for memory subtract link.
@property [memSubtractStatus='Subtract from memory'] {string} Status text for memory subtract link.
@property [base2Text='Bin'] {string} Display text for base 2 link.
@property [base2Status='Switch to binary'] {string} Status text for base 2 link.
@property [base8Text='Oct'] {string} Display text for base 8 link.
@property [base8Status='Switch to octal'] {string} Status text for base 8 link.
@property [base10Text='Dec'] {string} Display text for base 10 link.
@property [base10Status='Switch to decimal'] {string} Status text for base 10 link.
@property [base16Text='Hex'] {string} Display text for base 16 link.
@property [base16Status='Switch to hexadecimal'] {string} Status text for base 16 link.
@property [degreesText='Deg'] {string} Display text for degrees link.
@property [degreesStatus='Switch to degrees'] {string} Status text for degrees link.
@property [radiansText='Rad'] {string} Display text for radians link.
@property [radiansStatus='Switch to radians'] {string} Status text for radians link.
@property [isRTL=false] {boolean} <code>true</code> if right-to-left language, <code>false</code> if left-to-right. */
regionalOptions: { // Available regional settings, indexed by language/country code
'': { // Default regional settings - English/US
decimalChar: '.',
buttonText: '...',
buttonStatus: 'Open the calculator',
closeText: 'Close',
closeStatus: 'Close the calculator',
useText: 'Use',
useStatus: 'Use the current value',
eraseText: 'Erase',
eraseStatus: 'Erase the value from the field',
backspaceText: 'BS',
backspaceStatus: 'Erase the last digit',
clearErrorText: 'CE',
clearErrorStatus: 'Erase the last number',
clearText: 'CA',
clearStatus: 'Reset the calculator',
memClearText: 'MC',
memClearStatus: 'Clear the memory',
memRecallText: 'MR',
memRecallStatus: 'Recall the value from memory',
memStoreText: 'MS',
memStoreStatus: 'Store the value in memory',
memAddText: 'M+',
memAddStatus: 'Add to memory',
memSubtractText: 'M-',
memSubtractStatus: 'Subtract from memory',
base2Text: 'Bin',
base2Status: 'Switch to binary',
base8Text: 'Oct',
base8Status: 'Switch to octal',
base10Text: 'Dec',
base10Status: 'Switch to decimal',
base16Text: 'Hex',
base16Status: 'Switch to hexadecimal',
degreesText: 'Deg',
degreesStatus: 'Switch to degrees',
radiansText: 'Rad',
radiansStatus: 'Switch to radians',
isRTL: false
}
},
/** Names of getter methods - those that can't be chained. */
_getters: ['isDisabled'],
_curInst: null, // The current instance in use
_disabledFields: [], // List of calculator inputs that have been disabled
_showingCalculator: false, // True if the popup panel is showing , false if not
_showingKeystrokes: false, // True if showing keystrokes for calculator buttons
/* The definitions of the buttons that may appear on the calculator.
Key is ID. Fields are display text, button type, function,
class(es), field name, keystroke, keystroke name. */
_keyDefs: {},
/** Indicator of a digit key.
For use with <code>addKeyDef</code>. */
digit: digit,
/** Indicator of a binary operation key.
For use with <code>addKeyDef</code>. */
binary: binary,
/** Indicator of a unary operation key.
For use with <code>addKeyDef</code>. */
unary: unary,
/** Indicator of a control key.
For use with <code>addKeyDef</code>. */
control: control,
/** Indicator of a space.
For use with <code>addKeyDef</code>. */
space: space,
_mainDivClass: pluginName + '-popup', // The name of the main calculator division marker class
_inlineClass: pluginName + '-inline', // The name of the inline marker class
_appendClass: pluginName + '-append', // The name of the appended text marker class
_triggerClass: pluginName + '-trigger', // The name of the trigger marker class
_disableClass: pluginName + '-disabled', // The name of the disabled covering marker class
_inlineEntryClass: pluginName + '-keyentry', // The name of the inline entry marker class
_promptClass: pluginName + '-prompt', // The name of the prompt marker class
_formulaClass: pluginName + '-formula', // The name of the formula marker class
_resultClass: pluginName + '-result', // The name of the calculator result marker class
_focussedClass: pluginName + '-focussed', // The name of the focussed marker class
_keystrokeClass: pluginName + '-keystroke', // The name of the keystroke marker class
_rtlClass: pluginName + '-rtl', // The name of the right-to-left marker class
_rowClass: pluginName + '-row', // The name of the row marker class
_ctrlClass: pluginName + '-ctrl', // The name of the control key marker class
_baseActiveClass: pluginName + '-base-active', // The name of the active base marker class
_angleActiveClass: pluginName + '-angle-active', // The name of the active angle marker class
_digitClass: pluginName + '-digit', // The name of the digit key marker class
_operatorClass: pluginName + '-oper', // The name of the operator key marker class
_memEmptyClass: pluginName + '-mem-empty', // The name of the memory empty marker class
_keyNameClass: pluginName + '-keyname', // The name of the key name marker class
_keyDownClass: pluginName + '-key-down', // The name of the key down marker class
_keyStrokeClass: pluginName + '-keystroke', // The name of the key stroke marker class
/** The standard calculator layout with simple operations. */
standardLayout: layoutStandard,
/** The extended calculator layout with common scientific operations. */
scientificLayout: ['@X@U@E BSCECA', 'DGRD _ MC_ _7_8_9_+', 'SNASSRLG_ MR_ _4_5_6_-',
'CSACSQLN_ MS_ _1_2_3_*', 'TNATXYEX_ M+_ _0_.+-_/', 'PIRN1X _ M-_ _%_='],
/** Add a new key definition.
@param code {string} The two-character code for this key.
@param label {string} The display label for this key. If the label starts with '#' the corresponding
<code>regionalOptions</code> 'xxxText' is used instead to allow for localisation.
@param type {boolean|string} <code>true</code> if this is a binary operator, <code>false</code> if a unary operator,
or key type - use constants (<code>$.calculator.</code>) <code>digit</code>,
<code>binary</code>, <code>unary</code>, <code>space</code>, <code>control</code>.
@param func {mathCallback} The function that applies this key -
it is expected to take a parameter of the current instance.
@param [style] {string} Any additional CSS styles for this key.
@param [constant] {string} The name of a constant to create for this key.
@param keystroke {string|number} The character or key code of the keystroke for this key.
@param keyName {string} The name of the keystroke for this key.
@return {object} The calculator object for chaining further calls.
@example $.calculator.addKeyDef('BO', '#base8', $.calculator.control, $.calculator._base8, 'base base8', 'BASE_8', 'C'); */
addKeyDef: function(code, label, type, func, style, constant, keystroke, keyName) {
this._keyDefs[code] = [label, (typeof type === 'boolean' ? (type ? this.binary : this.unary) : type),
func, style, constant, keystroke, keyName];
if (constant) {
this[constant] = code;
}
if (keystroke) {
if (typeof keystroke === 'number') {
this._keyCodes[keystroke] = code;
}
else {
this._keyChars[keystroke] = code;
}
}
return this;
},
/** Additional setup for the calculator.
Create popup div. */
_init: function() {
this.mainDiv = $('<div class="' + this._mainDivClass + '" style="display: none;"></div>').
on('click.' + pluginName, this._focusEntry);
this._keyCodes = {};
this._keyChars = {};
this._super();
},
_instSettings: function(elem, options) {
var inline = elem[0].nodeName.toLowerCase() !== 'input';
var keyEntry = (!inline ? elem :
$('<input type="text" class="' + this._inlineEntryClass + '"/>'));
return {_input: keyEntry, _inline: inline, memory: 0,
_mainDiv: (inline ? $('<div class="' + this._inlineClass + '"></div>') : this.mainDiv)};
},
_postAttach: function(elem, inst) {
if (inst.options.memoryAsCookie) {
var memory = this._getMemoryCookie(inst);
if (memory && !isNaN(memory)) {
inst.memory = memory;
}
}
if (!inst._inline && elem.is(':disabled')) {
this.disable(elem[0]);
}
},
_optionsChanged: function(elem, inst, options) {
$.extend(inst.options, options);
if (this._curInst === inst) {
this.hide();
}
elem.empty().off('.' + inst.name).
siblings('.' + this._appendClass).remove().end().
siblings('.' + this._triggerClass).remove().end().
prev('.' + this._inlineEntryClass).remove();
if (inst.options.appendText) {
elem[inst.options.isRTL ? 'before' : 'after'](
'<span class="' + this._appendClass + '">' + inst.options.appendText + '</span>');
}
if (!inst._inline) {
if (inst.options.showOn === 'focus' || inst.options.showOn === 'both') {
// pop-up calculator when in the marked field
elem.on('focus.' + inst.name, this.show);
}
if (inst.options.showOn === 'button' || inst.options.showOn === 'both' ||
inst.options.showOn === 'opbutton') {
// pop-up calculator when button clicked
var trigger = $(inst.options.buttonImageOnly ?
$('<img/>').attr({src: inst.options.buttonImage,
alt: inst.options.buttonStatus, title: inst.options.buttonStatus}) :
$('<button type="button" title="' + inst.options.buttonStatus + '"></button>').
html(inst.options.buttonImage === '' ? inst.options.buttonText :
$('<img/>').attr({src: inst.options.buttonImage})));
elem[inst.options.isRTL ? 'before' : 'after'](trigger);
trigger.addClass(this._triggerClass).on('click.' + inst.name, function() {
if (plugin._showingCalculator && plugin._lastInput === elem[0]) {
plugin.hide();
}
else {
plugin.show(elem[0]);
}
return false;
});
}
}
inst._input.on('keydown.' + inst.name, this._doKeyDown).
on('keyup.' + inst.name, this._doKeyUp).
on('keypress.' + inst.name, this._doKeyPress);
if (inst._inline) {
elem.append(inst._input).append(inst._mainDiv).
on('click.' + inst.name, function() { inst._input.focus(); });
this._reset(inst, '0');
this._setValue(inst);
this._updateCalculator(inst);
inst._mainDiv.on('keydown.' + inst.name, this._doKeyDown).
on('keyup.' + inst.name, this._doKeyUp).
on('keypress.' + inst.name, this._doKeyPress);
inst._input.on('focus.' + inst.name, function() {
if (!plugin.isDisabled(elem[0])) {
inst._focussed = true;
$('.' + plugin._resultClass, inst._mainDiv).
addClass(plugin._focussedClass);
}
}).on('blur.' + inst.name, function() {
inst._focussed = false;
$('.' + plugin._resultClass, inst._mainDiv).
removeClass(plugin._focussedClass);
});
}
elem.addClass(this._getMarker()).
on('setData.' + inst.name, function(event, key, value) {
inst.options[key] = value;
}).
on('getData.' + inst.name, function(event, key) {
return inst.options[key];
}).
data(inst.name, inst);
inst._input.data(inst.name, inst);
if (inst._inline) {
this._setValue(inst);
}
this._updateCalculator(inst);
},
_preDestroy: function(elem, inst) {
inst._input.off('.' + inst.name).removeData(inst.name);
elem.empty().off('.' + inst.name).
siblings('.' + this._appendClass).remove().end().
siblings('.' + this._triggerClass).remove().end().
prev('.' + this._inlineEntryClass).remove();
},
/** Enable the calculator for a jQuery selection.
@param elem {Element} The target input field or division/span.
@example $(selector).calculator('enable'); */
enable: function(elem) {
elem = $(elem);
if (!elem.hasClass(this._getMarker())) {
return;
}
var nodeName = elem[0].nodeName.toLowerCase();
if (nodeName === 'input') {
elem.prop('disabled', false).siblings('button.' + this._triggerClass).prop('disabled', false).end().
siblings('img.' + this._triggerClass).css({opacity: '1.0', cursor: ''});
}
else if (nodeName === 'div' || nodeName === 'span') {
elem.find('.' + this._inlineEntryClass + ',button').prop('disabled', false).end().
children('.' + this._disableClass).remove();
}
this._disabledFields = $.map(this._disabledFields,
function(value) { return (value === elem[0] ? null : value); }); // delete entry
},
/** Disable the calculator for a jQuery selection.
@param elem {Element} The target input field or division/span.
@example $(selector).calculator('disable'); */
disable: function(elem) {
elem = $(elem);
if (!elem.hasClass(this._getMarker())) {
return;
}
var nodeName = elem[0].nodeName.toLowerCase();
if (nodeName === 'input') {
elem.prop('disabled', true).siblings('button.' + this._triggerClass).prop('disabled', true).end().
siblings('img.' + this._triggerClass).css({opacity: '0.5', cursor: 'default'});
}
else if (nodeName === 'div' || nodeName === 'span') {
var inline = elem.children('.' + this._inlineClass);
var offset = inline.offset();
var relOffset = {left: 0, top: 0};
inline.parents().each(function() {
if ($(this).css('position') === 'relative') {
relOffset = $(this).offset();
return false;
}
});
elem.find('.' + this._inlineEntryClass + ',button').prop('disabled', true);
if (elem.find('.' + this._disableClass).length === 0) {
elem.prepend('<div class="' + this._disableClass + '" style="width: ' +
inline.outerWidth() + 'px; height: ' + inline.outerHeight() +
'px; left: ' + (offset.left - relOffset.left) +
'px; top: ' + (offset.top - relOffset.top) + 'px;"></div>');
}
}
this._disabledFields = $.map(this._disabledFields,
function(value) { return (value === elem[0] ? null : value); }); // delete entry
this._disabledFields[this._disabledFields.length] = elem[0];
},
/** Is the input field or division/span disabled as a calculator?
@param elem {Element} the target control.
@return {boolean} <code>true</code> if disabled, <code>false</code> if enabled.
@example if ($(selector).calculator('isDisabled')) {...} */
isDisabled: function(elem) {
return (elem && $.inArray(elem, this._disabledFields) > -1);
},
/** Pop-up the calculator for a given input field or division/span.
@param input {Element|Event} The control attached to the calculator or the triggering event.
@example $(selector).calculator('show'); */
show: function(input) {
input = input.target || input;
if (plugin.isDisabled(input) || plugin._lastInput === input) { // already here
return;
}
var inst = plugin._getInst(input);
plugin.hide(null, '');
plugin._lastInput = input;
plugin._pos = plugin._findPos(input);
plugin._pos[1] += input.offsetHeight; // add the height
var isFixed = false;
$(input).parents().each(function() {
isFixed |= $(this).css('position') === 'fixed';
return !isFixed;
});
var offset = {left: plugin._pos[0], top: plugin._pos[1]};
plugin._pos = null;
// determine sizing offscreen
inst._mainDiv.css({position: 'absolute', display: 'block', top: '-1000px', width: 'auto'});
// callback before calculator opening
if ($.isFunction(inst.options.onOpen)) {
inst.options.onOpen.apply((inst._input ? inst._input[0] : null), // trigger custom callback
[(inst._inline ? inst.curValue : inst._input.val()), inst]);
}
plugin._reset(inst, inst._input.val());
plugin._updateCalculator(inst);
// and adjust position before showing
offset = plugin._checkOffset(inst, offset, isFixed);
inst._mainDiv.css({position: (isFixed ? 'fixed' : 'absolute'), display: 'none',
left: offset.left + 'px', top: offset.top + 'px'});
var duration = inst.options.duration;
duration = (duration == 'normal' && $.ui &&
parseInt($.ui.version.substring(2)) >= 8 ? '_default' : duration);
var postProcess = function() {
plugin._showingCalculator = true;
};
if ($.effects && ($.effects[inst.options.showAnim] ||
($.effects.effect && $.effects.effect[inst.options.showAnim]))) {
var data = inst._mainDiv.data(); // Update old effects data
for (var key in data) {
if (key.match(/^ec\.storage\./)) {
data[key] = inst._mainDiv.css(key.replace(/ec\.storage\./, ''));
}
}
inst._mainDiv.data(data).show(inst.options.showAnim,
inst.options.showOptions, duration, postProcess);
}
else {
inst._mainDiv[inst.options.showAnim || 'show']((inst.options.showAnim ? duration : null), postProcess);
}
if (!inst.options.showAnim) {
postProcess();
}
if (inst._input[0].type !== 'hidden') {
inst._input[0].focus();
}
plugin._curInst = inst;
},
/** Reinitialise the calculator.
@private
@param inst {object} The instance settings.
@param value {number} The starting value. */
_reset: function(inst, value) {
value = '' + (value || 0);
value = (inst.options.decimalChar !== '.' ?
value.replace(new RegExp(inst.options.decimalChar), '.') : value);
inst.curValue = (inst.options.base === 10 ? parseFloat(value) : parseInt(value, inst.options.base)) || 0;
inst.dispValue = this._setDisplay(inst);
inst.prevValue = inst._savedValue = 0;
inst._pendingOp = inst._savedOp = this._noOp;
inst._formula = '';
inst._newValue = true;
},
/** Retrieve the memory value from a cookie, if any.
@private
@param inst {object} The instance settings.
@return {number} The memory cookie value or NaN/null if unavailable. */
_getMemoryCookie: function(inst) {
var re = new RegExp('^.*' + inst.options.cookieName + '=([^;]*).*$');
return parseFloat(document.cookie.replace(re, '$1'));
},
/** Save the memory value as a cookie.
@private
@param inst {object} The instance settings. */
_setMemoryCookie: function(inst) {
if (!inst.options.memoryAsCookie) {
return;
}
var expires = inst.options.cookieExpires;
if (typeof expires === 'number') {
var time = new Date();
time.setTime(time.getTime() + expires * 1000);
expires = time.toUTCString();
}
else if (expires.constructor === Date) {
expires = time.toUTCString();
}
else {
expires = '';
}
document.cookie = inst.options.cookieName + '=' + inst.memory +
'; expires=' + expires + '; path=' + inst.options.cookiePath;
},
/** Set the initial value for display.
@private
@param inst {object} The instance settings. */
_setValue: function(inst) {
inst.curValue = inst.options.value || 0;
inst.dispValue = this._setDisplay(inst);
},
/** Generate the calculator content.
@private
@param inst {object} The instance settings. */
_updateCalculator: function(inst) {
var borders = this._getBorders(inst._mainDiv);
inst._mainDiv.html(this._generateHTML(inst)).removeClass().
addClass(inst.options.calculatorClass +
(inst.options.useThemeRoller ? ' ui-widget ui-widget-content' : '') +
(inst.options.isRTL ? ' ' + plugin._rtlClass : '') + ' ' +
(inst._inline ? this._inlineClass : this._mainDivClass));
if (this.isDisabled(inst.elem[0])) {
this.disable(inst.elem[0]);
}
if (this._curInst === inst) {
inst._input.focus();
}
},
/** Retrieve the size of left and top borders for an element.
@private
@param elem {jQuery} The element of interest.
@return {number[]} The left and top borders. */
_getBorders: function(elem) {
var convert = function(value) {
return {thin: 1, medium: 3, thick: 5}[value] || value;
};
return [parseFloat(convert(elem.css('border-left-width'))),
parseFloat(convert(elem.css('border-top-width')))];
},
/** Check positioning to remain on screen.
@private
@param inst {object} The instance settings.
@param offset {object} The current offset.
@param isFixed {boolean} <code>true</code> if the input field is fixed in position.
@return {object} The updated offset. */
_checkOffset: function(inst, offset, isFixed) {
var pos = inst._input ? this._findPos(inst._input[0]) : null;
var browserWidth = window.innerWidth || document.documentElement.clientWidth;
var browserHeight = window.innerHeight || document.documentElement.clientHeight;
var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
// reposition calculator panel horizontally if outside the browser window
if (inst.options.isRTL || (offset.left + inst._mainDiv.outerWidth() - scrollX) > browserWidth) {
offset.left = Math.max((isFixed ? 0 : scrollX),
pos[0] + (inst._input ? inst._input.outerWidth() : 0) -
(isFixed ? scrollX : 0) - inst._mainDiv.outerWidth());
}
else {
offset.left = Math.max((isFixed ? 0 : scrollX), offset.left - (isFixed ? scrollX : 0));
}
// reposition calculator panel vertically if outside the browser window
if ((offset.top + inst._mainDiv.outerHeight() - scrollY) > browserHeight) {
offset.top = Math.max((isFixed ? 0 : scrollY),
pos[1] - (isFixed ? scrollY : 0) - inst._mainDiv.outerHeight());
}
else {
offset.top = Math.max((isFixed ? 0 : scrollY), offset.top - (isFixed ? scrollY : 0));
}
return offset;
},
/** Find an object's position on the screen.
@private
@param obj {Element} The element to find the position for.
@return {number[]} The element's position. */
_findPos: function(obj) {
while (obj && (obj.type === 'hidden' || obj.nodeType !== 1)) {
obj = obj.nextSibling;
}
var position = $(obj).offset();
return [position.left, position.top];
},
/** Hide the calculator from view.
@param input {Element} The control attached to the calculator.
@param duration {string} The duration over which to close the calculator.
@example $(selector).calculator('hide'); */
hide: function(input, duration) {
var inst = this._curInst;
if (!inst || (input && inst !== plugin._getInst(input))) {
return;
}
if (this._showingCalculator) {
duration = (duration != null ? duration : inst.options.duration);
duration = (duration === 'normal' && $.ui &&
parseInt($.ui.version.substring(2)) >= 8 ? '_default' : duration);
if ($.effects && ($.effects[inst.options.showAnim] ||
($.effects.effect && $.effects.effect[inst.options.showAnim]))) {
inst._mainDiv.hide(inst.options.showAnim, inst.options.showOptions, duration);
}
else {
inst._mainDiv[(inst.options.showAnim === 'slideDown' ? 'slideUp' :
(inst.options.showAnim === 'fadeIn' ? 'fadeOut' : 'hide'))](
inst.options.showAnim ? duration : null);
}
}
if ($.isFunction(inst.options.onClose)) {
inst.options.onClose.apply((inst._input ? inst._input[0] : null), // trigger custom callback
[(inst._inline ? inst.curValue : inst._input.val()), inst]);
}
if (this._showingCalculator) {
this._showingCalculator = false;
this._lastInput = null;
}
this._curInst = null;
},
/** Close calculator if clicked elsewhere.
@private
@param event {Event} The mouseclick details. */
_checkExternalClick: function(event) {
if (!plugin._curInst) {
return;
}
var target = $(event.target);
if (!target.parents().andSelf().hasClass(plugin._mainDivClass) && !target.hasClass(plugin._getMarker()) &&
!target.parents().andSelf().hasClass(plugin._triggerClass) && plugin._showingCalculator) {
plugin.hide();
}
},
/** Focus back onto the input field.
@private */
_focusEntry: function() {
if (plugin._curInst && plugin._curInst._input) {
plugin._curInst._input.focus();
}
},
/** Handle keystrokes.
@private
@param e {Event} The key event. */
_doKeyDown: function(e) {
var handled = false;
var inst = plugin._getInst(e.target);
var div = (inst && inst._inline ? $(e.target).parent()[0] : null);
if (e.keyCode === 9) { // tab
plugin.mainDiv.stop(true, true);
plugin.hide();
if (inst && inst._inline) {
inst._input.blur();
}
}
else if (plugin._showingCalculator || (div && !plugin.isDisabled(div))) {
if (e.keyCode === 18) { // alt - show keystrokes
if (!plugin._showingKeystrokes) {
inst._mainDiv.find('.' + plugin._keystrokeClass).show();
plugin._showingKeystrokes = true;
}
handled = true;
}
else {
var code = plugin._keyCodes[e.keyCode];
if (code) {
$('button[data-keystroke="' + code + '"]', inst._mainDiv).not(':disabled').click();
handled = true;
}
}
}
else if (e.keyCode === 36 && e.ctrlKey && inst && !inst._inline) {
plugin.show(this); // display the date picker on ctrl+home
}
if (handled) {
e.preventDefault();
e.stopPropagation();
}
return !handled;
},
/** Hide keystrokes, if showing.
@private
@param e {Event} The key event. */
_doKeyUp: function(e) {
if (plugin._showingKeystrokes) {
var inst = plugin._getInst(e.target);
inst._mainDiv.find('.' + plugin._keystrokeClass).hide();
plugin._showingKeystrokes = false;
}
},
/** Convert characters into button clicks.
@private
@param e {Event} The key event.
@return {boolean} <code>true</code> if keystroke allowed, <code>false</code> if not. */
_doKeyPress: function(e) {
var inst = plugin._getInst(e.target);
if (!inst) {
return true;
}
var div = (inst && inst._inline ? $(e.target).parent()[0] : null);
var ch = String.fromCharCode(e.charCode === undefined ? e.keyCode : e.charCode);
var isOperator = inst.options.isOperator || plugin.isOperator;
if (!plugin._showingCalculator && !div &&
(inst.options.showOn === 'operator' || inst.options.showOn === 'opbutton') &&
isOperator.apply(inst._input,
[ch, e, inst._input.val(), inst.options.base, inst.options.decimalChar])) {
plugin.show(this); // display the date picker on operator usage
plugin._showingCalculator = true;
}
if (plugin._showingCalculator || (div && !plugin.isDisabled(div))) {
var code = plugin._keyChars[ch === inst.options.decimalChar ? '.' : ch];
if (code) {
$('button[data-keystroke="' + code + '"]', inst._mainDiv).not(':disabled').click();
}
return false;
}
if (ch >= ' ' && inst.options.constrainInput) {
var pattern = new RegExp('^-?' +
(inst.options.base === 10 ? '[0-9]*(\\' + inst.options.decimalChar + '[0-9]*)?' :
'[' + '0123456789abcdef'.substring(0, inst.options.base) + ']*') + '$');
return (inst._input.val() + ch).toLowerCase().match(pattern) != null;
}
return true;
},
/** Determine whether or not a keystroke is a trigger for opening the calculator.
@param ch {string} The current character.
@param event {KeyEvent} The entire key event.
@param value {string} The current input value.
@param base {number} The current number base.
@param decimalChar {string} The current decimal character.
@return {boolean} <code>true</code> if a trigger, <code>false</code> if not. */
isOperator: function(ch, event, value, base, decimalChar) {
return ch > ' ' && !(ch === '-' && value === '') &&
('0123456789abcdef'.substr(0, base) + '.' + decimalChar).indexOf(ch.toLowerCase()) === -1;
},
/** Generate the HTML for the current state of the calculator.
@private
@param inst {object} The instance settings.
@return {string} The HTML for this calculator. */
_generateHTML: function(inst) {
var html = (!inst.options.prompt ? '' : '<div class="' + this._promptClass +
(inst.options.useThemeRoller ? ' ui-widget-header ui-corner-all' : '') + '">' +
inst.options.prompt + '</div>') + '<div class="' + this._resultClass +
(inst.options.useThemeRoller ? ' ui-widget-header' : '' ) +
(inst._focussed ? ' ' + this._focussedClass: '') + '">' +
(inst.options.showFormula ?
'<span class="' + this._formulaClass + '">' + inst._formula + '</span>' : '') +
'<span>' + inst.dispValue + '</span></div>';
for (var i = 0; i < inst.options.layout.length; i++) {
html += '<div class="' + this._rowClass + '">';
for (var j = 0; j < inst.options.layout[i].length; j += 2) {
var code = inst.options.layout[i].substr(j, 2);
var def = this._keyDefs[code] || this._keyDefs['??'];
var label = (def[0].charAt(0) === '#' ? inst.options[def[0].substr(1) + 'Text'] : def[0]);
var status = (def[0].charAt(0) === '#' ? inst.options[def[0].substr(1) + 'Status'] : '');
var styles = (def[3] ? def[3].split(' ') : []);
for (var k = 0; k < styles.length; k++) {
styles[k] = inst.name + '-' + styles[k];
}
styles = styles.join(' ');
var uiActive = (inst.options.useThemeRoller ? ' ui-state-active' : '');
var uiHighlight = (inst.options.useThemeRoller ? ' ui-state-highlight' : '');
html += (def[1] === this.space ? '<span class="' + inst.name + '-' + def[3] + '"></span>' :
(inst._inline && (def[2] === '._close' || def[2] === '._erase') ? '' :
'<button type="button" data-keystroke="' + code + '"' +
// Control buttons
(def[1] === this.control ? ' class="' + this._ctrlClass +
(def[0].match(/^#base/) ? (def[0].replace(/^#base/, '') === inst.options.base ?
uiActive || ' ' + this._baseActiveClass : uiHighlight) :
(def[0] === '#degrees' ? (inst.options.useDegrees ?
uiActive || ' ' + this._angleActiveClass : uiHighlight) :
(def[0] === '#radians' ? (!inst.options.useDegrees ?
uiActive || ' ' + this._angleActiveClass : uiHighlight) : uiHighlight))) :
// Digits
(def[1] === this.digit ? (parseInt(def[0], 16) >= inst.options.base ||
(inst.options.base !== 10 && def[0] === '.') ?
' disabled="disabled"' : '') + ' class="' + this._digitClass :
// Binary operations
(def[1] === this.binary ? ' class="' + this._operatorClass :
// Unary operations
' class="' + this._operatorClass +
(def[0].match(/^#mem(Clear|Recall)$/) && !inst.memory ?
' ' + this._memEmptyClass : '')))) +
// Common
(inst.options.useThemeRoller ? ' ui-state-default' : '') +
(styles ? ' ' + styles : '') + '" ' +
(status ? 'title="' + status + '"' : '') + '>' +
(code === '_.' ? inst.options.decimalChar : label) +
// Keystrokes
(def[5] && def[5] !== def[0] ? '<span class="' + this._keystrokeClass +
(inst.options.useThemeRoller ? ' ui-state-error' : '') +
(def[6] ? ' ' + this._keyNameClass : '') + '">' + (def[6] || def[5]) + '</span>' : '') +
'</button>'));
}
html += '</div>';
}
html += '<div style="clear: both;"></div>';
html = $(html);
html.find('button').on('mouseover.' + inst.name, function() {
plugin._saveClasses = this.className;
}).
on('mousedown.' + inst.name, function() {
$(this).addClass(this._keyDownClass + (inst.options.useThemeRoller ? ' ui-state-active' : ''));
}).
on('mouseup.' + inst.name, function() {
$(this).removeClass().addClass(plugin._saveClasses);
}).
on('mouseout.' + inst.name, function() {
$(this).removeClass().addClass(plugin._saveClasses);
}).
on('click.' + inst.name, function() {
plugin._handleButton(inst, $(this));
});
return html;
},
/** Generate the display value.
Tidy the result to avoid JavaScript rounding errors.
@private
@param inst (object) the instance settings
@return (string) the rounded and formatted display value */
_setDisplay: function(inst) {
var fixed = new Number(inst.curValue).toFixed(inst.options.precision).valueOf(); // Round to specified precision
var exp = fixed.replace(/^.+(e.+)$/, '$1').replace(/^[^e].*$/, ''); // Extract exponent
if (exp) {
fixed = new Number(fixed.replace(/e.+$/, '')).toFixed(inst.options.precision).valueOf(); // Round mantissa
}
return parseFloat(fixed.replace(/0+$/, '') + exp). // Recombine
toString(inst.options.base).toUpperCase().replace(/\./, inst.options.decimalChar);
},
/** Send notification of a button activation.
@private
@param inst {object} The instance settings.
@param label {string} The label from the button. */
_sendButton: function(inst, label) {
if ($.isFunction(inst.options.onButton)) {
inst.options.onButton.apply((inst._input ? inst._input[0] : null),
[label, inst.dispValue, inst]); // trigger custom callback
}
},
/** Handle a button press.
@private
@param inst {object} The current instance settings.
@param button {jQuery} The button pressed. */
_handleButton: function(inst, button) {
var keyDef = this._keyDefs[button.data('keystroke')];
if (!keyDef) {
return;
}
var label = button.text().substr(0, button.text().length -
button.children('.' + this._keyStrokeClass).text().length);
switch (keyDef[1]) {
case this.control:
keyDef[2].apply(this, [inst, label]); break;
case this.digit:
this._digit(inst, label); break;
case this.binary:
this._binaryOp(inst, keyDef[2], label); break;
case this.unary:
this._unaryOp(inst, keyDef[2], label); break;
}
if (plugin._showingCalculator || inst._inline) {
inst._input.focus();
}
},
/** Do nothing.
@private */
_noOp: function(inst) {
},
/** Add a digit to the number in the calculator.
@private
@param inst {object} The instance settings.
@param digit {string} The digit to append. */
_digit: function(inst, digit) {
inst.dispValue = (inst._newValue ? '' : inst.dispValue);
if (digit === inst.options.decimalChar && inst.dispValue.indexOf(digit) > -1) {
return;
}
inst.dispValue = (inst.dispValue + digit).replace(/^0(\d)/, '$1').
replace(new RegExp('^(-?)([\\.' + inst.options.decimalChar + '])'), '$10$2');
inst._formula += digit;
if (inst.options.decimalChar !== '.') {
inst.dispValue = inst.dispValue.replace(new RegExp('^' + inst.options.decimalChar), '0.');
}
var value = (inst.options.decimalChar !== '.' ?
inst.dispValue.replace(new RegExp(inst.options.decimalChar), '.') : inst.dispValue);
inst.curValue = (inst.options.base === 10 ? parseFloat(value) : parseInt(value, inst.options.base));
inst._newValue = false;
this._sendButton(inst, digit);
this._updateCalculator(inst);
},
/** Save a binary operation for later use.
@private
@param inst {object} The instance settings.
@param op {function} The binary function.
@param label {string} The button label. */
_binaryOp: function(inst, op, label) {
if (!inst._newValue && inst._pendingOp) {
inst._pendingOp(inst);
inst.curValue = (inst.options.base === 10 ? inst.curValue : Math.floor(inst.curValue));
inst.dispValue = this._setDisplay(inst);
}
inst.prevValue = inst.curValue;
inst._newValue = true;
inst._pendingOp = op;
inst._formula = inst._formula.replace(/\D$/, '') + label;
this._sendButton(inst, label);
this._updateCalculator(inst);
},
_add: function(inst) {
inst.curValue = inst.prevValue + inst.curValue;
},
_subtract: function(inst) {
inst.curValue = inst.prevValue - inst.curValue;
},
_multiply: function(inst) {
inst.curValue = inst.prevValue * inst.curValue;
},
_divide: function(inst) {
inst.curValue = inst.prevValue / inst.curValue;
},
_power: function(inst) {
inst.curValue = Math.pow(inst.prevValue, inst.curValue);
},
/** Apply a unary operation to the calculator.
@private
@param inst {object} The instance settings.
@param op {function} The unary function.
@param label {string} The button label. */
_unaryOp: function(inst, op, label) {
inst._newValue = true;
op.apply(this, [inst]);
inst.curValue = (inst.options.base === 10 ? inst.curValue : Math.floor(inst.curValue));
inst.dispValue = this._setDisplay(inst);
inst._formula += (label === '=' ? '' : ' ' + label + ' ');
this._sendButton(inst, label);
this._updateCalculator(inst);
},
_plusMinus: function(inst) {
inst.curValue = -1 * inst.curValue;
inst.dispValue = this._setDisplay(inst);
inst._newValue = false;
},
_pi: function(inst) {
inst.curValue = Math.PI;
},
/** Perform a percentage calculation.
@private
@param inst {object} The instance settings. */
_percent: function(inst) {
if (inst._pendingOp === this._add) {
inst.curValue = inst.prevValue * (1 + inst.curValue / 100);
}
else if (inst._pendingOp === this._subtract) {
inst.curValue = inst.prevValue * (1 - inst.curValue / 100);
}
else if (inst._pendingOp === this._multiply) {
inst.curValue = inst.prevValue * inst.curValue / 100;
}
else if (inst._pendingOp === this._divide) {
inst.curValue = inst.prevValue / inst.curValue * 100;
}
inst._savedOp = inst._pendingOp;
inst._pendingOp = this._noOp;
},
/** Apply a pending binary operation.
@private
@param inst {object} The instance settings. */
_equals: function(inst) {
if (inst._pendingOp === this._noOp) {
if (inst._savedOp !== this._noOp) {
// Following x op y =: =, z =
inst.prevValue = inst.curValue;
inst.curValue = inst._savedValue;
inst._savedOp(inst);
}
}
else {
// Normal: x op y =
inst._savedOp = inst._pendingOp;
inst._savedValue = inst.curValue;
inst._pendingOp(inst);
inst._pendingOp = this._noOp;
}
inst._formula = '';
},
_memAdd: function(inst) {
inst.memory += inst.curValue;
this._setMemoryCookie(inst);
},
_memSubtract: function(inst) {
inst.memory -= inst.curValue;
this._setMemoryCookie(inst);
},
_memStore: function(inst) {
inst.memory = inst.curValue;
this._setMemoryCookie(inst);
},
_memRecall: function(inst) {
inst.curValue = inst.memory;
},
_memClear: function(inst) {
inst.memory = 0;
this._setMemoryCookie(inst);
},
_sin: function(inst) {
this._trig(inst, Math.sin);
},
_cos: function(inst) {
this._trig(inst, Math.cos);
},
_tan: function(inst) {
this._trig(inst, Math.tan);
},
_trig: function(inst, op, label) {
inst.curValue = op(inst.curValue * (inst.options.useDegrees ? Math.PI / 180 : 1));
},
_asin: function(inst) {
this._atrig(inst, Math.asin);
},
_acos: function(inst) {
this._atrig(inst, Math.acos);
},
_atan: function(inst) {
this._atrig(inst, Math.atan);
},
_atrig: function(inst, op, label) {
inst.curValue = op(inst.curValue);
if (inst.options.useDegrees) {
inst.curValue = inst.curValue / Math.PI * 180;
}
},
_inverse: function(inst) {
inst.curValue = 1 / inst.curValue;
},
_log: function(inst) {
inst.curValue = Math.log(inst.curValue) / Math.log(10);
},
_ln: function(inst) {
inst.curValue = Math.log(inst.curValue);
},
_exp: function(inst) {
inst.curValue = Math.exp(inst.curValue);
},
_sqr: function(inst) {
inst.curValue *= inst.curValue;
},
_sqrt: function(inst) {
inst.curValue = Math.sqrt(inst.curValue);
},
_random: function(inst) {
inst.curValue = Math.random();
},
_base2: function(inst, label) {
this._changeBase(inst, label, 2);
},
_base8: function(inst, label) {
this._changeBase(inst, label, 8);
},
_base10: function(inst, label) {
this._changeBase(inst, label, 10);
},
_base16: function(inst, label) {
this._changeBase(inst, label, 16);
},
/** Change the number base for the calculator.
@private
@param inst {object} The instance settings.
@param label {string} The button label.
@param newBase {number} The new number base. */
_changeBase: function(inst, label, newBase) {
inst.options.base = newBase;
inst.curValue = (newBase === 10 ? inst.curValue : Math.floor(inst.curValue));
inst.dispValue = this._setDisplay(inst);
inst._newValue = true;
this._sendButton(inst, label);
this._updateCalculator(inst);
},
_degrees: function(inst, label) {
this._degreesRadians(inst, label, true);
},
_radians: function(inst, label) {
this._degreesRadians(inst, label, false);
},
/** Swap between degrees and radians for trigonometric functions.
@private
@param inst {object} The instance settings.
@param label {string} The button label.
@param useDegrees {boolean} <code>true</code> to use degrees, <code>false</code> for radians. */
_degreesRadians: function(inst, label, useDegrees) {
inst.options.useDegrees = useDegrees;
this._sendButton(inst, label);
this._updateCalculator(inst);
},
/** Erase the last digit entered.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_undo: function(inst, label) {
inst.dispValue = inst.dispValue.substr(0, inst.dispValue.length - 1) || '0';
inst.curValue = (inst.options.base === 10 ?
parseFloat(inst.dispValue) : parseInt(inst.dispValue, inst.options.base));
inst._formula = inst._formula.replace(/[\.\d]$/, '');
this._sendButton(inst, label);
this._updateCalculator(inst);
},
/** Erase the last number entered.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_clearError: function(inst, label) {
inst.dispValue = '0';
inst.curValue = 0;
inst._formula = inst._formula.replace(/[\.\d]+$/, '');
inst._newValue = true;
this._sendButton(inst, label);
this._updateCalculator(inst);
},
/** Reset the calculator.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_clear: function(inst, label) {
this._reset(inst, 0);
this._sendButton(inst, label);
this._updateCalculator(inst);
},
/** Close the calculator without changing the value.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_close: function(inst, label) {
this._finished(inst, label, inst._input.val());
},
/** Copy the current value and close the calculator.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_use: function(inst, label) {
if (inst._pendingOp !== this._noOp) {
this._unaryOp(inst, this._equals, label);
}
this._finished(inst, label, inst.dispValue);
},
/** Erase the field and close the calculator.
@private
@param inst {object} The instance settings.
@param label {string} The button label. */
_erase: function(inst, label) {
this._reset(inst, 0);
this._updateCalculator(inst);
this._finished(inst, label, '');
},
/** Finish with the calculator.
@private
@param inst {object} The instance settings.
@param label {string} The button label.
@param value {string} The new field value. */
_finished: function(inst, label, value) {
if (inst._inline) {
this._curInst = inst;
}
else {
inst._input.val(value);
}
this._sendButton(inst, label);
this.hide(inst._input[0]);
}
});
var plugin = $.calculator;
/* The definitions of the buttons that may appear on the calculator.
Fields are ID, display text, button type, function,
class(es), field name, keystroke, keystroke name. */
var defaultKeys = [
['_0', '0', plugin.digit, null, '', '0', '0'],
['_1', '1', plugin.digit, null, '', '1', '1'],
['_2', '2', plugin.digit, null, '', '2', '2'],
['_3', '3', plugin.digit, null, '', '3', '3'],
['_4', '4', plugin.digit, null, '', '4', '4'],
['_5', '5', plugin.digit, null, '', '5', '5'],
['_6', '6', plugin.digit, null, '', '6', '6'],
['_7', '7', plugin.digit, null, '', '7', '7'],
['_8', '8', plugin.digit, null, '', '8', '8'],
['_9', '9', plugin.digit, null, '', '9', '9'],
['_A', 'A', plugin.digit, null, 'hex-digit', 'A', 'a'],
['_B', 'B', plugin.digit, null, 'hex-digit', 'B', 'b'],
['_C', 'C', plugin.digit, null, 'hex-digit', 'C', 'c'],
['_D', 'D', plugin.digit, null, 'hex-digit', 'D', 'd'],
['_E', 'E', plugin.digit, null, 'hex-digit', 'E', 'e'],
['_F', 'F', plugin.digit, null, 'hex-digit', 'F', 'f'],
['_.', '.', plugin.digit, null, 'decimal', 'DECIMAL', '.'],
['_+', '+', plugin.binary, plugin._add, 'arith add', 'ADD', '+'],
['_-', '-', plugin.binary, plugin._subtract, 'arith subtract', 'SUBTRACT', '-'],
['_*', '*', plugin.binary, plugin._multiply, 'arith multiply', 'MULTIPLY', '*'],
['_/', '/', plugin.binary, plugin._divide, 'arith divide', 'DIVIDE', '/'],
['_%', '%', plugin.unary, plugin._percent, 'arith percent', 'PERCENT', '%'],
['_=', '=', plugin.unary, plugin._equals, 'arith equals', 'EQUALS', '='],
['+-', '±', plugin.unary, plugin._plusMinus, 'arith plus-minus', 'PLUS_MINUS', '#'],
['PI', 'π', plugin.unary, plugin._pi, 'pi', 'PI', 'p'],
['1X', '1/x', plugin.unary, plugin._inverse, 'fn inverse', 'INV', 'i'],
['LG', 'log', plugin.unary, plugin._log, 'fn log', 'LOG', 'l'],
['LN', 'ln', plugin.unary, plugin._ln, 'fn ln', 'LN', 'n'],
['EX', 'eⁿ', plugin.unary, plugin._exp, 'fn exp', 'EXP', 'E'],
['SQ', 'x²', plugin.unary, plugin._sqr, 'fn sqr', 'SQR', '@'],
['SR', '√', plugin.unary, plugin._sqrt, 'fn sqrt', 'SQRT', '!'],
['XY', 'x^y', plugin.binary, plugin._power, 'fn power', 'POWER', '^'],
['RN', 'rnd', plugin.unary, plugin._random, 'random', 'RANDOM', '?'],
['SN', 'sin', plugin.unary, plugin._sin, 'trig sin', 'SIN', 's'],
['CS', 'cos', plugin.unary, plugin._cos, 'trig cos', 'COS', 'o'],
['TN', 'tan', plugin.unary, plugin._tan, 'trig tan', 'TAN', 't'],
['AS', 'asin', plugin.unary, plugin._asin, 'trig asin', 'ASIN', 'S'],
['AC', 'acos', plugin.unary, plugin._acos, 'trig acos', 'ACOS', 'O'],
['AT', 'atan', plugin.unary, plugin._atan, 'trig atan', 'ATAN', 'T'],
['MC', '#memClear', plugin.unary, plugin._memClear, 'memory mem-clear', 'MEM_CLEAR', 'x'],
['MR', '#memRecall', plugin.unary, plugin._memRecall, 'memory mem-recall', 'MEM_RECALL', 'r'],
['MS', '#memStore', plugin.unary, plugin._memStore, 'memory mem-store', 'MEM_STORE', 'm'],
['M+', '#memAdd', plugin.unary, plugin._memAdd, 'memory mem-add', 'MEM_ADD', '>'],
['M-', '#memSubtract', plugin.unary, plugin._memSubtract, 'memory mem-subtract', 'MEM_SUBTRACT', '<'],
['BB', '#base2', plugin.control, plugin._base2, 'base base2', 'BASE_2', 'B'],
['BO', '#base8', plugin.control, plugin._base8, 'base base8', 'BASE_8', 'C'],
['BD', '#base10', plugin.control, plugin._base10, 'base base10', 'BASE_10', 'D'],
['BH', '#base16', plugin.control, plugin._base16, 'base base16', 'BASE_16', 'H'],
['DG', '#degrees', plugin.control, plugin._degrees, 'angle degrees', 'DEGREES', 'G'],
['RD', '#radians', plugin.control, plugin._radians, 'angle radians', 'RADIANS', 'R'],
['BS', '#backspace', plugin.control, plugin._undo, 'undo', 'UNDO', 8, 'BSp'], // backspace
['CE', '#clearError', plugin.control, plugin._clearError, 'clear-error', 'CLEAR_ERROR', 36, 'Hom'], // home
['CA', '#clear', plugin.control, plugin._clear, 'clear', 'CLEAR', 35, 'End'], // end
['@X', '#close', plugin.control, plugin._close, 'close', 'CLOSE', 27, 'Esc'], // escape
['@U', '#use', plugin.control, plugin._use, 'use', 'USE', 13, 'Ent'], // enter
['@E', '#erase', plugin.control, plugin._erase, 'erase', 'ERASE', 46, 'Del'], // delete
[' ', '', plugin.space, null, 'space', 'SPACE'],
['_ ', '', plugin.space, null, 'half-space', 'HALF_SPACE'],
['??', '??', plugin.unary, plugin._noOp]
];
// Initialise the key definitions
$.each(defaultKeys, function(i, keyDef) {
plugin.addKeyDef.apply(plugin, keyDef);
});
// Add the calculator division and external click check
$(function() {
$('body').append(plugin.mainDiv).
on('mousedown.' + pluginName, plugin._checkExternalClick);
});
})(jQuery);
//}}}
!jquery.plugin.js
//{{{
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false;
// The base JQClass implementation (does nothing)
window.JQClass = function(){};
// Collection of derived classes
JQClass.classes = {};
// Create a new JQClass that inherits from this class
JQClass.extend = function extender(prop) {
var base = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == 'function' &&
typeof base[name] == 'function' ?
(function(name, fn){
return function() {
var __super = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = function(args) {
return base[name].apply(this, args || []);
};
var ret = fn.apply(this, arguments);
// The method only need to be bound temporarily, so we
// remove it when we're done executing
this._super = __super;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function JQClass() {
// All construction is actually done in the init method
if (!initializing && this._init) {
this._init.apply(this, arguments);
}
}
// Populate our constructed prototype object
JQClass.prototype = prototype;
// Enforce the constructor to be what we expect
JQClass.prototype.constructor = JQClass;
// And make this class extendable
JQClass.extend = extender;
return JQClass;
};
})();
(function($) { // Ensure $, encapsulate
/** Abstract base class for collection plugins v1.0.1.
Written by Keith Wood (kbwood{at}iinet.com.au) December 2013.
Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license.
@module $.JQPlugin
@abstract */
JQClass.classes.JQPlugin = JQClass.extend({
/** Name to identify this plugin.
@example name: 'tabs' */
name: 'plugin',
/** Default options for instances of this plugin (default: {}).
@example defaultOptions: {
selectedClass: 'selected',
triggers: 'click'
} */
defaultOptions: {},
/** Options dependent on the locale.
Indexed by language and (optional) country code, with '' denoting the default language (English/US).
@example regionalOptions: {
'': {
greeting: 'Hi'
}
} */
regionalOptions: {},
/** Names of getter methods - those that can't be chained (default: []).
@example _getters: ['activeTab'] */
_getters: [],
/** Retrieve a marker class for affected elements.
@private
@return {string} The marker class. */
_getMarker: function() {
return 'is-' + this.name;
},
/** Initialise the plugin.
Create the jQuery bridge - plugin name <code>xyz</code>
produces <code>$.xyz</code> and <code>$.fn.xyz</code>. */
_init: function() {
// Apply default localisations
$.extend(this.defaultOptions, (this.regionalOptions && this.regionalOptions['']) || {});
// Camel-case the name
var jqName = camelCase(this.name);
// Expose jQuery singleton manager
$[jqName] = this;
// Expose jQuery collection plugin
$.fn[jqName] = function(options) {
var otherArgs = Array.prototype.slice.call(arguments, 1);
if ($[jqName]._isNotChained(options, otherArgs)) {
return $[jqName][options].apply($[jqName], [this[0]].concat(otherArgs));
}
return this.each(function() {
if (typeof options === 'string') {
if (options[0] === '_' || !$[jqName][options]) {
throw 'Unknown method: ' + options;
}
$[jqName][options].apply($[jqName], [this].concat(otherArgs));
}
else {
$[jqName]._attach(this, options);
}
});
};
},
/** Set default values for all subsequent instances.
@param options {object} The new default options.
@example $.plugin.setDefauls({name: value}) */
setDefaults: function(options) {
$.extend(this.defaultOptions, options || {});
},
/** Determine whether a method is a getter and doesn't permit chaining.
@private
@param name {string} The method name.
@param otherArgs {any[]} Any other arguments for the method.
@return {boolean} True if this method is a getter, false otherwise. */
_isNotChained: function(name, otherArgs) {
if (name === 'option' && (otherArgs.length === 0 ||
(otherArgs.length === 1 && typeof otherArgs[0] === 'string'))) {
return true;
}
return $.inArray(name, this._getters) > -1;
},
/** Initialise an element. Called internally only.
Adds an instance object as data named for the plugin.
@param elem {Element} The element to enhance.
@param options {object} Overriding settings. */
_attach: function(elem, options) {
elem = $(elem);
if (elem.hasClass(this._getMarker())) {
return;
}
elem.addClass(this._getMarker());
options = $.extend({}, this.defaultOptions, this._getMetadata(elem), options || {});
var inst = $.extend({name: this.name, elem: elem, options: options},
this._instSettings(elem, options));
elem.data(this.name, inst); // Save instance against element
this._postAttach(elem, inst);
this.option(elem, options);
},
/** Retrieve additional instance settings.
Override this in a sub-class to provide extra settings.
@param elem {jQuery} The current jQuery element.
@param options {object} The instance options.
@return {object} Any extra instance values.
@example _instSettings: function(elem, options) {
return {nav: elem.find(options.navSelector)};
} */
_instSettings: function(elem, options) {
return {};
},
/** Plugin specific post initialisation.
Override this in a sub-class to perform extra activities.
@param elem {jQuery} The current jQuery element.
@param inst {object} The instance settings.
@example _postAttach: function(elem, inst) {
elem.on('click.' + this.name, function() {
...
});
} */
_postAttach: function(elem, inst) {
},
/** Retrieve metadata configuration from the element.
Metadata is specified as an attribute:
<code>data-<plugin name>="<setting name>: '<value>', ..."</code>.
Dates should be specified as strings in this format: 'new Date(y, m-1, d)'.
@private
@param elem {jQuery} The source element.
@return {object} The inline configuration or {}. */
_getMetadata: function(elem) {
try {
var data = elem.data(this.name.toLowerCase()) || '';
data = data.replace(/'/g, '"');
data = data.replace(/([a-zA-Z0-9]+):/g, function(match, group, i) {
var count = data.substring(0, i).match(/"/g); // Handle embedded ':'
return (!count || count.length % 2 === 0 ? '"' + group + '":' : group + ':');
});
data = $.parseJSON('{' + data + '}');
for (var name in data) { // Convert dates
var value = data[name];
if (typeof value === 'string' && value.match(/^new Date\((.*)\)$/)) {
data[name] = eval(value);
}
}
return data;
}
catch (e) {
return {};
}
},
/** Retrieve the instance data for element.
@param elem {Element} The source element.
@return {object} The instance data or {}. */
_getInst: function(elem) {
return $(elem).data(this.name) || {};
},
/** Retrieve or reconfigure the settings for a plugin.
@param elem {Element} The source element.
@param name {object|string} The collection of new option values or the name of a single option.
@param [value] {any} The value for a single named option.
@return {any|object} If retrieving a single value or all options.
@example $(selector).plugin('option', 'name', value)
$(selector).plugin('option', {name: value, ...})
var value = $(selector).plugin('option', 'name')
var options = $(selector).plugin('option') */
option: function(elem, name, value) {
elem = $(elem);
var inst = elem.data(this.name);
if (!name || (typeof name === 'string' && value == null)) {
var options = (inst || {}).options;
return (options && name ? options[name] : options);
}
if (!elem.hasClass(this._getMarker())) {
return;
}
var options = name || {};
if (typeof name === 'string') {
options = {};
options[name] = value;
}
this._optionsChanged(elem, inst, options);
$.extend(inst.options, options);
},
/** Plugin specific options processing.
Old value available in <code>inst.options[name]</code>, new value in <code>options[name]</code>.
Override this in a sub-class to perform extra activities.
@param elem {jQuery} The current jQuery element.
@param inst {object} The instance settings.
@param options {object} The new options.
@example _optionsChanged: function(elem, inst, options) {
if (options.name != inst.options.name) {
elem.removeClass(inst.options.name).addClass(options.name);
}
} */
_optionsChanged: function(elem, inst, options) {
},
/** Remove all trace of the plugin.
Override <code>_preDestroy</code> for plugin-specific processing.
@param elem {Element} The source element.
@example $(selector).plugin('destroy') */
destroy: function(elem) {
elem = $(elem);
if (!elem.hasClass(this._getMarker())) {
return;
}
this._preDestroy(elem, this._getInst(elem));
elem.removeData(this.name).removeClass(this._getMarker());
},
/** Plugin specific pre destruction.
Override this in a sub-class to perform extra activities and undo everything that was
done in the <code>_postAttach</code> or <code>_optionsChanged</code> functions.
@param elem {jQuery} The current jQuery element.
@param inst {object} The instance settings.
@example _preDestroy: function(elem, inst) {
elem.off('.' + this.name);
} */
_preDestroy: function(elem, inst) {
}
});
/** Convert names from hyphenated to camel-case.
@private
@param value {string} The original hyphenated name.
@return {string} The camel-case version. */
function camelCase(name) {
return name.replace(/-([a-z])/g, function(match, group) {
return group.toUpperCase();
});
}
/** Expose the plugin base.
@namespace "$.JQPlugin" */
$.JQPlugin = {
/** Create a new collection plugin.
@memberof "$.JQPlugin"
@param [superClass='JQPlugin'] {string} The name of the parent class to inherit from.
@param overrides {object} The property/function overrides for the new class.
@example $.JQPlugin.createPlugin({
name: 'tabs',
defaultOptions: {selectedClass: 'selected'},
_initSettings: function(elem, options) { return {...}; },
_postAttach: function(elem, inst) { ... }
}); */
createPlugin: function(superClass, overrides) {
if (typeof superClass === 'object') {
overrides = superClass;
superClass = 'JQPlugin';
}
superClass = camelCase(superClass);
var className = camelCase(overrides.name);
JQClass.classes[className] = JQClass.classes[superClass].extend(overrides);
new JQClass.classes[className]();
}
};
})(jQuery);
//}}}
!jquery.calculator-fr.js
//{{{
/* http://keith-wood.name/calculator.html
French initialisation for the jQuery calculator extension
Written by PiwEL (piwel{at}piwel.fr). */
(function($) { // hide the namespace
$.calculator.regionalOptions['fr'] = {
decimalChar: ',',
buttonText: '...', buttonStatus: 'Ouvrir la calculatrice',
closeText: 'Fermer', closeStatus: 'Fermer la calculatrice',
useText: 'Utiliser', useStatus: 'Utiliser la valeur actuelle',
eraseText: 'Effacer', eraseStatus: 'Effacer la valeur',
backspaceText: 'DF', backspaceStatus: 'Effacer le dernier chiffre',
clearErrorText: 'CE', clearErrorStatus: 'Effacer le dernier nombre',
clearText: 'CT', clearStatus: 'Réinitialiser la calculatrice',
memClearText: 'MD', memClearStatus: 'Vider la mémoire',
memRecallText: 'MS', memRecallStatus: 'Récupérer la valeur de la mémoire',
memStoreText: 'MC', memStoreStatus: 'Sauvegarder la valeur dans la mémoire',
memAddText: 'M+', memAddStatus: 'Ajouter à la mémoire',
memSubtractText: 'M-', memSubtractStatus: 'Supprimer de la mémoire',
base2Text: 'Bin', base2Status: 'Conversion en binaire',
base8Text: 'Oct', base8Status: 'Conversion en octal',
base10Text: 'Déc', base10Status: 'Conversion en décimal',
base16Text: 'Hex', base16Status: 'Conversion en hexadécimal',
degreesText: 'Deg', degreesStatus: ' Conversion en degrés',
radiansText: 'Rad', radiansStatus: ' Conversion en radians',
isRTL: false};
$.calculator.setDefaults($.calculator.regionalOptions['fr']);
})(jQuery);
//}}}
/***
|Name|jquery.keyboard.js|
|Source|https://github.com/Mottie/Keyboard|
|Documentation|https://github.com/Mottie/Keyboard/wiki|
|Version|1.30.4|
|License|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
|Description|A jQuery on-screen keyboard (OSK) plugin that works in the browser|
!!!!!Characters with diacritics
<<<
Here are some default combination examples available with the {{{qwerty}}} keyboard layout
* {{{'}}} + vowel ( vowel with acute accent, e.g. {{{'}}} + {{{e}}} = {{{é}}} )
* {{{`}}} + vowel ( vowel with grave accent, e.g., {{{`}}} + {{{e}}} = {{{è}}} )
* {{{"}}} + vowel ( vowel with diaresis, e.g., {{{"}}} + {{{e}}} = {{{ë}}} )
* {{{^}}} + vowel ( vowel with circumflex accent, e.g., {{{^}}} + {{{e}}} = {{{ê}}} )
* {{{~}}} + certain letters ( letter with tilde, e.g. {{{~}}} + {{{n}}} = {{{ñ}}}, {{{~}}} + {{{o}}} = {{{õ}}} )
<<<
!!!!!CSS
//{{{
/* keyboard - jQuery UI Widget * /
.ui-keyboard {
text-align: center;
padding: .3em;
position: absolute;
left: 0;
top: 0;
z-index: 16000;
/* see issue #484 * /
-ms-touch-action: manipulation;
touch-action: manipulation;
}
.ui-keyboard-has-focus {
z-index: 16001;
}
.ui-keyboard div {
font-size: 1.1em;
}
.ui-keyboard[contenteditable] {
white-space: pre;
}
.ui-keyboard-button {
height: 2em;
min-width: 2em;
margin: .1em;
cursor: pointer;
overflow: hidden;
line-height: 2em;
-moz-user-focus: ignore;
}
.ui-keyboard-button span {
padding: 0;
margin: 0;
white-space: nowrap;
display: inline-block;
}
.ui-keyboard-button-endrow {
clear: left;
}
.ui-keyboard-space {
width: 15em;
}
/* see http://nicolasgallagher.com/another-css-image-replacement-technique/ * /
.ui-keyboard-space span, .ui-keyboard-empty span {
font: 0/0 a;
text-shadow: none;
color: transparent;
}
.ui-keyboard-preview-wrapper {
text-align: center;
position: relative;
overflow: hidden;
}
/* width is calculated in IE, since 99% = 99% full browser width =( * /
.ui-keyboard-preview {
text-align: left;
margin: 0 0 3px 0;
display: inline;
width: 99%;
}
.ui-keyboard-keyset {
text-align: center;
white-space: nowrap;
}
.ui-keyboard-input {
text-align: left;
}
.ui-keyboard-input-current {
-moz-box-shadow: 0 0 5px #4d90fe;
-webkit-box-shadow: 0 0 5px #4d90fe;
box-shadow: 0 0 5px #4d90fe;
}
.ui-keyboard-placeholder {
color: #888;
}
/* disabled or readonly inputs, or use
input[disabled='disabled'] { color: #f00; } * /
.ui-keyboard-nokeyboard {
color: #888;
border-color: #888;
}
.ui-keyboard-spacer {
display: inline-block;
width: 1px;
height: 0;
cursor: default;
}
.ui-keyboard-NBSP span, .ui-keyboard-ZWSP span, .ui-keyboard-ZWNJ span,
.ui-keyboard-ZWJ span, .ui-keyboard-LRM span, .ui-keyboard-RLM span {
font-size: 0.5em;
line-height: 1.5em;
white-space: normal;
}
/* combo key styling - toggles diacritics on/off * /
.ui-keyboard-button.ui-keyboard-combo.ui-state-default {
border-color: #ffaf0f;
}
/* (in)valid inputs * /
button.ui-keyboard-accept.ui-keyboard-valid-input {
border-color: #0c0;
background: #080;
color: #fff;
}
button.ui-keyboard-accept.ui-keyboard-valid-input:not([disabled]):hover {
background: #0a0;
}
button.ui-keyboard-accept.ui-keyboard-invalid-input {
border-color: #c00;
background: #800;
color: #fff;
opacity: 0.5;
filter: alpha(opacity=50);
}
button.ui-keyboard-accept.ui-keyboard-invalid-input:not([disabled]):hover {
background: #a00;
}
/*** Caret extension definition *** /
/* margin-top => is added to the caret height (top & bottom) * /
.ui-keyboard-caret {
background: #c00;
width: 1px;
margin-top: 3px;
}
/*** jQuery Mobile definitions *** /
/* jQuery Mobile styles - need wider buttons because of font size and
text-overflow:ellipsis * /
div.ui-body.ui-keyboard button.ui-keyboard-button.ui-btn {
padding: 0.5em 1em;
border-color: transparent;
}
.ui-body .ui-keyboard-button {
width: 3em;
height: 3em;
display: inline-block;
}
.ui-body .ui-keyboard-widekey {
width: 5.5em;
}
.ui-body .ui-keyboard-space {
width: 15em;
}
.ui-body .ui-keyboard-space span {
visibility: hidden; /* hides the ellipsis * /
}
.ui-body .ui-keyboard-keyset {
line-height: 0.5em;
}
.ui-body input.ui-input-text, .ui-body textarea.ui-input-text {
width: 95%;
}
/* over-ride padding set by mobile ui theme - needed because the mobile script
wraps button text with several more spans * /
.ui-body .ui-btn-inner {
height: 2em;
padding: 0.2em 0;
margin: 0;
}
.ui-body .ui-btn {
margin: 0;
font-size: 13px; /* mobile default size is 13px * /
}
/* override Bootstrap excessive button padding * /
button.ui-keyboard-button.btn {
padding: 1px 6px;
}
/* enable/disable icons * /
button.ui-keyboard-toggle span {
width: .8em;
height: .8em;
display: inline-block;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
/* unlocked icon (keyboard enabled) * /
button.ui-keyboard-toggle span {
/* light theme unlocked icon - fill: #111 * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgZmlsbD0iIzExMSI+PHBhdGggZD0iTTguNyw0LjRINy41SDUuMHYtMS45YzAtMS40LTEuMS0yLjUtMi41LTIuNWMtMS40LDAtMi41LDEuMS0yLjUsMi41djEuOWgxLjIgdi0xLjljMC0wLjcsMC42LTEuMiwxLjItMS4yczEuMiwwLjYsMS4yLDEuMnYxLjljLTAuNywwLTEuMiwwLjYtMS4yLDEuMlY4LjggYzAsMC43LDAuNiwxLjIsMS4yLDEuMmg1LjBDOS40LDEwLDEwLDkuNCwxMCw4LjhWNS42QzEwLDUuMCw5LjQsNC40LDguOCw0LjR6IE02LjYsNy40djEuMCBjMCwwLjItMC4xLDAuMy0wLjMsMC4zUzYuMCw4LjYsNi4wLDguNFY3LjRjLTAuMi0wLjEtMC4zLTAuMy0wLjMtMC41YzAtMC4zLDAuMy0wLjYsMC42LTAuNiBTNi45LDYuNiw2LjksNi45QzYuOSw3LjEsNi44LDcuMyw2LjYsNy40eiIvPjwvc3ZnPg==);
}
.ui-keyboard-dark-theme button.ui-keyboard-toggle span {
/* dark theme unlocked icon - fill: #eee * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgZmlsbD0iI2VlZSI+PHBhdGggZD0iTTguNyw0LjRINy41SDUuMHYtMS45YzAtMS40LTEuMS0yLjUtMi41LTIuNWMtMS40LDAtMi41LDEuMS0yLjUsMi41djEuOWgxLjIgdi0xLjljMC0wLjcsMC42LTEuMiwxLjItMS4yczEuMiwwLjYsMS4yLDEuMnYxLjljLTAuNywwLTEuMiwwLjYtMS4yLDEuMlY4LjggYzAsMC43LDAuNiwxLjIsMS4yLDEuMmg1LjBDOS40LDEwLDEwLDkuNCwxMCw4LjhWNS42QzEwLDUuMCw5LjQsNC40LDguOCw0LjR6IE02LjYsNy40djEuMCBjMCwwLjItMC4xLDAuMy0wLjMsMC4zUzYuMCw4LjYsNi4wLDguNFY3LjRjLTAuMi0wLjEtMC4zLTAuMy0wLjMtMC41YzAtMC4zLDAuMy0wLjYsMC42LTAuNiBTNi45LDYuNiw2LjksNi45QzYuOSw3LjEsNi44LDcuMyw2LjYsNy40eiIvPjwvc3ZnPg==);
}
/* locked icon (keyboard disabled) * /
button.ui-keyboard-toggle.ui-keyboard-disabled span {
/* light theme locked icon - fill: #111 * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgZmlsbD0iIzExMSI+PHBhdGggZD0iTTcuNCA0LjRWMi41YzAtMS40LTEuMS0yLjUtMi41LTIuNWMtMS40IDAtMi41IDEuMS0yLjUgMi41djEuOSBjLTAuNyAwLTEuMiAwLjUtMS4yIDEuMnYzLjFDMS4zIDkuNCAxLjggMTAgMi41IDEwaDQuOWMwLjcgMCAxLjItMC42IDEuMi0xLjJWNS42IEM4LjcgNC45IDguMSA0LjQgNy40IDQuNHogTTUuMyA3LjR2MS4wYzAgMC4yLTAuMSAwLjMtMC4zIDAuM2MtMC4yIDAtMC4zLTAuMS0wLjMtMC4zVjcuNCBjLTAuMi0wLjEtMC4zLTAuMy0wLjMtMC41YzAtMC4zIDAuMy0wLjYgMC42LTAuNmMwLjMgMCAwLjYgMC4zIDAuNiAwLjYgQzUuNiA3LjEgNS41IDcuMyA1LjMgNy40eiBNNi4yIDQuNEgzLjdWMi41YzAtMC43IDAuNS0xLjIgMS4yLTEuMmMwLjcgMCAxLjIgMC42IDEuMiAxLjIgVjQuNHoiLz48L3N2Zz4=);
}
.ui-keyboard-dark-theme button.ui-keyboard-toggle.ui-keyboard-disabled span {
/* dark theme locked icon - fill: #eee * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgZmlsbD0iI2VlZSI+PHBhdGggZD0iTTcuNCA0LjRWMi41YzAtMS40LTEuMS0yLjUtMi41LTIuNWMtMS40IDAtMi41IDEuMS0yLjUgMi41djEuOSBjLTAuNyAwLTEuMiAwLjUtMS4yIDEuMnYzLjFDMS4zIDkuNCAxLjggMTAgMi41IDEwaDQuOWMwLjcgMCAxLjItMC42IDEuMi0xLjJWNS42IEM4LjcgNC45IDguMSA0LjQgNy40IDQuNHogTTUuMyA3LjR2MS4wYzAgMC4yLTAuMSAwLjMtMC4zIDAuM2MtMC4yIDAtMC4zLTAuMS0wLjMtMC4zVjcuNCBjLTAuMi0wLjEtMC4zLTAuMy0wLjMtMC41YzAtMC4zIDAuMy0wLjYgMC42LTAuNmMwLjMgMCAwLjYgMC4zIDAuNiAwLjYgQzUuNiA3LjEgNS41IDcuMyA1LjMgNy40eiBNNi4yIDQuNEgzLjdWMi41YzAtMC43IDAuNS0xLjIgMS4yLTEuMmMwLjcgMCAxLjIgMC42IDEuMiAxLjIgVjQuNHoiLz48L3N2Zz4=);
}
.ui-keyboard.ui-keyboard-disabled button:not(.ui-keyboard-toggle),
.ui-keyboard.ui-keyboard-disabled input {
opacity: 0.5;
}
/*** Alt-Keys Popup extension *** /
/* clickable overlay on top of keyboard to hide the popup * /
.ui-keyboard-overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
}
/* the actual popup styling, class names from the css.container option are also
added * /
.ui-keyboard-popup {
display: inline-block;
/* default buttons are 2em wide + .1em margin on either side (set in
.ui-keyboard-button definition); so use multiples of 2.2em for a max-width
if you don't want any extra white space on the sides, e.g.
5 buttons * 2.2em = 11em,
6 buttons * 2.2em = 13.2em, etc
* /
max-width: 22em; /* 10 buttons * /
}
.ui-keyboard.ui-keyboard-popup-open .ui-keyboard-keyset .ui-keyboard-button {
/* Disable keys under overlay while popup is open - see #654 * /
pointer-events: none;
}
/*** Extender keyboard extension *** /
div.ui-keyboard-extender {
margin-left: 5px;
}
button.ui-keyboard-extender span {
width: .9em;
height: .9em;
display: inline-block;
margin-bottom: 3px;
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
/* light theme extender icon - fill: #111 * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgc3R5bGU9ImZpbGw6IzExMSI+PGc+PHBhdGggc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzExMSIgZD0iTSAwLjUsNC41IDE1LjUsNC41IDE1LjUsMTUuNSAwLjUsMTUuNSBaIj48L3BhdGg+PHJlY3Qgd2lkdGg9IjIiIGhlaWdodD0iMiIgeD0iMiIgeT0iNiI+PC9yZWN0PjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjUiIHk9IjYiPjwvcmVjdD48cmVjdCB3aWR0aD0iMiIgaGVpZ2h0PSIyIiB4PSI4IiB5PSI2Ij48L3JlY3Q+PHBhdGggZD0ibSAxMSw2IDMsMCAwLDUgLTIsMCAwLC0zIC0xLDAgeiI+PC9wYXRoPjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjEyIiB5PSIxMiI+PC9yZWN0PjxyZWN0IHdpZHRoPSI2IiBoZWlnaHQ9IjIiIHg9IjUiIHk9IjEyIj48L3JlY3Q+PHJlY3Qgd2lkdGg9IjIiIGhlaWdodD0iMiIgeD0iOSIgeT0iOSI+PC9yZWN0PjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjYiIHk9IjkiPjwvcmVjdD48cmVjdCB3aWR0aD0iMiIgaGVpZ2h0PSIyIiB4PSIyIiB5PSIxMiI+PC9yZWN0PjxyZWN0IHdpZHRoPSIzIiBoZWlnaHQ9IjIiIHg9IjIiIHk9IjkiPjwvcmVjdD48L2c+PC9zdmc+);
}
.ui-keyboard-dark-theme button.ui-keyboard-extender span {
/* dark theme extender icon - fill: #eee * /
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgc3R5bGU9ImZpbGw6I2VlZSI+PGc+PHBhdGggc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6I2VlZSIgZD0iTSAwLjUsNC41IDE1LjUsNC41IDE1LjUsMTUuNSAwLjUsMTUuNSBaIj48L3BhdGg+PHJlY3Qgd2lkdGg9IjIiIGhlaWdodD0iMiIgeD0iMiIgeT0iNiI+PC9yZWN0PjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjUiIHk9IjYiPjwvcmVjdD48cmVjdCB3aWR0aD0iMiIgaGVpZ2h0PSIyIiB4PSI4IiB5PSI2Ij48L3JlY3Q+PHBhdGggZD0ibSAxMSw2IDMsMCAwLDUgLTIsMCAwLC0zIC0xLDAgeiI+PC9wYXRoPjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjEyIiB5PSIxMiI+PC9yZWN0PjxyZWN0IHdpZHRoPSI2IiBoZWlnaHQ9IjIiIHg9IjUiIHk9IjEyIj48L3JlY3Q+PHJlY3Qgd2lkdGg9IjIiIGhlaWdodD0iMiIgeD0iOSIgeT0iOSI+PC9yZWN0PjxyZWN0IHdpZHRoPSIyIiBoZWlnaHQ9IjIiIHg9IjYiIHk9IjkiPjwvcmVjdD48cmVjdCB3aWR0aD0iMiIgaGVpZ2h0PSIyIiB4PSIyIiB5PSIxMiI+PC9yZWN0PjxyZWN0IHdpZHRoPSIzIiBoZWlnaHQ9IjIiIHg9IjIiIHk9IjkiPjwvcmVjdD48L2c+PC9zdmc+);
}
/* Media Queries (optimized for jQuery UI themes;
may be slightly off in jQuery Mobile themes) * /
/* 240 x 320 (small phone) * /
@media all and (max-width: 319px) {
.ui-keyboard div {
font-size: 9px;
}
.ui-keyboard .ui-keyboard-input {
font-size: 12px;
}
/* I don't own an iPhone so I have no idea how small this really is... is it
even clickable with your finger? * /
.ui-body .ui-btn {
margin: 0;
font-size: 9px;
}
.ui-body .ui-keyboard-button {
width: 1.8em;
height: 2.5em;
}
.ui-body .ui-keyboard-widekey {
width: 4em;
}
.ui-body .ui-keyboard-space {
width: 8em;
}
.ui-body .ui-btn-inner {
height: 2.5em;
padding: 0.3em 0;
}
}
/* 320 x 480 (iPhone) * /
@media all and (min-width: 320px) and (max-width: 479px) {
.ui-keyboard div {
font-size: 9px;
}
.ui-keyboard .ui-keyboard-input {
font-size: 14px;
}
/* I don't own an iPhone so I have no idea how small this really is... is it
even clickable with your finger? * /
.ui-body .ui-btn {
margin: 0;
font-size: 11px;
}
.ui-body .ui-keyboard-button {
width: 1.8em;
height: 3em;
}
.ui-body .ui-keyboard-widekey {
width: 4.5em;
}
.ui-body .ui-keyboard-space {
width: 10em;
}
.ui-body .ui-btn-inner {
height: 3em;
padding: 0.7em 0;
}
}
/* 480 x 640 (small tablet) * /
@media all and (min-width: 480px) and (max-width: 767px) {
.ui-keyboard div {
font-size: 13px;
}
.ui-keyboard .ui-keyboard-input {
font-size: 14px;
}
.ui-body .ui-btn {
margin: 0;
font-size: 10px;
}
.ui-body .ui-keyboard-button {
height: 2.5em;
}
.ui-body .ui-btn-inner {
height: 2.5em;
padding: 0.5em 0;
}
}
//}}}
//{{{
/*!
* jQuery UI CSS Framework 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/category/theming/
*
* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif
* /
/* Component containers
----------------------------------* /
.ui-widget {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget .ui-widget {
font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget.ui-widget-content {
border: 1px solid #c5c5c5;
}
.ui-widget-content {
border: 1px solid #dddddd;
background: #ffffff;
color: #333333;
}
.ui-widget-content a {
color: #333333;
}
.ui-widget-header {
border: 1px solid #dddddd;
background: #e9e9e9;
color: #333333;
font-weight: bold;
}
.ui-widget-header a {
color: #333333;
}
/* Interaction states
----------------------------------* /
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default,
.ui-button,
/* We use html here because we need a greater specificity to make sure disabled
works properly when clicked or hovered * /
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 1px solid #c5c5c5;
background: #f6f6f6;
font-weight: normal;
color: #454545;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited,
a.ui-button,
a:link.ui-button,
a:visited.ui-button,
.ui-button {
color: #454545;
text-decoration: none;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus,
.ui-button:hover,
.ui-button:focus {
border: 1px solid #cccccc;
background: #ededed;
font-weight: normal;
color: #2b2b2b;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited,
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited,
a.ui-button:hover,
a.ui-button:focus {
color: #2b2b2b;
text-decoration: none;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(94, 158, 214);
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active,
a.ui-button:active,
.ui-button:active,
.ui-button.ui-state-active:hover {
border: 1px solid #003eff;
background: #007fff;
font-weight: normal;
color: #ffffff;
}
.ui-icon-background,
.ui-state-active .ui-icon-background {
border: #003eff;
background-color: #ffffff;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
color: #ffffff;
text-decoration: none;
}
/* Interaction Cues
----------------------------------* /
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #dad55e;
background: #fffa90;
color: #777620;
}
.ui-state-checked {
border: 1px solid #dad55e;
background: #fffa90;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
color: #777620;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #f1a899;
background: #fddfdf;
color: #5f3f3f;
}
.ui-state-error a,
.ui-widget-content .ui-state-error a,
.ui-widget-header .ui-state-error a {
color: #5f3f3f;
}
.ui-state-error-text,
.ui-widget-content .ui-state-error-text,
.ui-widget-header .ui-state-error-text {
color: #5f3f3f;
}
.ui-priority-primary,
.ui-widget-content .ui-priority-primary,
.ui-widget-header .ui-priority-primary {
font-weight: bold;
}
.ui-priority-secondary,
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
filter:Alpha(Opacity=70); /* support: IE8 * /
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
filter:Alpha(Opacity=35); /* support: IE8 * /
background-image: none;
}
.ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 * /
}
/* Icons
----------------------------------* /
/* states and images * /
.ui-icon {
width: 16px;
height: 16px;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-widget-header .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon,
.ui-button:hover .ui-icon,
.ui-button:focus .ui-icon {
background-image: url("images/ui-icons_555555_256x240.png");
}
.ui-state-active .ui-icon,
.ui-button:active .ui-icon {
background-image: url("images/ui-icons_ffffff_256x240.png");
}
.ui-state-highlight .ui-icon,
.ui-button .ui-state-highlight.ui-icon {
background-image: url("images/ui-icons_777620_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("images/ui-icons_cc0000_256x240.png");
}
.ui-button .ui-icon {
background-image: url("images/ui-icons_777777_256x240.png");
}
/* positioning * /
.ui-icon-blank { background-position: 16px 16px; }
.ui-icon-caret-1-n { background-position: 0 0; }
.ui-icon-caret-1-ne { background-position: -16px 0; }
.ui-icon-caret-1-e { background-position: -32px 0; }
.ui-icon-caret-1-se { background-position: -48px 0; }
.ui-icon-caret-1-s { background-position: -65px 0; }
.ui-icon-caret-1-sw { background-position: -80px 0; }
.ui-icon-caret-1-w { background-position: -96px 0; }
.ui-icon-caret-1-nw { background-position: -112px 0; }
.ui-icon-caret-2-n-s { background-position: -128px 0; }
.ui-icon-caret-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -65px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -65px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 1px -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead * /
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------* /
/* Corner radius * /
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 3px;
}
/* Overlays * /
.ui-widget-overlay {
background: #aaaaaa;
opacity: .3;
filter: Alpha(Opacity=30); /* support: IE8 * /
}
.ui-widget-shadow {
-webkit-box-shadow: 0px 0px 5px #666666;
box-shadow: 0px 0px 5px #666666;
}
//}}}
!!!!!keyboard.svg
//{{{
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" style="fill:#888">
<g>
<path style="fill:none;stroke:#888" d="M 0.5,4.5 15.5,4.5 15.5,15.5 0.5,15.5 Z"></path>
<rect width="2" height="2" x="2" y="6"></rect>
<rect width="2" height="2" x="5" y="6"></rect>
<rect width="2" height="2" x="8" y="6"></rect>
<path d="m 11,6 3,0 0,5 -2,0 0,-3 -1,0 z"></path>
<rect width="2" height="2" x="12" y="12"></rect>
<rect width="6" height="2" x="5" y="12"></rect>
<rect width="2" height="2" x="9" y="9"></rect>
<rect width="2" height="2" x="6" y="9"></rect>
<rect width="2" height="2" x="2" y="12"></rect>
<rect width="3" height="2" x="2" y="9"></rect>
</g>
</svg>
//}}}
!!!!!Code
***/
//{{{
/*! jQuery UI Virtual Keyboard v1.30.4 *//*
Author: Jeremy Satterfield
Maintained: Rob Garrison (Mottie on github)
Licensed under the MIT License
An on-screen virtual keyboard embedded within the browser window which
will popup when a specified entry field is focused. The user can then
type and preview their input before Accepting or Canceling.
This plugin adds default class names to match jQuery UI theme styling.
Bootstrap & custom themes may also be applied - See
https://github.com/Mottie/Keyboard#themes
Requires:
jQuery v1.4.3+
Caret plugin (included)
Optional:
jQuery UI (position utility only) & CSS theme
jQuery mousewheel
Setup/Usage:
Please refer to https://github.com/Mottie/Keyboard/wiki
-----------------------------------------
Caret code modified from jquery.caret.1.02.js
Licensed under the MIT License:
http://www.opensource.org/licenses/mit-license.php
-----------------------------------------
*/
/*jshint browser:true, jquery:true, unused:false */
/*global require:false, define:false, module:false */
;(function (factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
}(function ($) {
'use strict';
var $keyboard = $.keyboard = function (el, options) {
var o, base = this;
base.version = '1.30.4';
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data('keyboard', base);
base.init = function () {
base.initialized = false;
base.isTextArea = base.el.nodeName.toLowerCase() === 'textarea';
base.isInput = base.el.nodeName.toLowerCase() === 'input';
// detect contenteditable
base.isContentEditable = !base.isTextArea &&
!base.isInput &&
base.el.isContentEditable;
var k, position, tmp,
kbcss = $keyboard.css,
kbevents = $keyboard.events;
if (
base.isInput &&
$.inArray((base.el.type || '').toLowerCase(), $keyboard.supportedInputTypes) < 0
) {
throw new TypeError('Input of type "' + base.el.type + '" is not supported; use type text, search, URL, tel or password');
}
base.settings = options || {};
// shallow copy position to prevent performance issues; see #357
if (options && options.position) {
position = $.extend({}, options.position);
options.position = null;
}
base.options = o = $.extend(true, {}, $keyboard.defaultOptions, options);
if (position) {
o.position = position;
options.position = position;
}
// keyboard is active (not destroyed);
base.el.active = true;
// unique keyboard namespace
base.namespace = '.keyboard' + Math.random().toString(16).slice(2);
// extension namespaces added here (to unbind listeners on base.$el upon destroy)
base.extensionNamespace = [];
// Shift and Alt key toggles, sets is true if a layout has more than one keyset
// used for mousewheel message
base.shiftActive = base.altActive = base.metaActive = base.sets = base.capsLock = false;
// Class names of the basic key set - meta keysets are handled by the keyname
base.rows = ['', '-shift', '-alt', '-alt-shift'];
base.inPlaceholder = base.$el.attr('placeholder') || '';
// html 5 placeholder/watermark
base.watermark = $keyboard.watermark && base.inPlaceholder !== '';
// convert mouse repeater rate (characters per second) into a time in milliseconds.
base.repeatTime = 1000 / (o.repeatRate || 20);
// delay in ms to prevent mousedown & touchstart from both firing events at the same time
o.preventDoubleEventTime = o.preventDoubleEventTime || 100;
// flag indication that a keyboard is open
base.isOpen = false;
// is mousewheel plugin loaded?
base.wheel = typeof $.fn.mousewheel === 'function';
// special character in regex that need to be escaped
base.escapeRegex = /[-\/\\^$*+?.()|[\]{}]/g;
// keyCode of keys always allowed to be typed
k = $keyboard.keyCodes;
// base.alwaysAllowed = [20,33,34,35,36,37,38,39,40,45,46];
base.alwaysAllowed = [
k.capsLock,
k.pageUp,
k.pageDown,
k.end,
k.home,
k.left,
k.up,
k.right,
k.down,
k.insert,
k.delete
];
base.$keyboard = [];
// keyboard enabled; set to false on destroy
base.enabled = true;
base.checkCaret = (o.lockInput || $keyboard.checkCaretSupport());
// disable problematic usePreview for contenteditable
if (base.isContentEditable) {
o.usePreview = false;
}
base.last = {
start: 0,
end: 0,
key: '',
val: '',
preVal: '',
layout: '',
virtual: true,
keyset: [false, false, false], // [shift, alt, meta]
wheel_$Keys: [],
wheelIndex: 0,
wheelLayers: []
};
// used when building the keyboard - [keyset element, row, index]
base.temp = ['', 0, 0];
// Callbacks
$.each([
kbevents.kbInit,
kbevents.kbBeforeVisible,
kbevents.kbVisible,
kbevents.kbHidden,
kbevents.inputCanceled,
kbevents.inputAccepted,
kbevents.kbBeforeClose,
kbevents.inputRestricted
], function (i, callback) {
if (typeof o[callback] === 'function') {
// bind callback functions within options to triggered events
base.$el.bind(callback + base.namespace + 'callbacks', o[callback]);
}
});
// Close with esc key & clicking outside
if (o.alwaysOpen) {
o.stayOpen = true;
}
tmp = $(document);
if (base.el.ownerDocument !== document) {
tmp = tmp.add(base.el.ownerDocument);
}
var bindings = 'keyup checkkeyboard mousedown touchstart ';
if (o.closeByClickEvent) {
bindings += 'click ';
}
// debounce bindings... see #542
tmp.bind(bindings.split(' ').join(base.namespace + ' '), function(e) {
clearTimeout(base.timer3);
base.timer3 = setTimeout(function() {
base.checkClose(e);
}, 1);
});
// Display keyboard on focus
base.$el
.addClass(kbcss.input + ' ' + o.css.input)
.attr({
'aria-haspopup': 'true',
'role': 'textbox'
});
// set lockInput if the element is readonly; or make the element readonly if lockInput is set
if (o.lockInput || base.el.readOnly) {
o.lockInput = true;
base.$el
.addClass(kbcss.locked)
.attr({
'readonly': 'readonly'
});
}
// add disabled/readonly class - dynamically updated on reveal
if (base.isUnavailable()) {
base.$el.addClass(kbcss.noKeyboard);
}
if (o.openOn) {
base.bindFocus();
}
// Add placeholder if not supported by the browser
if (
!base.watermark &&
base.getValue(base.$el) === '' &&
base.inPlaceholder !== '' &&
base.$el.attr('placeholder') !== ''
) {
// css watermark style (darker text)
base.$el.addClass(kbcss.placeholder);
base.setValue(base.inPlaceholder, base.$el);
}
base.$el.trigger(kbevents.kbInit, [base, base.el]);
// initialized with keyboard open
if (o.alwaysOpen) {
base.reveal();
}
base.initialized = true;
};
base.toggle = function () {
if (!base.hasKeyboard()) { return; }
var $toggle = base.$keyboard.find('.' + $keyboard.css.keyToggle),
locked = !base.enabled;
// prevent physical keyboard from working
base.preview.readonly = locked || base.options.lockInput;
// disable all buttons
base.$keyboard
.toggleClass($keyboard.css.keyDisabled, locked)
.find('.' + $keyboard.css.keyButton)
.not($toggle)
.attr('aria-disabled', locked)
.each(function() {
this.disabled = locked;
});
$toggle.toggleClass($keyboard.css.keyDisabled, locked);
// stop auto typing
if (locked && base.typing_options) {
base.typing_options.text = '';
}
// allow chaining
return base;
};
base.setCurrent = function () {
var kbcss = $keyboard.css,
// close any "isCurrent" keyboard (just in case they are always open)
$current = $('.' + kbcss.isCurrent),
kb = $current.data('keyboard');
// close keyboard, if not self
if (!$.isEmptyObject(kb) && kb.el !== base.el) {
kb.close(kb.options.autoAccept ? 'true' : false);
}
$current.removeClass(kbcss.isCurrent);
// ui-keyboard-has-focus is applied in case multiple keyboards have
// alwaysOpen = true and are stacked
$('.' + kbcss.hasFocus).removeClass(kbcss.hasFocus);
base.$el.addClass(kbcss.isCurrent);
base.$preview.focus();
base.$keyboard.addClass(kbcss.hasFocus);
base.isCurrent(true);
base.isOpen = true;
};
base.isUnavailable = function() {
return (
base.$el.is(':disabled') || (
!base.options.activeOnReadonly &&
base.$el.attr('readonly') &&
!base.$el.hasClass($keyboard.css.locked)
)
);
};
base.isCurrent = function (set) {
var cur = $keyboard.currentKeyboard || false;
if (set) {
cur = $keyboard.currentKeyboard = base.el;
} else if (set === false && cur === base.el) {
cur = $keyboard.currentKeyboard = '';
}
return cur === base.el;
};
base.hasKeyboard = function () {
return base.$keyboard && base.$keyboard.length > 0;
};
base.isVisible = function () {
return base.hasKeyboard() ? base.$keyboard.is(':visible') : false;
};
base.setFocus = function () {
var $el = base.$preview || base.$el;
if (!o.noFocus) {
$el.focus();
}
if (base.isContentEditable) {
$keyboard.setEditableCaret($el, base.last.start, base.last.end);
} else {
$keyboard.caret($el, base.last);
}
};
base.focusOn = function () {
if (!base || !base.el.active) {
// keyboard was destroyed
return;
}
if (!base.isVisible()) {
clearTimeout(base.timer);
base.reveal();
} else {
// keyboard already open, make it the current keyboard
base.setCurrent();
}
};
// add redraw method to make API more clear
base.redraw = function (layout) {
if (layout) {
// allow updating the layout by calling redraw
base.options.layout = layout;
}
// update keyboard after a layout change
if (base.$keyboard.length) {
base.last.preVal = '' + base.last.val;
base.saveLastChange();
base.setValue(base.last.val, base.$el);
base.removeKeyboard();
base.shiftActive = base.altActive = base.metaActive = false;
}
base.isOpen = o.alwaysOpen;
base.reveal(true);
return base;
};
base.reveal = function (redraw) {
var temp,
alreadyOpen = base.isOpen,
kbcss = $keyboard.css;
base.opening = !alreadyOpen;
// remove all 'extra' keyboards by calling close function
$('.' + kbcss.keyboard).not('.' + kbcss.alwaysOpen).each(function(){
var kb = $(this).data('keyboard');
if (!$.isEmptyObject(kb)) {
// this closes previous keyboard when clicking another input - see #515
kb.close(kb.options.autoAccept ? 'true' : false);
}
});
// Don't open if disabled
if (base.isUnavailable()) {
return;
}
base.$el.removeClass(kbcss.noKeyboard);
// Unbind focus to prevent recursion - openOn may be empty if keyboard is opened externally
if (o.openOn) {
base.$el.unbind($.trim((o.openOn + ' ').split(/\s+/).join(base.namespace + ' ')));
}
// build keyboard if it doesn't exist; or attach keyboard if it was removed, but not cleared
if (!base.$keyboard || base.$keyboard &&
(!base.$keyboard.length || $.contains(base.el.ownerDocument.body, base.$keyboard[0]))) {
base.startup();
}
// clear watermark
if (!base.watermark && base.getValue() === base.inPlaceholder) {
base.$el.removeClass(kbcss.placeholder);
base.setValue('', base.$el);
}
// save starting content, in case we cancel
base.originalContent = base.isContentEditable ?
base.$el.html() :
base.getValue(base.$el);
if (base.el !== base.preview && !base.isContentEditable) {
base.setValue(base.originalContent);
}
// disable/enable accept button
if (o.acceptValid && o.checkValidOnInit) {
base.checkValid();
}
if (o.resetDefault) {
base.shiftActive = base.altActive = base.metaActive = false;
}
base.showSet();
// beforeVisible event
if (!base.isVisible()) {
base.$el.trigger($keyboard.events.kbBeforeVisible, [base, base.el]);
}
if (
base.initialized ||
o.initialFocus ||
( !o.initialFocus && base.$el.hasClass($keyboard.css.initialFocus) )
) {
base.setCurrent();
}
// update keyboard - enabled or disabled?
base.toggle();
// show keyboard
base.$keyboard.show();
// adjust keyboard preview window width - save width so IE won't keep expanding (fix issue #6)
if (o.usePreview && $keyboard.msie) {
if (typeof base.width === 'undefined') {
base.$preview.hide(); // preview is 100% browser width in IE7, so hide the damn thing
base.width = Math.ceil(base.$keyboard.width()); // set input width to match the widest keyboard row
base.$preview.show();
}
base.$preview.width(base.width);
}
base.reposition();
base.checkDecimal();
// get preview area line height
// add roughly 4px to get line height from font height, works well for font-sizes from 14-36px
// needed for textareas
base.lineHeight = parseInt(base.$preview.css('lineHeight'), 10) ||
parseInt(base.$preview.css('font-size'), 10) + 4;
if (o.caretToEnd) {
temp = base.isContentEditable ? $keyboard.getEditableLength(base.el) : base.originalContent.length;
base.saveCaret(temp, temp);
}
// IE caret haxx0rs
if ($keyboard.allie) {
// sometimes end = 0 while start is > 0
if (base.last.end === 0 && base.last.start > 0) {
base.last.end = base.last.start;
}
// IE will have start -1, end of 0 when not focused (see demo: https://jsfiddle.net/Mottie/fgryQ/3/)
if (base.last.start < 0) {
// ensure caret is at the end of the text (needed for IE)
base.last.start = base.last.end = base.originalContent.length;
}
}
if (alreadyOpen || redraw) {
// restore caret position (userClosed)
$keyboard.caret(base.$preview, base.last);
base.opening = false;
return base;
}
// opening keyboard flag; delay allows switching between keyboards without immediately closing
// the keyboard
base.timer2 = setTimeout(function () {
var undef;
base.opening = false;
// Number inputs don't support selectionStart and selectionEnd
// Number/email inputs don't support selectionStart and selectionEnd
if (!/(number|email)/i.test(base.el.type) && !o.caretToEnd) {
// caret position is always 0,0 in webkit; and nothing is focused at this point... odd
// save caret position in the input to transfer it to the preview
// inside delay to get correct caret position
base.saveCaret(undef, undef, base.$el);
}
if (o.initialFocus || base.$el.hasClass($keyboard.css.initialFocus)) {
$keyboard.caret(base.$preview, base.last);
}
// save event time for keyboards with stayOpen: true
base.last.eventTime = new Date().getTime();
base.$el.trigger($keyboard.events.kbVisible, [base, base.el]);
base.timer = setTimeout(function () {
// get updated caret information after visible event - fixes #331
if (base) { // Check if base exists, this is a case when destroy is called, before timers fire
base.saveCaret();
}
}, 200);
}, 10);
// return base to allow chaining in typing extension
return base;
};
base.updateLanguage = function () {
// change language if layout is named something like 'french-azerty-1'
var layouts = $keyboard.layouts,
lang = o.language || layouts[o.layout] && layouts[o.layout].lang &&
layouts[o.layout].lang || [o.language || 'en'],
kblang = $keyboard.language;
// some languages include a dash, e.g. 'en-gb' or 'fr-ca'
// allow o.language to be a string or array...
// array is for future expansion where a layout can be set for multiple languages
lang = (Object.prototype.toString.call(lang) === '[object Array]' ? lang[0] : lang);
base.language = lang;
lang = lang.split('-')[0];
// set keyboard language
o.display = $.extend(true, {},
kblang.en.display,
kblang[lang] && kblang[lang].display || {},
base.settings.display
);
o.combos = $.extend(true, {},
kblang.en.combos,
kblang[lang] && kblang[lang].combos || {},
base.settings.combos
);
o.wheelMessage = kblang[lang] && kblang[lang].wheelMessage || kblang.en.wheelMessage;
// rtl can be in the layout or in the language definition; defaults to false
o.rtl = layouts[o.layout] && layouts[o.layout].rtl || kblang[lang] && kblang[lang].rtl || false;
// save default regex (in case loading another layout changes it)
if (kblang[lang] && kblang[lang].comboRegex) {
base.regex = kblang[lang].comboRegex;
}
// determine if US '.' or European ',' system being used
base.decimal = /^\./.test(o.display.dec);
base.$el
.toggleClass('rtl', o.rtl)
.css('direction', o.rtl ? 'rtl' : '');
};
base.startup = function () {
var kbcss = $keyboard.css;
// ensure base.$preview is defined; but don't overwrite it if keyboard is always visible
if (!((o.alwaysOpen || o.userClosed) && base.$preview)) {
base.makePreview();
}
if (!base.hasKeyboard()) {
// custom layout - create a unique layout name based on the hash
if (o.layout === 'custom') {
o.layoutHash = 'custom' + base.customHash();
}
base.layout = o.layout === 'custom' ? o.layoutHash : o.layout;
base.last.layout = base.layout;
base.updateLanguage();
if (typeof $keyboard.builtLayouts[base.layout] === 'undefined') {
if (typeof o.create === 'function') {
// create must call buildKeyboard() function; or create it's own keyboard
base.$keyboard = o.create(base);
} else if (!base.$keyboard.length) {
base.buildKeyboard(base.layout, true);
}
}
base.$keyboard = $keyboard.builtLayouts[base.layout].$keyboard.clone();
base.$keyboard.data('keyboard', base);
if ((base.el.id || '') !== '') {
// add ID to keyboard for styling purposes
base.$keyboard.attr('id', base.el.id + $keyboard.css.idSuffix);
}
base.makePreview();
}
// Add layout and laguage data-attibutes
base.$keyboard
.attr('data-' + kbcss.keyboard + '-layout', o.layout)
.attr('data-' + kbcss.keyboard + '-language', base.language);
base.$decBtn = base.$keyboard.find('.' + kbcss.keyPrefix + 'dec');
// add enter to allowed keys; fixes #190
if (o.enterNavigation || base.isTextArea) {
base.alwaysAllowed.push($keyboard.keyCodes.enter);
}
base.bindKeyboard();
base.$keyboard.appendTo(o.appendLocally ? base.$el.parent() : o.appendTo || 'body');
base.bindKeys();
// reposition keyboard on window resize
if (o.reposition && $.ui && $.ui.position && o.appendTo === 'body') {
$(window).bind('resize' + base.namespace, function () {
base.reposition();
});
}
};
base.reposition = function () {
base.position = $.isEmptyObject(o.position) ? false : o.position;
// position after keyboard is visible (required for UI position utility)
// and appropriately sized
if ($.ui && $.ui.position && base.position) {
base.position.of =
// get single target position
base.position.of ||
// OR target stored in element data (multiple targets)
base.$el.data('keyboardPosition') ||
// OR default @ element
base.$el;
base.position.collision = base.position.collision || 'flipfit flipfit';
base.position.at = o.usePreview ? o.position.at : o.position.at2;
if (base.isVisible()) {
base.$keyboard.position(base.position);
}
}
// make chainable
return base;
};
base.makePreview = function () {
if (o.usePreview) {
var indx, attrs, attr, removedAttr,
kbcss = $keyboard.css;
base.$preview = base.$el.clone(false)
.data('keyboard', base)
.removeClass(kbcss.placeholder + ' ' + kbcss.input)
.addClass(kbcss.preview + ' ' + o.css.input)
.attr('tabindex', '-1')
.show(); // for hidden inputs
base.preview = base.$preview[0];
// remove extraneous attributes.
removedAttr = /^(data-|id|aria-haspopup)/i;
attrs = base.$preview.get(0).attributes;
for (indx = attrs.length - 1; indx >= 0; indx--) {
attr = attrs[indx] && attrs[indx].name;
if (removedAttr.test(attr)) {
// remove data-attributes - see #351
base.preview.removeAttribute(attr);
}
}
// build preview container and append preview display
$('<div />')
.addClass(kbcss.wrapper)
.append(base.$preview)
.prependTo(base.$keyboard);
} else {
base.$preview = base.$el;
base.preview = base.el;
}
};
// Added in v1.26.8 to allow chaining of the caret function, e.g.
// keyboard.reveal().caret(4,5).insertText('test').caret('end');
base.caret = function(param1, param2) {
var result = $keyboard.caret(base.$preview, param1, param2),
wasSetCaret = result instanceof $;
// Caret was set, save last position & make chainable
if (wasSetCaret) {
base.saveCaret(result.start, result.end);
return base;
}
// return caret position if using .caret()
return result;
};
base.saveCaret = function (start, end, $el) {
if (base.isCurrent()) {
var p;
if (typeof start === 'undefined') {
// grab & save current caret position
p = $keyboard.caret($el || base.$preview);
} else {
p = $keyboard.caret($el || base.$preview, start, end);
}
base.last.start = typeof start === 'undefined' ? p.start : start;
base.last.end = typeof end === 'undefined' ? p.end : end;
}
};
base.saveLastChange = function (val) {
base.last.val = val || base.getValue(base.$preview || base.$el);
if (base.isContentEditable) {
base.last.elms = base.el.cloneNode(true);
}
};
base.setScroll = function () {
// Set scroll so caret & current text is in view
// needed for virtual keyboard typing, NOT manual typing - fixes #23
if (!base.isContentEditable && base.last.virtual) {
var scrollWidth, clientWidth, adjustment, direction,
value = base.last.val.substring(0, Math.max(base.last.start, base.last.end));
if (!base.$previewCopy) {
// clone preview
base.$previewCopy = base.$preview.clone()
.removeAttr('id') // fixes #334
.css({
position: 'absolute',
left: 0,
zIndex: -10,
visibility: 'hidden'
})
.addClass($keyboard.css.inputClone);
// prevent submitting content on form submission
base.$previewCopy[0].disabled = true;
if (!base.isTextArea) {
// make input zero-width because we need an accurate scrollWidth
base.$previewCopy.css({
'white-space': 'pre',
'width': 0
});
}
if (o.usePreview) {
// add clone inside of preview wrapper
base.$preview.after(base.$previewCopy);
} else {
// just slap that thing in there somewhere
base.$keyboard.prepend(base.$previewCopy);
}
}
if (base.isTextArea) {
// need the textarea scrollHeight, so set the clone textarea height to be the line height
base.$previewCopy
.height(base.lineHeight)
.val(value);
// set scrollTop for Textarea
base.preview.scrollTop = base.lineHeight *
(Math.floor(base.$previewCopy[0].scrollHeight / base.lineHeight) - 1);
} else {
// add non-breaking spaces
base.$previewCopy.val(value.replace(/\s/g, '\xa0'));
// if scrollAdjustment option is set to "c" or "center" then center the caret
adjustment = /c/i.test(o.scrollAdjustment) ? base.preview.clientWidth / 2 : o.scrollAdjustment;
scrollWidth = base.$previewCopy[0].scrollWidth - 1;
// set initial state as moving right
if (typeof base.last.scrollWidth === 'undefined') {
base.last.scrollWidth = scrollWidth;
base.last.direction = true;
}
// if direction = true; we're scrolling to the right
direction = base.last.scrollWidth === scrollWidth ?
base.last.direction :
base.last.scrollWidth < scrollWidth;
clientWidth = base.preview.clientWidth - adjustment;
// set scrollLeft for inputs; try to mimic the inherit caret positioning + scrolling:
// hug right while scrolling right...
if (direction) {
if (scrollWidth < clientWidth) {
base.preview.scrollLeft = 0;
} else {
base.preview.scrollLeft = scrollWidth - clientWidth;
}
} else {
// hug left while scrolling left...
if (scrollWidth >= base.preview.scrollWidth - clientWidth) {
base.preview.scrollLeft = base.preview.scrollWidth - adjustment;
} else if (scrollWidth - adjustment > 0) {
base.preview.scrollLeft = scrollWidth - adjustment;
} else {
base.preview.scrollLeft = 0;
}
}
base.last.scrollWidth = scrollWidth;
base.last.direction = direction;
}
}
};
base.bindFocus = function () {
if (o.openOn) {
// make sure keyboard isn't destroyed
// Check if base exists, this is a case when destroy is called, before timers have fired
if (base && base.el.active) {
base.$el.bind(o.openOn + base.namespace, function () {
base.focusOn();
});
// remove focus from element (needed for IE since blur doesn't seem to work)
if ($(':focus')[0] === base.el) {
base.$el.blur();
}
}
}
};
base.bindKeyboard = function () {
var evt,
keyCodes = $keyboard.keyCodes,
layout = $keyboard.builtLayouts[base.layout],
namespace = base.namespace + 'keybindings';
base.$preview
.unbind(base.namespace)
.bind('click' + namespace + ' touchstart' + namespace, function () {
if (o.alwaysOpen && !base.isCurrent()) {
base.reveal();
}
// update last caret position after user click, use at least 150ms or it doesn't work in IE
base.timer2 = setTimeout(function () {
if (base){
base.saveCaret();
}
}, 150);
})
.bind('keypress' + namespace, function (e) {
if (o.lockInput) {
return false;
}
if (!base.isCurrent()) {
return;
}
var k = e.charCode || e.which,
// capsLock can only be checked while typing a-z
k1 = k >= keyCodes.A && k <= keyCodes.Z,
k2 = k >= keyCodes.a && k <= keyCodes.z,
str = base.last.key = String.fromCharCode(k);
// check, that keypress wasn't rise by functional key
// space is first typing symbol in UTF8 table
if (k < keyCodes.space) { //see #549
return;
}
base.last.virtual = false;
base.last.event = e;
base.last.$key = []; // not a virtual keyboard key
if (base.checkCaret) {
base.saveCaret();
}
// update capsLock
if (k !== keyCodes.capsLock && (k1 || k2)) {
base.capsLock = (k1 && !e.shiftKey) || (k2 && e.shiftKey);
// if shifted keyset not visible, then show it
if (base.capsLock && !base.shiftActive) {
base.shiftActive = true;
base.showSet();
}
}
// restrict input - keyCode in keypress special keys:
// see http://www.asquare.net/javascript/tests/KeyCode.html
if (o.restrictInput) {
// allow navigation keys to work - Chrome doesn't fire a keypress event (8 = bksp)
if ((e.which === keyCodes.backSpace || e.which === 0) &&
$.inArray(e.keyCode, base.alwaysAllowed)) {
return;
}
// quick key check
if ($.inArray(str, layout.acceptedKeys) === -1) {
e.preventDefault();
// copy event object in case e.preventDefault() breaks when changing the type
evt = $.extend({}, e);
evt.type = $keyboard.events.inputRestricted;
base.$el.trigger(evt, [base, base.el]);
}
} else if ((e.ctrlKey || e.metaKey) &&
(e.which === keyCodes.A || e.which === keyCodes.C || e.which === keyCodes.V ||
(e.which >= keyCodes.X && e.which <= keyCodes.Z))) {
// Allow select all (ctrl-a), copy (ctrl-c), paste (ctrl-v) & cut (ctrl-x) &
// redo (ctrl-y)& undo (ctrl-z); meta key for mac
return;
}
// Mapped Keys - allows typing on a regular keyboard and the mapped key is entered
// Set up a key in the layout as follows: 'm(a):label'; m = key to map, (a) = actual keyboard key
// to map to (optional), ':label' = title/tooltip (optional)
// example: \u0391 or \u0391(A) or \u0391:alpha or \u0391(A):alpha
if (layout.hasMappedKeys && layout.mappedKeys.hasOwnProperty(str)) {
base.last.key = layout.mappedKeys[str];
base.insertText(base.last.key);
e.preventDefault();
}
if (typeof o.beforeInsert === 'function') {
base.insertText(base.last.key);
e.preventDefault();
}
base.checkMaxLength();
})
.bind('keyup' + namespace, function (e) {
if (!base.isCurrent()) { return; }
base.last.virtual = false;
switch (e.which) {
// Insert tab key
case keyCodes.tab:
// Added a flag to prevent from tabbing into an input, keyboard opening, then adding the tab
// to the keyboard preview area on keyup. Sadly it still happens if you don't release the tab
// key immediately because keydown event auto-repeats
if (base.tab && !o.lockInput) {
base.shiftActive = e.shiftKey;
// when switching inputs, the tab keyaction returns false
var notSwitching = $keyboard.keyaction.tab(base);
base.tab = false;
if (!notSwitching) {
return false;
}
} else {
e.preventDefault();
}
break;
// Escape will hide the keyboard
case keyCodes.escape:
if (!o.ignoreEsc) {
base.close(o.autoAccept && o.autoAcceptOnEsc ? 'true' : false);
}
return false;
}
// throttle the check combo function because fast typers will have an incorrectly positioned caret
clearTimeout(base.throttled);
base.throttled = setTimeout(function () {
// fix error in OSX? see issue #102
if (base && base.isVisible()) {
base.checkCombos();
}
}, 100);
base.checkMaxLength();
base.last.preVal = '' + base.last.val;
base.saveLastChange();
// don't alter "e" or the "keyup" event never finishes processing; fixes #552
var event = $.Event( $keyboard.events.kbChange );
// base.last.key may be empty string (shift, enter, tab, etc) when keyboard is first visible
// use e.key instead, if browser supports it
event.action = base.last.key;
base.$el.trigger(event, [base, base.el]);
// change callback is no longer bound to the input element as the callback could be
// called during an external change event with all the necessary parameters (issue #157)
if (typeof o.change === 'function') {
event.type = $keyboard.events.inputChange;
o.change(event, base, base.el);
return false;
}
if (o.acceptValid && o.autoAcceptOnValid) {
if (
typeof o.validate === 'function' &&
o.validate(base, base.getValue(base.$preview))
) {
base.$preview.blur();
base.accept();
}
}
})
.bind('keydown' + namespace, function (e) {
base.last.keyPress = e.which;
// ensure alwaysOpen keyboards are made active
if (o.alwaysOpen && !base.isCurrent()) {
base.reveal();
}
// prevent tab key from leaving the preview window
if (e.which === keyCodes.tab) {
// allow tab to pass through - tab to next input/shift-tab for prev
base.tab = true;
return false;
}
if (o.lockInput || e.timeStamp === base.last.timeStamp) {
return !o.lockInput;
}
base.last.timeStamp = e.timeStamp; // fixes #659
base.last.virtual = false;
switch (e.which) {
case keyCodes.backSpace:
$keyboard.keyaction.bksp(base, null, e);
e.preventDefault();
break;
case keyCodes.enter:
$keyboard.keyaction.enter(base, null, e);
break;
// Show capsLock
case keyCodes.capsLock:
base.shiftActive = base.capsLock = !base.capsLock;
base.showSet();
break;
case keyCodes.V:
// prevent ctrl-v/cmd-v
if (e.ctrlKey || e.metaKey) {
if (o.preventPaste) {
e.preventDefault();
return;
}
base.checkCombos(); // check pasted content
}
break;
}
})
.bind('mouseup touchend '.split(' ').join(namespace + ' '), function () {
base.last.virtual = true;
base.saveCaret();
});
// prevent keyboard event bubbling
base.$keyboard.bind('mousedown click touchstart '.split(' ').join(base.namespace + ' '), function (e) {
e.stopPropagation();
if (!base.isCurrent()) {
base.reveal();
$(base.el.ownerDocument).trigger('checkkeyboard' + base.namespace);
}
base.setFocus();
});
// If preventing paste, block context menu (right click)
if (o.preventPaste) {
base.$preview.bind('contextmenu' + base.namespace, function (e) {
e.preventDefault();
});
base.$el.bind('contextmenu' + base.namespace, function (e) {
e.preventDefault();
});
}
};
base.bindButton = function(events, handler) {
var button = '.' + $keyboard.css.keyButton,
callback = function(e) {
e.stopPropagation();
// save closest keyboard wrapper/input to check in checkClose function
e.$target = $(this).closest('.' + $keyboard.css.keyboard + ', .' + $keyboard.css.input);
handler.call(this, e);
};
if ($.fn.on) {
// jQuery v1.7+
base.$keyboard.on(events, button, callback);
} else if ($.fn.delegate) {
// jQuery v1.4.2 - 3.0.0
base.$keyboard.delegate(button, events, callback);
}
return base;
};
base.unbindButton = function(namespace) {
if ($.fn.off) {
// jQuery v1.7+
base.$keyboard.off(namespace);
} else if ($.fn.undelegate) {
// jQuery v1.4.2 - 3.0.0 (namespace only added in v1.6)
base.$keyboard.undelegate('.' + $keyboard.css.keyButton, namespace);
}
return base;
};
base.bindKeys = function () {
var kbcss = $keyboard.css;
base
.unbindButton(base.namespace + ' ' + base.namespace + 'kb')
// Change hover class and tooltip - moved this touchstart before option.keyBinding touchstart
// to prevent mousewheel lag/duplication - Fixes #379 & #411
.bindButton('mouseenter mouseleave touchstart '.split(' ').join(base.namespace + ' '), function (e) {
if ((o.alwaysOpen || o.userClosed) && e.type !== 'mouseleave' && !base.isCurrent()) {
base.reveal();
base.setFocus();
}
if (!base.isCurrent() || this.disabled) {
return;
}
var $keys, txt,
last = base.last,
$this = $(this),
type = e.type;
if (o.useWheel && base.wheel) {
$keys = base.getLayers($this);
txt = ($keys.length ? $keys.map(function () {
return $(this).attr('data-value') || '';
})
.get() : '') || [$this.text()];
last.wheel_$Keys = $keys;
last.wheelLayers = txt;
last.wheelIndex = $.inArray($this.attr('data-value'), txt);
}
if ((type === 'mouseenter' || type === 'touchstart') && base.el.type !== 'password' &&
!$this.hasClass(o.css.buttonDisabled)) {
$this.addClass(o.css.buttonHover);
if (o.useWheel && base.wheel) {
$this.attr('title', function (i, t) {
// show mouse wheel message
return (base.wheel && t === '' && base.sets && txt.length > 1 && type !== 'touchstart') ?
o.wheelMessage : t;
});
}
}
if (type === 'mouseleave') {
// needed or IE flickers really bad
$this.removeClass((base.el.type === 'password') ? '' : o.css.buttonHover);
if (o.useWheel && base.wheel) {
last.wheelIndex = 0;
last.wheelLayers = [];
last.wheel_$Keys = [];
$this
.attr('title', function (i, t) {
return (t === o.wheelMessage) ? '' : t;
})
.html($this.attr('data-html')); // restore original button text
}
}
})
// keyBinding = 'mousedown touchstart' by default
.bindButton(o.keyBinding.split(' ').join(base.namespace + ' ') + base.namespace + ' ' +
$keyboard.events.kbRepeater, function (e) {
e.preventDefault();
// prevent errors when external triggers attempt to 'type' - see issue #158
if (!base.$keyboard.is(':visible') || this.disabled) {
return false;
}
var action,
last = base.last,
$key = $(this),
// prevent mousedown & touchstart from both firing events at the same time - see #184
timer = new Date().getTime();
if (o.useWheel && base.wheel) {
// get keys from other layers/keysets (shift, alt, meta, etc) that line up by data-position
// target mousewheel selected key
$key = last.wheel_$Keys.length && last.wheelIndex > -1 ? last.wheel_$Keys.eq(last.wheelIndex) : $key;
}
action = $key.attr('data-action');
if (timer - (last.eventTime || 0) < o.preventDoubleEventTime) {
return;
}
last.eventTime = timer;
last.event = e;
last.virtual = true;
last.$key = $key;
last.key = $key.attr('data-value');
last.keyPress = '';
// Start caret in IE when not focused (happens with each virtual keyboard button click
base.setFocus();
if (/^meta/.test(action)) {
action = 'meta';
}
// keyaction is added as a string, override original action & text
if (action === last.key && typeof $keyboard.keyaction[action] === 'string') {
last.key = action = $keyboard.keyaction[action];
} else if (action in $keyboard.keyaction && typeof $keyboard.keyaction[action] === 'function') {
// stop processing if action returns false (close & cancel)
if ($keyboard.keyaction[action](base, this, e) === false) {
return false;
}
action = null; // prevent inserting action name
}
// stop processing if keyboard closed and keyaction did not return false - see #536
if (!base.hasKeyboard()) {
return false;
}
if (typeof action !== 'undefined' && action !== null) {
last.key = $(this).hasClass(kbcss.keyAction) ? action : last.key;
base.insertText(last.key);
if (!base.capsLock && !o.stickyShift && !e.shiftKey) {
base.shiftActive = false;
base.showSet($key.attr('data-name'));
}
}
// set caret if caret moved by action function; also, attempt to fix issue #131
$keyboard.caret(base.$preview, last);
base.checkCombos();
e = $.extend({}, e, $.Event($keyboard.events.kbChange));
e.target = base.el;
e.action = last.key;
base.$el.trigger(e, [base, base.el]);
last.preVal = '' + last.val;
base.saveLastChange();
if (typeof o.change === 'function') {
e.type = $keyboard.events.inputChange;
o.change(e, base, base.el);
// return false to prevent reopening keyboard if base.accept() was called
return false;
}
})
// using 'kb' namespace for mouse repeat functionality to keep it separate
// I need to trigger a 'repeater.keyboard' to make it work
.bindButton('mouseup' + base.namespace + ' ' + 'mouseleave touchend touchmove touchcancel '.split(' ')
.join(base.namespace + 'kb '), function (e) {
base.last.virtual = true;
var offset,
$this = $(this);
if (e.type === 'touchmove') {
// if moving within the same key, don't stop repeating
offset = $this.offset();
offset.right = offset.left + $this.outerWidth();
offset.bottom = offset.top + $this.outerHeight();
if (e.originalEvent.touches[0].pageX >= offset.left &&
e.originalEvent.touches[0].pageX < offset.right &&
e.originalEvent.touches[0].pageY >= offset.top &&
e.originalEvent.touches[0].pageY < offset.bottom) {
return true;
}
} else if (/(mouseleave|touchend|touchcancel)/i.test(e.type)) {
$this.removeClass(o.css.buttonHover); // needed for touch devices
} else {
if (!o.noFocus && base.isCurrent() && base.isVisible()) {
base.$preview.focus();
}
if (base.checkCaret) {
$keyboard.caret(base.$preview, base.last);
}
}
base.mouseRepeat = [false, ''];
clearTimeout(base.repeater); // make sure key repeat stops!
if (o.acceptValid && o.autoAcceptOnValid) {
if (
typeof o.validate === 'function' &&
o.validate(base, base.getValue())
) {
base.$preview.blur();
base.accept();
}
}
return false;
})
// prevent form submits when keyboard is bound locally - issue #64
.bindButton('click' + base.namespace, function () {
return false;
})
// Allow mousewheel to scroll through other keysets of the same (non-action) key
.bindButton('mousewheel' + base.namespace, base.throttleEvent(function (e, delta) {
var $btn = $(this);
// no mouse repeat for action keys (shift, ctrl, alt, meta, etc)
if (!$btn || $btn.hasClass(kbcss.keyAction) || base.last.wheel_$Keys[0] !== this) {
return;
}
if (o.useWheel && base.wheel) {
// deltaY used by newer versions of mousewheel plugin
delta = delta || e.deltaY;
var n,
txt = base.last.wheelLayers || [];
if (txt.length > 1) {
n = base.last.wheelIndex + (delta > 0 ? -1 : 1);
if (n > txt.length - 1) {
n = 0;
}
if (n < 0) {
n = txt.length - 1;
}
} else {
n = 0;
}
base.last.wheelIndex = n;
$btn.html(txt[n]);
return false;
}
}, 30))
.bindButton('mousedown touchstart '.split(' ').join(base.namespace + 'kb '), function () {
var $btn = $(this);
// no mouse repeat for action keys (shift, ctrl, alt, meta, etc)
if (
!$btn || (
$btn.hasClass(kbcss.keyAction) &&
// mouse repeated action key exceptions
!$btn.is('.' + kbcss.keyPrefix + ('tab bksp space enter'.split(' ').join(',.' + kbcss.keyPrefix)))
)
) {
return;
}
if (o.repeatRate !== 0) {
// save the key, make sure we are repeating the right one (fast typers)
base.mouseRepeat = [true, $btn];
setTimeout(function () {
// don't repeat keys if it is disabled - see #431
if (base && base.mouseRepeat[0] && base.mouseRepeat[1] === $btn && !$btn[0].disabled) {
base.repeatKey($btn);
}
}, o.repeatDelay);
}
return false;
});
};
// No call on tailing event
base.throttleEvent = function(cb, time) {
var interm;
return function() {
if (!interm) {
cb.apply(this, arguments);
interm = true;
setTimeout(function() {
interm = false;
}, time);
}
};
};
base.execCommand = function(cmd, str) {
base.el.ownerDocument.execCommand(cmd, false, str);
base.el.normalize();
if (o.reposition) {
base.reposition();
}
};
base.getValue = function ($el) {
$el = $el || base.$preview;
return $el[base.isContentEditable ? 'text' : 'val']();
};
base.setValue = function (txt, $el) {
$el = $el || base.$preview;
if (base.isContentEditable) {
if (txt !== $el.text()) {
$keyboard.replaceContent($el, txt);
base.saveCaret();
}
} else {
$el.val(txt);
}
return base;
};
// Insert text at caret/selection - thanks to Derek Wickwire for fixing this up!
base.insertText = function (txt) {
if (!base.$preview) { return base; }
if (typeof o.beforeInsert === 'function') {
txt = o.beforeInsert(base.last.event, base, base.el, txt);
}
if (typeof txt === 'undefined' || txt === false) {
base.last.key = '';
return base;
}
if (base.isContentEditable) {
return base.insertContentEditable(txt);
}
var t,
bksp = false,
isBksp = txt === '\b',
// use base.$preview.val() instead of base.preview.value (val.length includes carriage returns in IE).
val = base.getValue(),
pos = $keyboard.caret(base.$preview),
len = val.length; // save original content length
// silly IE caret hacks... it should work correctly, but navigating using arrow keys in a textarea
// is still difficult
// in IE, pos.end can be zero after input loses focus
if (pos.end < pos.start) {
pos.end = pos.start;
}
if (pos.start > len) {
pos.end = pos.start = len;
}
if (base.isTextArea) {
// This makes sure the caret moves to the next line after clicking on enter (manual typing works fine)
if ($keyboard.msie && val.substring(pos.start, pos.start + 1) === '\n') {
pos.start += 1;
pos.end += 1;
}
}
t = pos.start;
if (txt === '{d}') {
txt = '';
pos.end += 1;
}
if (isBksp) {
txt = '';
bksp = isBksp && t === pos.end && t > 0;
}
val = val.substring(0, t - (bksp ? 1 : 0)) + txt + val.substring(pos.end);
t += bksp ? -1 : txt.length;
base.setValue(val);
base.saveCaret(t, t); // save caret in case of bksp
base.setScroll();
// see #506.. allow chaining of insertText
return base;
};
base.insertContentEditable = function (txt) {
base.$preview.focus();
base.execCommand('insertText', txt);
base.saveCaret();
return base;
};
// check max length
base.checkMaxLength = function () {
if (!base.$preview) { return; }
var start, caret,
val = base.getValue(),
len = base.isContentEditable ? $keyboard.getEditableLength(base.el) : val.length;
if (o.maxLength !== false && len > o.maxLength) {
start = $keyboard.caret(base.$preview).start;
caret = Math.min(start, o.maxLength);
// prevent inserting new characters when maxed #289
if (!o.maxInsert) {
val = base.last.val;
caret = start - 1; // move caret back one
}
base.setValue(val.substring(0, o.maxLength));
// restore caret on change, otherwise it ends up at the end.
base.saveCaret(caret, caret);
}
if (base.$decBtn.length) {
base.checkDecimal();
}
// allow chaining
return base;
};
// mousedown repeater
base.repeatKey = function (key) {
key.trigger($keyboard.events.kbRepeater);
if (base.mouseRepeat[0]) {
base.repeater = setTimeout(function () {
if (base){
base.repeatKey(key);
}
}, base.repeatTime);
}
};
base.getKeySet = function () {
var sets = [];
if (base.altActive) {
sets.push('alt');
}
if (base.shiftActive) {
sets.push('shift');
}
if (base.metaActive) {
// base.metaActive contains the string name of the
// current meta keyset
sets.push(base.metaActive);
}
return sets.length ? sets.join('+') : 'normal';
};
// make it easier to switch keysets via API
// showKeySet('shift+alt+meta1')
base.showKeySet = function (str) {
if (typeof str === 'string') {
base.last.keyset = [base.shiftActive, base.altActive, base.metaActive];
base.shiftActive = /shift/i.test(str);
base.altActive = /alt/i.test(str);
if (/\bmeta/.test(str)) {
base.metaActive = true;
base.showSet(str.match(/\bmeta[\w-]+/i)[0]);
} else {
base.metaActive = false;
base.showSet();
}
} else {
base.showSet(str);
}
// allow chaining
return base;
};
base.showSet = function (name) {
if (!base.hasKeyboard()) { return; }
o = base.options; // refresh options
var kbcss = $keyboard.css,
prefix = '.' + kbcss.keyPrefix,
active = o.css.buttonActive,
key = '',
toShow = (base.shiftActive ? 1 : 0) + (base.altActive ? 2 : 0);
if (!base.shiftActive) {
base.capsLock = false;
}
// check meta key set
if (base.metaActive) {
// remove "-shift" and "-alt" from meta name if it exists
if (base.shiftActive) {
name = (name || '').replace('-shift', '');
}
if (base.altActive) {
name = (name || '').replace('-alt', '');
}
// the name attribute contains the meta set name 'meta99'
key = (/^meta/i.test(name)) ? name : '';
// save active meta keyset name
if (key === '') {
key = (base.metaActive === true) ? '' : base.metaActive;
} else {
base.metaActive = key;
}
// if meta keyset doesn't have a shift or alt keyset, then show just the meta key set
if ((!o.stickyShift && base.last.keyset[2] !== base.metaActive) ||
((base.shiftActive || base.altActive) &&
!base.$keyboard.find('.' + kbcss.keySet + '-' + key + base.rows[toShow]).length)) {
base.shiftActive = base.altActive = false;
}
} else if (!o.stickyShift && base.last.keyset[2] !== base.metaActive && base.shiftActive) {
// switching from meta key set back to default, reset shift & alt if using stickyShift
base.shiftActive = base.altActive = false;
}
toShow = (base.shiftActive ? 1 : 0) + (base.altActive ? 2 : 0);
key = (toShow === 0 && !base.metaActive) ? '-normal' : (key === '') ? '' : '-' + key;
if (!base.$keyboard.find('.' + kbcss.keySet + key + base.rows[toShow]).length) {
// keyset doesn't exist, so restore last keyset settings
base.shiftActive = base.last.keyset[0];
base.altActive = base.last.keyset[1];
base.metaActive = base.last.keyset[2];
return;
}
base.$keyboard
.find(prefix + 'alt,' + prefix + 'shift,.' + kbcss.keyAction + '[class*=meta]')
.removeClass(active)
.end()
.find(prefix + 'alt')
.toggleClass(active, base.altActive)
.end()
.find(prefix + 'shift')
.toggleClass(active, base.shiftActive)
.end()
.find(prefix + 'lock')
.toggleClass(active, base.capsLock)
.end()
.find('.' + kbcss.keySet)
.hide()
.end()
.find('.' + (kbcss.keyAction + prefix + key).replace('--', '-'))
.addClass(active);
// show keyset using inline-block ( extender layout will then line up )
base.$keyboard.find('.' + kbcss.keySet + key + base.rows[toShow])[0].style.display = 'inline-block';
if (base.metaActive) {
base.$keyboard.find(prefix + base.metaActive)
// base.metaActive contains the string "meta#" or false
// without the !== false, jQuery UI tries to transition the classes
.toggleClass(active, base.metaActive !== false);
}
base.last.keyset = [base.shiftActive, base.altActive, base.metaActive];
base.$el.trigger($keyboard.events.kbKeysetChange, [base, base.el]);
if (o.reposition) {
base.reposition();
}
};
// check for key combos (dead keys)
base.checkCombos = function () {
// return val for close function
if ( !(
base.isVisible() || (
base.hasKeyboard() &&
base.$keyboard.hasClass( $keyboard.css.hasFocus )
)
) ) {
return base.getValue(base.$preview || base.$el);
}
var r, t, t2, repl,
// use base.$preview.val() instead of base.preview.value
// (val.length includes carriage returns in IE).
val = base.getValue(),
pos = $keyboard.caret(base.$preview),
layout = $keyboard.builtLayouts[base.layout],
max = base.isContentEditable ? $keyboard.getEditableLength(base.el) : val.length,
// save original content length
len = max;
// return if val is empty; fixes #352
if (val === '') {
// check valid on empty string - see #429
if (o.acceptValid) {
base.checkValid();
}
return val;
}
// silly IE caret hacks... it should work correctly, but navigating using arrow keys in a textarea
// is still difficult
// in IE, pos.end can be zero after input loses focus
if (pos.end < pos.start) {
pos.end = pos.start;
}
if (pos.start > len) {
pos.end = pos.start = len;
}
// This makes sure the caret moves to the next line after clicking on enter (manual typing works fine)
if ($keyboard.msie && val.substring(pos.start, pos.start + 1) === '\n') {
pos.start += 1;
pos.end += 1;
}
if (o.useCombos) {
// keep 'a' and 'o' in the regex for ae and oe ligature (æ,œ)
// thanks to KennyTM: http://stackoverflow.com/q/4275077
// original regex /([`\'~\^\"ao])([a-z])/mig moved to $.keyboard.comboRegex
if ($keyboard.msie) {
// old IE may not have the caret positioned correctly, so just check the whole thing
val = val.replace(base.regex, function (s, accent, letter) {
return (o.combos.hasOwnProperty(accent)) ? o.combos[accent][letter] || s : s;
});
// prevent combo replace error, in case the keyboard closes - see issue #116
} else if (base.$preview.length) {
// Modern browsers - check for combos from last two characters left of the caret
t = pos.start - (pos.start - 2 >= 0 ? 2 : 0);
// target last two characters
$keyboard.caret(base.$preview, t, pos.end);
// do combo replace
t = $keyboard.caret(base.$preview);
repl = function (txt) {
return (txt || '').replace(base.regex, function (s, accent, letter) {
return (o.combos.hasOwnProperty(accent)) ? o.combos[accent][letter] || s : s;
});
};
t2 = repl(t.text);
// add combo back
// prevent error if caret doesn't return a function
if (t && t.replaceStr && t2 !== t.text) {
if (base.isContentEditable) {
$keyboard.replaceContent(el, repl);
} else {
base.setValue(t.replaceStr(t2));
}
}
val = base.getValue();
}
}
// check input restrictions - in case content was pasted
if (o.restrictInput && val !== '') {
t = layout.acceptedKeys.length;
r = layout.acceptedKeysRegex;
if (!r) {
t2 = $.map(layout.acceptedKeys, function (v) {
// escape any special characters
return v.replace(base.escapeRegex, '\\$&');
});
if (base.alwaysAllowed.indexOf($keyboard.keyCodes.enter) > -1) {
t2.push('\\n'); // Fixes #686
}
r = layout.acceptedKeysRegex = new RegExp('(' + t2.join('|') + ')', 'g');
}
// only save matching keys
t2 = val.match(r);
if (t2) {
val = t2.join('');
} else {
// no valid characters
val = '';
len = 0;
}
}
// save changes, then reposition caret
pos.start += max - len;
pos.end += max - len;
base.setValue(val);
base.saveCaret(pos.start, pos.end);
// set scroll to keep caret in view
base.setScroll();
base.checkMaxLength();
if (o.acceptValid) {
base.checkValid();
}
return val; // return text, used for keyboard closing section
};
// Toggle accept button classes, if validating
base.checkValid = function () {
var kbcss = $keyboard.css,
$accept = base.$keyboard.find('.' + kbcss.keyPrefix + 'accept'),
valid = true;
if (typeof o.validate === 'function') {
valid = o.validate(base, base.getValue(), false);
}
// toggle accept button classes; defined in the css
$accept
.toggleClass(kbcss.inputInvalid, !valid)
.toggleClass(kbcss.inputValid, valid)
// update title to indicate that the entry is valid or invalid
.attr('title', $accept.attr('data-title') + ' (' + o.display[valid ? 'valid' : 'invalid'] + ')');
};
// Decimal button for num pad - only allow one (not used by default)
base.checkDecimal = function () {
// Check US '.' or European ',' format
if ((base.decimal && /\./g.test(base.preview.value)) ||
(!base.decimal && /\,/g.test(base.preview.value))) {
base.$decBtn
.attr({
'disabled': 'disabled',
'aria-disabled': 'true'
})
.removeClass(o.css.buttonHover)
.addClass(o.css.buttonDisabled);
} else {
base.$decBtn
.removeAttr('disabled')
.attr({
'aria-disabled': 'false'
})
.addClass(o.css.buttonDefault)
.removeClass(o.css.buttonDisabled);
}
};
// get other layer values for a specific key
base.getLayers = function ($el) {
var kbcss = $keyboard.css,
key = $el.attr('data-pos'),
$keys = $el.closest('.' + kbcss.keyboard)
.find('button[data-pos="' + key + '"]');
return $keys.filter(function () {
return $(this)
.find('.' + kbcss.keyText)
.text() !== '';
})
.add($el);
};
// Go to next or prev inputs
// goToNext = true, then go to next input; if false go to prev
// isAccepted is from autoAccept option or true if user presses shift+enter
base.switchInput = function (goToNext, isAccepted) {
if (typeof o.switchInput === 'function') {
o.switchInput(base, goToNext, isAccepted);
} else {
// base.$keyboard may be an empty array - see #275 (apod42)
if (base.$keyboard.length) {
base.$keyboard.hide();
}
var kb,
stopped = false,
all = $('button, input, select, textarea, a, [contenteditable]')
.filter(':visible')
.not(':disabled'),
indx = all.index(base.$el) + (goToNext ? 1 : -1);
if (base.$keyboard.length) {
base.$keyboard.show();
}
if (indx > all.length - 1) {
stopped = o.stopAtEnd;
indx = 0; // go to first input
}
if (indx < 0) {
stopped = o.stopAtEnd;
indx = all.length - 1; // stop or go to last
}
if (!stopped) {
isAccepted = base.close(isAccepted);
if (!isAccepted) {
return;
}
kb = all.eq(indx).data('keyboard');
if (kb && kb.options.openOn.length) {
kb.focusOn();
} else {
all.eq(indx).focus();
}
}
}
return false;
};
// Close the keyboard, if visible. Pass a status of true, if the content was accepted
// (for the event trigger).
base.close = function (accepted) {
if (base.isOpen && base.$keyboard.length) {
clearTimeout(base.throttled);
var kbcss = $keyboard.css,
kbevents = $keyboard.events,
val = accepted ? base.checkCombos() : base.originalContent;
// validate input if accepted
if (accepted && typeof o.validate === 'function' && !o.validate(base, val, true)) {
val = base.originalContent;
accepted = false;
if (o.cancelClose) {
return;
}
}
base.isCurrent(false);
base.isOpen = o.alwaysOpen || o.userClosed;
if (base.isContentEditable && !accepted) {
// base.originalContent stores the HTML
base.$el.html(val);
} else {
base.setValue(val, base.$el);
}
base.$el
.removeClass(kbcss.isCurrent + ' ' + kbcss.inputAutoAccepted)
// add 'ui-keyboard-autoaccepted' to inputs - see issue #66
.addClass((accepted || false) ? accepted === true ? '' : kbcss.inputAutoAccepted : '')
// trigger default change event - see issue #146
.trigger(kbevents.inputChange);
// don't trigger an empty event - see issue #463
if (!o.alwaysOpen) {
// don't trigger beforeClose if keyboard is always open
base.$el.trigger(kbevents.kbBeforeClose, [base, base.el, (accepted || false)]);
}
// save caret after updating value (fixes userClosed issue with changing focus)
$keyboard.caret(base.$preview, base.last);
base.$el
.trigger(((accepted || false) ? kbevents.inputAccepted : kbevents.inputCanceled), [base, base.el])
.trigger((o.alwaysOpen) ? kbevents.kbInactive : kbevents.kbHidden, [base, base.el])
.blur();
// base is undefined if keyboard was destroyed - fixes #358
if (base) {
// add close event time
base.last.eventTime = new Date().getTime();
if (!(o.alwaysOpen || o.userClosed && accepted === 'true') && base.$keyboard.length) {
// free up memory
base.removeKeyboard();
// rebind input focus - delayed to fix IE issue #72
base.timer = setTimeout(function () {
if (base) {
base.bindFocus();
}
}, 200);
}
if (!base.watermark && base.el.value === '' && base.inPlaceholder !== '') {
base.$el.addClass(kbcss.placeholder);
base.setValue(base.inPlaceholder, base.$el);
}
}
}
return !!accepted;
};
base.accept = function () {
return base.close(true);
};
base.checkClose = function (e) {
if (base.opening) {
return;
}
var kbcss = $.keyboard.css,
$target = e.$target || $(e.target).closest('.' + $keyboard.css.keyboard + ', .' + $keyboard.css.input);
if (!$target.length) {
$target = $(e.target);
}
// needed for IE to allow switching between keyboards smoothly
if ($target.length && $target.hasClass(kbcss.keyboard)) {
var kb = $target.data('keyboard');
// only trigger on self
if (
kb !== base &&
!kb.$el.hasClass(kbcss.isCurrent) &&
kb.options.openOn &&
e.type === o.openOn
) {
kb.focusOn();
}
} else {
base.escClose(e, $target);
}
};
// callback functions called to check if the keyboard needs to be closed
// e.g. on escape or clicking outside the keyboard
base.escCloseCallback = {
// keep keyboard open if alwaysOpen or stayOpen is true - fixes mutliple
// always open keyboards or single stay open keyboard
keepOpen: function() {
return !base.isOpen;
}
};
base.escClose = function (e, $el) {
if (!base.isOpen) {
return;
}
if (e && e.type === 'keyup') {
return (e.which === $keyboard.keyCodes.escape && !o.ignoreEsc) ?
base.close(o.autoAccept && o.autoAcceptOnEsc ? 'true' : false) :
'';
}
var shouldStayOpen = false,
$target = $el.length && $el || $(e.target);
$.each(base.escCloseCallback, function(i, callback) {
if (typeof callback === 'function') {
shouldStayOpen = shouldStayOpen || callback($target);
}
});
if (shouldStayOpen) {
return;
}
// ignore autoaccept if using escape - good idea?
if (!base.isCurrent() && base.isOpen || base.isOpen && $target[0] !== base.el) {
// don't close if stayOpen is set; but close if a different keyboard is being opened
if ((o.stayOpen || o.userClosed) && !$target.hasClass($keyboard.css.input)) {
return;
}
// stop propogation in IE - an input getting focus doesn't open a keyboard if one is already open
if ($keyboard.allie) {
e.preventDefault();
}
if (o.closeByClickEvent) {
// only close the keyboard if the user is clicking on an input or if they cause a click
// event (touchstart/mousedown will not force the close with this setting)
var name = $target[0] && $target[0].nodeName.toLowerCase();
if (name === 'input' || name === 'textarea' || e.type === 'click') {
base.close(o.autoAccept ? 'true' : false);
}
} else {
// send 'true' instead of a true (boolean), the input won't get a 'ui-keyboard-autoaccepted'
// class name - see issue #66
base.close(o.autoAccept ? 'true' : false);
}
}
};
// Build default button
base.keyBtn = $('<button />')
.attr({
'role': 'button',
'type': 'button',
'aria-disabled': 'false',
'tabindex': '-1'
})
.addClass($keyboard.css.keyButton);
// convert key names into a class name
base.processName = function (name) {
var index, n,
process = (name || '').replace(/[^a-z0-9-_]/gi, ''),
len = process.length,
newName = [];
if (len > 1 && name === process) {
// return name if basic text
return name;
}
// return character code sequence
len = name.length;
if (len) {
for (index = 0; index < len; index++) {
n = name[index];
// keep '-' and '_'... so for dash, we get two dashes in a row
newName.push(/[a-z0-9-_]/i.test(n) ?
(/[-_]/.test(n) && index !== 0 ? '' : n) :
(index === 0 ? '' : '-') + n.charCodeAt(0)
);
}
return newName.join('');
}
return name;
};
base.processKeys = function (name) {
var tmp, parts,
htmlIndex = name.indexOf('</'),
data = {
name: name,
map: '',
title: ''
};
if (htmlIndex > -1) {
// If it looks like HTML, skip processing; see #743
// html may include colons; see #701
return data;
}
// Don't split colons followed by //, e.g. https://; Fixes #555
parts = name.split(/:(?!\/\/)/);
/* map defined keys
format 'key(A):Label_for_key_(ignore_parentheses_here)'
'key' = key that is seen (can any character(s); but it might need to be escaped using '\'
or entered as unicode '\u####'
'(A)' = the actual key on the real keyboard to remap
':Label_for_key' ends up in the title/tooltip
Examples:
'\u0391(A):alpha', 'x(y):this_(might)_cause_problems
or edge cases of ':(x)', 'x(:)', 'x(()' or 'x())'
Enhancement (if I can get alt keys to work):
A mapped key will include the mod key, e.g. 'x(alt-x)' or 'x(alt-shift-x)'
*/
if (/\(.+\)/.test(parts[0]) || /^:\(.+\)/.test(name) || /\([(:)]\)/.test(name)) {
// edge cases 'x(:)', 'x(()' or 'x())'
if (/\([(:)]\)/.test(name)) {
tmp = parts[0].match(/([^(]+)\((.+)\)/);
if (tmp && tmp.length) {
data.name = tmp[1];
data.map = tmp[2];
data.title = parts.length > 1 ? parts.slice(1).join(':') : '';
} else {
// edge cases 'x(:)', ':(x)' or ':(:)'
data.name = name.match(/([^(]+)/)[0];
if (data.name === ':') {
// ':(:):test' => parts = [ '', '(', ')', 'title' ] need to slice 1
parts = parts.slice(1);
}
if (tmp === null) {
// 'x(:):test' => parts = [ 'x(', ')', 'title' ] need to slice 2
data.map = ':';
parts = parts.slice(2);
}
data.title = parts.length ? parts.join(':') : '';
}
} else {
// example: \u0391(A):alpha; extract 'A' from '(A)'
data.map = name.match(/\(([^()]+?)\)/)[1];
// remove '(A)', left with '\u0391:alpha'
name = name.replace(/\(([^()]+)\)/, '');
tmp = name.split(':');
// get '\u0391' from '\u0391:alpha'
if (tmp[0] === '') {
data.name = ':';
parts = parts.slice(1);
} else {
data.name = tmp[0];
}
data.title = parts.length > 1 ? parts.slice(1).join(':') : '';
}
} else {
// find key label
// corner case of '::;' reduced to ':;', split as ['', ';']
if (name !== '' && parts[0] === '') {
data.name = ':';
parts = parts.slice(1);
} else {
data.name = parts[0];
}
data.title = parts.length > 1 ? parts.slice(1).join(':') : '';
}
data.title = $.trim(data.title).replace(/_/g, ' ');
return data;
};
// Add key function
// keyName = the name of the function called in $.keyboard.keyaction when the button is clicked
// name = name added to key, or cross-referenced in the display options
// base.temp[0] = keyset to attach the new button
// regKey = true when it is not an action key
base.addKey = function (keyName, action, regKey) {
var keyClass, tmp, keys,
data = {},
txt = base.processKeys(regKey ? keyName : action),
kbcss = $keyboard.css;
if (!regKey && o.display[txt.name]) {
keys = base.processKeys(o.display[txt.name]);
// action contained in "keyName" (e.g. keyName = "accept",
// action = "a" (use checkmark instead of text))
keys.action = base.processKeys(keyName).name;
} else {
// when regKey is true, keyName is the same as action
keys = txt;
keys.action = txt.name;
}
data.name = base.processName(txt.name);
if (keys.name !== '') {
if (keys.map !== '') {
$keyboard.builtLayouts[base.layout].mappedKeys[keys.map] = keys.name;
$keyboard.builtLayouts[base.layout].acceptedKeys.push(keys.name);
} else if (regKey) {
$keyboard.builtLayouts[base.layout].acceptedKeys.push(keys.name);
}
}
if (regKey) {
keyClass = data.name === '' ? '' : kbcss.keyPrefix + data.name;
} else {
// Action keys will have the 'ui-keyboard-actionkey' class
keyClass = kbcss.keyAction + ' ' + kbcss.keyPrefix + keys.action;
}
// '\u2190'.length = 1 because the unicode is converted, so if more than one character,
// add the wide class
keyClass += (keys.name.length > 2 ? ' ' + kbcss.keyWide : '') + ' ' + o.css.buttonDefault;
// Allow HTML in the key.name
data.html = '<span class="' + kbcss.keyText + '">' + keys.name + '</span>';
data.$key = base.keyBtn
.clone()
.attr({
'data-value': regKey ? keys.name : keys.action, // value
'data-name': keys.action,
'data-pos': base.temp[1] + ',' + base.temp[2],
'data-action': keys.action,
'data-html': data.html
})
// add 'ui-keyboard-' + data.name for all keys
// (e.g. 'Bksp' will have 'ui-keyboard-bskp' class)
// any non-alphanumeric characters will be replaced with
// their decimal unicode value
// (e.g. '~' is a regular key, class = 'ui-keyboard-126'
// (126 is the unicode decimal value - same as ~)
// See https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes
.addClass(keyClass)
.html(data.html)
.appendTo(base.temp[0]);
if (keys.map) {
data.$key.attr('data-mapped', keys.map);
}
if (keys.title || txt.title) {
data.$key.attr({
'data-title': txt.title || keys.title, // used to allow adding content to title
'title': txt.title || keys.title
});
}
if (typeof o.buildKey === 'function') {
data = o.buildKey(base, data);
// copy html back to attributes
tmp = data.$key.html();
data.$key.attr('data-html', tmp);
}
return data.$key;
};
base.customHash = function (layout) {
/*jshint bitwise:false */
var i, array, hash, character, len,
arrays = [],
merged = [];
// pass layout to allow for testing
layout = typeof layout === 'undefined' ? o.customLayout : layout;
// get all layout arrays
for (array in layout) {
if (layout.hasOwnProperty(array)) {
arrays.push(layout[array]);
}
}
// flatten array
merged = merged.concat.apply(merged, arrays).join(' ');
// produce hash name - http://stackoverflow.com/a/7616484/145346
hash = 0;
len = merged.length;
if (len === 0) {
return hash;
}
for (i = 0; i < len; i++) {
character = merged.charCodeAt(i);
hash = ((hash << 5) - hash) + character;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
};
base.buildKeyboard = function (name, internal) {
// o.display is empty when this is called from the scramble extension (when alwaysOpen:true)
if ($.isEmptyObject(o.display)) {
// set keyboard language
base.updateLanguage();
}
var index, row, $row, currentSet,
kbcss = $keyboard.css,
sets = 0,
layout = $keyboard.builtLayouts[name || base.layout || o.layout] = {
mappedKeys: {},
acceptedKeys: []
},
acceptedKeys = layout.acceptedKeys = o.restrictInclude ?
('' + o.restrictInclude).split(/\s+/) || [] :
[],
// using $layout temporarily to hold keyboard popup classnames
$layout = kbcss.keyboard + ' ' + o.css.popup + ' ' + o.css.container +
(o.alwaysOpen || o.userClosed ? ' ' + kbcss.alwaysOpen : ''),
container = $('<div />')
.addClass($layout)
.attr({
'role': 'textbox'
})
.hide();
// allow adding "{space}" as an accepted key - Fixes #627
index = $.inArray('{space}', acceptedKeys);
if (index > -1) {
acceptedKeys[index] = ' ';
}
// verify layout or setup custom keyboard
if ((internal && o.layout === 'custom') || !$keyboard.layouts.hasOwnProperty(o.layout)) {
o.layout = 'custom';
$layout = $keyboard.layouts.custom = o.customLayout || {
'normal': ['{cancel}']
};
} else {
$layout = $keyboard.layouts[internal ? o.layout : name || base.layout || o.layout];
}
// Main keyboard building loop
$.each($layout, function (set, keySet) {
// skip layout name & lang settings
if (set !== '' && !/^(name|lang|rtl)$/i.test(set)) {
// keep backwards compatibility for change from default to normal naming
if (set === 'default') {
set = 'normal';
}
sets++;
$row = $('<div />')
.attr('name', set) // added for typing extension
.addClass(kbcss.keySet + ' ' + kbcss.keySet + '-' + set)
.appendTo(container)
.toggle(set === 'normal');
for (row = 0; row < keySet.length; row++) {
// remove extra spaces before spliting (regex probably could be improved)
currentSet = $.trim(keySet[row]).replace(/\{(\.?)[\s+]?:[\s+]?(\.?)\}/g, '{$1:$2}');
base.buildRow($row, row, currentSet.split(/\s+/), acceptedKeys);
$row.find('.' + kbcss.keyButton + ',.' + kbcss.keySpacer)
.filter(':last')
.after('<br class="' + kbcss.endRow + '"/>');
}
}
});
if (sets > 1) {
base.sets = true;
}
layout.hasMappedKeys = !($.isEmptyObject(layout.mappedKeys));
layout.$keyboard = container;
return container;
};
base.buildRow = function ($row, row, keys, acceptedKeys) {
var t, txt, key, isAction, action, margin,
kbcss = $keyboard.css;
for (key = 0; key < keys.length; key++) {
// used by addKey function
base.temp = [$row, row, key];
isAction = false;
// ignore empty keys
if (keys[key].length === 0) {
continue;
}
// process here if it's an action key
if (/^\{\S+\}$/.test(keys[key])) {
action = keys[key].match(/^\{(\S+)\}$/)[1];
// add active class if there are double exclamation points in the name
if (/\!\!/.test(action)) {
action = action.replace('!!', '');
isAction = true;
}
// add empty space
if (/^sp:((\d+)?([\.|,]\d+)?)(em|px)?$/i.test(action)) {
// not perfect globalization, but allows you to use {sp:1,1em}, {sp:1.2em} or {sp:15px}
margin = parseFloat(action
.replace(/,/, '.')
.match(/^sp:((\d+)?([\.|,]\d+)?)(em|px)?$/i)[1] || 0
);
$('<span class="' + kbcss.keyText + '"></span>')
// previously {sp:1} would add 1em margin to each side of a 0 width span
// now Firefox doesn't seem to render 0px dimensions, so now we set the
// 1em margin x 2 for the width
.width((action.match(/px/i) ? margin + 'px' : (margin * 2) + 'em'))
.addClass(kbcss.keySpacer)
.appendTo($row);
}
// add empty button
if (/^empty(:((\d+)?([\.|,]\d+)?)(em|px)?)?$/i.test(action)) {
margin = (/:/.test(action)) ? parseFloat(action
.replace(/,/, '.')
.match(/^empty:((\d+)?([\.|,]\d+)?)(em|px)?$/i)[1] || 0
) : '';
base
.addKey('', ' ', true)
.addClass(o.css.buttonDisabled + ' ' + o.css.buttonEmpty)
.attr('aria-disabled', true)
.width(margin ? (action.match('px') ? margin + 'px' : (margin * 2) + 'em') : '');
continue;
}
// meta keys
if (/^meta[\w-]+\:?(\w+)?/i.test(action)) {
base
.addKey(action.split(':')[0], action)
.addClass(kbcss.keyHasActive);
continue;
}
// switch needed for action keys with multiple names/shortcuts or
// default will catch all others
txt = action.split(':');
switch (txt[0].toLowerCase()) {
case 'a':
case 'accept':
base
.addKey('accept', action)
.addClass(o.css.buttonAction + ' ' + kbcss.keyAction);
break;
case 'alt':
case 'altgr':
base
.addKey('alt', action)
.addClass(kbcss.keyHasActive);
break;
case 'b':
case 'bksp':
base.addKey('bksp', action);
break;
case 'c':
case 'cancel':
base
.addKey('cancel', action)
.addClass(o.css.buttonAction + ' ' + kbcss.keyAction);
break;
// toggle combo/diacritic key
/*jshint -W083 */
case 'combo':
base
.addKey('combo', action)
.addClass(kbcss.keyHasActive)
.attr('title', function (indx, title) {
// add combo key state to title
return title + ' ' + o.display[o.useCombos ? 'active' : 'disabled'];
})
.toggleClass(o.css.buttonActive, o.useCombos);
break;
// Decimal - unique decimal point (num pad layout)
case 'dec':
acceptedKeys.push((base.decimal) ? '.' : ',');
base.addKey('dec', action);
break;
case 'e':
case 'enter':
base
.addKey('enter', action)
.addClass(o.css.buttonAction + ' ' + kbcss.keyAction);
break;
case 'lock':
base
.addKey('lock', action)
.addClass(kbcss.keyHasActive);
break;
case 's':
case 'shift':
base
.addKey('shift', action)
.addClass(kbcss.keyHasActive);
break;
// Change sign (for num pad layout)
case 'sign':
acceptedKeys.push('-');
base.addKey('sign', action);
break;
case 'space':
acceptedKeys.push(' ');
base.addKey('space', action);
break;
case 't':
case 'tab':
base.addKey('tab', action);
break;
default:
if ($keyboard.keyaction.hasOwnProperty(txt[0])) {
base
.addKey(txt[0], action)
.toggleClass(o.css.buttonAction + ' ' + kbcss.keyAction, isAction);
}
}
} else {
// regular button (not an action key)
t = keys[key];
base.addKey(t, t, true);
}
}
};
base.removeBindings = function (namespace) {
$(document).unbind(namespace);
if (base.el.ownerDocument !== document) {
$(base.el.ownerDocument).unbind(namespace);
}
$(window).unbind(namespace);
base.$el.unbind(namespace);
};
base.removeKeyboard = function () {
base.$decBtn = [];
// base.$preview === base.$el when o.usePreview is false - fixes #442
if (o.usePreview) {
base.$preview.removeData('keyboard');
}
base.$preview.unbind(base.namespace + 'keybindings');
base.preview = null;
base.$preview = null;
base.$previewCopy = null;
base.$keyboard.removeData('keyboard');
base.$keyboard.remove();
base.$keyboard = [];
base.isOpen = false;
base.isCurrent(false);
};
base.destroy = function (callback) {
var index,
kbcss = $keyboard.css,
len = base.extensionNamespace.length,
tmp = [
kbcss.input,
kbcss.locked,
kbcss.placeholder,
kbcss.noKeyboard,
kbcss.alwaysOpen,
o.css.input,
kbcss.isCurrent
].join(' ');
clearTimeout(base.timer);
clearTimeout(base.timer2);
clearTimeout(base.timer3);
if (base.$keyboard.length) {
base.removeKeyboard();
}
if (base.options.openOn) {
base.removeBindings(base.options.openOn);
}
base.removeBindings(base.namespace);
base.removeBindings(base.namespace + 'callbacks');
for (index = 0; index < len; index++) {
base.removeBindings(base.extensionNamespace[index]);
}
base.el.active = false;
base.$el
.removeClass(tmp)
.removeAttr('aria-haspopup')
.removeAttr('role')
.removeData('keyboard');
base = null;
if (typeof callback === 'function') {
callback();
}
};
// Run initializer
base.init();
}; // end $.keyboard definition
// event.which & ASCII values
$keyboard.keyCodes = {
backSpace: 8,
tab: 9,
enter: 13,
capsLock: 20,
escape: 27,
space: 32,
pageUp: 33,
pageDown: 34,
end: 35,
home: 36,
left: 37,
up: 38,
right: 39,
down: 40,
insert: 45,
delete: 46,
// event.which keyCodes (uppercase letters)
A: 65,
Z: 90,
V: 86,
C: 67,
X: 88,
// ASCII lowercase a & z
a: 97,
z: 122
};
$keyboard.css = {
// keyboard id suffix
idSuffix: '_keyboard',
// class name to set initial focus
initialFocus: 'keyboard-init-focus',
// element class names
input: 'ui-keyboard-input',
inputClone: 'ui-keyboard-preview-clone',
wrapper: 'ui-keyboard-preview-wrapper',
preview: 'ui-keyboard-preview',
keyboard: 'ui-keyboard',
keySet: 'ui-keyboard-keyset',
keyButton: 'ui-keyboard-button',
keyWide: 'ui-keyboard-widekey',
keyPrefix: 'ui-keyboard-',
keyText: 'ui-keyboard-text', // span with button text
keyHasActive: 'ui-keyboard-hasactivestate',
keyAction: 'ui-keyboard-actionkey',
keySpacer: 'ui-keyboard-spacer', // empty keys
keyToggle: 'ui-keyboard-toggle',
keyDisabled: 'ui-keyboard-disabled',
// Class for BRs with a div wrapper inside of contenteditable
divWrapperCE: 'ui-keyboard-div-wrapper',
// states
locked: 'ui-keyboard-lockedinput',
alwaysOpen: 'ui-keyboard-always-open',
noKeyboard: 'ui-keyboard-nokeyboard',
placeholder: 'ui-keyboard-placeholder',
hasFocus: 'ui-keyboard-has-focus',
isCurrent: 'ui-keyboard-input-current',
// validation & autoaccept
inputValid: 'ui-keyboard-valid-input',
inputInvalid: 'ui-keyboard-invalid-input',
inputAutoAccepted: 'ui-keyboard-autoaccepted',
endRow: 'ui-keyboard-button-endrow' // class added to <br>
};
$keyboard.events = {
// keyboard events
kbChange: 'keyboardChange',
kbBeforeClose: 'beforeClose',
kbBeforeVisible: 'beforeVisible',
kbVisible: 'visible',
kbInit: 'initialized',
kbInactive: 'inactive',
kbHidden: 'hidden',
kbRepeater: 'repeater',
kbKeysetChange: 'keysetChange',
// input events
inputAccepted: 'accepted',
inputCanceled: 'canceled',
inputChange: 'change',
inputRestricted: 'restricted'
};
// Action key function list
$keyboard.keyaction = {
accept: function (base) {
base.close(true); // same as base.accept();
return false; // return false prevents further processing
},
alt: function (base) {
base.altActive = !base.altActive;
base.showSet();
},
bksp: function (base) {
if (base.isContentEditable) {
base.execCommand('delete');
// save new caret position
base.saveCaret();
} else {
// the script looks for the '\b' string and initiates a backspace
base.insertText('\b');
}
},
cancel: function (base) {
base.close();
return false; // return false prevents further processing
},
clear: function (base) {
base.$preview[base.isContentEditable ? 'text' : 'val']('');
if (base.$decBtn.length) {
base.checkDecimal();
}
},
combo: function (base) {
var o = base.options,
c = !o.useCombos,
$combo = base.$keyboard.find('.' + $keyboard.css.keyPrefix + 'combo');
o.useCombos = c;
$combo
.toggleClass(o.css.buttonActive, c)
// update combo key state
.attr('title', $combo.attr('data-title') + ' (' + o.display[c ? 'active' : 'disabled'] + ')');
if (c) {
base.checkCombos();
}
return false;
},
dec: function (base) {
base.insertText((base.decimal) ? '.' : ',');
},
del: function (base) {
if (base.isContentEditable) {
base.execCommand('forwardDelete');
} else {
// the script looks for the '{d}' string and initiates a delete
base.insertText('{d}');
}
},
// resets to base keyset (deprecated because "default" is a reserved word)
'default': function (base) {
base.shiftActive = base.altActive = base.metaActive = false;
base.showSet();
},
// el is the pressed key (button) object; it is null when the real keyboard enter is pressed
enter: function (base, el, e) {
var o = base.options;
// shift+enter in textareas
if (e.shiftKey) {
// textarea, input & contenteditable - enterMod + shift + enter = accept,
// then go to prev; base.switchInput(goToNext, autoAccept)
// textarea & input - shift + enter = accept (no navigation)
return (o.enterNavigation) ? base.switchInput(!e[o.enterMod], true) : base.close(true);
}
// input only - enterMod + enter to navigate
if (o.enterNavigation && (!base.isTextArea || e[o.enterMod])) {
return base.switchInput(!e[o.enterMod], o.autoAccept ? 'true' : false);
}
// pressing virtual enter button inside of a textarea - add a carriage return
// e.target is span when clicking on text and button at other times
if (base.isTextArea && $(e.target).closest('button').length) {
// IE8 fix (space + \n) - fixes #71 thanks Blookie!
base.insertText(($keyboard.msie ? ' ' : '') + '\n');
}
if (base.isContentEditable && !o.enterNavigation) {
base.execCommand('insertHTML', '<div><br class="' + $keyboard.css.divWrapperCE + '"></div>');
// Using backspace on wrapped BRs will now shift the textnode inside of the wrapped BR
// Although not ideal, the caret is moved after the block - see the wiki page for
// more details: https://github.com/Mottie/Keyboard/wiki/Contenteditable#limitations
// move caret after a delay to allow rendering of HTML
setTimeout(function() {
$keyboard.keyaction.right(base);
base.saveCaret();
}, 0);
}
},
// caps lock key
lock: function (base) {
base.last.keyset[0] = base.shiftActive = base.capsLock = !base.capsLock;
base.showSet();
},
left: function (base) {
var p = $keyboard.caret(base.$preview);
if (p.start - 1 >= 0) {
// move both start and end of caret (prevents text selection) & save caret position
base.last.start = base.last.end = p.start - 1;
$keyboard.caret(base.$preview, base.last);
base.setScroll();
}
},
meta: function (base, el) {
var $el = $(el);
base.metaActive = !$el.hasClass(base.options.css.buttonActive);
base.showSet($el.attr('data-name'));
},
next: function (base) {
base.switchInput(true, base.options.autoAccept);
return false;
},
// same as 'default' - resets to base keyset
normal: function (base) {
base.shiftActive = base.altActive = base.metaActive = false;
base.showSet();
},
prev: function (base) {
base.switchInput(false, base.options.autoAccept);
return false;
},
right: function (base) {
var p = $keyboard.caret(base.$preview),
len = base.isContentEditable ? $keyboard.getEditableLength(base.el) : base.getValue().length;
if (p.end + 1 <= len) {
// move both start and end of caret to end position
// (prevents text selection) && save caret position
base.last.start = base.last.end = p.end + 1;
$keyboard.caret(base.$preview, base.last);
base.setScroll();
}
},
shift: function (base) {
base.last.keyset[0] = base.shiftActive = !base.shiftActive;
base.showSet();
},
sign: function (base) {
var signRegex = base.decimal ? /^[+-]?\d*\.?\d*$/ : /^[+-]?\d*,?\d*$/;
if (signRegex.test(base.getValue())) {
var caret,
p = $keyboard.caret(base.$preview),
val = base.getValue(),
len = base.isContentEditable ? $keyboard.getEditableLength(base.el) : val.length;
base.setValue(val * -1);
caret = len - val.length;
base.last.start = p.start + caret;
base.last.end = p.end + caret;
$keyboard.caret(base.$preview, base.last);
base.setScroll();
}
},
space: function (base) {
base.insertText(' ');
},
tab: function (base) {
var o = base.options;
if (!base.isTextArea) {
if (o.tabNavigation) {
return base.switchInput(!base.shiftActive, true);
} else if (base.isInput) {
// ignore tab key in input
return false;
}
}
base.insertText('\t');
},
toggle: function (base) {
base.enabled = !base.enabled;
base.toggle();
},
// *** Special action keys: NBSP & zero-width characters ***
// Non-breaking space
NBSP: '\u00a0',
// zero width space
ZWSP: '\u200b',
// Zero width non-joiner
ZWNJ: '\u200c',
// Zero width joiner
ZWJ: '\u200d',
// Left-to-right Mark
LRM: '\u200e',
// Right-to-left Mark
RLM: '\u200f'
};
// Default keyboard layouts
$keyboard.builtLayouts = {};
$keyboard.layouts = {
'alpha': {
'normal': [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} a b c d e f g h i j [ ] \\',
'k l m n o p q r s ; \' {enter}',
'{shift} t u v w x y z , . / {shift}',
'{accept} {space} {cancel}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} A B C D E F G H I J { } |',
'K L M N O P Q R S : " {enter}',
'{shift} T U V W X Y Z < > ? {shift}',
'{accept} {space} {cancel}'
]
},
'qwerty': {
'normal': [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} q w e r t y u i o p [ ] \\',
'a s d f g h j k l ; \' {enter}',
'{shift} z x c v b n m , . / {shift}',
'{accept} {space} {cancel}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} Q W E R T Y U I O P { } |',
'A S D F G H J K L : " {enter}',
'{shift} Z X C V B N M < > ? {shift}',
'{accept} {space} {cancel}'
]
},
'international': {
'normal': [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} q w e r t y u i o p [ ] \\',
'a s d f g h j k l ; \' {enter}',
'{shift} z x c v b n m , . / {shift}',
'{accept} {alt} {space} {alt} {cancel}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} Q W E R T Y U I O P { } |',
'A S D F G H J K L : " {enter}',
'{shift} Z X C V B N M < > ? {shift}',
'{accept} {alt} {space} {alt} {cancel}'
],
'alt': [
'~ \u00a1 \u00b2 \u00b3 \u00a4 \u20ac \u00bc \u00bd \u00be \u2018 \u2019 \u00a5 \u00d7 {bksp}',
'{tab} \u00e4 \u00e5 \u00e9 \u00ae \u00fe \u00fc \u00fa \u00ed \u00f3 \u00f6 \u00ab \u00bb \u00ac',
'\u00e1 \u00df \u00f0 f g h j k \u00f8 \u00b6 \u00b4 {enter}',
'{shift} \u00e6 x \u00a9 v b \u00f1 \u00b5 \u00e7 > \u00bf {shift}',
'{accept} {alt} {space} {alt} {cancel}'
],
'alt-shift': [
'~ \u00b9 \u00b2 \u00b3 \u00a3 \u20ac \u00bc \u00bd \u00be \u2018 \u2019 \u00a5 \u00f7 {bksp}',
'{tab} \u00c4 \u00c5 \u00c9 \u00ae \u00de \u00dc \u00da \u00cd \u00d3 \u00d6 \u00ab \u00bb \u00a6',
'\u00c4 \u00a7 \u00d0 F G H J K \u00d8 \u00b0 \u00a8 {enter}',
'{shift} \u00c6 X \u00a2 V B \u00d1 \u00b5 \u00c7 . \u00bf {shift}',
'{accept} {alt} {space} {alt} {cancel}'
]
},
'colemak': {
'normal': [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} q w f p g j l u y ; [ ] \\',
'{bksp} a r s t d h n e i o \' {enter}',
'{shift} z x c v b k m , . / {shift}',
'{accept} {space} {cancel}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} Q W F P G J L U Y : { } |',
'{bksp} A R S T D H N E I O " {enter}',
'{shift} Z X C V B K M < > ? {shift}',
'{accept} {space} {cancel}'
]
},
'dvorak': {
'normal': [
'` 1 2 3 4 5 6 7 8 9 0 [ ] {bksp}',
'{tab} \' , . p y f g c r l / = \\',
'a o e u i d h t n s - {enter}',
'{shift} ; q j k x b m w v z {shift}',
'{accept} {space} {cancel}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) { } {bksp}',
'{tab} " < > P Y F G C R L ? + |',
'A O E U I D H T N S _ {enter}',
'{shift} : Q J K X B M W V Z {shift}',
'{accept} {space} {cancel}'
]
},
'num': {
'normal': [
'= ( ) {b}',
'{clear} / * -',
'7 8 9 +',
'4 5 6 {sign}',
'1 2 3 %',
'0 {dec} {a} {c}'
]
}
};
$keyboard.language = {
en: {
display: {
// check mark - same action as accept
'a': '\u2714:Accept (Shift+Enter)',
'accept': 'Accept:Accept (Shift+Enter)',
// other alternatives \u2311
'alt': 'Alt:\u2325 AltGr',
// Left arrow (same as ←)
'b': '\u232b:Backspace',
'bksp': 'Bksp:Backspace',
// big X, close - same action as cancel
'c': '\u2716:Cancel (Esc)',
'cancel': 'Cancel:Cancel (Esc)',
// clear num pad
'clear': 'C:Clear',
'combo': '\u00f6:Toggle Combo Keys',
// decimal point for num pad (optional), change '.' to ',' for European format
'dec': '.:Decimal',
// down, then left arrow - enter symbol
'e': '\u23ce:Enter',
'empty': '\u00a0',
'enter': 'Enter:Enter \u23ce',
// left arrow (move caret)
'left': '\u2190',
// caps lock
'lock': 'Lock:\u21ea Caps Lock',
'next': 'Next \u21e8',
'prev': '\u21e6 Prev',
// right arrow (move caret)
'right': '\u2192',
// thick hollow up arrow
's': '\u21e7:Shift',
'shift': 'Shift:Shift',
// +/- sign for num pad
'sign': '\u00b1:Change Sign',
'space': '\u00a0:Space',
// right arrow to bar (used since this virtual keyboard works with one directional tabs)
't': '\u21e5:Tab',
// \u21b9 is the true tab symbol (left & right arrows)
'tab': '\u21e5 Tab:Tab',
// replaced by an image
'toggle': ' ',
// added to titles of keys
// accept key status when acceptValid:true
'valid': 'valid',
'invalid': 'invalid',
// combo key states
'active': 'active',
'disabled': 'disabled'
},
// Message added to the key title while hovering, if the mousewheel plugin exists
wheelMessage: 'Use mousewheel to see other keys',
comboRegex: /([`\'~\^\"ao])([a-z])/mig,
combos: {
// grave
'`': { a: '\u00e0', A: '\u00c0', e: '\u00e8', E: '\u00c8', i: '\u00ec', I: '\u00cc', o: '\u00f2',
O: '\u00d2', u: '\u00f9', U: '\u00d9', y: '\u1ef3', Y: '\u1ef2' },
// acute & cedilla
"'": { a: '\u00e1', A: '\u00c1', e: '\u00e9', E: '\u00c9', i: '\u00ed', I: '\u00cd', o: '\u00f3',
O: '\u00d3', u: '\u00fa', U: '\u00da', y: '\u00fd', Y: '\u00dd' },
// umlaut/trema
'"': { a: '\u00e4', A: '\u00c4', e: '\u00eb', E: '\u00cb', i: '\u00ef', I: '\u00cf', o: '\u00f6',
O: '\u00d6', u: '\u00fc', U: '\u00dc', y: '\u00ff', Y: '\u0178' },
// circumflex
'^': { a: '\u00e2', A: '\u00c2', e: '\u00ea', E: '\u00ca', i: '\u00ee', I: '\u00ce', o: '\u00f4',
O: '\u00d4', u: '\u00fb', U: '\u00db', y: '\u0177', Y: '\u0176' },
// tilde
'~': { a: '\u00e3', A: '\u00c3', e: '\u1ebd', E: '\u1ebc', i: '\u0129', I: '\u0128', o: '\u00f5',
O: '\u00d5', u: '\u0169', U: '\u0168', y: '\u1ef9', Y: '\u1ef8', n: '\u00f1', N: '\u00d1' }
}
}
};
$keyboard.defaultOptions = {
// set this to ISO 639-1 language code to override language set by the layout
// http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
// language defaults to 'en' if not found
language: null,
rtl: false,
// *** choose layout & positioning ***
layout: 'qwerty',
customLayout: null,
position: {
// optional - null (attach to input/textarea) or a jQuery object (attach elsewhere)
of: null,
my: 'center top',
at: 'center top',
// used when 'usePreview' is false (centers the keyboard at the bottom of the input/textarea)
at2: 'center bottom'
},
// allow jQuery position utility to reposition the keyboard on window resize
reposition: true,
// preview added above keyboard if true, original input/textarea used if false
usePreview: true,
// if true, the keyboard will always be visible
alwaysOpen: false,
// give the preview initial focus when the keyboard becomes visible
initialFocus: true,
// avoid changing the focus (hardware keyboard probably won't work)
noFocus: false,
// if true, keyboard will remain open even if the input loses focus, but closes on escape
// or when another keyboard opens.
stayOpen: false,
// Prevents the keyboard from closing when the user clicks or presses outside the keyboard
// the `autoAccept` option must also be set to true when this option is true or changes are lost
userClosed: false,
// if true, keyboard will not close if you press escape.
ignoreEsc: false,
// if true, keyboard will only closed on click event instead of mousedown and touchstart
closeByClickEvent: false,
css: {
// input & preview
input: 'ui-widget-content ui-corner-all',
// keyboard container
container: 'ui-widget-content ui-widget ui-corner-all ui-helper-clearfix',
// keyboard container extra class (same as container, but separate)
popup: '',
// default state
buttonDefault: 'ui-state-default ui-corner-all',
// hovered button
buttonHover: 'ui-state-hover',
// Action keys (e.g. Accept, Cancel, Tab, etc); this replaces 'actionClass' option
buttonAction: 'ui-state-active',
// Active keys (e.g. shift down, meta keyset active, combo keys active)
buttonActive: 'ui-state-active',
// used when disabling the decimal button {dec} when a decimal exists in the input area
buttonDisabled: 'ui-state-disabled',
buttonEmpty: 'ui-keyboard-empty'
},
// *** Useability ***
// Auto-accept content when clicking outside the keyboard (popup will close)
autoAccept: false,
// Auto-accept content even if the user presses escape (only works if `autoAccept` is `true`)
autoAcceptOnEsc: false,
// Prevents direct input in the preview window when true
lockInput: false,
// Prevent keys not in the displayed keyboard from being typed in
restrictInput: false,
// Additional allowed characters while restrictInput is true
restrictInclude: '', // e.g. 'a b foo \ud83d\ude38'
// Check input against validate function, if valid the accept button gets a class name of
// 'ui-keyboard-valid-input'. If invalid, the accept button gets a class name of
// 'ui-keyboard-invalid-input'
acceptValid: false,
// Auto-accept when input is valid; requires `acceptValid` set `true` & validate callback
autoAcceptOnValid: false,
// Check validation on keyboard initialization. If false, the "Accept" key state (color)
// will not change to show if the content is valid, or not
checkValidOnInit: true,
// if acceptValid is true & the validate function returns a false, this option will cancel
// a keyboard close only after the accept button is pressed
cancelClose: true,
// tab to go to next, shift-tab for previous (default behavior)
tabNavigation: false,
// enter for next input; shift+enter accepts content & goes to next
// shift + 'enterMod' + enter ('enterMod' is the alt as set below) will accept content and go
// to previous in a textarea
enterNavigation: false,
// mod key options: 'ctrlKey', 'shiftKey', 'altKey', 'metaKey' (MAC only)
enterMod: 'altKey', // alt-enter to go to previous; shift-alt-enter to accept & go to previous
// if true, the next button will stop on the last keyboard input/textarea; prev button stops at first
// if false, the next button will wrap to target the first input/textarea; prev will go to the last
stopAtEnd: true,
// Set this to append the keyboard after the input/textarea (appended to the input/textarea parent).
// This option works best when the input container doesn't have a set width & when the 'tabNavigation'
// option is true.
appendLocally: false,
// When appendLocally is false, the keyboard will be appended to this object
appendTo: 'body',
// Wrap all <br>s inside of a contenteditable in a div; without wrapping, the caret
// position will not be accurate
wrapBRs: true,
// If false, the shift key will remain active until the next key is (mouse) clicked on; if true it will
// stay active until pressed again
stickyShift: true,
// Prevent pasting content into the area
preventPaste: false,
// caret placed at the end of any text when keyboard becomes visible
caretToEnd: false,
// caret stays this many pixels from the edge of the input while scrolling left/right;
// use "c" or "center" to center the caret while scrolling
scrollAdjustment: 10,
// Set the max number of characters allowed in the input, setting it to false disables this option
maxLength: false,
// allow inserting characters @ caret when maxLength is set
maxInsert: true,
// Mouse repeat delay - when clicking/touching a virtual keyboard key, after this delay the key will
// start repeating
repeatDelay: 500,
// Mouse repeat rate - after the repeatDelay, this is the rate (characters per second) at which the
// key is repeated Added to simulate holding down a real keyboard key and having it repeat. I haven't
// calculated the upper limit of this rate, but it is limited to how fast the javascript can process
// the keys. And for me, in Firefox, it's around 20.
repeatRate: 20,
// resets the keyboard to the default keyset when visible
resetDefault: true,
// Event (namespaced) on the input to reveal the keyboard. To disable it, just set it to ''.
openOn: 'focus',
// enable the keyboard on readonly inputs
activeOnReadonly: false,
// Event (namepaced) for when the character is added to the input (clicking on the keyboard)
keyBinding: 'mousedown touchstart',
// enable/disable mousewheel functionality
// enabling still depends on the mousewheel plugin
useWheel: true,
// combos (emulate dead keys : http://en.wikipedia.org/wiki/Keyboard_layout#US-International)
// if user inputs `a the script converts it to à, ^o becomes ô, etc.
useCombos: true,
/*
// *** Methods ***
// commenting these out to reduce the size of the minified version
// Callbacks - attach a function to any of these callbacks as desired
initialized : function(e, keyboard, el) {},
beforeVisible : function(e, keyboard, el) {},
visible : function(e, keyboard, el) {},
beforeInsert : function(e, keyboard, el, textToAdd) { return textToAdd; },
change : function(e, keyboard, el) {},
beforeClose : function(e, keyboard, el, accepted) {},
accepted : function(e, keyboard, el) {},
canceled : function(e, keyboard, el) {},
restricted : function(e, keyboard, el) {},
hidden : function(e, keyboard, el) {},
// called instead of base.switchInput
switchInput : function(keyboard, goToNext, isAccepted) {},
// used if you want to create a custom layout or modify the built-in keyboard
create : function(keyboard) { return keyboard.buildKeyboard(); },
// build key callback
buildKey : function( keyboard, data ) {
/ *
data = {
// READ ONLY
isAction : [boolean] true if key is an action key
name : [string] key class name suffix ( prefix = 'ui-keyboard-' );
may include decimal ascii value of character
value : [string] text inserted (non-action keys)
title : [string] title attribute of key
action : [string] keyaction name
html : [string] HTML of the key; it includes a <span> wrapping the text
// use to modify key HTML
$key : [object] jQuery selector of key which is already appended to keyboard
}
* /
return data;
},
*/
// this callback is called, if the acceptValid is true, and just before the 'beforeClose' to check
// the value if the value is valid, return true and the keyboard will continue as it should
// (close if not always open, etc). If the value is not valid, return false and clear the keyboard
// value ( like this "keyboard.$preview.val('');" ), if desired. The validate function is called after
// each input, the 'isClosing' value will be false; when the accept button is clicked,
// 'isClosing' is true
validate: function (/* keyboard, value, isClosing */) {
return true;
}
};
$keyboard.supportedInputTypes = [
'text',
'search',
'url',
'tel',
'password'
];
// for checking combos
$keyboard.comboRegex = /([`\'~\^\"ao])([a-z])/mig;
// store current keyboard element; used by base.isCurrent()
$keyboard.currentKeyboard = '';
$('<!--[if lte IE 8]><script>jQuery("body").addClass("oldie");</script><![endif]--><!--[if IE]>' +
'<script>jQuery("body").addClass("ie");</script><![endif]-->')
.appendTo('body')
.remove();
$keyboard.msie = $('body').hasClass('oldie'); // Old IE flag, used for caret positioning
$keyboard.allie = $('body').hasClass('ie');
$keyboard.watermark = (typeof (document.createElement('input').placeholder) !== 'undefined');
$keyboard.checkCaretSupport = function () {
if (typeof $keyboard.checkCaret !== 'boolean') {
var elWithFocus = document.activeElement;
// Check if caret position is saved when input is hidden or loses focus
// (*cough* all versions of IE and I think Opera has/had an issue as well
var $temp = $('<div style="height:0px;width:0px;overflow:hidden;position:fixed;top:0;left:-100px;">' +
'<input type="text" value="testing"/></div>').prependTo('body'); // stop page scrolling
$keyboard.caret($temp.find('input'), 3, 3);
// Also save caret position of the input if it is locked
$keyboard.checkCaret = $keyboard.caret($temp.find('input').hide().show()).start !== 3;
$temp.remove();
if (elWithFocus && elWithFocus.focus) {
elWithFocus.focus();
}
}
return $keyboard.checkCaret;
};
$keyboard.caret = function($el, param1, param2) {
if (!$el || !$el.length || $el.is(':hidden') || $el.css('visibility') === 'hidden') {
return {};
}
var start, end, txt, pos,
kb = $el.data( 'keyboard' ),
noFocus = kb && kb.options.noFocus,
formEl = /(textarea|input)/i.test($el[0].nodeName);
if (!noFocus) { $el.focus(); }
// set caret position
if (typeof param1 !== 'undefined') {
// allow setting caret using ( $el, { start: x, end: y } )
if (typeof param1 === 'object' && 'start' in param1 && 'end' in param1) {
start = param1.start;
end = param1.end;
} else if (typeof param2 === 'undefined') {
param2 = param1; // set caret using start position
}
// set caret using ( $el, start, end );
if (typeof param1 === 'number' && typeof param2 === 'number') {
start = param1;
end = param2;
} else if ( param1 === 'start' ) {
start = end = 0;
} else if ( typeof param1 === 'string' ) {
// unknown string setting, move caret to end
start = end = 'end';
}
// *** SET CARET POSITION ***
// modify the line below to adapt to other caret plugins
return formEl ?
$el.caret( start, end, noFocus ) :
$keyboard.setEditableCaret( $el, start, end );
}
// *** GET CARET POSITION ***
// modify the line below to adapt to other caret plugins
if (formEl) {
// modify the line below to adapt to other caret plugins
pos = $el.caret();
} else {
// contenteditable
pos = $keyboard.getEditableCaret($el[0]);
}
start = pos.start;
end = pos.end;
// *** utilities ***
txt = formEl && $el[0].value || $el.text() || '';
return {
start : start,
end : end,
// return selected text
text : txt.substring( start, end ),
// return a replace selected string method
replaceStr : function( str ) {
return txt.substring( 0, start ) + str + txt.substring( end, txt.length );
}
};
};
$keyboard.isTextNode = function(el) {
return el && el.nodeType === 3;
};
$keyboard.isBlock = function(el, node) {
var win = el.ownerDocument.defaultView;
if (
node && node.nodeType === 1 && node !== el &&
win.getComputedStyle(node).display === 'block'
) {
return 1;
}
return 0;
};
// Wrap all BR's inside of contenteditable
$keyboard.wrapBRs = function(container) {
var $el = $(container).find('br:not(.' + $keyboard.css.divWrapperCE + ')');
if ($el.length) {
$.each($el, function(i, el) {
var len = el.parentNode.childNodes.length;
if (
// wrap BRs if not solo child
len !== 1 ||
// Or if BR is wrapped by a span
len === 1 && !$keyboard.isBlock(container, el.parentNode)
) {
$(el).addClass($keyboard.css.divWrapperCE).wrap('<div>');
}
});
}
};
$keyboard.getEditableCaret = function(container) {
container = $(container)[0];
if (!container.isContentEditable) { return {}; }
var end, text,
options = ($(container).data('keyboard') || {}).options,
doc = container.ownerDocument,
range = doc.getSelection().getRangeAt(0),
result = pathToNode(range.startContainer, range.startOffset),
start = result.position;
if (options.wrapBRs !== false) {
$keyboard.wrapBRs(container);
}
function pathToNode(endNode, offset) {
var node, adjust,
txt = '',
done = false,
position = 0,
nodes = $.makeArray(container.childNodes);
function checkBlock(val) {
if (val) {
position += val;
txt += options && options.replaceCR || '\n';
}
}
while (!done && nodes.length) {
node = nodes.shift();
if (node === endNode) {
done = true;
}
// Add one if previous sibling was a block node (div, p, etc)
adjust = $keyboard.isBlock(container, node.previousSibling);
checkBlock(adjust);
if ($keyboard.isTextNode(node)) {
position += done ? offset : node.length;
txt += node.textContent;
if (done) {
return {position: position, text: txt};
}
} else if (!done && node.childNodes) {
nodes = $.makeArray(node.childNodes).concat(nodes);
}
// Add one if we're inside a block node (div, p, etc)
// and previous sibling was a text node
adjust = $keyboard.isTextNode(node.previousSibling) && $keyboard.isBlock(container, node);
checkBlock(adjust);
}
return {position: position, text: txt};
}
// check of start and end are the same
if (range.endContainer === range.startContainer && range.endOffset === range.startOffset) {
end = start;
text = '';
} else {
result = pathToNode(range.endContainer, range.endOffset);
end = result.position;
text = result.text.substring(start, end);
}
return {
start: start,
end: end,
text: text
};
};
$keyboard.getEditableLength = function(container) {
var result = $keyboard.setEditableCaret(container, 'getMax');
// if not a number, the container is not a contenteditable element
return typeof result === 'number' ? result : null;
};
$keyboard.setEditableCaret = function(container, start, end) {
container = $(container)[0];
if (!container.isContentEditable) { return {}; }
var doc = container.ownerDocument,
range = doc.createRange(),
sel = doc.getSelection(),
options = ($(container).data('keyboard') || {}).options,
s = start,
e = end,
text = '',
result = findNode(start === 'getMax' ? 'end' : start);
function findNode(offset) {
if (offset === 'end') {
// Set some value > content length; but return max
offset = container.innerHTML.length;
} else if (offset < 0) {
offset = 0;
}
var node, check,
txt = '',
done = false,
position = 0,
last = 0,
max = 0,
nodes = $.makeArray(container.childNodes);
function updateText(val) {
txt += val ? options && options.replaceCR || '\n' : '';
return val > 0;
}
function checkDone(adj) {
var val = position + adj;
last = max;
max += adj;
if (offset - val >= 0) {
position = val;
return offset - position <= 0;
}
return offset - val <= 0;
}
while (!done && nodes.length) {
node = nodes.shift();
// Add one if the previous sibling was a block node (div, p, etc)
check = $keyboard.isBlock(container, node.previousSibling);
if (updateText(check) && checkDone(check)) {
done = true;
}
// Add one if we're inside a block node (div, p, etc)
check = $keyboard.isTextNode(node.previousSibling) && $keyboard.isBlock(container, node);
if (updateText(check) && checkDone(check)) {
done = true;
}
if ($keyboard.isTextNode(node)) {
txt += node.textContent;
if (checkDone(node.length)) {
check = offset - position === 0 && position - last >= 1 ? node.length : offset - position;
return {
node: node,
offset: check,
position: offset,
text: txt
};
}
} else if (!done && node.childNodes) {
nodes = $.makeArray(node.childNodes).concat(nodes);
}
}
return nodes.length ?
{node: node, offset: offset - position, position: offset, text: txt} :
// Offset is larger than content, return max
{node: node, offset: node && node.length || 0, position: max, text: txt};
}
if (result.node) {
s = result.position; // Adjust if start > content length
if (start === 'getMax') {
return s;
}
range.setStart(result.node, result.offset);
// Only find end if > start and is defined... this allows passing
// setEditableCaret(el, 'end') or setEditableCaret(el, 10, 'end');
if (typeof end !== 'undefined' && end !== start) {
result = findNode(end);
}
if (result.node) {
e = result.position; // Adjust if end > content length
range.setEnd(result.node, result.offset);
text = s === e ? '' : result.text.substring(s, e);
}
sel.removeAllRanges();
sel.addRange(range);
}
return {
start: s,
end: e,
text: text
};
};
$keyboard.replaceContent = function (el, param) {
el = $(el)[0];
var node, i, str,
type = typeof param,
caret = $keyboard.getEditableCaret(el).start,
charIndex = 0,
nodeStack = [el];
while ((node = nodeStack.pop())) {
if ($keyboard.isTextNode(node)) {
if (type === 'function') {
if (caret >= charIndex && caret <= charIndex + node.length) {
node.textContent = param(node.textContent);
}
} else if (type === 'string') {
// maybe not the best method, but it works for simple changes
str = param.substring(charIndex, charIndex + node.length);
if (str !== node.textContent) {
node.textContent = str;
}
}
charIndex += node.length;
} else if (node && node.childNodes) {
i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
i = $keyboard.getEditableCaret(el);
$keyboard.setEditableCaret(el, i.start, i.start);
};
$.fn.keyboard = function (options) {
return this.each(function () {
if (!$(this).data('keyboard')) {
/*jshint nonew:false */
(new $.keyboard(this, options));
}
});
};
$.fn.getkeyboard = function () {
return this.data('keyboard');
};
/* Copyright (c) 2010 C. F., Wong (<a href="http://cloudgen.w0ng.hk">Cloudgen Examplet Store</a>)
* Licensed under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
* Highly modified from the original
*/
$.fn.caret = function (start, end, noFocus) {
if (
typeof this[0] === 'undefined' ||
this.is(':hidden') ||
this.css('visibility') === 'hidden' ||
!/(INPUT|TEXTAREA)/i.test(this[0].nodeName)
) {
return this;
}
var selRange, range, stored_range, txt, val,
$el = this,
el = $el[0],
selection = el.ownerDocument.selection,
sTop = el.scrollTop,
ss = false,
supportCaret = true;
try {
ss = 'selectionStart' in el;
} catch (err) {
supportCaret = false;
}
if (supportCaret && typeof start !== 'undefined') {
if (!/(email|number)/i.test(el.type)) {
if (ss) {
el.selectionStart = start;
el.selectionEnd = end;
} else {
selRange = el.createTextRange();
selRange.collapse(true);
selRange.moveStart('character', start);
selRange.moveEnd('character', end - start);
selRange.select();
}
}
// must be visible or IE8 crashes; IE9 in compatibility mode works fine - issue #56
if (!noFocus && ($el.is(':visible') || $el.css('visibility') !== 'hidden')) {
el.focus();
}
el.scrollTop = sTop;
return this;
}
if (/(email|number)/i.test(el.type)) {
// fix suggested by raduanastase (https://github.com/Mottie/Keyboard/issues/105#issuecomment-40456535)
start = end = $el.val().length;
} else if (ss) {
start = el.selectionStart;
end = el.selectionEnd;
} else if (selection) {
if (el.nodeName.toUpperCase() === 'TEXTAREA') {
val = $el.val();
range = selection.createRange();
stored_range = range.duplicate();
stored_range.moveToElementText(el);
stored_range.setEndPoint('EndToEnd', range);
// thanks to the awesome comments in the rangy plugin
start = stored_range.text.replace(/\r/g, '\n').length;
end = start + range.text.replace(/\r/g, '\n').length;
} else {
val = $el.val().replace(/\r/g, '\n');
range = selection.createRange().duplicate();
range.moveEnd('character', val.length);
start = (range.text === '' ? val.length : val.lastIndexOf(range.text));
range = selection.createRange().duplicate();
range.moveStart('character', -val.length);
end = range.text.length;
}
} else {
// caret positioning not supported
start = end = (el.value || '').length;
}
txt = (el.value || '');
return {
start: start,
end: end,
text: txt.substring(start, end),
replace: function (str) {
return txt.substring(0, start) + str + txt.substring(end, txt.length);
}
};
};
return $keyboard;
}));
//}}}
//{{{
/*! jQuery UI Virtual Keyboard Virtual Caret v1.1.5 (beta) *//*
* for Keyboard v1.18+ only (2/20/2016)
* modified from https://github.com/component/textarea-caret-position
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* CSS changes
* NOTE: caret margin-top => is added to the caret height (top & bottom)
* .ui-keyboard-preview-wrapper { position: relative; overflow: hidden; }
* .ui-keyboard-caret { background: red; width: 1px; margin-top: 3px; }
*/
/*jshint browser:true, jquery:true, unused:false */
/*global require:false, define:false, module:false */
;( function( factory ) {
if ( typeof define === 'function' && define.amd ) {
define( [ 'jquery' ], factory );
} else if ( typeof module === 'object' && typeof module.exports === 'object' ) {
module.exports = factory( require( 'jquery' ) );
} else {
factory( jQuery );
}
}( function( $ ) {
'use strict';
var $keyboard = $.keyboard;
$keyboard.firefox = typeof window.mozInnerScreenX !== 'undefined';
$.extend( $keyboard.css, {
caret : 'ui-keyboard-caret',
caretMirror : 'ui-keyboard-mirror-div'
});
$.fn.addCaret = function( options ) {
var defaults = {
caretClass : '',
// *** for future use ***
// data-attribute containing the character(s) next to the caret
charAttr : 'data-character',
// # character(s) next to the caret (can be negative for RTL)
charIndex : 1,
offsetX : 0,
offsetY : 0,
adjustHt : 0
};
return this.each( function() {
// make sure a keyboard is attached
var o, namespace,
kbevents = $keyboard.events,
base = $( this ).data( 'keyboard' );
if ( !base ) { return; }
// variables
o = base.caret_options = $.extend( {}, defaults, options );
namespace = base.caret_namespace = base.namespace + 'caret';
base.extensionNamespace.push( namespace );
// modified from https://github.com/component/textarea-caret-position
// The properties that we copy into a mirrored div.
// Note that some browsers, such as Firefox,
// do not concatenate properties, i.e. padding-top, bottom etc. -> padding,
// so we have to do every single property specifically.
base.textareaCaretProperties = [
'direction', 'boxSizing', 'width', 'height', 'overflowX', 'overflowY',
'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth', 'borderStyle',
'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft',
'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'fontSizeAdjust',
'lineHeight', 'fontFamily', 'textAlign', 'textTransform', 'textIndent', 'textDecoration',
'letterSpacing', 'wordSpacing', 'tabSize', 'MozTabSize'
];
base.caret_setup = function() {
var kbcss = $keyboard.css,
events = 'keyup keypress mouseup mouseleave '.split( ' ' ).join( namespace + ' ' ),
style = 'position:absolute;visibility:hidden;top:-9999em;left:-9999em;' +
'white-space:pre-wrap;z-index:-10;' +
( base.preview.nodeName === 'INPUT' ? '' : 'word-wrap:break-word;' );
// add mirrored div
base.$keyboard.find( '.' + kbcss.caretMirror ).remove();
base.caret_$div = $( '<div class="' + kbcss.caretMirror + '" style="' + style + '">' )
.appendTo( base.$keyboard );
// remove caret, just-in-case
if (base.$caret) { base.$caret.remove(); }
base.$caret = $( '<div class="' + kbcss.caret + ' ' + o.caretClass + '" style="position:absolute;">' )
.insertAfter( base.$preview );
base.$el
.unbind( kbevents.kbChange + namespace )
.bind( kbevents.kbChange + namespace, function() {
base.findCaretPos();
});
base.$preview
.unbind( events )
.bind( events, function() {
base.findCaretPos();
});
};
// getCaretCoordinatesFn = function (element, position, recalculate) {
base.findCaretPos = function() {
if ( !base.caret_$div ) { return; }
var style, computed, margin, pos, position, txt, span, offset,
element = base.preview,
fontWidth = parseFloat( base.$preview.css('fontSize') ),
isInput = element.nodeName === 'INPUT',
div = base.caret_$div[0];
style = div.style;
// getComputedStyle with null - fixes #384
computed = window.getComputedStyle ? getComputedStyle( element, null ) : element.currentStyle;
// get caret position based on text-direction
pos = $keyboard.caret( base.$preview );
position = Math[ computed.direction === 'ltr' ? 'max' : 'min' ]( pos.start, pos.end );
// transfer the element's properties to the div
base.textareaCaretProperties.forEach(function ( prop ) {
style[ prop ] = computed[ prop ];
});
if ( $keyboard.firefox ) {
// Firefox adds 2 pixels to the padding - https://bugzilla.mozilla.org/show_bug.cgi?id=753662
style.width = parseInt( computed.width, 10 ) - 2 + 'px';
// Firefox lies about the overflow property for textareas:
// https://bugzilla.mozilla.org/show_bug.cgi?id=984275
if ( element.scrollHeight > parseInt( computed.height, 10 ) ) {
style.overflowY = 'scroll';
}
}
// for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
// style.overflow = 'hidden';
style.width = parseInt( isInput ? element.scrollWidth : computed.width, 10 ) +
// add 2em extra width if it's an input to prevent wrap
( isInput ? fontWidth * 2 : 0 ) + 'px';
div.textContent = element.value.substring( 0, position );
// the second special handling for input type="text" vs textarea:
// spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
if ( element.nodeName === 'INPUT' ) {
div.textContent = div.textContent.replace( /\x20/g, '\xa0' );
}
span = document.createElement( 'span' );
// Wrapping must be replicated *exactly*, including when a long word gets
// onto the next line, with whitespace at the end of the line before (#7).
// The *only* reliable way to do that is to copy the *entire* rest of the
// textarea's content into the <span> created at the caret position.
// for inputs, just '.' would be enough, but why bother?
// || because a completely empty faux span doesn't render at all
// changed to zero-width space due to inaccuracy when textAlign = center; see #436
span.textContent = element.value.substring( position ) || '\u200b';
div.appendChild( span );
offset = $(span).position();
// adjust for 2em added to width moves caret, use half; see #436
pos = style.textAlign === 'center' ? fontWidth : 0;
base.caretPos = {
top: offset.top + parseInt( computed.borderTopWidth, 10 ) + o.offsetY,
left: offset.left + parseInt( computed.borderLeftWidth, 10 ) + o.offsetX - pos
};
// make caret height = font-size + any margin-top x2 added by the css
margin = parseInt( base.$caret.css( 'margin-top' ), 10 );
style = Math.round( fontWidth + margin * 2 ) + o.adjustHt;
offset = base.$preview.position();
base.$caret.css({
top: offset.top - element.scrollTop + base.caretPos.top - margin,
left: offset.left - element.scrollLeft + base.caretPos.left,
height: style
});
txt = element.value.substring( position, position + o.charIndex ).replace(/\s/, '\xa0' ) || '\xa0';
base.$caret.attr( o.charAttr, txt );
};
// setup caret when keyboard is visible
base.$el
.unbind( namespace )
.bind( kbevents.kbBeforeVisible + namespace, function() {
base.caret_setup();
})
.bind( kbevents.kbVisible + namespace, function() {
base.findCaretPos();
})
.bind( kbevents.kbHidden + namespace, function() {
// unbind events in case usePreview: false; see #376
var events = 'keyup keypress mouseup mouseleave '.split( ' ' ).join( namespace + ' ' );
base.$preview.unbind( events );
base.$caret.remove();
base.$caret = null;
base.caret_$div = null;
});
// visible event is fired before this extension is initialized, so check!
if ( base.options.alwaysOpen && base.isVisible() ) {
base.caret_setup();
base.findCaretPos();
}
});
};
}));
//}}}
//{{{
/*! jQuery UI Virtual Keyboard Typing Simulator v1.12.0 *//*
* for Keyboard v1.18+ only (2019-05-02)
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* Use this extension with the Virtual Keyboard to simulate
* typing for tutorials or whatever else use you can find
*
* Requires:
* jQuery
* Keyboard plugin : https://github.com/Mottie/Keyboard
*
* Setup:
* $('.ui-keyboard-input')
* .keyboard(options)
* .addTyping(typing-options);
*
* // or if targeting a specific keyboard
* $('#keyboard1')
* .keyboard(options)
* .addTyping(typing-options);
*
* Basic Usage:
* // To disable manual typing on the virtual keyboard, just set "showTyping"
* // option to false
* $('#keyboard-input').keyboard(options).addTyping({ showTyping: false });
*
* // Change the default typing delay (time the virtual keyboard highlights the
* // manually typed key) - default = 250 milliseconds
* $('#keyboard-input').keyboard(options).addTyping({ delay: 500 });
*
* // get keyboard object, open it, then start typing simulation
* $('#keyboard-input').getkeyboard().reveal().typeIn('Hello World', 700);
*
* // get keyboard object, open it, type in "This is a test" with 700ms delay
* // between types, then accept & close the keyboard
* $('#keyboard-input')
* .getkeyboard()
* .reveal()
* .typeIn('This is a test', 700, function(keyboard) {
* keyboard.accept();
* });
*/
/* More Examples:
* $('#inter').getkeyboard().reveal().typeIn('\tHello \b\n\tWorld', 500);
* $('#meta')
* .getkeyboard().reveal()
* .typeIn('abCDd11123\u2648\u2649\u264A\u264B', 700, function() {
* alert('all done!');
* });
*/
/* jshint browser:true, jquery:true, unused:false */
/* global require:false, define:false, module:false */
;( function( factory ) {
if ( typeof define === 'function' && define.amd ) {
define( ['jquery'], factory );
} else if (
typeof module === 'object' &&
typeof module.exports === 'object'
) {
module.exports = factory( require( 'jquery' ) );
} else {
factory( jQuery );
}
}( function( $ ) {
$.fn.addTyping = function( options ) {
//Set the default values, use comma to separate the settings, example:
var defaults = {
showTyping : true,
lockTypeIn : false,
delay : 250,
hoverDelay : 250
},
$keyboard = $.keyboard;
return this.each( function() {
// make sure a keyboard is attached
var o, base = $( this ).data( 'keyboard' );
if ( !base ) {
return;
}
// variables
o = base.typing_options = $.extend( {}, defaults, options );
base.typing_keymap = {
' ' : 'space',
'"' : '34',
"'" : '39',
' ' : 'space',
'\b' : 'bksp', // delete character to the left
'{b}' : 'bksp',
'{d}' : 'del', // delete character to the right
'{l}' : 'left', // move caret left
'{r}' : 'right', // move caret right
'\n' : 'enter',
'\r' : 'enter',
'{e}' : 'enter',
'\t' : 'tab',
'{t}' : 'tab'
};
base.typing_xref = {
8 : 'bksp',
9 : 'tab',
13 : 'enter',
32 : 'space',
37 : 'left',
39 : 'right',
46 : 'del'
};
base.typing_event = false;
base.typing_namespace = base.namespace + 'typing';
base.extensionNamespace.push( base.typing_namespace );
// save lockInput setting
o.savedLockInput = base.options.lockInput;
base.typing_setup_reset = function() {
var kbevents = $keyboard.events,
namespace = base.typing_namespace,
events = [ kbevents.kbHidden, kbevents.kbInactive, '' ]
.join( namespace + ' ' );
// reset "typeIn" when keyboard is closed
base.$el
.unbind( namespace )
.bind( events, function() {
base.typing_reset();
});
base
.unbindButton( namespace )
.bindButton( 'mousedown' + namespace, function() {
base.typing_reset();
});
};
base.typing_setup = function() {
var namespace = base.typing_namespace;
base.typing_setup_reset();
base.$el
.bind( $keyboard.events.kbBeforeVisible + namespace, function() {
base.typing_setup();
});
base.$preview
.unbind( namespace )
.bind( 'keyup' + namespace, function( e ) {
if ( o.init && o.lockTypeIn || !o.showTyping ) {
return false;
}
if ( e.which >= 37 && e.which <=40 ) {
return; // ignore arrow keys
}
if ( e.which === 16 ) {
base.shiftActive = false;
}
if ( e.which === 18 ) {
base.altActive = false;
}
if ( e.which === 16 || e.which === 18 ) {
base.showSet();
// Alt key will shift focus to the menu - doesn't work in Windows
setTimeout( function() {
if (base.$preview) {
base.$preview.focus();
}
}, 200 );
return;
}
})
// change keyset when either shift or alt is held down
.bind( 'keydown' + namespace, function( e ) {
if ( o.init && o.lockTypeIn || !o.showTyping ) {
return false;
}
e.temp = false; // prevent repetitive calls while keydown repeats.
if ( e.which === 16 ) {
e.temp = !base.shiftActive; base.shiftActive = true;
}
// it should be ok to reset e.temp, since both alt and shift will
// call this function separately
if ( e.which === 18 ) {
e.temp = !base.altActive; base.altActive = true;
}
if ( e.temp ) {
base.showSet();
base.$preview.focus(); // Alt shift focus to the menu
}
base.typing_event = true;
// Simulate key press for tab and backspace since they don't fire
// the keypress event
if ( base.typing_xref[ e.which ] ) {
base.typing_findKey( '', e ); // pass event object
}
})
.bind( 'keypress' + namespace, function( e ) {
if ( o.init && o.lockTypeIn ) {
return false;
}
// Simulate key press on virtual keyboard
if ( base.typing_event && !base.options.lockInput ) {
base.typing_reset();
base.typing_event = true;
base.typing_findKey( '', e ); // pass event object
}
});
};
base.typing_reset = function() {
base.typing_event = o.init = false;
o.text = '';
o.len = o.current = 0;
base.options.lockInput = o.savedLockInput;
// clearTimeout( base.typing_timer );
};
// Store typing text
base.typeIn = function( txt, delay, callback, e ) {
if ( !base.isVisible() ) {
// keyboard was closed
clearTimeout( base.typing_timer );
base.typing_reset();
return;
}
if ( !base.typing_event ) {
if ( o.init !== true ) {
o.init = true;
base.options.lockInput = o.lockTypeIn;
o.text = txt || o.text || '';
o.len = o.text.length;
o.delay = delay || o.delay;
o.current = 0; // position in text string
if ( callback ) {
o.callback = callback;
}
}
// function that loops through and types each character
txt = o.text.substring( o.current, ++o.current );
// add support for curly-wrapped single character: {l}, {r}, {d}, etc.
if (
txt === '{' &&
o.text.substring( o.current + 1, o.current + 2 ) === '}'
) {
txt += o.text.substring( o.current, o.current += 2 );
}
base.typing_findKey( txt, e );
} else if ( typeof txt === 'undefined' ) {
// typeIn called by user input
base.typing_event = false;
base.options.lockInput = o.savedLockInput;
return;
}
};
base.typing_findKey = function( txt, e ) {
var tar, m, n, k, key, ks, meta, set,
kbcss = $keyboard.css,
mappedKeys = $keyboard.builtLayouts[base.layout].mappedKeys;
// stop if keyboard is closed
if ( !base.isOpen || !base.$keyboard.length ) {
return;
}
ks = base.$keyboard.find( '.' + kbcss.keySet );
k = txt in base.typing_keymap ? base.typing_keymap[ txt ] : txt;
// typing_event is true when typing on the actual keyboard - look for
// actual key; All of this breaks when the CapLock is on... unable to
// find a cross-browser method that works.
tar = '.' + kbcss.keyButton + '[data-action="' + k + '"]';
if ( base.typing_event && e ) {
// xref used for keydown
// ( 46 = delete in keypress & period on keydown )
if (
e.type !== 'keypress' &&
base.typing_xref.hasOwnProperty( e.keyCode || e.which )
) {
// special named keys: bksp, tab and enter
tar = '.' +
kbcss.keyPrefix +
base.processName( base.typing_xref[ e.keyCode || e.which ] );
} else {
m = String.fromCharCode( e.charCode || e.which );
tar = ( mappedKeys.hasOwnProperty( m ) ) ?
'.' + kbcss.keyButton + '[data-value="' +
mappedKeys[ m ].replace(/"/g, '\\"') + '"]' :
'.' + kbcss.keyPrefix + base.processName( m );
}
}
// find key
key = ks.filter( ':visible' ).find( tar );
if ( key.length ) {
// key is visible, simulate typing
base.typing_simulateKey( key, txt, e );
} else {
// key not found, check if it is in the keymap
// (tab, space, enter, etc)
if ( base.typing_event ) {
key = ks.find( tar );
} else {
// key not found, check if it is in the keymap
// (tab, space, enter, etc)
n = txt in base.typing_keymap ?
base.typing_keymap[ txt ] :
base.processName( txt );
// find actual key on keyboard
key = ks.find( '.' + kbcss.keyPrefix + n );
}
// find the keyset
set = key.closest( '.' + kbcss.keySet );
// figure out which keyset the key is in then simulate clicking on
// that meta key, then on the key
if ( set.attr('name' ) ) {
if ( o.showTyping ) {
// get meta key name
meta = set.attr( 'name' );
// show correct key set
base.shiftActive = /shift/.test( meta );
base.altActive = /alt/.test( meta );
base.metaActive = base.last.keyset[ 2 ] = /\bmeta/.test(meta) ?
( meta ).match(/meta[\w-]+/)[0] : false;
base.showSet( base.metaActive );
}
// Add the key
base.typing_simulateKey( key, txt, e );
} else {
if ( !base.typing_event ) {
// Key doesn't exist on the keyboard, so just enter it
if (
txt in base.typing_keymap &&
base.typing_keymap[txt] in $keyboard.keyaction
) {
$keyboard.keyaction[ base.typing_keymap[ txt ] ]( base, key, e );
} else {
base.insertText( txt );
}
base.checkCombos();
base.$el.trigger( $keyboard.events.kbChange, [ base, base.el ] );
}
}
}
if ( o.current <= o.len && o.len !== 0 ) {
if ( !base.isVisible() ) {
return; // keyboard was closed, abort!!
}
base.typing_timer = setTimeout( function() {
base.typeIn();
}, o.delay );
} else if ( o.len !== 0 ) {
// o.len is zero when the user typed on the actual keyboard during
// simulation
base.typing_reset();
if ( typeof o.callback === 'function' ) {
// ensure all typing animation is done before the callback
base.typing_timer = setTimeout( function() {
// if the user typed during the key simulation, the "o" variable
// may sometimes be undefined
if ( typeof o.callback === 'function' ) {
o.callback( base );
}
}, o.delay );
}
return;
} else {
base.typing_reset();
}
};
// mouseover the key, add the text directly, then mouseout on the key
base.typing_simulateKey = function( el, txt, e ) {
var len = el.length;
if ( !base.isVisible() ) {
return;
}
if ( o.showTyping && len ) {
el.filter( ':visible' ).trigger( 'mouseenter' + base.namespace );
if ( o.showTyping && len ) {
setTimeout( function() {
el.trigger( 'mouseleave' + base.namespace );
}, Math.min( o.hoverDelay, o.delay ) );
}
}
if ( !base.typing_event ) {
// delay required or initial tab does not get added
// in the main demo (international keyboard)
setTimeout( function() {
if (
txt in base.typing_keymap &&
base.typing_keymap[ txt ] in $keyboard.keyaction
) {
e = e || $.Event( 'keypress' );
e.target = el; // "Enter" checks for the e.target
$keyboard.keyaction[ base.typing_keymap[ txt ] ]( base, el, e );
} else {
base.insertText( txt );
}
base.checkCombos();
base.$el.trigger( $keyboard.events.kbChange, [ base, base.el ] );
}, o.delay/3 );
}
};
// visible event is fired before this extension is initialized, so check!
if ( o.showTyping && base.options.alwaysOpen && base.isVisible() ) {
base.typing_setup();
} else {
// capture and simulate typing
base.$el
.unbind( $keyboard.events.kbBeforeVisible + base.typing_namespace )
.bind( $keyboard.events.kbBeforeVisible + base.typing_namespace, function() {
if ( o.showTyping ) {
base.typing_setup();
} else {
base.typing_setup_reset();
}
});
}
});
};
}));
//}}}
//{{{
/*! jQuery UI Virtual Keyboard Alt Key Popup v2.0.0 *//*
* for Keyboard v1.18+ only (2018-04-19)
*
* By Rob Garrison (aka Mottie)
* Licensed under the MIT License
*
* Note: Use of `event.key` requires a modern browser
* (https://caniuse.com/#feat=keyboardevent-key)
*/
/* jshint browser:true, jquery:true, unused:false */
/* global require:false, define:false, module:false */
;( function( factory ) {
if ( typeof define === 'function' && define.amd ) {
define( [ 'jquery' ], factory );
} else if (
typeof module === 'object' &&
typeof module.exports === 'object'
) {
module.exports = factory( require( 'jquery' ) );
} else {
factory( jQuery );
}
}( function( $ ) {
'use strict';
var $keyboard = $.keyboard;
$.extend( $keyboard.css, {
altKeyPopup : 'ui-keyboard-popup',
altKeyOverlay : 'ui-keyboard-overlay',
altKeyPopupOpen : 'ui-keyboard-popup-open'
});
$keyboard.altKeys = $.extend({
a : '\u00e5 \u00e6 \u0101 \u0103 \u0105 \u00e0 \u00e1 \u00e2 \u00e3 \u00e4', // å æ ā ă ą à á â ã ä
A : '\u00c5 \u00c6 \u0100 \u0102 \u0104 \u00c0 \u00c1 \u00c2 \u00c3 \u00c4', // Å Æ Ā Ă Ą À Á Â Ã Ä
c : '\u00e7 \u0107 \u0109 \u010b \u010d', // ç ć ĉ ċ č
C : '\u00c7 \u0106 \u0108 \u010a \u010c', // Ç Ć Ĉ Ċ Č
d : '\u010f \u00f0 \u010f', // ď ð ď
D : '\u010e \u00d0 \u010e', // Ď Ð Ď
e : '\u0117 \u0119 \u0115 \u011b \u0259 \u00e8 \u00e9 \u00ea \u00eb \u0113', // ė ę ĕ ě ə è é ê ë ē
E : '\u0116 \u0118 \u0114 \u011a \u018e \u00c8 \u00c9 \u00ca \u00cb \u0112', // Ė Ę Ĕ Ě Ǝ È É Ê Ë Ē
g : '\u0123 \u011f \u011d \u0121', // ģ ğ ĝ ġ
G : '\u0122 \u011e \u011c \u0120', // Ģ Ğ Ĝ Ġ
h : '\u0125 \u0127', // ĥ ħ
H : '\u0124 \u0126', // Ĥ Ħ
i : '\u0131 \u012f \u012b \u00ef \u00ee \u00ed \u00ec \u0129 \u012d', // ı į ī ï î í ì ĩ ĭ
I : '\u0130 \u012e \u012a \u00cf \u00ce \u00cd \u00cc \u0128 \u012c', // İ Į Ī Ï Î Í Ì Ĩ Ĭ
j : '\u0135', // ĵ
J : '\u0134', // Ĵ
k : '\u0137', // ķ
K : '\u0136', // Ķ
l : '\u0141 \u013d \u013b \u0139 \u013f', // Ł Ľ Ļ Ĺ Ŀ
L : '\u0142 \u013e \u013c \u013a \u0140', // ł ľ ļ ĺ ŀ
n : '\u0149 \u0148 \u0146 \u0144 \u00f1', // ʼn ň ņ ń ñ
N : '\u0149 \u0147 \u0145 \u0143 \u00d1', // ʼn Ň Ņ Ń Ñ
o : '\u0153 \u0151 \u00f8 \u00f6 \u00f5 \u00f4 \u00f3 \u00f2 \u014d \u014f', // œ ő ø ö õ ô ó ò ō ŏ
O : '\u0152 \u0150 \u00d8 \u00d6 \u00d5 \u00d4 \u00d3 \u00d2 \u014c \u014e', // Œ Ő Ø Ö Õ Ô Ó Ò Ō Ŏ
r : '\u0155 \u0159 \u0157', // ŕ ř ŗ
R : '\u0154 \u0158 \u0156', // Ŕ Ř Ŗ
s : '\u015b \u0161 \u015f \u00df \u00a7 \u015d', // ś š ş ß § ŝ
S : '\u015a \u0160 \u015e \u1e9e \u00a7 \u015c', // Ś Š Ş ẞ § Ŝ
t : '\u00fe \u0165 \u021b \u0163 \u0167', // þ ť ț ţ ŧ
T : '\u00de \u0164 \u021a \u0162 \u0166', // Þ Ť Ț Ţ Ŧ
u : '\u0173 \u0171 \u016f \u016b \u00fc \u00fb \u00fa \u00f9 \u0169 \u016d', // ų ű ů ū ü û ú ù ũ ŭ
U : '\u0172 \u0170 \u016e \u016a \u00dc \u00db \u00da \u00d9 \u0168 \u016c', // Ų Ű Ů Ū Ü Û Ú Ù Ũ Ŭ
w : '\u0175', // ŵ
W : '\u0174', // Ŵ
y : '\u00fd', // ý
Y : '\u00dd', // Ý
z : '\u017a \u017c \u017e', // ź ż ž
Z : '\u0179 \u017b \u017d', // Ź Ż Ž
'!' : '\u00a1', // ¡
'$' : '\u20ac \u00a3 \u00a4 \u00a5 \u00a2 \u20a1 \u20b1 \u20a9 \u20b9 \u20aa \u20ad \u20ae \u20a6 \u20a4', // €£¤¥¢₡₱₩₹₪₭₮₦₤
'?' : '\u00bf', // ¿
"'" : '\u3008 \u300c \u300e \u201c', // 〈 「 『 “
'"' : '\u3009 \u300d \u300f \u201d', // 〉 」 』 ”
'(' : '\u300a \u3010 \u3014', // « 【 〔
')' : '\u300b \u3011 \u3015' // » 】 〕
}, $keyboard.altKeys );
$.fn.addAltKeyPopup = function( options ) {
//Set the default values, use comma to separate the settings, example:
var defaults = {
// time to hold down a button in ms to trigger a popup
holdTime : 500,
// events triggered when popup is visible & hidden
popupVisible : 'popup-visible',
popupHidden : 'popup-hidden',
popupPosition : null
};
return this.each( function() {
// make sure a keyboard is attached
var base = $( this ).data( 'keyboard' );
if (!base) { return; }
// variables
base.altkeypopup_options = $.extend(
{},
defaults,
base.altkeypopup_options, // restore prev options on layout update
options
);
// already initialized
if ( base.altkeypopup_namespace ) {
return base.altkeypopup_setup();
}
base.altkeypopup_namespace = base.namespace + 'AltKeyPopup';
base.extensionNamespace.push( base.altkeypopup_namespace );
base.altkeypopup_setup = function() {
var timer,
start = 'mousedown touchstart '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' ),
end = 'mouseup touchend touchcancel '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' );
// force disable repeat keys
base.options.repeatRate = 0;
// add hold key functionality for popups
base
.unbindButton( base.altkeypopup_namespace )
.bindButton( start, function() {
clearTimeout( timer );
var $key = $( this ),
key = $key.attr( 'data-value' ) || '',
delay = base.altkeypopup_options.holdTime;
if ( key in $keyboard.altKeys ) {
if (delay) {
timer = setTimeout( function() {
base.altKeyPopup_popup( key, $key );
}, delay );
} else {
// holdTime set to zero.. don't use a setTimeout
base.altKeyPopup_popup( key, $key );
}
}
})
.bindButton( end, function() {
clearTimeout( timer );
});
base.altkeypopup_blockingFlag = false;
base.$preview
.unbind(
'keypress keydown keyup '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' )
.trim()
)
.bind(
'keypress keydown keyup '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' ),
function( event ) {
if ( event.type === 'keyup' ) {
clearTimeout( timer );
base.altkeypopup_blockingFlag = false;
return event.key !== 'Escape';
}
var layout = $keyboard.builtLayouts[ base.layout ],
$key = $( event.target ),
origKey = event.key,
key = event.key;
if ( event.type === 'keydown' && key in $keyboard.altKeys ) {
// Compare typed key to prevent blocking issues reported in #664
if ( base.altkeypopup_blockingFlag === origKey ) {
return false;
}
base.altkeypopup_blockingFlag = origKey;
// return true on initial keydown or keypress never fires
// then return false to prevent repeat key
return true;
}
if ( base.altkeypopup_blockingFlag ) {
// find mapped key, if any
if (
layout.hasMappedKeys &&
layout.mappedKeys.hasOwnProperty( key )
) {
key = layout.mappedKeys[ key ];
}
if ( key in $keyboard.altKeys ) {
clearTimeout( timer );
timer = setTimeout( function() {
if ( base.altkeypopup_blockingFlag === origKey ) {
base.altKeyPopup_popup( key, $key );
}
}, base.altkeypopup_options.holdTime );
}
return true;
}
}
);
};
base.altKeyPopup_close = function() {
base.altkeypopup_blockingFlag = false;
base.altKeyPopup_$overlay = null;
setTimeout(function() {
if (base.$keyboard.length) {
base.$keyboard.removeClass($keyboard.css.altKeyPopupOpen);
var $el = base.$keyboard.find( '.' + $keyboard.css.altKeyOverlay );
if ($el) {
$el.remove();
}
}
}, 1);
$( document ).unbind( base.altkeypopup_namespace );
base.$preview.focus();
// restore ignoreEsc option
base.options.ignoreEsc = base.altKeyPopup_savedIgnoreEsc;
// trigger popup hidden event
base.$el.trigger( base.altkeypopup_options.popupHidden, [ base ] );
};
base.altKeyPopup_popup = function( key, $key ) {
if ( base.$keyboard.find( '.' + $keyboard.css.altKeyOverlay ).length ) {
return;
}
var keys, $container, $keys, positionHoriz, positionVert, top,
popupWidth, popupHeight, evts,
kbcss = $keyboard.css,
data = {
$kb : base.$keyboard,
kbWidth : base.$keyboard.outerWidth(),
kbHeight : base.$keyboard.outerHeight(),
$key : $key
};
// overlay keyboard
base.altKeyPopup_$overlay =
$( '<div class="' + kbcss.altKeyOverlay + '" />' )
.css({
width : data.kbWidth,
height: data.kbHeight
})
.appendTo( base.$keyboard )
.bind( 'click touchstart', function() {
base.altKeyPopup_close();
});
evts = 'inactive hidden '
.split( ' ' )
.join( base.altkeypopup_namespace + ' ' );
base.$keyboard.addClass($keyboard.css.altKeyPopupOpen);
base.$el.unbind( evts ).bind( evts, function() {
base.altKeyPopup_close();
});
// remove character added when key was initially pressed, unless it
// was a backspace key
if ( key !== 'bksp' ) {
$keyboard.keyaction.bksp( base );
}
// make popup; use the same classes as the keyboard container
$container = $(
'<div class="' + kbcss.altKeyPopup + ' ' +
base.options.css.container + '" />'
);
keys = $keyboard.altKeys[ key ].split( /\s+/ );
// make popup keys
base.buildRow( $container, 0, keys, [] );
// add popup & add bindings
$keys = $container
.appendTo( base.altKeyPopup_$overlay )
.children()
.bind( 'mousedown touchstart', function() {
// action/value now processed by core functions
base.altKeyPopup_close();
})
.bind( 'mouseover mouseleave', function( event ){
// remove hover from physical keyboard highlighted key
$keys.removeClass( base.options.css.buttonHover );
if ( event.type !== 'mouseleave' ) {
$( this ).addClass( base.options.css.buttonHover );
}
});
// popup opened... add key highlight
base.altKeyPopup_navigate( true ); // init
// set ignoreEsc to allow escape to ONLY close the popup
base.altKeyPopup_savedIgnoreEsc = base.options.ignoreEsc;
base.options.ignoreEsc = true;
$( document )
.unbind( base.altkeypopup_namespace )
.bind( 'keydown' + base.altkeypopup_namespace, function() {
// keep home & end from scrolling the page
return false;
})
.bind( 'keyup' + base.altkeypopup_namespace, function( event ) {
if ( event.key === 'Escape' ) {
event.which = 0; // prevent escClose from closing the keyboard
base.altKeyPopup_close();
} else {
base.altKeyPopup_navigate( event );
}
return false;
});
data.$popup = $container;
popupWidth = $container.outerWidth();
// position popup within $keyboard container
positionHoriz = $key.position().left - popupWidth / 2;
if ( positionHoriz + popupWidth > data.kbWidth ) {
positionHoriz = data.kbWidth - popupWidth;
if ( positionHoriz < 0 ) {
$container.css({
width : data.kbWidth,
height : 'auto'
});
}
}
positionVert = $key.position().top - $key.outerHeight() - 5;
popupHeight = $container.outerHeight();
// find top of keyset (don't cover up the preview input)
top = base.$keyboard.find( '.' + kbcss.keySet ).position().top;
if ( positionVert + popupHeight > data.kbHeight ) {
positionVert = data.kbHeight - popupHeight;
if ( positionVert < top ) {
$container.css({
height : data.popupHeight,
width : 'auto'
});
}
}
data.popupWidth = $container.outerWidth();
data.popupHeight = $container.outerHeight();
data.popupLeft = positionHoriz < 0 ? 0 : positionHoriz;
data.popupTop = positionVert < top ? top : positionVert;
$container.css({
position : 'absolute',
left : data.popupLeft,
top : data.popupTop
});
// adjust position as needed using popupPosition callback function
if ( typeof base.altkeypopup_options.popupPosition === 'function' ) {
base.altkeypopup_options.popupPosition(base, data);
}
base.$preview.blur();
// trigger popup visible event
base.$el.trigger( base.altkeypopup_options.popupVisible, [ base ] );
};
base.altKeyPopup_navigate = function( event ) {
var indx,
kbcss = $keyboard.css,
k = $keyboard.navigationKeys,
hover = base.options.css.buttonHover,
$keys = base.$keyboard
.find( '.' + kbcss.altKeyPopup )
.find( '.' + kbcss.keyButton ),
max = $keys.length - 1;
// popup visible, add key highlight
if ( event === true ) {
$keys.eq( 0 ).addClass( hover );
base.altKeyPopup_currentIndex = 0;
return;
}
indx = base.altKeyPopup_currentIndex;
if ( event.key === 'Enter' ) {
base.insertText( $keys.eq( indx ).attr( 'data-value' ) );
base.altKeyPopup_close();
return true;
}
switch( event.key ) {
case 'End': indx = max; break;
case 'Home': indx = 0; break;
case 'ArrowLeft': indx -= 1; break;
case 'ArrowRight': indx += 1; break;
}
if ( indx < 0 ) { indx = 0; }
if ( indx > max ) { indx = max; }
base.altKeyPopup_currentIndex = indx;
$keys
.removeClass( hover )
.eq( indx )
.addClass( hover );
};
// visible event is fired before this extension is initialized, so check!
if ( base.options.alwaysOpen && base.isVisible() ) {
base.altkeypopup_setup();
}
// setup altkey popup
base.$el
.unbind(
$keyboard.events.kbBeforeVisible + base.altkeypopup_namespace
)
.bind(
$keyboard.events.kbBeforeVisible + base.altkeypopup_namespace,
function() {
base.altkeypopup_setup();
}
);
});
};
}));
//}}}
//{{{
/*! jQuery UI - v1.12.1 - 2020-06-19
* http://jqueryui.com
* Includes: widget.js, position.js, data.js, scroll-parent.js, widgets/draggable.js, widgets/mouse.js
* Copyright jQuery Foundation and other contributors; Licensed MIT */
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
$.ui = $.ui || {};
var version = $.ui.version = "1.12.1";
/*!
* jQuery UI Widget 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Widget
//>>group: Core
//>>description: Provides a factory for creating stateful widgets with a common API.
//>>docs: http://api.jqueryui.com/jQuery.widget/
//>>demos: http://jqueryui.com/widget/
var widgetUuid = 0;
var widgetSlice = Array.prototype.slice;
$.cleanData = ( function( orig ) {
return function( elems ) {
var events, elem, i;
for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
try {
// Only trigger remove when necessary to save time
events = $._data( elem, "events" );
if ( events && events.remove ) {
$( elem ).triggerHandler( "remove" );
}
// Http://bugs.jquery.com/ticket/8235
} catch ( e ) {}
}
orig( elems );
};
} )( $.cleanData );
$.widget = function( name, base, prototype ) {
var existingConstructor, constructor, basePrototype;
// ProxiedPrototype allows the provided prototype to remain unmodified
// so that it can be used as a mixin for multiple widgets (#8876)
var proxiedPrototype = {};
var namespace = name.split( "." )[ 0 ];
name = name.split( "." )[ 1 ];
var fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
if ( $.isArray( prototype ) ) {
prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
}
// Create selector for plugin
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
return !!$.data( elem, fullName );
};
$[ namespace ] = $[ namespace ] || {};
existingConstructor = $[ namespace ][ name ];
constructor = $[ namespace ][ name ] = function( options, element ) {
// Allow instantiation without "new" keyword
if ( !this._createWidget ) {
return new constructor( options, element );
}
// Allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
if ( arguments.length ) {
this._createWidget( options, element );
}
};
// Extend with the existing constructor to carry over any static properties
$.extend( constructor, existingConstructor, {
version: prototype.version,
// Copy the object used to create the prototype in case we need to
// redefine the widget later
_proto: $.extend( {}, prototype ),
// Track widgets that inherit from this widget in case this widget is
// redefined after a widget inherits from it
_childConstructors: []
} );
basePrototype = new base();
// We need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options );
$.each( prototype, function( prop, value ) {
if ( !$.isFunction( value ) ) {
proxiedPrototype[ prop ] = value;
return;
}
proxiedPrototype[ prop ] = ( function() {
function _super() {
return base.prototype[ prop ].apply( this, arguments );
}
function _superApply( args ) {
return base.prototype[ prop ].apply( this, args );
}
return function() {
var __super = this._super;
var __superApply = this._superApply;
var returnValue;
this._super = _super;
this._superApply = _superApply;
returnValue = value.apply( this, arguments );
this._super = __super;
this._superApply = __superApply;
return returnValue;
};
} )();
} );
constructor.prototype = $.widget.extend( basePrototype, {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
}, proxiedPrototype, {
constructor: constructor,
namespace: namespace,
widgetName: name,
widgetFullName: fullName
} );
// If this widget is being redefined then we need to find all widgets that
// are inheriting from it and redefine all of them so that they inherit from
// the new version of this widget. We're essentially trying to replace one
// level in the prototype chain.
if ( existingConstructor ) {
$.each( existingConstructor._childConstructors, function( i, child ) {
var childPrototype = child.prototype;
// Redefine the child widget using the same prototype that was
// originally used, but inherit from the new version of the base
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
child._proto );
} );
// Remove the list of existing child constructors from the old constructor
// so the old child constructors can be garbage collected
delete existingConstructor._childConstructors;
} else {
base._childConstructors.push( constructor );
}
$.widget.bridge( name, constructor );
return constructor;
};
$.widget.extend = function( target ) {
var input = widgetSlice.call( arguments, 1 );
var inputIndex = 0;
var inputLength = input.length;
var key;
var value;
for ( ; inputIndex < inputLength; inputIndex++ ) {
for ( key in input[ inputIndex ] ) {
value = input[ inputIndex ][ key ];
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
// Clone objects
if ( $.isPlainObject( value ) ) {
target[ key ] = $.isPlainObject( target[ key ] ) ?
$.widget.extend( {}, target[ key ], value ) :
// Don't extend strings, arrays, etc. with objects
$.widget.extend( {}, value );
// Copy everything else by reference
} else {
target[ key ] = value;
}
}
}
}
return target;
};
$.widget.bridge = function( name, object ) {
var fullName = object.prototype.widgetFullName || name;
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string";
var args = widgetSlice.call( arguments, 1 );
var returnValue = this;
if ( isMethodCall ) {
// If this is an empty collection, we need to have the instance method
// return undefined instead of the jQuery instance
if ( !this.length && options === "instance" ) {
returnValue = undefined;
} else {
this.each( function() {
var methodValue;
var instance = $.data( this, fullName );
if ( options === "instance" ) {
returnValue = instance;
return false;
}
if ( !instance ) {
return $.error( "cannot call methods on " + name +
" prior to initialization; " +
"attempted to call method '" + options + "'" );
}
if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
return $.error( "no such method '" + options + "' for " + name +
" widget instance" );
}
methodValue = instance[ options ].apply( instance, args );
if ( methodValue !== instance && methodValue !== undefined ) {
returnValue = methodValue && methodValue.jquery ?
returnValue.pushStack( methodValue.get() ) :
methodValue;
return false;
}
} );
}
} else {
// Allow multiple hashes to be passed on init
if ( args.length ) {
options = $.widget.extend.apply( null, [ options ].concat( args ) );
}
this.each( function() {
var instance = $.data( this, fullName );
if ( instance ) {
instance.option( options || {} );
if ( instance._init ) {
instance._init();
}
} else {
$.data( this, fullName, new object( options, this ) );
}
} );
}
return returnValue;
};
};
$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];
$.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
defaultElement: "<div>",
options: {
classes: {},
disabled: false,
// Callbacks
create: null
},
_createWidget: function( options, element ) {
element = $( element || this.defaultElement || this )[ 0 ];
this.element = $( element );
this.uuid = widgetUuid++;
this.eventNamespace = "." + this.widgetName + this.uuid;
this.bindings = $();
this.hoverable = $();
this.focusable = $();
this.classesElementLookup = {};
if ( element !== this ) {
$.data( element, this.widgetFullName, this );
this._on( true, this.element, {
remove: function( event ) {
if ( event.target === element ) {
this.destroy();
}
}
} );
this.document = $( element.style ?
// Element within the document
element.ownerDocument :
// Element is window or document
element.document || element );
this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
}
this.options = $.widget.extend( {},
this.options,
this._getCreateOptions(),
options );
this._create();
if ( this.options.disabled ) {
this._setOptionDisabled( this.options.disabled );
}
this._trigger( "create", null, this._getCreateEventData() );
this._init();
},
_getCreateOptions: function() {
return {};
},
_getCreateEventData: $.noop,
_create: $.noop,
_init: $.noop,
destroy: function() {
var that = this;
this._destroy();
$.each( this.classesElementLookup, function( key, value ) {
that._removeClass( value, key );
} );
// We can probably remove the unbind calls in 2.0
// all event bindings should go through this._on()
this.element
.off( this.eventNamespace )
.removeData( this.widgetFullName );
this.widget()
.off( this.eventNamespace )
.removeAttr( "aria-disabled" );
// Clean up events and states
this.bindings.off( this.eventNamespace );
},
_destroy: $.noop,
widget: function() {
return this.element;
},
option: function( key, value ) {
var options = key;
var parts;
var curOption;
var i;
if ( arguments.length === 0 ) {
// Don't return a reference to the internal hash
return $.widget.extend( {}, this.options );
}
if ( typeof key === "string" ) {
// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
options = {};
parts = key.split( "." );
key = parts.shift();
if ( parts.length ) {
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
for ( i = 0; i < parts.length - 1; i++ ) {
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
curOption = curOption[ parts[ i ] ];
}
key = parts.pop();
if ( arguments.length === 1 ) {
return curOption[ key ] === undefined ? null : curOption[ key ];
}
curOption[ key ] = value;
} else {
if ( arguments.length === 1 ) {
return this.options[ key ] === undefined ? null : this.options[ key ];
}
options[ key ] = value;
}
}
this._setOptions( options );
return this;
},
_setOptions: function( options ) {
var key;
for ( key in options ) {
this._setOption( key, options[ key ] );
}
return this;
},
_setOption: function( key, value ) {
if ( key === "classes" ) {
this._setOptionClasses( value );
}
this.options[ key ] = value;
if ( key === "disabled" ) {
this._setOptionDisabled( value );
}
return this;
},
_setOptionClasses: function( value ) {
var classKey, elements, currentElements;
for ( classKey in value ) {
currentElements = this.classesElementLookup[ classKey ];
if ( value[ classKey ] === this.options.classes[ classKey ] ||
!currentElements ||
!currentElements.length ) {
continue;
}
// We are doing this to create a new jQuery object because the _removeClass() call
// on the next line is going to destroy the reference to the current elements being
// tracked. We need to save a copy of this collection so that we can add the new classes
// below.
elements = $( currentElements.get() );
this._removeClass( currentElements, classKey );
// We don't use _addClass() here, because that uses this.options.classes
// for generating the string of classes. We want to use the value passed in from
// _setOption(), this is the new value of the classes option which was passed to
// _setOption(). We pass this value directly to _classes().
elements.addClass( this._classes( {
element: elements,
keys: classKey,
classes: value,
add: true
} ) );
}
},
_setOptionDisabled: function( value ) {
this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
// If the widget is becoming disabled, then nothing is interactive
if ( value ) {
this._removeClass( this.hoverable, null, "ui-state-hover" );
this._removeClass( this.focusable, null, "ui-state-focus" );
}
},
enable: function() {
return this._setOptions( { disabled: false } );
},
disable: function() {
return this._setOptions( { disabled: true } );
},
_classes: function( options ) {
var full = [];
var that = this;
options = $.extend( {
element: this.element,
classes: this.options.classes || {}
}, options );
function processClassString( classes, checkOption ) {
var current, i;
for ( i = 0; i < classes.length; i++ ) {
current = that.classesElementLookup[ classes[ i ] ] || $();
if ( options.add ) {
current = $( $.unique( current.get().concat( options.element.get() ) ) );
} else {
current = $( current.not( options.element ).get() );
}
that.classesElementLookup[ classes[ i ] ] = current;
full.push( classes[ i ] );
if ( checkOption && options.classes[ classes[ i ] ] ) {
full.push( options.classes[ classes[ i ] ] );
}
}
}
this._on( options.element, {
"remove": "_untrackClassesElement"
} );
if ( options.keys ) {
processClassString( options.keys.match( /\S+/g ) || [], true );
}
if ( options.extra ) {
processClassString( options.extra.match( /\S+/g ) || [] );
}
return full.join( " " );
},
_untrackClassesElement: function( event ) {
var that = this;
$.each( that.classesElementLookup, function( key, value ) {
if ( $.inArray( event.target, value ) !== -1 ) {
that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
}
} );
},
_removeClass: function( element, keys, extra ) {
return this._toggleClass( element, keys, extra, false );
},
_addClass: function( element, keys, extra ) {
return this._toggleClass( element, keys, extra, true );
},
_toggleClass: function( element, keys, extra, add ) {
add = ( typeof add === "boolean" ) ? add : extra;
var shift = ( typeof element === "string" || element === null ),
options = {
extra: shift ? keys : extra,
keys: shift ? element : keys,
element: shift ? this.element : element,
add: add
};
options.element.toggleClass( this._classes( options ), add );
return this;
},
_on: function( suppressDisabledCheck, element, handlers ) {
var delegateElement;
var instance = this;
// No suppressDisabledCheck flag, shuffle arguments
if ( typeof suppressDisabledCheck !== "boolean" ) {
handlers = element;
element = suppressDisabledCheck;
suppressDisabledCheck = false;
}
// No element argument, shuffle and use this.element
if ( !handlers ) {
handlers = element;
element = this.element;
delegateElement = this.widget();
} else {
element = delegateElement = $( element );
this.bindings = this.bindings.add( element );
}
$.each( handlers, function( event, handler ) {
function handlerProxy() {
// Allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
if ( !suppressDisabledCheck &&
( instance.options.disabled === true ||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
return;
}
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
// Copy the guid so direct unbinding works
if ( typeof handler !== "string" ) {
handlerProxy.guid = handler.guid =
handler.guid || handlerProxy.guid || $.guid++;
}
var match = event.match( /^([\w:-]*)\s*(.*)$/ );
var eventName = match[ 1 ] + instance.eventNamespace;
var selector = match[ 2 ];
if ( selector ) {
delegateElement.on( eventName, selector, handlerProxy );
} else {
element.on( eventName, handlerProxy );
}
} );
},
_off: function( element, eventName ) {
eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
this.eventNamespace;
element.off( eventName ).off( eventName );
// Clear the stack to avoid memory leaks (#10056)
this.bindings = $( this.bindings.not( element ).get() );
this.focusable = $( this.focusable.not( element ).get() );
this.hoverable = $( this.hoverable.not( element ).get() );
},
_delay: function( handler, delay ) {
function handlerProxy() {
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
var instance = this;
return setTimeout( handlerProxy, delay || 0 );
},
_hoverable: function( element ) {
this.hoverable = this.hoverable.add( element );
this._on( element, {
mouseenter: function( event ) {
this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
},
mouseleave: function( event ) {
this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
}
} );
},
_focusable: function( element ) {
this.focusable = this.focusable.add( element );
this._on( element, {
focusin: function( event ) {
this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
},
focusout: function( event ) {
this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
}
} );
},
_trigger: function( type, event, data ) {
var prop, orig;
var callback = this.options[ type ];
data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
// The original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[ 0 ];
// Copy original event properties over to the new event
orig = event.originalEvent;
if ( orig ) {
for ( prop in orig ) {
if ( !( prop in event ) ) {
event[ prop ] = orig[ prop ];
}
}
}
this.element.trigger( event, data );
return !( $.isFunction( callback ) &&
callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
event.isDefaultPrevented() );
}
};
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
if ( typeof options === "string" ) {
options = { effect: options };
}
var hasOptions;
var effectName = !options ?
method :
options === true || typeof options === "number" ?
defaultEffect :
options.effect || defaultEffect;
options = options || {};
if ( typeof options === "number" ) {
options = { duration: options };
}
hasOptions = !$.isEmptyObject( options );
options.complete = callback;
if ( options.delay ) {
element.delay( options.delay );
}
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
element[ method ]( options );
} else if ( effectName !== method && element[ effectName ] ) {
element[ effectName ]( options.duration, options.easing, callback );
} else {
element.queue( function( next ) {
$( this )[ method ]();
if ( callback ) {
callback.call( element[ 0 ] );
}
next();
} );
}
};
} );
var widget = $.widget;
/*!
* jQuery UI Position 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/position/
*/
//>>label: Position
//>>group: Core
//>>description: Positions elements relative to other elements.
//>>docs: http://api.jqueryui.com/position/
//>>demos: http://jqueryui.com/position/
( function() {
var cachedScrollbarWidth,
max = Math.max,
abs = Math.abs,
rhorizontal = /left|center|right/,
rvertical = /top|center|bottom/,
roffset = /[\+\-]\d+(\.[\d]+)?%?/,
rposition = /^\w+/,
rpercent = /%$/,
_position = $.fn.position;
function getOffsets( offsets, width, height ) {
return [
parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
];
}
function parseCss( element, property ) {
return parseInt( $.css( element, property ), 10 ) || 0;
}
function getDimensions( elem ) {
var raw = elem[ 0 ];
if ( raw.nodeType === 9 ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: 0, left: 0 }
};
}
if ( $.isWindow( raw ) ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
};
}
if ( raw.preventDefault ) {
return {
width: 0,
height: 0,
offset: { top: raw.pageY, left: raw.pageX }
};
}
return {
width: elem.outerWidth(),
height: elem.outerHeight(),
offset: elem.offset()
};
}
$.position = {
scrollbarWidth: function() {
if ( cachedScrollbarWidth !== undefined ) {
return cachedScrollbarWidth;
}
var w1, w2,
div = $( "<div " +
"style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
"<div style='height:100px;width:auto;'></div></div>" ),
innerDiv = div.children()[ 0 ];
$( "body" ).append( div );
w1 = innerDiv.offsetWidth;
div.css( "overflow", "scroll" );
w2 = innerDiv.offsetWidth;
if ( w1 === w2 ) {
w2 = div[ 0 ].clientWidth;
}
div.remove();
return ( cachedScrollbarWidth = w1 - w2 );
},
getScrollInfo: function( within ) {
var overflowX = within.isWindow || within.isDocument ? "" :
within.element.css( "overflow-x" ),
overflowY = within.isWindow || within.isDocument ? "" :
within.element.css( "overflow-y" ),
hasOverflowX = overflowX === "scroll" ||
( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
hasOverflowY = overflowY === "scroll" ||
( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
return {
width: hasOverflowY ? $.position.scrollbarWidth() : 0,
height: hasOverflowX ? $.position.scrollbarWidth() : 0
};
},
getWithinInfo: function( element ) {
var withinElement = $( element || window ),
isWindow = $.isWindow( withinElement[ 0 ] ),
isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
hasOffset = !isWindow && !isDocument;
return {
element: withinElement,
isWindow: isWindow,
isDocument: isDocument,
offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
scrollLeft: withinElement.scrollLeft(),
scrollTop: withinElement.scrollTop(),
width: withinElement.outerWidth(),
height: withinElement.outerHeight()
};
}
};
$.fn.position = function( options ) {
if ( !options || !options.of ) {
return _position.apply( this, arguments );
}
// Make a copy, we don't want to modify arguments
options = $.extend( {}, options );
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
target = $( options.of ),
within = $.position.getWithinInfo( options.within ),
scrollInfo = $.position.getScrollInfo( within ),
collision = ( options.collision || "flip" ).split( " " ),
offsets = {};
dimensions = getDimensions( target );
if ( target[ 0 ].preventDefault ) {
// Force left top to allow flipping
options.at = "left top";
}
targetWidth = dimensions.width;
targetHeight = dimensions.height;
targetOffset = dimensions.offset;
// Clone to reuse original targetOffset later
basePosition = $.extend( {}, targetOffset );
// Force my and at to have valid horizontal and vertical positions
// if a value is missing or invalid, it will be converted to center
$.each( [ "my", "at" ], function() {
var pos = ( options[ this ] || "" ).split( " " ),
horizontalOffset,
verticalOffset;
if ( pos.length === 1 ) {
pos = rhorizontal.test( pos[ 0 ] ) ?
pos.concat( [ "center" ] ) :
rvertical.test( pos[ 0 ] ) ?
[ "center" ].concat( pos ) :
[ "center", "center" ];
}
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
// Calculate offsets
horizontalOffset = roffset.exec( pos[ 0 ] );
verticalOffset = roffset.exec( pos[ 1 ] );
offsets[ this ] = [
horizontalOffset ? horizontalOffset[ 0 ] : 0,
verticalOffset ? verticalOffset[ 0 ] : 0
];
// Reduce to just the positions without the offsets
options[ this ] = [
rposition.exec( pos[ 0 ] )[ 0 ],
rposition.exec( pos[ 1 ] )[ 0 ]
];
} );
// Normalize collision option
if ( collision.length === 1 ) {
collision[ 1 ] = collision[ 0 ];
}
if ( options.at[ 0 ] === "right" ) {
basePosition.left += targetWidth;
} else if ( options.at[ 0 ] === "center" ) {
basePosition.left += targetWidth / 2;
}
if ( options.at[ 1 ] === "bottom" ) {
basePosition.top += targetHeight;
} else if ( options.at[ 1 ] === "center" ) {
basePosition.top += targetHeight / 2;
}
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
basePosition.left += atOffset[ 0 ];
basePosition.top += atOffset[ 1 ];
return this.each( function() {
var collisionPosition, using,
elem = $( this ),
elemWidth = elem.outerWidth(),
elemHeight = elem.outerHeight(),
marginLeft = parseCss( this, "marginLeft" ),
marginTop = parseCss( this, "marginTop" ),
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
scrollInfo.width,
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
scrollInfo.height,
position = $.extend( {}, basePosition ),
myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
if ( options.my[ 0 ] === "right" ) {
position.left -= elemWidth;
} else if ( options.my[ 0 ] === "center" ) {
position.left -= elemWidth / 2;
}
if ( options.my[ 1 ] === "bottom" ) {
position.top -= elemHeight;
} else if ( options.my[ 1 ] === "center" ) {
position.top -= elemHeight / 2;
}
position.left += myOffset[ 0 ];
position.top += myOffset[ 1 ];
collisionPosition = {
marginLeft: marginLeft,
marginTop: marginTop
};
$.each( [ "left", "top" ], function( i, dir ) {
if ( $.ui.position[ collision[ i ] ] ) {
$.ui.position[ collision[ i ] ][ dir ]( position, {
targetWidth: targetWidth,
targetHeight: targetHeight,
elemWidth: elemWidth,
elemHeight: elemHeight,
collisionPosition: collisionPosition,
collisionWidth: collisionWidth,
collisionHeight: collisionHeight,
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
my: options.my,
at: options.at,
within: within,
elem: elem
} );
}
} );
if ( options.using ) {
// Adds feedback as second argument to using callback, if present
using = function( props ) {
var left = targetOffset.left - position.left,
right = left + targetWidth - elemWidth,
top = targetOffset.top - position.top,
bottom = top + targetHeight - elemHeight,
feedback = {
target: {
element: target,
left: targetOffset.left,
top: targetOffset.top,
width: targetWidth,
height: targetHeight
},
element: {
element: elem,
left: position.left,
top: position.top,
width: elemWidth,
height: elemHeight
},
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
};
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
feedback.horizontal = "center";
}
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
feedback.vertical = "middle";
}
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
feedback.important = "horizontal";
} else {
feedback.important = "vertical";
}
options.using.call( this, props, feedback );
};
}
elem.offset( $.extend( position, { using: using } ) );
} );
};
$.ui.position = {
fit: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
outerWidth = within.width,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = withinOffset - collisionPosLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
newOverRight;
// Element is wider than within
if ( data.collisionWidth > outerWidth ) {
// Element is initially over the left side of within
if ( overLeft > 0 && overRight <= 0 ) {
newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
withinOffset;
position.left += overLeft - newOverRight;
// Element is initially over right side of within
} else if ( overRight > 0 && overLeft <= 0 ) {
position.left = withinOffset;
// Element is initially over both left and right sides of within
} else {
if ( overLeft > overRight ) {
position.left = withinOffset + outerWidth - data.collisionWidth;
} else {
position.left = withinOffset;
}
}
// Too far left -> align with left edge
} else if ( overLeft > 0 ) {
position.left += overLeft;
// Too far right -> align with right edge
} else if ( overRight > 0 ) {
position.left -= overRight;
// Adjust based on position and margin
} else {
position.left = max( position.left - collisionPosLeft, position.left );
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
outerHeight = data.within.height,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = withinOffset - collisionPosTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
newOverBottom;
// Element is taller than within
if ( data.collisionHeight > outerHeight ) {
// Element is initially over the top of within
if ( overTop > 0 && overBottom <= 0 ) {
newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
withinOffset;
position.top += overTop - newOverBottom;
// Element is initially over bottom of within
} else if ( overBottom > 0 && overTop <= 0 ) {
position.top = withinOffset;
// Element is initially over both top and bottom of within
} else {
if ( overTop > overBottom ) {
position.top = withinOffset + outerHeight - data.collisionHeight;
} else {
position.top = withinOffset;
}
}
// Too far up -> align with top
} else if ( overTop > 0 ) {
position.top += overTop;
// Too far down -> align with bottom edge
} else if ( overBottom > 0 ) {
position.top -= overBottom;
// Adjust based on position and margin
} else {
position.top = max( position.top - collisionPosTop, position.top );
}
}
},
flip: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.offset.left + within.scrollLeft,
outerWidth = within.width,
offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = collisionPosLeft - offsetLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
myOffset = data.my[ 0 ] === "left" ?
-data.elemWidth :
data.my[ 0 ] === "right" ?
data.elemWidth :
0,
atOffset = data.at[ 0 ] === "left" ?
data.targetWidth :
data.at[ 0 ] === "right" ?
-data.targetWidth :
0,
offset = -2 * data.offset[ 0 ],
newOverRight,
newOverLeft;
if ( overLeft < 0 ) {
newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
outerWidth - withinOffset;
if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
position.left += myOffset + atOffset + offset;
}
} else if ( overRight > 0 ) {
newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
atOffset + offset - offsetLeft;
if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
position.left += myOffset + atOffset + offset;
}
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.offset.top + within.scrollTop,
outerHeight = within.height,
offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = collisionPosTop - offsetTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
top = data.my[ 1 ] === "top",
myOffset = top ?
-data.elemHeight :
data.my[ 1 ] === "bottom" ?
data.elemHeight :
0,
atOffset = data.at[ 1 ] === "top" ?
data.targetHeight :
data.at[ 1 ] === "bottom" ?
-data.targetHeight :
0,
offset = -2 * data.offset[ 1 ],
newOverTop,
newOverBottom;
if ( overTop < 0 ) {
newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
outerHeight - withinOffset;
if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
position.top += myOffset + atOffset + offset;
}
} else if ( overBottom > 0 ) {
newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
offset - offsetTop;
if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
position.top += myOffset + atOffset + offset;
}
}
}
},
flipfit: {
left: function() {
$.ui.position.flip.left.apply( this, arguments );
$.ui.position.fit.left.apply( this, arguments );
},
top: function() {
$.ui.position.flip.top.apply( this, arguments );
$.ui.position.fit.top.apply( this, arguments );
}
}
};
} )();
var position = $.ui.position;
/*!
* jQuery UI :data 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: :data Selector
//>>group: Core
//>>description: Selects elements which have data stored under the specified key.
//>>docs: http://api.jqueryui.com/data-selector/
var data = $.extend( $.expr[ ":" ], {
data: $.expr.createPseudo ?
$.expr.createPseudo( function( dataName ) {
return function( elem ) {
return !!$.data( elem, dataName );
};
} ) :
// Support: jQuery <1.8
function( elem, i, match ) {
return !!$.data( elem, match[ 3 ] );
}
} );
/*!
* jQuery UI Scroll Parent 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: scrollParent
//>>group: Core
//>>description: Get the closest ancestor element that is scrollable.
//>>docs: http://api.jqueryui.com/scrollParent/
var scrollParent = $.fn.scrollParent = function( includeHidden ) {
var position = this.css( "position" ),
excludeStaticParent = position === "absolute",
overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
scrollParent = this.parents().filter( function() {
var parent = $( this );
if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
return false;
}
return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
parent.css( "overflow-x" ) );
} ).eq( 0 );
return position === "fixed" || !scrollParent.length ?
$( this[ 0 ].ownerDocument || document ) :
scrollParent;
};
// This file is deprecated
var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
/*!
* jQuery UI Mouse 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Mouse
//>>group: Widgets
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
//>>docs: http://api.jqueryui.com/mouse/
var mouseHandled = false;
$( document ).on( "mouseup", function() {
mouseHandled = false;
} );
var widgetsMouse = $.widget( "ui.mouse", {
version: "1.12.1",
options: {
cancel: "input, textarea, button, select, option",
distance: 1,
delay: 0
},
_mouseInit: function() {
var that = this;
this.element
.on( "mousedown." + this.widgetName, function( event ) {
return that._mouseDown( event );
} )
.on( "click." + this.widgetName, function( event ) {
if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
$.removeData( event.target, that.widgetName + ".preventClickEvent" );
event.stopImmediatePropagation();
return false;
}
} );
this.started = false;
},
// TODO: make sure destroying one instance of mouse doesn't mess with
// other instances of mouse
_mouseDestroy: function() {
this.element.off( "." + this.widgetName );
if ( this._mouseMoveDelegate ) {
this.document
.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
}
},
_mouseDown: function( event ) {
// don't let more than one widget handle mouseStart
if ( mouseHandled ) {
return;
}
this._mouseMoved = false;
// We may have missed mouseup (out of window)
( this._mouseStarted && this._mouseUp( event ) );
this._mouseDownEvent = event;
var that = this,
btnIsLeft = ( event.which === 1 ),
// event.target.nodeName works around a bug in IE 8 with
// disabled inputs (#7620)
elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
$( event.target ).closest( this.options.cancel ).length : false );
if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
return true;
}
this.mouseDelayMet = !this.options.delay;
if ( !this.mouseDelayMet ) {
this._mouseDelayTimer = setTimeout( function() {
that.mouseDelayMet = true;
}, this.options.delay );
}
if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
this._mouseStarted = ( this._mouseStart( event ) !== false );
if ( !this._mouseStarted ) {
event.preventDefault();
return true;
}
}
// Click event may never have fired (Gecko & Opera)
if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
$.removeData( event.target, this.widgetName + ".preventClickEvent" );
}
// These delegates are required to keep context
this._mouseMoveDelegate = function( event ) {
return that._mouseMove( event );
};
this._mouseUpDelegate = function( event ) {
return that._mouseUp( event );
};
this.document
.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
.on( "mouseup." + this.widgetName, this._mouseUpDelegate );
event.preventDefault();
mouseHandled = true;
return true;
},
_mouseMove: function( event ) {
// Only check for mouseups outside the document if you've moved inside the document
// at least once. This prevents the firing of mouseup in the case of IE<9, which will
// fire a mousemove event if content is placed under the cursor. See #7778
// Support: IE <9
if ( this._mouseMoved ) {
// IE mouseup check - mouseup happened when mouse was out of window
if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
!event.button ) {
return this._mouseUp( event );
// Iframe mouseup check - mouseup occurred in another document
} else if ( !event.which ) {
// Support: Safari <=8 - 9
// Safari sets which to 0 if you press any of the following keys
// during a drag (#14461)
if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
this.ignoreMissingWhich = true;
} else if ( !this.ignoreMissingWhich ) {
return this._mouseUp( event );
}
}
}
if ( event.which || event.button ) {
this._mouseMoved = true;
}
if ( this._mouseStarted ) {
this._mouseDrag( event );
return event.preventDefault();
}
if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
this._mouseStarted =
( this._mouseStart( this._mouseDownEvent, event ) !== false );
( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
}
return !this._mouseStarted;
},
_mouseUp: function( event ) {
this.document
.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
if ( this._mouseStarted ) {
this._mouseStarted = false;
if ( event.target === this._mouseDownEvent.target ) {
$.data( event.target, this.widgetName + ".preventClickEvent", true );
}
this._mouseStop( event );
}
if ( this._mouseDelayTimer ) {
clearTimeout( this._mouseDelayTimer );
delete this._mouseDelayTimer;
}
this.ignoreMissingWhich = false;
mouseHandled = false;
event.preventDefault();
},
_mouseDistanceMet: function( event ) {
return ( Math.max(
Math.abs( this._mouseDownEvent.pageX - event.pageX ),
Math.abs( this._mouseDownEvent.pageY - event.pageY )
) >= this.options.distance
);
},
_mouseDelayMet: function( /* event */ ) {
return this.mouseDelayMet;
},
// These are placeholder methods, to be overriden by extending plugin
_mouseStart: function( /* event */ ) {},
_mouseDrag: function( /* event */ ) {},
_mouseStop: function( /* event */ ) {},
_mouseCapture: function( /* event */ ) { return true; }
} );
// $.ui.plugin is deprecated. Use $.widget() extensions instead.
var plugin = $.ui.plugin = {
add: function( module, option, set ) {
var i,
proto = $.ui[ module ].prototype;
for ( i in set ) {
proto.plugins[ i ] = proto.plugins[ i ] || [];
proto.plugins[ i ].push( [ option, set[ i ] ] );
}
},
call: function( instance, name, args, allowDisconnected ) {
var i,
set = instance.plugins[ name ];
if ( !set ) {
return;
}
if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||
instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
return;
}
for ( i = 0; i < set.length; i++ ) {
if ( instance.options[ set[ i ][ 0 ] ] ) {
set[ i ][ 1 ].apply( instance.element, args );
}
}
}
};
var safeActiveElement = $.ui.safeActiveElement = function( document ) {
var activeElement;
// Support: IE 9 only
// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
try {
activeElement = document.activeElement;
} catch ( error ) {
activeElement = document.body;
}
// Support: IE 9 - 11 only
// IE may return null instead of an element
// Interestingly, this only seems to occur when NOT in an iframe
if ( !activeElement ) {
activeElement = document.body;
}
// Support: IE 11 only
// IE11 returns a seemingly empty object in some cases when accessing
// document.activeElement from an <iframe>
if ( !activeElement.nodeName ) {
activeElement = document.body;
}
return activeElement;
};
var safeBlur = $.ui.safeBlur = function( element ) {
// Support: IE9 - 10 only
// If the <body> is blurred, IE will switch windows, see #9420
if ( element && element.nodeName.toLowerCase() !== "body" ) {
$( element ).trigger( "blur" );
}
};
/*!
* jQuery UI Draggable 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Draggable
//>>group: Interactions
//>>description: Enables dragging functionality for any element.
//>>docs: http://api.jqueryui.com/draggable/
//>>demos: http://jqueryui.com/draggable/
//>>css.structure: ../../themes/base/draggable.css
$.widget( "ui.draggable", $.ui.mouse, {
version: "1.12.1",
widgetEventPrefix: "drag",
options: {
addClasses: true,
appendTo: "parent",
axis: false,
connectToSortable: false,
containment: false,
cursor: "auto",
cursorAt: false,
grid: false,
handle: false,
helper: "original",
iframeFix: false,
opacity: false,
refreshPositions: false,
revert: false,
revertDuration: 500,
scope: "default",
scroll: true,
scrollSensitivity: 20,
scrollSpeed: 20,
snap: false,
snapMode: "both",
snapTolerance: 20,
stack: false,
zIndex: false,
// Callbacks
drag: null,
start: null,
stop: null
},
_create: function() {
if ( this.options.helper === "original" ) {
this._setPositionRelative();
}
if ( this.options.addClasses ) {
this._addClass( "ui-draggable" );
}
this._setHandleClassName();
this._mouseInit();
},
_setOption: function( key, value ) {
this._super( key, value );
if ( key === "handle" ) {
this._removeHandleClassName();
this._setHandleClassName();
}
},
_destroy: function() {
if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
this.destroyOnClear = true;
return;
}
this._removeHandleClassName();
this._mouseDestroy();
},
_mouseCapture: function( event ) {
var o = this.options;
// Among others, prevent a drag on a resizable-handle
if ( this.helper || o.disabled ||
$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
return false;
}
//Quit if we're not on a valid handle
this.handle = this._getHandle( event );
if ( !this.handle ) {
return false;
}
this._blurActiveElement( event );
this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
return true;
},
_blockFrames: function( selector ) {
this.iframeBlocks = this.document.find( selector ).map( function() {
var iframe = $( this );
return $( "<div>" )
.css( "position", "absolute" )
.appendTo( iframe.parent() )
.outerWidth( iframe.outerWidth() )
.outerHeight( iframe.outerHeight() )
.offset( iframe.offset() )[ 0 ];
} );
},
_unblockFrames: function() {
if ( this.iframeBlocks ) {
this.iframeBlocks.remove();
delete this.iframeBlocks;
}
},
_blurActiveElement: function( event ) {
var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
target = $( event.target );
// Don't blur if the event occurred on an element that is within
// the currently focused element
// See #10527, #12472
if ( target.closest( activeElement ).length ) {
return;
}
// Blur any element that currently has focus, see #4261
$.ui.safeBlur( activeElement );
},
_mouseStart: function( event ) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper( event );
this._addClass( this.helper, "ui-draggable-dragging" );
//Cache the helper size
this._cacheHelperProportions();
//If ddmanager is used for droppables, set the global draggable
if ( $.ui.ddmanager ) {
$.ui.ddmanager.current = this;
}
/*
* - Position generation -
* This block generates everything position related - it's the core of draggables.
*/
//Cache the margins of the original element
this._cacheMargins();
//Store the helper's css position
this.cssPosition = this.helper.css( "position" );
this.scrollParent = this.helper.scrollParent( true );
this.offsetParent = this.helper.offsetParent();
this.hasFixedAncestor = this.helper.parents().filter( function() {
return $( this ).css( "position" ) === "fixed";
} ).length > 0;
//The element's absolute position on the page minus margins
this.positionAbs = this.element.offset();
this._refreshOffsets( event );
//Generate the original position
this.originalPosition = this.position = this._generatePosition( event, false );
this.originalPageX = event.pageX;
this.originalPageY = event.pageY;
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
//Set a containment if given in the options
this._setContainment();
//Trigger event + callbacks
if ( this._trigger( "start", event ) === false ) {
this._clear();
return false;
}
//Recache the helper size
this._cacheHelperProportions();
//Prepare the droppable offsets
if ( $.ui.ddmanager && !o.dropBehaviour ) {
$.ui.ddmanager.prepareOffsets( this, event );
}
// Execute the drag once - this causes the helper not to be visible before getting its
// correct position
this._mouseDrag( event, true );
// If the ddmanager is used for droppables, inform the manager that dragging has started
// (see #5003)
if ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStart( this, event );
}
return true;
},
_refreshOffsets: function( event ) {
this.offset = {
top: this.positionAbs.top - this.margins.top,
left: this.positionAbs.left - this.margins.left,
scroll: false,
parent: this._getParentOffset(),
relative: this._getRelativeOffset()
};
this.offset.click = {
left: event.pageX - this.offset.left,
top: event.pageY - this.offset.top
};
},
_mouseDrag: function( event, noPropagation ) {
// reset any necessary cached properties (see #5009)
if ( this.hasFixedAncestor ) {
this.offset.parent = this._getParentOffset();
}
//Compute the helpers position
this.position = this._generatePosition( event, true );
this.positionAbs = this._convertPositionTo( "absolute" );
//Call plugins and callbacks and use the resulting position if something is returned
if ( !noPropagation ) {
var ui = this._uiHash();
if ( this._trigger( "drag", event, ui ) === false ) {
this._mouseUp( new $.Event( "mouseup", event ) );
return false;
}
this.position = ui.position;
}
this.helper[ 0 ].style.left = this.position.left + "px";
this.helper[ 0 ].style.top = this.position.top + "px";
if ( $.ui.ddmanager ) {
$.ui.ddmanager.drag( this, event );
}
return false;
},
_mouseStop: function( event ) {
//If we are using droppables, inform the manager about the drop
var that = this,
dropped = false;
if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
dropped = $.ui.ddmanager.drop( this, event );
}
//if a drop comes from outside (a sortable)
if ( this.dropped ) {
dropped = this.dropped;
this.dropped = false;
}
if ( ( this.options.revert === "invalid" && !dropped ) ||
( this.options.revert === "valid" && dropped ) ||
this.options.revert === true || ( $.isFunction( this.options.revert ) &&
this.options.revert.call( this.element, dropped ) )
) {
$( this.helper ).animate(
this.originalPosition,
parseInt( this.options.revertDuration, 10 ),
function() {
if ( that._trigger( "stop", event ) !== false ) {
that._clear();
}
}
);
} else {
if ( this._trigger( "stop", event ) !== false ) {
this._clear();
}
}
return false;
},
_mouseUp: function( event ) {
this._unblockFrames();
// If the ddmanager is used for droppables, inform the manager that dragging has stopped
// (see #5003)
if ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStop( this, event );
}
// Only need to focus if the event occurred on the draggable itself, see #10527
if ( this.handleElement.is( event.target ) ) {
// The interaction is over; whether or not the click resulted in a drag,
// focus the element
this.element.trigger( "focus" );
}
return $.ui.mouse.prototype._mouseUp.call( this, event );
},
cancel: function() {
if ( this.helper.is( ".ui-draggable-dragging" ) ) {
this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
} else {
this._clear();
}
return this;
},
_getHandle: function( event ) {
return this.options.handle ?
!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
true;
},
_setHandleClassName: function() {
this.handleElement = this.options.handle ?
this.element.find( this.options.handle ) : this.element;
this._addClass( this.handleElement, "ui-draggable-handle" );
},
_removeHandleClassName: function() {
this._removeClass( this.handleElement, "ui-draggable-handle" );
},
_createHelper: function( event ) {
var o = this.options,
helperIsFunction = $.isFunction( o.helper ),
helper = helperIsFunction ?
$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
( o.helper === "clone" ?
this.element.clone().removeAttr( "id" ) :
this.element );
if ( !helper.parents( "body" ).length ) {
helper.appendTo( ( o.appendTo === "parent" ?
this.element[ 0 ].parentNode :
o.appendTo ) );
}
// Http://bugs.jqueryui.com/ticket/9446
// a helper function can return the original element
// which wouldn't have been set to relative in _create
if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
this._setPositionRelative();
}
if ( helper[ 0 ] !== this.element[ 0 ] &&
!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
helper.css( "position", "absolute" );
}
return helper;
},
_setPositionRelative: function() {
if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
this.element[ 0 ].style.position = "relative";
}
},
_adjustOffsetFromHelper: function( obj ) {
if ( typeof obj === "string" ) {
obj = obj.split( " " );
}
if ( $.isArray( obj ) ) {
obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
}
if ( "left" in obj ) {
this.offset.click.left = obj.left + this.margins.left;
}
if ( "right" in obj ) {
this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
}
if ( "top" in obj ) {
this.offset.click.top = obj.top + this.margins.top;
}
if ( "bottom" in obj ) {
this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
}
},
_isRootNode: function( element ) {
return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
},
_getParentOffset: function() {
//Get the offsetParent and cache its position
var po = this.offsetParent.offset(),
document = this.document[ 0 ];
// This is a special case where we need to modify a offset calculated on start, since the
// following happened:
// 1. The position of the helper is absolute, so it's position is calculated based on the
// next positioned parent
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
// the document, which means that the scroll is included in the initial calculation of the
// offset of the parent, and never recalculated upon drag
if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
po.left += this.scrollParent.scrollLeft();
po.top += this.scrollParent.scrollTop();
}
if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
po = { top: 0, left: 0 };
}
return {
top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
};
},
_getRelativeOffset: function() {
if ( this.cssPosition !== "relative" ) {
return { top: 0, left: 0 };
}
var p = this.element.position(),
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
return {
top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
};
},
_cacheMargins: function() {
this.margins = {
left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
};
},
_cacheHelperProportions: function() {
this.helperProportions = {
width: this.helper.outerWidth(),
height: this.helper.outerHeight()
};
},
_setContainment: function() {
var isUserScrollable, c, ce,
o = this.options,
document = this.document[ 0 ];
this.relativeContainer = null;
if ( !o.containment ) {
this.containment = null;
return;
}
if ( o.containment === "window" ) {
this.containment = [
$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
$( window ).scrollLeft() + $( window ).width() -
this.helperProportions.width - this.margins.left,
$( window ).scrollTop() +
( $( window ).height() || document.body.parentNode.scrollHeight ) -
this.helperProportions.height - this.margins.top
];
return;
}
if ( o.containment === "document" ) {
this.containment = [
0,
0,
$( document ).width() - this.helperProportions.width - this.margins.left,
( $( document ).height() || document.body.parentNode.scrollHeight ) -
this.helperProportions.height - this.margins.top
];
return;
}
if ( o.containment.constructor === Array ) {
this.containment = o.containment;
return;
}
if ( o.containment === "parent" ) {
o.containment = this.helper[ 0 ].parentNode;
}
c = $( o.containment );
ce = c[ 0 ];
if ( !ce ) {
return;
}
isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
this.containment = [
( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
this.helperProportions.width -
this.margins.left -
this.margins.right,
( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
this.helperProportions.height -
this.margins.top -
this.margins.bottom
];
this.relativeContainer = c;
},
_convertPositionTo: function( d, pos ) {
if ( !pos ) {
pos = this.position;
}
var mod = d === "absolute" ? 1 : -1,
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
return {
top: (
// The absolute mouse position
pos.top +
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.top * mod +
// The offsetParent's offset without borders (offset + border)
this.offset.parent.top * mod -
( ( this.cssPosition === "fixed" ?
-this.offset.scroll.top :
( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
),
left: (
// The absolute mouse position
pos.left +
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.left * mod +
// The offsetParent's offset without borders (offset + border)
this.offset.parent.left * mod -
( ( this.cssPosition === "fixed" ?
-this.offset.scroll.left :
( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
)
};
},
_generatePosition: function( event, constrainPosition ) {
var containment, co, top, left,
o = this.options,
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
pageX = event.pageX,
pageY = event.pageY;
// Cache the scroll
if ( !scrollIsRootNode || !this.offset.scroll ) {
this.offset.scroll = {
top: this.scrollParent.scrollTop(),
left: this.scrollParent.scrollLeft()
};
}
/*
* - Position constraining -
* Constrain the position to a mix of grid, containment.
*/
// If we are not dragging yet, we won't check for options
if ( constrainPosition ) {
if ( this.containment ) {
if ( this.relativeContainer ) {
co = this.relativeContainer.offset();
containment = [
this.containment[ 0 ] + co.left,
this.containment[ 1 ] + co.top,
this.containment[ 2 ] + co.left,
this.containment[ 3 ] + co.top
];
} else {
containment = this.containment;
}
if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
pageX = containment[ 0 ] + this.offset.click.left;
}
if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
pageY = containment[ 1 ] + this.offset.click.top;
}
if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
pageX = containment[ 2 ] + this.offset.click.left;
}
if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
pageY = containment[ 3 ] + this.offset.click.top;
}
}
if ( o.grid ) {
//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
// argument errors in IE (see ticket #6950)
top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
top - this.offset.click.top > containment[ 3 ] ) ?
top :
( ( top - this.offset.click.top >= containment[ 1 ] ) ?
top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
left = o.grid[ 0 ] ? this.originalPageX +
Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
this.originalPageX;
pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
left - this.offset.click.left > containment[ 2 ] ) ?
left :
( ( left - this.offset.click.left >= containment[ 0 ] ) ?
left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
}
if ( o.axis === "y" ) {
pageX = this.originalPageX;
}
if ( o.axis === "x" ) {
pageY = this.originalPageY;
}
}
return {
top: (
// The absolute mouse position
pageY -
// Click offset (relative to the element)
this.offset.click.top -
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.top -
// The offsetParent's offset without borders (offset + border)
this.offset.parent.top +
( this.cssPosition === "fixed" ?
-this.offset.scroll.top :
( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
),
left: (
// The absolute mouse position
pageX -
// Click offset (relative to the element)
this.offset.click.left -
// Only for relative positioned nodes: Relative offset from element to offset parent
this.offset.relative.left -
// The offsetParent's offset without borders (offset + border)
this.offset.parent.left +
( this.cssPosition === "fixed" ?
-this.offset.scroll.left :
( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
)
};
},
_clear: function() {
this._removeClass( this.helper, "ui-draggable-dragging" );
if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
this.helper.remove();
}
this.helper = null;
this.cancelHelperRemoval = false;
if ( this.destroyOnClear ) {
this.destroy();
}
},
// From now on bulk stuff - mainly helpers
_trigger: function( type, event, ui ) {
ui = ui || this._uiHash();
$.ui.plugin.call( this, type, [ event, ui, this ], true );
// Absolute position and offset (see #6884 ) have to be recalculated after plugins
if ( /^(drag|start|stop)/.test( type ) ) {
this.positionAbs = this._convertPositionTo( "absolute" );
ui.offset = this.positionAbs;
}
return $.Widget.prototype._trigger.call( this, type, event, ui );
},
plugins: {},
_uiHash: function() {
return {
helper: this.helper,
position: this.position,
originalPosition: this.originalPosition,
offset: this.positionAbs
};
}
} );
$.ui.plugin.add( "draggable", "connectToSortable", {
start: function( event, ui, draggable ) {
var uiSortable = $.extend( {}, ui, {
item: draggable.element
} );
draggable.sortables = [];
$( draggable.options.connectToSortable ).each( function() {
var sortable = $( this ).sortable( "instance" );
if ( sortable && !sortable.options.disabled ) {
draggable.sortables.push( sortable );
// RefreshPositions is called at drag start to refresh the containerCache
// which is used in drag. This ensures it's initialized and synchronized
// with any changes that might have happened on the page since initialization.
sortable.refreshPositions();
sortable._trigger( "activate", event, uiSortable );
}
} );
},
stop: function( event, ui, draggable ) {
var uiSortable = $.extend( {}, ui, {
item: draggable.element
} );
draggable.cancelHelperRemoval = false;
$.each( draggable.sortables, function() {
var sortable = this;
if ( sortable.isOver ) {
sortable.isOver = 0;
// Allow this sortable to handle removing the helper
draggable.cancelHelperRemoval = true;
sortable.cancelHelperRemoval = false;
// Use _storedCSS To restore properties in the sortable,
// as this also handles revert (#9675) since the draggable
// may have modified them in unexpected ways (#8809)
sortable._storedCSS = {
position: sortable.placeholder.css( "position" ),
top: sortable.placeholder.css( "top" ),
left: sortable.placeholder.css( "left" )
};
sortable._mouseStop( event );
// Once drag has ended, the sortable should return to using
// its original helper, not the shared helper from draggable
sortable.options.helper = sortable.options._helper;
} else {
// Prevent this Sortable from removing the helper.
// However, don't set the draggable to remove the helper
// either as another connected Sortable may yet handle the removal.
sortable.cancelHelperRemoval = true;
sortable._trigger( "deactivate", event, uiSortable );
}
} );
},
drag: function( event, ui, draggable ) {
$.each( draggable.sortables, function() {
var innermostIntersecting = false,
sortable = this;
// Copy over variables that sortable's _intersectsWith uses
sortable.positionAbs = draggable.positionAbs;
sortable.helperProportions = draggable.helperProportions;
sortable.offset.click = draggable.offset.click;
if ( sortable._intersectsWith( sortable.containerCache ) ) {
innermostIntersecting = true;
$.each( draggable.sortables, function() {
// Copy over variables that sortable's _intersectsWith uses
this.positionAbs = draggable.positionAbs;
this.helperProportions = draggable.helperProportions;
this.offset.click = draggable.offset.click;
if ( this !== sortable &&
this._intersectsWith( this.containerCache ) &&
$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
innermostIntersecting = false;
}
return innermostIntersecting;
} );
}
if ( innermostIntersecting ) {
// If it intersects, we use a little isOver variable and set it once,
// so that the move-in stuff gets fired only once.
if ( !sortable.isOver ) {
sortable.isOver = 1;
// Store draggable's parent in case we need to reappend to it later.
draggable._parent = ui.helper.parent();
sortable.currentItem = ui.helper
.appendTo( sortable.element )
.data( "ui-sortable-item", true );
// Store helper option to later restore it
sortable.options._helper = sortable.options.helper;
sortable.options.helper = function() {
return ui.helper[ 0 ];
};
// Fire the start events of the sortable with our passed browser event,
// and our own helper (so it doesn't create a new one)
event.target = sortable.currentItem[ 0 ];
sortable._mouseCapture( event, true );
sortable._mouseStart( event, true, true );
// Because the browser event is way off the new appended portlet,
// modify necessary variables to reflect the changes
sortable.offset.click.top = draggable.offset.click.top;
sortable.offset.click.left = draggable.offset.click.left;
sortable.offset.parent.left -= draggable.offset.parent.left -
sortable.offset.parent.left;
sortable.offset.parent.top -= draggable.offset.parent.top -
sortable.offset.parent.top;
draggable._trigger( "toSortable", event );
// Inform draggable that the helper is in a valid drop zone,
// used solely in the revert option to handle "valid/invalid".
draggable.dropped = sortable.element;
// Need to refreshPositions of all sortables in the case that
// adding to one sortable changes the location of the other sortables (#9675)
$.each( draggable.sortables, function() {
this.refreshPositions();
} );
// Hack so receive/update callbacks work (mostly)
draggable.currentItem = draggable.element;
sortable.fromOutside = draggable;
}
if ( sortable.currentItem ) {
sortable._mouseDrag( event );
// Copy the sortable's position because the draggable's can potentially reflect
// a relative position, while sortable is always absolute, which the dragged
// element has now become. (#8809)
ui.position = sortable.position;
}
} else {
// If it doesn't intersect with the sortable, and it intersected before,
// we fake the drag stop of the sortable, but make sure it doesn't remove
// the helper by using cancelHelperRemoval.
if ( sortable.isOver ) {
sortable.isOver = 0;
sortable.cancelHelperRemoval = true;
// Calling sortable's mouseStop would trigger a revert,
// so revert must be temporarily false until after mouseStop is called.
sortable.options._revert = sortable.options.revert;
sortable.options.revert = false;
sortable._trigger( "out", event, sortable._uiHash( sortable ) );
sortable._mouseStop( event, true );
// Restore sortable behaviors that were modfied
// when the draggable entered the sortable area (#9481)
sortable.options.revert = sortable.options._revert;
sortable.options.helper = sortable.options._helper;
if ( sortable.placeholder ) {
sortable.placeholder.remove();
}
// Restore and recalculate the draggable's offset considering the sortable
// may have modified them in unexpected ways. (#8809, #10669)
ui.helper.appendTo( draggable._parent );
draggable._refreshOffsets( event );
ui.position = draggable._generatePosition( event, true );
draggable._trigger( "fromSortable", event );
// Inform draggable that the helper is no longer in a valid drop zone
draggable.dropped = false;
// Need to refreshPositions of all sortables just in case removing
// from one sortable changes the location of other sortables (#9675)
$.each( draggable.sortables, function() {
this.refreshPositions();
} );
}
}
} );
}
} );
$.ui.plugin.add( "draggable", "cursor", {
start: function( event, ui, instance ) {
var t = $( "body" ),
o = instance.options;
if ( t.css( "cursor" ) ) {
o._cursor = t.css( "cursor" );
}
t.css( "cursor", o.cursor );
},
stop: function( event, ui, instance ) {
var o = instance.options;
if ( o._cursor ) {
$( "body" ).css( "cursor", o._cursor );
}
}
} );
$.ui.plugin.add( "draggable", "opacity", {
start: function( event, ui, instance ) {
var t = $( ui.helper ),
o = instance.options;
if ( t.css( "opacity" ) ) {
o._opacity = t.css( "opacity" );
}
t.css( "opacity", o.opacity );
},
stop: function( event, ui, instance ) {
var o = instance.options;
if ( o._opacity ) {
$( ui.helper ).css( "opacity", o._opacity );
}
}
} );
$.ui.plugin.add( "draggable", "scroll", {
start: function( event, ui, i ) {
if ( !i.scrollParentNotHidden ) {
i.scrollParentNotHidden = i.helper.scrollParent( false );
}
if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
i.overflowOffset = i.scrollParentNotHidden.offset();
}
},
drag: function( event, ui, i ) {
var o = i.options,
scrolled = false,
scrollParent = i.scrollParentNotHidden[ 0 ],
document = i.document[ 0 ];
if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
if ( !o.axis || o.axis !== "x" ) {
if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
o.scrollSensitivity ) {
scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
}
}
if ( !o.axis || o.axis !== "y" ) {
if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
o.scrollSensitivity ) {
scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
}
}
} else {
if ( !o.axis || o.axis !== "x" ) {
if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
o.scrollSensitivity ) {
scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
}
}
if ( !o.axis || o.axis !== "y" ) {
if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
scrolled = $( document ).scrollLeft(
$( document ).scrollLeft() - o.scrollSpeed
);
} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
o.scrollSensitivity ) {
scrolled = $( document ).scrollLeft(
$( document ).scrollLeft() + o.scrollSpeed
);
}
}
}
if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
$.ui.ddmanager.prepareOffsets( i, event );
}
}
} );
$.ui.plugin.add( "draggable", "snap", {
start: function( event, ui, i ) {
var o = i.options;
i.snapElements = [];
$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
.each( function() {
var $t = $( this ),
$o = $t.offset();
if ( this !== i.element[ 0 ] ) {
i.snapElements.push( {
item: this,
width: $t.outerWidth(), height: $t.outerHeight(),
top: $o.top, left: $o.left
} );
}
} );
},
drag: function( event, ui, inst ) {
var ts, bs, ls, rs, l, r, t, b, i, first,
o = inst.options,
d = o.snapTolerance,
x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
l = inst.snapElements[ i ].left - inst.margins.left;
r = l + inst.snapElements[ i ].width;
t = inst.snapElements[ i ].top - inst.margins.top;
b = t + inst.snapElements[ i ].height;
if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
!$.contains( inst.snapElements[ i ].item.ownerDocument,
inst.snapElements[ i ].item ) ) {
if ( inst.snapElements[ i ].snapping ) {
( inst.options.snap.release &&
inst.options.snap.release.call(
inst.element,
event,
$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
) );
}
inst.snapElements[ i ].snapping = false;
continue;
}
if ( o.snapMode !== "inner" ) {
ts = Math.abs( t - y2 ) <= d;
bs = Math.abs( b - y1 ) <= d;
ls = Math.abs( l - x2 ) <= d;
rs = Math.abs( r - x1 ) <= d;
if ( ts ) {
ui.position.top = inst._convertPositionTo( "relative", {
top: t - inst.helperProportions.height,
left: 0
} ).top;
}
if ( bs ) {
ui.position.top = inst._convertPositionTo( "relative", {
top: b,
left: 0
} ).top;
}
if ( ls ) {
ui.position.left = inst._convertPositionTo( "relative", {
top: 0,
left: l - inst.helperProportions.width
} ).left;
}
if ( rs ) {
ui.position.left = inst._convertPositionTo( "relative", {
top: 0,
left: r
} ).left;
}
}
first = ( ts || bs || ls || rs );
if ( o.snapMode !== "outer" ) {
ts = Math.abs( t - y1 ) <= d;
bs = Math.abs( b - y2 ) <= d;
ls = Math.abs( l - x1 ) <= d;
rs = Math.abs( r - x2 ) <= d;
if ( ts ) {
ui.position.top = inst._convertPositionTo( "relative", {
top: t,
left: 0
} ).top;
}
if ( bs ) {
ui.position.top = inst._convertPositionTo( "relative", {
top: b - inst.helperProportions.height,
left: 0
} ).top;
}
if ( ls ) {
ui.position.left = inst._convertPositionTo( "relative", {
top: 0,
left: l
} ).left;
}
if ( rs ) {
ui.position.left = inst._convertPositionTo( "relative", {
top: 0,
left: r - inst.helperProportions.width
} ).left;
}
}
if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
( inst.options.snap.snap &&
inst.options.snap.snap.call(
inst.element,
event,
$.extend( inst._uiHash(), {
snapItem: inst.snapElements[ i ].item
} ) ) );
}
inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
}
}
} );
$.ui.plugin.add( "draggable", "stack", {
start: function( event, ui, instance ) {
var min,
o = instance.options,
group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
} );
if ( !group.length ) { return; }
min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
$( group ).each( function( i ) {
$( this ).css( "zIndex", min + i );
} );
this.css( "zIndex", ( min + group.length ) );
}
} );
$.ui.plugin.add( "draggable", "zIndex", {
start: function( event, ui, instance ) {
var t = $( ui.helper ),
o = instance.options;
if ( t.css( "zIndex" ) ) {
o._zIndex = t.css( "zIndex" );
}
t.css( "zIndex", o.zIndex );
},
stop: function( event, ui, instance ) {
var o = instance.options;
if ( o._zIndex ) {
$( ui.helper ).css( "zIndex", o._zIndex );
}
}
} );
var widgetsDraggable = $.ui.draggable;
}));
//}}}
//{{{
(function() {
var css = store.getTiddlerText("jquery.keyboard.js##CSS").replace(/\* \//g, "*/");
css = css.replace(/\/\/\{\{\{|\/\/\}\}\}/g, "");
css = css.replace(/images\/ui-icons_444444_256x240\.png/g, "data:image/png;base64,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");
css = css.replace(/images\/ui-icons_555555_256x240\.png/g, "data:image/png;base64,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");
css = css.replace(/images\/ui-icons_ffffff_256x240\.png/g, "data:image/png;base64,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");
css = css.replace(/images\/ui-icons_777620_256x240\.png/g, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAADwCAMAAADYSUr5AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABEVBMVEV3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diB3diBh/q0oAAAAWnRSTlMAWEd8IjKY4b3Ld2acsomqpVpOeudAZYVTi0aRlGNhTaBobZtzv33AvOOt5enkgY5d393gudbb0sTV1Ndi2nD+Dc6Zafg4KhYcxXnRp8iT0Niv8M2QiLC7x6vji8qEAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+AHDQ0aNu2VSf4AAA9rSURBVHja7V0LY9u2EQbJSKrpmJIbuUubOG69JnM2L8leXdd1bdSma+M6qbuk63r//4cM4AuHOzzEUqZoG59syUeAAO7jAbwDQFmIiIiIESCBZNtN2LL+MHIGejcvAb/+CWZAZR6WEGKBoGASQNtjpKvUBB8BYBWApwJyfvnnoCZBLZATkND2GOn0CjIC6Om8ApY5GZAB1n67Cp50zqAguf2l2zIn3kwbBbNg3kR6gbmFeK8wtwBPA4a3ADsprIk+AtjZwEzEUzoxweHHgDX4uNS7AO1Cw98Fto6b7ghFREREREREjBiX7glu2Q9KWLQG1HlP6Bk0uxHgJ6wC33wAmxCSpQ9KiaUBjACWgWY3w8WEVUAJ8ESXqvQhgyHWfnuLPcmWGZXEc7oITAFCScBg0wHcgoXNZo1obY35gMQszdejtm0BViSeFvefD+A9ZKtjQBjX/S4QEREREREREbE99HP8g65psPzegUfPAoAvp0KH3L3bx7cDsAYFSgP/Ea4e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");
css = css.replace(/images\/ui-icons_cc0000_256x240\.png/g, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAADwCAMAAADYSUr5AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABEVBMVEXMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAADMAAAWt1qrAAAAWnRSTlMAWEd8IjKY4b3Ld2acsomqpVpOeudAZYVTi0aRlGNhTaBobZtzv33AvOOt5enkgY5d393gudbb0sTV1Ndi2nD+Dc6Zafg4KhYcxXnRp8iT0Niv8M2QiLC7x6vji8qEAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+AHDQ0aNu2VSf4AAA9rSURBVHja7V0LY9u2EQbJSKrpmJIbuUubOG69JnM2L8leXdd1bdSma+M6qbuk63r//4cM4AuHOzzEUqZoG59syUeAAO7jAbwDQFmIiIiIESCBZNtN2LL+MHIGejcvAb/+CWZAZR6WEGKBoGASQNtjpKvUBB8BYBWApwJyfvnnoCZBLZATkND2GOn0CjIC6Om8ApY5GZAB1n67Cp50zqAguf2l2zIn3kwbBbNg3kR6gbmFeK8wtwBPA4a3ADsprIk+AtjZwEzEUzoxweHHgDX4uNS7AO1Cw98Fto6b7ghFREREREREjBiX7glu2Q9KWLQG1HlP6Bk0uxHgJ6wC33wAmxCSpQ9KiaUBjACWgWY3w8WEVUAJ8ESXqvQhgyHWfnuLPcmWGZXEc7oITAFCScBg0wHcgoXNZo1obY35gMQszdejtm0BViSeFvefD+A9ZKtjQBjX/S4QEREREREREbE99HP8g65psPzegUfPAoAvp0KH3L3bx7cDsAYFSgP/Ea4e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");
css = css.replace(/images\/ui-icons_777777_256x240\.png/g, "data:image/png;base64,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");
css += "\nspan.ui-keyboard-text { font-size: 1.1em; } .ui-keyboard-button { height: 2em; width: 2em; margin: .1em; cursor: pointer; } .ui-keyboard-widekey { width: 5.5em; } .ui-keyboard-space { width: 15em; }";
css += "\n.ui-keyboard-empty { background: none !important; border: none !important; cursor: default !important; }";
var highlightColour = store.getTiddlerSlice("ColorPalette", "SecondaryLight") || "#fe8";
css += "\n.ui-keyboard-button-highlight { background: " + highlightColour + " !important }";
setStylesheet(css, "jquery.keyboard.js-stylesheet");
})();
//}}}
/***
|Name|jsmind.js|
|Source|https://github.com/hizzgdev/jsmind|
|Documentation|https://hizzgdev.github.io/jsmind/docs/en|
|Version|0.5.7|
|License|[[BSD license|https://opensource.org/license/bsd-3-clause]]|
|Description|jsMind is mind map library built by javascript, it base on html5 canvas and svg|
!!!!!CSS
//{{{
/**
* @license BSD
* @copyright 2014-2023 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
* /
/* important section * /
.jsmind-inner {
position: relative;
overflow: auto;
width: 100%;
height: 100%;
outline: none;
} /*box-shadow:0 0 2px #000;* /
.jsmind-inner {
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jsmind-inner canvas {
position: absolute;
}
/* z-index:1 * /
svg.jsmind {
position: absolute;
z-index: 1;
}
canvas.jsmind {
position: absolute;
z-index: 1;
}
/* z-index:2 * /
jmnodes {
position: absolute;
z-index: 2;
background-color: rgba(0, 0, 0, 0);
} /*background color is necessary* /
jmnode {
position: absolute;
cursor: default;
max-width: 400px;
}
jmexpander {
position: absolute;
width: 11px;
height: 11px;
display: block;
overflow: hidden;
line-height: 12px;
font-size: 12px;
text-align: center;
border-radius: 6px;
border-width: 1px;
border-style: solid;
cursor: pointer;
}
.jmnode-overflow-wrap jmnodes {
min-width: 420px;
}
.jmnode-overflow-hidden jmnode {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* default theme * /
jmnode {
padding: 10px;
background-color: #fff;
color: #333;
border-radius: 5px;
box-shadow: 1px 1px 1px #666;
font: 16px/1.125 Verdana, Arial, Helvetica, sans-serif;
}
jmnode:hover {
box-shadow: 2px 2px 8px #000;
background-color: #ebebeb;
color: #333;
}
jmnode.selected {
background-color: #11f;
color: #fff;
box-shadow: 2px 2px 8px #000;
}
jmnode.root {
font-size: 24px;
}
jmexpander {
border-color: gray;
}
jmexpander:hover {
border-color: #000;
}
@media screen and (max-device-width: 1024px) {
jmnode {
padding: 5px;
border-radius: 3px;
font-size: 14px;
}
jmnode.root {
font-size: 21px;
}
}
/* primary theme * /
jmnodes.theme-primary jmnode {
background-color: #428bca;
color: #fff;
border-color: #357ebd;
}
jmnodes.theme-primary jmnode:hover {
background-color: #3276b1;
border-color: #285e8e;
}
jmnodes.theme-primary jmnode.selected {
background-color: #f1c40f;
color: #fff;
}
jmnodes.theme-primary jmnode.root {
}
jmnodes.theme-primary jmexpander {
}
jmnodes.theme-primary jmexpander:hover {
}
/* warning theme * /
jmnodes.theme-warning jmnode {
background-color: #f0ad4e;
border-color: #eea236;
color: #fff;
}
jmnodes.theme-warning jmnode:hover {
background-color: #ed9c28;
border-color: #d58512;
}
jmnodes.theme-warning jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-warning jmnode.root {
}
jmnodes.theme-warning jmexpander {
}
jmnodes.theme-warning jmexpander:hover {
}
/* danger theme * /
jmnodes.theme-danger jmnode {
background-color: #d9534f;
border-color: #d43f3a;
color: #fff;
}
jmnodes.theme-danger jmnode:hover {
background-color: #d2322d;
border-color: #ac2925;
}
jmnodes.theme-danger jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-danger jmnode.root {
}
jmnodes.theme-danger jmexpander {
}
jmnodes.theme-danger jmexpander:hover {
}
/* success theme * /
jmnodes.theme-success jmnode {
background-color: #5cb85c;
border-color: #4cae4c;
color: #fff;
}
jmnodes.theme-success jmnode:hover {
background-color: #47a447;
border-color: #398439;
}
jmnodes.theme-success jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-success jmnode.root {
}
jmnodes.theme-success jmexpander {
}
jmnodes.theme-success jmexpander:hover {
}
/* info theme * /
jmnodes.theme-info jmnode {
background-color: #5dc0de;
border-color: #46b8da;
color: #fff;
}
jmnodes.theme-info jmnode:hover {
background-color: #39b3d7;
border-color: #269abc;
}
jmnodes.theme-info jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-info jmnode.root {
}
jmnodes.theme-info jmexpander {
}
jmnodes.theme-info jmexpander:hover {
}
/* greensea theme * /
jmnodes.theme-greensea jmnode {
background-color: #1abc9c;
color: #fff;
}
jmnodes.theme-greensea jmnode:hover {
background-color: #16a085;
}
jmnodes.theme-greensea jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-greensea jmnode.root {
}
jmnodes.theme-greensea jmexpander {
}
jmnodes.theme-greensea jmexpander:hover {
}
/* nephrite theme * /
jmnodes.theme-nephrite jmnode {
background-color: #2ecc71;
color: #fff;
}
jmnodes.theme-nephrite jmnode:hover {
background-color: #27ae60;
}
jmnodes.theme-nephrite jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-nephrite jmnode.root {
}
jmnodes.theme-nephrite jmexpander {
}
jmnodes.theme-nephrite jmexpander:hover {
}
/* belizehole theme * /
jmnodes.theme-belizehole jmnode {
background-color: #3498db;
color: #fff;
}
jmnodes.theme-belizehole jmnode:hover {
background-color: #2980b9;
}
jmnodes.theme-belizehole jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-belizehole jmnode.root {
}
jmnodes.theme-belizehole jmexpander {
}
jmnodes.theme-belizehole jmexpander:hover {
}
/* wisteria theme * /
jmnodes.theme-wisteria jmnode {
background-color: #9b59b6;
color: #fff;
}
jmnodes.theme-wisteria jmnode:hover {
background-color: #8e44ad;
}
jmnodes.theme-wisteria jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-wisteria jmnode.root {
}
jmnodes.theme-wisteria jmexpander {
}
jmnodes.theme-wisteria jmexpander:hover {
}
/* asphalt theme * /
jmnodes.theme-asphalt jmnode {
background-color: #34495e;
color: #fff;
}
jmnodes.theme-asphalt jmnode:hover {
background-color: #2c3e50;
}
jmnodes.theme-asphalt jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-asphalt jmnode.root {
}
jmnodes.theme-asphalt jmexpander {
}
jmnodes.theme-asphalt jmexpander:hover {
}
/* orange theme * /
jmnodes.theme-orange jmnode {
background-color: #f1c40f;
color: #fff;
}
jmnodes.theme-orange jmnode:hover {
background-color: #f39c12;
}
jmnodes.theme-orange jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-orange jmnode.root {
}
jmnodes.theme-orange jmexpander {
}
jmnodes.theme-orange jmexpander:hover {
}
/* pumpkin theme * /
jmnodes.theme-pumpkin jmnode {
background-color: #e67e22;
color: #fff;
}
jmnodes.theme-pumpkin jmnode:hover {
background-color: #d35400;
}
jmnodes.theme-pumpkin jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-pumpkin jmnode.root {
}
jmnodes.theme-pumpkin jmexpander {
}
jmnodes.theme-pumpkin jmexpander:hover {
}
/* pomegranate theme * /
jmnodes.theme-pomegranate jmnode {
background-color: #e74c3c;
color: #fff;
}
jmnodes.theme-pomegranate jmnode:hover {
background-color: #c0392b;
}
jmnodes.theme-pomegranate jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-pomegranate jmnode.root {
}
jmnodes.theme-pomegranate jmexpander {
}
jmnodes.theme-pomegranate jmexpander:hover {
}
/* clouds theme * /
jmnodes.theme-clouds jmnode {
background-color: #ecf0f1;
color: #333;
}
jmnodes.theme-clouds jmnode:hover {
background-color: #bdc3c7;
}
jmnodes.theme-clouds jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-clouds jmnode.root {
}
jmnodes.theme-clouds jmexpander {
}
jmnodes.theme-clouds jmexpander:hover {
}
/* asbestos theme * /
jmnodes.theme-asbestos jmnode {
background-color: #95a5a6;
color: #fff;
}
jmnodes.theme-asbestos jmnode:hover {
background-color: #7f8c8d;
}
jmnodes.theme-asbestos jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-asbestos jmnode.root {
}
jmnodes.theme-asbestos jmexpander {
}
jmnodes.theme-asbestos jmexpander:hover {
}
//}}}
!!!!!Code
***/
//{{{
(function (console) {
//}}}
// // ''jsmind.js''
//{{{
/**
* @license BSD
* @copyright 2014-2023 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
*/
; (function ($w) {
'use strict';
// set 'jsMind' as the library name.
// __name__ should be a const value, Never try to change it easily.
var __name__ = 'jsMind';
// library version
var __version__ = '0.5.7';
// author
var __author__ = 'hizzgdev@163.com';
// an noop function define
var _noop = function () { };
var logger = (typeof console === 'undefined') ? {
log: _noop, debug: _noop, error: _noop, warn: _noop, info: _noop
} : console;
// check global variables
if (typeof module === 'undefined' || !module.exports) {
if (typeof $w[__name__] != 'undefined') {
logger.log(__name__ + ' has been already exist.');
return;
}
}
// shortcut of methods in dom
var $d = $w.document;
var $g = function (id) { return $d.getElementById(id); };
var $c = function (tag) { return $d.createElement(tag); };
var $t = function (n, t) { if (n.hasChildNodes()) { n.firstChild.nodeValue = t; } else { n.appendChild($d.createTextNode(t)); } };
var $h = function (n, t) {
if (t instanceof HTMLElement) {
n.innerHTML = '';
n.appendChild(t);
} else {
n.innerHTML = t;
}
};
// detect isElement
var $i = function (el) { return !!el && (typeof el === 'object') && (el.nodeType === 1) && (typeof el.style === 'object') && (typeof el.ownerDocument === 'object'); };
if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (p) { return this.slice(0, p.length) === p; }; }
var DEFAULT_OPTIONS = {
container: '', // id of the container
editable: false, // you can change it in your options
theme: null,
mode: 'full', // full or side
support_html: true,
view: {
engine: 'canvas',
hmargin: 100,
vmargin: 50,
line_width: 2,
line_color: '#555',
draggable: false, // drag the mind map with your mouse, when it's larger that the container
hide_scrollbars_when_draggable: false, // hide container scrollbars, when mind map is larger than container and draggable option is true.
node_overflow: 'hidden' // hidden or wrap
},
layout: {
hspace: 30,
vspace: 20,
pspace: 13,
cousin_space: 0
},
default_event_handle: {
enable_mousedown_handle: true,
enable_click_handle: true,
enable_dblclick_handle: true,
enable_mousewheel_handle: true
},
shortcut: {
enable: true,
handles: {
},
mapping: {
addchild: [45, 4096+13], // Insert, Ctrl+Enter
addbrother: 13, // Enter
editnode: 113,// F2
delnode: 46, // Delete
toggle: 32, // Space
left: 37, // Left
up: 38, // Up
right: 39, // Right
down: 40, // Down
}
},
};
// core object
var jm = function (options) {
jm.current = this;
this.version = __version__;
var opts = {};
jm.util.json.merge(opts, DEFAULT_OPTIONS);
jm.util.json.merge(opts, options);
if (!opts.container) {
logger.error('the options.container should not be null or empty.');
return;
}
this.options = opts;
this.initialized = false;
this.mind = null;
this.event_handles = [];
this.init();
};
// ============= static object =============================================
jm.direction = {
left: -1, center: 0, right: 1, of: function (dir) {
if (!dir || dir === -1 || dir === 0 || dir === 1) {
return dir;
}
if (dir === '-1' || dir === '0' || dir === '1') {
return parseInt(dir);
}
if (dir.toLowerCase() === 'left') {
return this.left;
}
if (dir.toLowerCase() === 'right') {
return this.right;
}
if (dir.toLowerCase() === 'center') {
return this.center;
}
}
};
jm.event_type = { show: 1, resize: 2, edit: 3, select: 4 };
jm.key = { meta: 1 << 13, ctrl: 1 << 12, alt: 1 << 11, shift: 1 << 10 };
jm.node = function (sId, iIndex, sTopic, oData, bIsRoot, oParent, eDirection, bExpanded) {
if (!sId) { logger.error('invalid node id'); return; }
if (typeof iIndex != 'number') { logger.error('invalid node index'); return; }
if (typeof bExpanded === 'undefined') { bExpanded = true; }
this.id = sId;
this.index = iIndex;
this.topic = sTopic;
this.data = oData || {};
this.isroot = bIsRoot;
this.parent = oParent;
this.direction = eDirection;
this.expanded = !!bExpanded;
this.children = [];
this._data = {};
};
jm.node.compare = function (node1, node2) {
// '-1' is alwary the last
var r = 0;
var i1 = node1.index;
var i2 = node2.index;
if (i1 >= 0 && i2 >= 0) {
r = i1 - i2;
} else if (i1 == -1 && i2 == -1) {
r = 0;
} else if (i1 == -1) {
r = 1;
} else if (i2 == -1) {
r = -1;
} else {
r = 0;
}
//logger.debug(i1+' <> '+i2+' = '+r);
return r;
};
jm.node.inherited = function (pnode, node) {
if (!!pnode && !!node) {
if (pnode.id === node.id) {
return true;
}
if (pnode.isroot) {
return true;
}
var pid = pnode.id;
var p = node;
while (!p.isroot) {
p = p.parent;
if (p.id === pid) {
return true;
}
}
}
return false;
};
jm.node.is_node = function (n) {
return !!n && n instanceof jm.node;
};
jm.node.prototype = {
get_location: function () {
var vd = this._data.view;
return {
x: vd.abs_x,
y: vd.abs_y
};
},
get_size: function () {
var vd = this._data.view;
return {
w: vd.width,
h: vd.height
}
}
};
jm.mind = function () {
this.name = null;
this.author = null;
this.version = null;
this.root = null;
this.selected = null;
this.nodes = {};
};
jm.mind.prototype = {
get_node: function (nodeid) {
if (nodeid in this.nodes) {
return this.nodes[nodeid];
} else {
logger.warn('the node[id=' + nodeid + '] can not be found');
return null;
}
},
set_root: function (nodeid, topic, data) {
if (this.root == null) {
this.root = new jm.node(nodeid, 0, topic, data, true);
this._put_node(this.root);
return this.root;
} else {
logger.error('root node is already exist');
return null;
}
},
add_node: function (parent_node, nodeid, topic, data, direction, expanded, idx) {
if (!jm.util.is_node(parent_node)) {
logger.error('the parent_node ' + parent_node + ' is not a node.');
return null;
}
var node_index = idx || -1;
var node = new jm.node(nodeid, node_index, topic, data, false, parent_node, parent_node.direction, expanded);
if (parent_node.isroot) {
node.direction = direction || jm.direction.right;
}
if (this._put_node(node)) {
parent_node.children.push(node);
this._reindex(parent_node);
} else {
logger.error('fail, the nodeid \'' + node.id + '\' has been already exist.');
node = null;
}
return node;
},
insert_node_before: function (node_before, nodeid, topic, data, direction) {
if (!jm.util.is_node(node_before)) {
logger.error('the node_before ' + node_before + ' is not a node.');
return null;
}
var node_index = node_before.index - 0.5;
return this.add_node(node_before.parent, nodeid, topic, data, direction, true, node_index);
},
get_node_before: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return null;
} else {
return this.get_node_before(the_node);
}
}
if (node.isroot) { return null; }
var idx = node.index - 2;
if (idx >= 0) {
return node.parent.children[idx];
} else {
return null;
}
},
insert_node_after: function (node_after, nodeid, topic, data, direction) {
if (!jm.util.is_node(node_after)) {
logger.error('the node_after ' + node_after + ' is not a node.');
return null;
}
var node_index = node_after.index + 0.5;
return this.add_node(node_after.parent, nodeid, topic, data, direction, true, node_index);
},
get_node_after: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return null;
} else {
return this.get_node_after(the_node);
}
}
if (node.isroot) { return null; }
var idx = node.index;
var brothers = node.parent.children;
if (brothers.length > idx) {
return node.parent.children[idx];
} else {
return null;
}
},
move_node: function (node, before_id, parent_id, direction) {
if (!jm.util.is_node(node)) {
logger.error('the parameter node ' + node + ' is not a node.');
return null;
}
if (!parent_id) {
parent_id = node.parent.id;
}
return this._move_node(node, before_id, parent_id, direction);
},
_flow_node_direction: function (node, direction) {
if (typeof direction === 'undefined') {
direction = node.direction;
} else {
node.direction = direction;
}
var len = node.children.length;
while (len--) {
this._flow_node_direction(node.children[len], direction);
}
},
_move_node_internal: function (node, beforeid) {
if (!!node && !!beforeid) {
if (beforeid == '_last_') {
node.index = -1;
this._reindex(node.parent);
} else if (beforeid == '_first_') {
node.index = 0;
this._reindex(node.parent);
} else {
var node_before = (!!beforeid) ? this.get_node(beforeid) : null;
if (node_before != null && node_before.parent != null && node_before.parent.id == node.parent.id) {
node.index = node_before.index - 0.5;
this._reindex(node.parent);
}
}
}
return node;
},
_move_node: function (node, beforeid, parentid, direction) {
if (!!node && !!parentid) {
var parent_node = this.get_node(parentid)
if (jm.node.inherited(node, parent_node)) {
logger.error('can not move a node to its children');
return null;
}
if (node.parent.id != parentid) {
// remove from parent's children
var sibling = node.parent.children;
var si = sibling.length;
while (si--) {
if (sibling[si].id == node.id) {
sibling.splice(si, 1);
break;
}
}
node.parent = parent_node;
parent_node.children.push(node);
}
if (node.parent.isroot) {
if (direction == jm.direction.left) {
node.direction = direction;
} else {
node.direction = jm.direction.right;
}
} else {
node.direction = node.parent.direction;
}
this._move_node_internal(node, beforeid);
this._flow_node_direction(node);
}
return node;
},
remove_node: function (node) {
if (!jm.util.is_node(node)) {
logger.error('the parameter node ' + node + ' is not a node.');
return false;
}
if (node.isroot) {
logger.error('fail, can not remove root node');
return false;
}
if (this.selected != null && this.selected.id == node.id) {
this.selected = null;
}
// clean all subordinate nodes
var children = node.children;
var ci = children.length;
while (ci--) {
this.remove_node(children[ci]);
}
// clean all children
children.length = 0;
// remove from parent's children
var sibling = node.parent.children;
var si = sibling.length;
while (si--) {
if (sibling[si].id == node.id) {
sibling.splice(si, 1);
break;
}
}
// remove from global nodes
delete this.nodes[node.id];
// clean all properties
for (var k in node) {
delete node[k];
}
// remove it's self
node = null;
//delete node;
return true;
},
_put_node: function (node) {
if (node.id in this.nodes) {
logger.warn('the nodeid \'' + node.id + '\' has been already exist.');
return false;
} else {
this.nodes[node.id] = node;
return true;
}
},
_reindex: function (node) {
if (node instanceof jm.node) {
node.children.sort(jm.node.compare);
for (var i = 0; i < node.children.length; i++) {
node.children[i].index = i + 1;
}
}
},
};
jm.format = {
node_tree: {
example: {
"meta": {
"name": __name__,
"author": __author__,
"version": __version__
},
"format": "node_tree",
"data": { "id": "root", "topic": "jsMind Example" }
},
get_mind: function (source) {
var df = jm.format.node_tree;
var mind = new jm.mind();
mind.name = source.meta.name;
mind.author = source.meta.author;
mind.version = source.meta.version;
df._parse(mind, source.data);
return mind;
},
get_data: function (mind) {
var df = jm.format.node_tree;
var json = {};
json.meta = {
name: mind.name,
author: mind.author,
version: mind.version
};
json.format = 'node_tree';
json.data = df._buildnode(mind.root);
return json;
},
_parse: function (mind, node_root) {
var df = jm.format.node_tree;
var data = df._extract_data(node_root);
mind.set_root(node_root.id, node_root.topic, data);
if ('children' in node_root) {
var children = node_root.children;
for (var i = 0; i < children.length; i++) {
df._extract_subnode(mind, mind.root, children[i]);
}
}
},
_extract_data: function (node_json) {
var data = {};
for (var k in node_json) {
if (k == 'id' || k == 'topic' || k == 'children' || k == 'direction' || k == 'expanded') {
continue;
}
data[k] = node_json[k];
}
return data;
},
_extract_subnode: function (mind, node_parent, node_json) {
var df = jm.format.node_tree;
var data = df._extract_data(node_json);
var d = null;
if (node_parent.isroot) {
d = node_json.direction == 'left' ? jm.direction.left : jm.direction.right;
}
var node = mind.add_node(node_parent, node_json.id, node_json.topic, data, d, node_json.expanded);
if (!!node_json['children']) {
var children = node_json.children;
for (var i = 0; i < children.length; i++) {
df._extract_subnode(mind, node, children[i]);
}
}
},
_buildnode: function (node) {
var df = jm.format.node_tree;
if (!(node instanceof jm.node)) { return; }
var o = {
id: node.id,
topic: node.topic,
expanded: node.expanded
};
if (!!node.parent && node.parent.isroot) {
o.direction = node.direction == jm.direction.left ? 'left' : 'right';
}
if (node.data != null) {
var node_data = node.data;
for (var k in node_data) {
o[k] = node_data[k];
}
}
var children = node.children;
if (children.length > 0) {
o.children = [];
for (var i = 0; i < children.length; i++) {
o.children.push(df._buildnode(children[i]));
}
}
return o;
}
},
node_array: {
example: {
"meta": {
"name": __name__,
"author": __author__,
"version": __version__
},
"format": "node_array",
"data": [
{ "id": "root", "topic": "jsMind Example", "isroot": true }
]
},
get_mind: function (source) {
var df = jm.format.node_array;
var mind = new jm.mind();
mind.name = source.meta.name;
mind.author = source.meta.author;
mind.version = source.meta.version;
df._parse(mind, source.data);
return mind;
},
get_data: function (mind) {
var df = jm.format.node_array;
var json = {};
json.meta = {
name: mind.name,
author: mind.author,
version: mind.version
};
json.format = 'node_array';
json.data = [];
df._array(mind, json.data);
return json;
},
_parse: function (mind, node_array) {
var df = jm.format.node_array;
var narray = node_array.slice(0);
// reverse array for improving looping performance
narray.reverse();
var root_node = df._extract_root(mind, narray);
if (!!root_node) {
df._extract_subnode(mind, root_node, narray);
} else {
logger.error('root node can not be found');
}
},
_extract_root: function (mind, node_array) {
var df = jm.format.node_array;
var i = node_array.length;
while (i--) {
if ('isroot' in node_array[i] && node_array[i].isroot) {
var root_json = node_array[i];
var data = df._extract_data(root_json);
var node = mind.set_root(root_json.id, root_json.topic, data);
node_array.splice(i, 1);
return node;
}
}
return null;
},
_extract_subnode: function (mind, parent_node, node_array) {
var df = jm.format.node_array;
var i = node_array.length;
var node_json = null;
var data = null;
var extract_count = 0;
while (i--) {
node_json = node_array[i];
if (node_json.parentid == parent_node.id) {
data = df._extract_data(node_json);
var d = null;
var node_direction = node_json.direction;
if (!!node_direction) {
d = node_direction == 'left' ? jm.direction.left : jm.direction.right;
}
var node = mind.add_node(parent_node, node_json.id, node_json.topic, data, d, node_json.expanded);
node_array.splice(i, 1);
extract_count++;
var sub_extract_count = df._extract_subnode(mind, node, node_array);
if (sub_extract_count > 0) {
// reset loop index after extract subordinate node
i = node_array.length;
extract_count += sub_extract_count;
}
}
}
return extract_count;
},
_extract_data: function (node_json) {
var data = {};
for (var k in node_json) {
if (k == 'id' || k == 'topic' || k == 'parentid' || k == 'isroot' || k == 'direction' || k == 'expanded') {
continue;
}
data[k] = node_json[k];
}
return data;
},
_array: function (mind, node_array) {
var df = jm.format.node_array;
df._array_node(mind.root, node_array);
},
_array_node: function (node, node_array) {
var df = jm.format.node_array;
if (!(node instanceof jm.node)) { return; }
var o = {
id: node.id,
topic: node.topic,
expanded: node.expanded
};
if (!!node.parent) {
o.parentid = node.parent.id;
}
if (node.isroot) {
o.isroot = true;
}
if (!!node.parent && node.parent.isroot) {
o.direction = node.direction == jm.direction.left ? 'left' : 'right';
}
if (node.data != null) {
var node_data = node.data;
for (var k in node_data) {
o[k] = node_data[k];
}
}
node_array.push(o);
var ci = node.children.length;
for (var i = 0; i < ci; i++) {
df._array_node(node.children[i], node_array);
}
},
},
freemind: {
example: {
"meta": {
"name": __name__,
"author": __author__,
"version": __version__
},
"format": "freemind",
"data": "<map version=\"1.0.1\"><node ID=\"root\" TEXT=\"freemind Example\"/></map>"
},
get_mind: function (source) {
var df = jm.format.freemind;
var mind = new jm.mind();
mind.name = source.meta.name;
mind.author = source.meta.author;
mind.version = source.meta.version;
var xml = source.data;
var xml_doc = df._parse_xml(xml);
var xml_root = df._find_root(xml_doc);
df._load_node(mind, null, xml_root);
return mind;
},
get_data: function (mind) {
var df = jm.format.freemind;
var json = {};
json.meta = {
name: mind.name,
author: mind.author,
version: mind.version
};
json.format = 'freemind';
var xmllines = [];
xmllines.push('<map version=\"1.0.1\">');
df._buildmap(mind.root, xmllines);
xmllines.push('</map>');
json.data = xmllines.join(' ');
return json;
},
_parse_xml: function (xml) {
var xml_doc = null;
if (window.DOMParser) {
var parser = new DOMParser();
xml_doc = parser.parseFromString(xml, 'text/xml');
} else { // Internet Explorer
xml_doc = new ActiveXObject('Microsoft.XMLDOM');
xml_doc.async = false;
xml_doc.loadXML(xml);
}
return xml_doc;
},
_find_root: function (xml_doc) {
var nodes = xml_doc.childNodes;
var node = null;
var root = null;
var n = null;
for (var i = 0; i < nodes.length; i++) {
n = nodes[i];
if (n.nodeType == 1 && n.tagName == 'map') {
node = n;
break;
}
}
if (!!node) {
var ns = node.childNodes;
node = null;
for (var i = 0; i < ns.length; i++) {
n = ns[i];
if (n.nodeType == 1 && n.tagName == 'node') {
node = n;
break;
}
}
}
return node;
},
_load_node: function (mind, parent_node, xml_node) {
var df = jm.format.freemind;
var node_id = xml_node.getAttribute('ID');
var node_topic = xml_node.getAttribute('TEXT');
// look for richcontent
if (node_topic == null) {
var topic_children = xml_node.childNodes;
var topic_child = null;
for (var i = 0; i < topic_children.length; i++) {
topic_child = topic_children[i];
//logger.debug(topic_child.tagName);
if (topic_child.nodeType == 1 && topic_child.tagName === 'richcontent') {
node_topic = topic_child.textContent;
break;
}
}
}
var node_data = df._load_attributes(xml_node);
var node_expanded = ('expanded' in node_data) ? (node_data.expanded == 'true') : true;
delete node_data.expanded;
var node_position = xml_node.getAttribute('POSITION');
var node_direction = null;
if (!!node_position) {
node_direction = node_position == 'left' ? jm.direction.left : jm.direction.right;
}
//logger.debug(node_position +':'+ node_direction);
var node = null;
if (!!parent_node) {
node = mind.add_node(parent_node, node_id, node_topic, node_data, node_direction, node_expanded);
} else {
node = mind.set_root(node_id, node_topic, node_data);
}
var children = xml_node.childNodes;
var child = null;
for (var i = 0; i < children.length; i++) {
child = children[i];
if (child.nodeType == 1 && child.tagName == 'node') {
df._load_node(mind, node, child);
}
}
},
_load_attributes: function (xml_node) {
var children = xml_node.childNodes;
var attr = null;
var attr_data = {};
for (var i = 0; i < children.length; i++) {
attr = children[i];
if (attr.nodeType == 1 && attr.tagName === 'attribute') {
attr_data[attr.getAttribute('NAME')] = attr.getAttribute('VALUE');
}
}
return attr_data;
},
_buildmap: function (node, xmllines) {
var df = jm.format.freemind;
var pos = null;
if (!!node.parent && node.parent.isroot) {
pos = node.direction === jm.direction.left ? 'left' : 'right';
}
xmllines.push('<node');
xmllines.push('ID=\"' + node.id + '\"');
if (!!pos) {
xmllines.push('POSITION=\"' + pos + '\"');
}
xmllines.push('TEXT=\"' + node.topic + '\">');
// store expanded status as an attribute
xmllines.push('<attribute NAME=\"expanded\" VALUE=\"' + node.expanded + '\"/>');
// for attributes
var node_data = node.data;
if (node_data != null) {
for (var k in node_data) {
xmllines.push('<attribute NAME=\"' + k + '\" VALUE=\"' + node_data[k] + '\"/>');
}
}
// for children
var children = node.children;
for (var i = 0; i < children.length; i++) {
df._buildmap(children[i], xmllines);
}
xmllines.push('</node>');
},
},
};
// ============= utility object =============================================
jm.util = {
is_node: function (node) {
return !!node && node instanceof jm.node;
},
ajax: {
request: function (url, param, method, callback, fail_callback) {
var a = jm.util.ajax;
var p = null;
var tmp_param = [];
for (var k in param) {
tmp_param.push(encodeURIComponent(k) + '=' + encodeURIComponent(param[k]));
}
if (tmp_param.length > 0) {
p = tmp_param.join('&');
}
var xhr = new XMLHttpRequest();
if (!xhr) { return; }
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 0) {
if (typeof callback === 'function') {
var data = jm.util.json.string2json(xhr.responseText);
if (data != null) {
callback(data);
} else {
callback(xhr.responseText);
}
}
} else {
if (typeof fail_callback === 'function') {
fail_callback(xhr);
} else {
logger.error('xhr request failed.', xhr);
}
}
}
}
method = method || 'GET';
xhr.open(method, url, true);
xhr.setRequestHeader('If-Modified-Since', '0');
if (method == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
xhr.send(p);
} else {
xhr.send();
}
},
get: function (url, callback) {
return jm.util.ajax.request(url, {}, 'GET', callback);
},
post: function (url, param, callback) {
return jm.util.ajax.request(url, param, 'POST', callback);
}
},
dom: {
//target,eventType,handler
add_event: function (t, e, h) {
if (!!t.addEventListener) {
t.addEventListener(e, h, false);
} else {
t.attachEvent('on' + e, h);
}
}
},
file: {
read: function (file_data, fn_callback) {
var reader = new FileReader();
reader.onload = function () {
if (typeof fn_callback === 'function') {
fn_callback(this.result, file_data.name);
}
};
reader.readAsText(file_data);
},
save: function (file_data, type, name) {
var blob;
if (typeof $w.Blob === 'function') {
blob = new Blob([file_data], { type: type });
} else {
var BlobBuilder = $w.BlobBuilder || $w.MozBlobBuilder || $w.WebKitBlobBuilder || $w.MSBlobBuilder;
var bb = new BlobBuilder();
bb.append(file_data);
blob = bb.getBlob(type);
}
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, name);
} else {
var URL = $w.URL || $w.webkitURL;
var bloburl = URL.createObjectURL(blob);
var anchor = $c('a');
if ('download' in anchor) {
anchor.style.visibility = 'hidden';
anchor.href = bloburl;
anchor.download = name;
$d.body.appendChild(anchor);
var evt = $d.createEvent('MouseEvents');
evt.initEvent('click', true, true);
anchor.dispatchEvent(evt);
$d.body.removeChild(anchor);
} else {
location.href = bloburl;
}
}
}
},
json: {
json2string: function (json) {
return JSON.stringify(json);
},
string2json: function (json_str) {
return JSON.parse(json_str);
},
merge: function (b, a) {
for (var o in a) {
if (o in b) {
if (typeof b[o] === 'object' &&
Object.prototype.toString.call(b[o]).toLowerCase() == '[object object]' &&
!b[o].length) {
jm.util.json.merge(b[o], a[o]);
} else {
b[o] = a[o];
}
} else {
b[o] = a[o];
}
}
return b;
}
},
uuid: {
newid: function () {
return (new Date().getTime().toString(16) + Math.random().toString(16).substring(2)).substring(2, 18);
}
},
text: {
is_empty: function (s) {
if (!s) { return true; }
return s.replace(/\s*/, '').length == 0;
}
}
};
jm.prototype = {
init: function () {
if (this.initialized) { return; }
this.initialized = true;
var opts = this.options;
var opts_layout = {
mode: opts.mode,
hspace: opts.layout.hspace,
vspace: opts.layout.vspace,
pspace: opts.layout.pspace,
cousin_space: opts.layout.cousin_space
}
var opts_view = {
container: opts.container,
support_html: opts.support_html,
engine: opts.view.engine,
hmargin: opts.view.hmargin,
vmargin: opts.view.vmargin,
line_width: opts.view.line_width,
line_color: opts.view.line_color,
draggable: opts.view.draggable,
hide_scrollbars_when_draggable: opts.view.hide_scrollbars_when_draggable,
node_overflow: opts.view.node_overflow
};
// create instance of function provider
this.data = new jm.data_provider(this);
this.layout = new jm.layout_provider(this, opts_layout);
this.view = new jm.view_provider(this, opts_view);
this.shortcut = new jm.shortcut_provider(this, opts.shortcut);
this.data.init();
this.layout.init();
this.view.init();
this.shortcut.init();
this._event_bind();
jm.init_plugins(this);
},
enable_edit: function () {
this.options.editable = true;
},
disable_edit: function () {
this.options.editable = false;
},
// call enable_event_handle('dblclick')
// options are 'mousedown', 'click', 'dblclick'
enable_event_handle: function (event_handle) {
this.options.default_event_handle['enable_' + event_handle + '_handle'] = true;
},
// call disable_event_handle('dblclick')
// options are 'mousedown', 'click', 'dblclick'
disable_event_handle: function (event_handle) {
this.options.default_event_handle['enable_' + event_handle + '_handle'] = false;
},
get_editable: function () {
return this.options.editable;
},
set_theme: function (theme) {
var theme_old = this.options.theme;
this.options.theme = (!!theme) ? theme : null;
if (theme_old != this.options.theme) {
this.view.reset_theme();
this.view.reset_custom_style();
}
},
_event_bind: function () {
this.view.add_event(this, 'mousedown', this.mousedown_handle);
this.view.add_event(this, 'click', this.click_handle);
this.view.add_event(this, 'dblclick', this.dblclick_handle);
this.view.add_event(this, "mousewheel", this.mousewheel_handle)
},
mousedown_handle: function (e) {
if (!this.options.default_event_handle['enable_mousedown_handle']) {
return;
}
var element = e.target || event.srcElement;
var nodeid = this.view.get_binded_nodeid(element);
if (!!nodeid) {
if (this.view.is_node(element)) {
this.select_node(nodeid);
}
} else {
this.select_clear();
}
},
click_handle: function (e) {
if (!this.options.default_event_handle['enable_click_handle']) {
return;
}
var element = e.target || event.srcElement;
var is_expander = this.view.is_expander(element);
if (is_expander) {
var nodeid = this.view.get_binded_nodeid(element);
if (!!nodeid) {
this.toggle_node(nodeid);
}
}
},
dblclick_handle: function (e) {
if (!this.options.default_event_handle['enable_dblclick_handle']) {
return;
}
if (this.get_editable()) {
var element = e.target || event.srcElement;
var is_node = this.view.is_node(element);
if (is_node) {
var nodeid = this.view.get_binded_nodeid(element);
if (!!nodeid) {
this.begin_edit(nodeid);
}
}
}
},
// Use [Ctrl] + Mousewheel, to zoom in/out.
mousewheel_handle: function (event) {
// Test if mousewheel option is enabled and Ctrl key is pressed.
if (!this.options.default_event_handle["enable_mousewheel_handle"] || !window.event.ctrlKey) {
return
}
// Avoid default page scrolling behavior.
event.preventDefault()
if (event.deltaY < 0) {
this.view.zoomIn()
} else {
this.view.zoomOut()
}
},
begin_edit: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return false;
} else {
return this.begin_edit(the_node);
}
}
if (this.get_editable()) {
this.view.edit_node_begin(node);
} else {
logger.error('fail, this mind map is not editable.');
return;
}
},
end_edit: function () {
this.view.edit_node_end();
},
toggle_node: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.toggle_node(the_node);
}
}
if (node.isroot) { return; }
this.view.save_location(node);
this.layout.toggle_node(node);
this.view.relayout();
this.view.restore_location(node);
},
expand_node: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.expand_node(the_node);
}
}
if (node.isroot) { return; }
this.view.save_location(node);
this.layout.expand_node(node);
this.view.relayout();
this.view.restore_location(node);
},
collapse_node: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.collapse_node(the_node);
}
}
if (node.isroot) { return; }
this.view.save_location(node);
this.layout.collapse_node(node);
this.view.relayout();
this.view.restore_location(node);
},
expand_all: function () {
this.layout.expand_all();
this.view.relayout();
},
collapse_all: function () {
this.layout.collapse_all();
this.view.relayout();
},
expand_to_depth: function (depth) {
this.layout.expand_to_depth(depth);
this.view.relayout();
},
_reset: function () {
this.view.reset();
this.layout.reset();
this.data.reset();
},
_show: function (mind) {
var m = mind || jm.format.node_array.example;
this.mind = this.data.load(m);
if (!this.mind) {
logger.error('data.load error');
return;
} else {
logger.debug('data.load ok');
}
this.view.load();
logger.debug('view.load ok');
this.layout.layout();
logger.debug('layout.layout ok');
this.view.show(true);
logger.debug('view.show ok');
this.invoke_event_handle(jm.event_type.show, { data: [mind] });
},
show: function (mind) {
this._reset();
this._show(mind);
},
get_meta: function () {
return {
name: this.mind.name,
author: this.mind.author,
version: this.mind.version
};
},
get_data: function (data_format) {
var df = data_format || 'node_tree';
return this.data.get_data(df);
},
get_root: function () {
return this.mind.root;
},
get_node: function (node) {
if (jm.util.is_node(node)) {
return node;
}
return this.mind.get_node(node);
},
add_node: function (parent_node, nodeid, topic, data, direction) {
if (this.get_editable()) {
var the_parent_node = this.get_node(parent_node);
var dir = jm.direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_parent_node);
}
var node = this.mind.add_node(the_parent_node, nodeid, topic, data, dir);
if (!!node) {
this.view.add_node(node);
this.layout.layout();
this.view.show(false);
this.view.reset_node_custom_style(node);
this.expand_node(the_parent_node);
this.invoke_event_handle(jm.event_type.edit, { evt: 'add_node', data: [the_parent_node.id, nodeid, topic, data, dir], node: nodeid });
}
return node;
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
insert_node_before: function (node_before, nodeid, topic, data, direction) {
if (this.get_editable()) {
var the_node_before = this.get_node(node_before);
var dir = jm.direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_node_before.parent);
}
var node = this.mind.insert_node_before(the_node_before, nodeid, topic, data, dir);
if (!!node) {
this.view.add_node(node);
this.layout.layout();
this.view.show(false);
this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_before', data: [the_node_before.id, nodeid, topic, data, dir], node: nodeid });
}
return node;
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
insert_node_after: function (node_after, nodeid, topic, data, direction) {
if (this.get_editable()) {
var the_node_after = this.get_node(node_after);
var dir = jm.direction.of(direction)
if (dir === undefined) {
dir = this.layout.calculate_next_child_direction(the_node_after.parent);
}
var node = this.mind.insert_node_after(the_node_after, nodeid, topic, data, dir);
if (!!node) {
this.view.add_node(node);
this.layout.layout();
this.view.show(false);
this.invoke_event_handle(jm.event_type.edit, { evt: 'insert_node_after', data: [the_node_after.id, nodeid, topic, data, dir], node: nodeid });
}
return node;
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
remove_node: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return false;
} else {
return this.remove_node(the_node);
}
}
if (this.get_editable()) {
if (node.isroot) {
logger.error('fail, can not remove root node');
return false;
}
var nodeid = node.id;
var parentid = node.parent.id;
var parent_node = this.get_node(parentid);
this.view.save_location(parent_node);
this.view.remove_node(node);
this.mind.remove_node(node);
this.layout.layout();
this.view.show(false);
this.view.restore_location(parent_node);
this.invoke_event_handle(jm.event_type.edit, { evt: 'remove_node', data: [nodeid], node: parentid });
return true;
} else {
logger.error('fail, this mind map is not editable');
return false;
}
},
update_node: function (nodeid, topic) {
if (this.get_editable()) {
if (jm.util.text.is_empty(topic)) {
logger.warn('fail, topic can not be empty');
return;
}
var node = this.get_node(nodeid);
if (!!node) {
if (node.topic === topic) {
logger.info('nothing changed');
this.view.update_node(node);
return;
}
node.topic = topic;
this.view.update_node(node);
this.layout.layout();
this.view.show(false);
this.invoke_event_handle(jm.event_type.edit, { evt: 'update_node', data: [nodeid, topic], node: nodeid });
}
} else {
logger.error('fail, this mind map is not editable');
return;
}
},
move_node: function (nodeid, beforeid, parentid, direction) {
if (this.get_editable()) {
var node = this.get_node(nodeid);
var updated_node = this.mind.move_node(node, beforeid, parentid, direction);
if (!!updated_node) {
this.view.update_node(updated_node);
this.layout.layout();
this.view.show(false);
this.invoke_event_handle(jm.event_type.edit, { evt: 'move_node', data: [nodeid, beforeid, parentid, direction], node: nodeid });
}
} else {
logger.error('fail, this mind map is not editable');
return;
}
},
select_node: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.select_node(the_node);
}
}
if (!this.layout.is_visible(node)) {
return;
}
this.mind.selected = node;
this.view.select_node(node);
this.invoke_event_handle(jm.event_type.select, { evt: 'select_node', data: [], node: node.id });
},
get_selected_node: function () {
if (!!this.mind) {
return this.mind.selected;
} else {
return null;
}
},
select_clear: function () {
if (!!this.mind) {
this.mind.selected = null;
this.view.select_clear();
}
},
is_node_visible: function (node) {
return this.layout.is_visible(node);
},
find_node_before: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.find_node_before(the_node);
}
}
if (node.isroot) { return null; }
var n = null;
if (node.parent.isroot) {
var c = node.parent.children;
var prev = null;
var ni = null;
for (var i = 0; i < c.length; i++) {
ni = c[i];
if (node.direction === ni.direction) {
if (node.id === ni.id) {
n = prev;
}
prev = ni;
}
}
} else {
n = this.mind.get_node_before(node);
}
return n;
},
find_node_after: function (node) {
if (!jm.util.is_node(node)) {
var the_node = this.get_node(node);
if (!the_node) {
logger.error('the node[id=' + node + '] can not be found.');
return;
} else {
return this.find_node_after(the_node);
}
}
if (node.isroot) { return null; }
var n = null;
if (node.parent.isroot) {
var c = node.parent.children;
var getthis = false;
var ni = null;
for (var i = 0; i < c.length; i++) {
ni = c[i];
if (node.direction === ni.direction) {
if (getthis) {
n = ni;
break;
}
if (node.id === ni.id) {
getthis = true;
}
}
}
} else {
n = this.mind.get_node_after(node);
}
return n;
},
set_node_color: function (nodeid, bgcolor, fgcolor) {
if (this.get_editable()) {
var node = this.mind.get_node(nodeid);
if (!!node) {
if (!!bgcolor) {
node.data['background-color'] = bgcolor;
}
if (!!fgcolor) {
node.data['foreground-color'] = fgcolor;
}
this.view.reset_node_custom_style(node);
}
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
set_node_font_style: function (nodeid, size, weight, style) {
if (this.get_editable()) {
var node = this.mind.get_node(nodeid);
if (!!node) {
if (!!size) {
node.data['font-size'] = size;
}
if (!!weight) {
node.data['font-weight'] = weight;
}
if (!!style) {
node.data['font-style'] = style;
}
this.view.reset_node_custom_style(node);
this.view.update_node(node);
this.layout.layout();
this.view.show(false);
}
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
set_node_background_image: function (nodeid, image, width, height, rotation) {
if (this.get_editable()) {
var node = this.mind.get_node(nodeid);
if (!!node) {
if (!!image) {
node.data['background-image'] = image;
}
if (!!width) {
node.data['width'] = width;
}
if (!!height) {
node.data['height'] = height;
}
if (!!rotation) {
node.data['background-rotation'] = rotation;
}
this.view.reset_node_custom_style(node);
this.view.update_node(node);
this.layout.layout();
this.view.show(false);
}
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
set_node_background_rotation: function (nodeid, rotation) {
if (this.get_editable()) {
var node = this.mind.get_node(nodeid);
if (!!node) {
if (!node.data['background-image']) {
logger.error('fail, only can change rotation angle of node with background image');
return null;
}
node.data['background-rotation'] = rotation;
this.view.reset_node_custom_style(node);
this.view.update_node(node);
this.layout.layout();
this.view.show(false);
}
} else {
logger.error('fail, this mind map is not editable');
return null;
}
},
resize: function () {
this.view.resize();
},
// callback(type ,data)
add_event_listener: function (callback) {
if (typeof callback === 'function') {
this.event_handles.push(callback);
}
},
clear_event_listener: function () {
this.event_handles = [];
},
invoke_event_handle: function (type, data) {
var j = this;
$w.setTimeout(function () {
j._invoke_event_handle(type, data);
}, 0);
},
_invoke_event_handle: function (type, data) {
var l = this.event_handles.length;
for (var i = 0; i < l; i++) {
this.event_handles[i](type, data);
}
},
};
// ============= data provider =============================================
jm.data_provider = function (jm) {
this.jm = jm;
};
jm.data_provider.prototype = {
init: function () {
logger.debug('data.init');
},
reset: function () {
logger.debug('data.reset');
},
load: function (mind_data) {
var df = null;
var mind = null;
if (typeof mind_data === 'object') {
if (!!mind_data.format) {
df = mind_data.format;
} else {
df = 'node_tree';
}
} else {
df = 'freemind';
}
if (df == 'node_array') {
mind = jm.format.node_array.get_mind(mind_data);
} else if (df == 'node_tree') {
mind = jm.format.node_tree.get_mind(mind_data);
} else if (df == 'freemind') {
mind = jm.format.freemind.get_mind(mind_data);
} else {
logger.warn('unsupported format');
}
return mind;
},
get_data: function (data_format) {
var data = null;
if (data_format == 'node_array') {
data = jm.format.node_array.get_data(this.jm.mind);
} else if (data_format == 'node_tree') {
data = jm.format.node_tree.get_data(this.jm.mind);
} else if (data_format == 'freemind') {
data = jm.format.freemind.get_data(this.jm.mind);
} else {
logger.error('unsupported ' + data_format + ' format');
}
return data;
},
};
// ============= layout provider ===========================================
jm.layout_provider = function (jm, options) {
this.opts = options;
this.jm = jm;
this.isside = (this.opts.mode == 'side');
this.bounds = null;
this.cache_valid = false;
};
jm.layout_provider.prototype = {
init: function () {
logger.debug('layout.init');
},
reset: function () {
logger.debug('layout.reset');
this.bounds = { n: 0, s: 0, w: 0, e: 0 };
},
calculate_next_child_direction: function (node) {
if (this.isside) {
return jm.direction.right;
}
var children = node.children || [];
var children_len = children.length;
var r = 0;
for (var i = 0; i < children_len; i++) { if (children[i].direction === jm.direction.left) { r--; } else { r++; } }
return (children_len > 1 && r > 0) ? jm.direction.left : jm.direction.right;
},
layout: function () {
logger.debug('layout.layout');
this.layout_direction();
this.layout_offset();
},
layout_direction: function () {
this._layout_direction_root();
},
_layout_direction_root: function () {
var node = this.jm.mind.root;
// logger.debug(node);
var layout_data = null;
if ('layout' in node._data) {
layout_data = node._data.layout;
} else {
layout_data = {};
node._data.layout = layout_data;
}
var children = node.children;
var children_count = children.length;
layout_data.direction = jm.direction.center;
layout_data.side_index = 0;
if (this.isside) {
var i = children_count;
while (i--) {
this._layout_direction_side(children[i], jm.direction.right, i);
}
} else {
var i = children_count;
var subnode = null;
while (i--) {
subnode = children[i];
if (subnode.direction == jm.direction.left) {
this._layout_direction_side(subnode, jm.direction.left, i);
} else {
this._layout_direction_side(subnode, jm.direction.right, i);
}
}
/*
var boundary = Math.ceil(children_count/2);
var i = children_count;
while(i--){
if(i>=boundary){
this._layout_direction_side(children[i],jm.direction.left, children_count-i-1);
}else{
this._layout_direction_side(children[i],jm.direction.right, i);
}
}*/
}
},
_layout_direction_side: function (node, direction, side_index) {
var layout_data = null;
if ('layout' in node._data) {
layout_data = node._data.layout;
} else {
layout_data = {};
node._data.layout = layout_data;
}
var children = node.children;
var children_count = children.length;
layout_data.direction = direction;
layout_data.side_index = side_index;
var i = children_count;
while (i--) {
this._layout_direction_side(children[i], direction, i);
}
},
layout_offset: function () {
var node = this.jm.mind.root;
var layout_data = node._data.layout;
layout_data.offset_x = 0;
layout_data.offset_y = 0;
layout_data.outer_height = 0;
var children = node.children;
var i = children.length;
var left_nodes = [];
var right_nodes = [];
var subnode = null;
while (i--) {
subnode = children[i];
if (subnode._data.layout.direction == jm.direction.right) {
right_nodes.unshift(subnode);
} else {
left_nodes.unshift(subnode);
}
}
layout_data.left_nodes = left_nodes;
layout_data.right_nodes = right_nodes;
layout_data.outer_height_left = this._layout_offset_subnodes(left_nodes);
layout_data.outer_height_right = this._layout_offset_subnodes(right_nodes);
this.bounds.e = node._data.view.width / 2;
this.bounds.w = 0 - this.bounds.e;
//logger.debug(this.bounds.w);
this.bounds.n = 0;
this.bounds.s = Math.max(layout_data.outer_height_left, layout_data.outer_height_right);
},
// layout both the x and y axis
_layout_offset_subnodes: function (nodes) {
var total_height = 0;
var nodes_count = nodes.length;
var i = nodes_count;
var node = null;
var node_outer_height = 0;
var layout_data = null;
var base_y = 0;
var pd = null; // parent._data
while (i--) {
node = nodes[i];
layout_data = node._data.layout;
if (pd == null) {
pd = node.parent._data;
}
node_outer_height = this._layout_offset_subnodes(node.children);
if (!node.expanded) {
node_outer_height = 0;
this.set_visible(node.children, false);
}
node_outer_height = Math.max(node._data.view.height, node_outer_height);
if (node.children.length > 1) {
node_outer_height += this.opts.cousin_space;
}
layout_data.outer_height = node_outer_height;
layout_data.offset_y = base_y - node_outer_height / 2;
layout_data.offset_x = this.opts.hspace * layout_data.direction + pd.view.width * (pd.layout.direction + layout_data.direction) / 2;
if (!node.parent.isroot) {
layout_data.offset_x += this.opts.pspace * layout_data.direction;
}
base_y = base_y - node_outer_height - this.opts.vspace;
total_height += node_outer_height;
}
if (nodes_count > 1) {
total_height += this.opts.vspace * (nodes_count - 1);
}
i = nodes_count;
var middle_height = total_height / 2;
while (i--) {
node = nodes[i];
node._data.layout.offset_y += middle_height;
}
return total_height;
},
// layout the y axis only, for collapse/expand a node
_layout_offset_subnodes_height: function (nodes) {
var total_height = 0;
var nodes_count = nodes.length;
var i = nodes_count;
var node = null;
var node_outer_height = 0;
var layout_data = null;
var base_y = 0;
var pd = null; // parent._data
while (i--) {
node = nodes[i];
layout_data = node._data.layout;
if (pd == null) {
pd = node.parent._data;
}
node_outer_height = this._layout_offset_subnodes_height(node.children);
if (!node.expanded) {
node_outer_height = 0;
}
node_outer_height = Math.max(node._data.view.height, node_outer_height);
if (node.children.length > 1) {
node_outer_height += this.opts.cousin_space;
}
layout_data.outer_height = node_outer_height;
layout_data.offset_y = base_y - node_outer_height / 2;
base_y = base_y - node_outer_height - this.opts.vspace;
total_height += node_outer_height;
}
if (nodes_count > 1) {
total_height += this.opts.vspace * (nodes_count - 1);
}
i = nodes_count;
var middle_height = total_height / 2;
while (i--) {
node = nodes[i];
node._data.layout.offset_y += middle_height;
//logger.debug(node.topic);
//logger.debug(node._data.layout.offset_y);
}
return total_height;
},
get_node_offset: function (node) {
var layout_data = node._data.layout;
var offset_cache = null;
if (('_offset_' in layout_data) && this.cache_valid) {
offset_cache = layout_data._offset_;
} else {
offset_cache = { x: -1, y: -1 };
layout_data._offset_ = offset_cache;
}
if (offset_cache.x == -1 || offset_cache.y == -1) {
var x = layout_data.offset_x;
var y = layout_data.offset_y;
if (!node.isroot) {
var offset_p = this.get_node_offset(node.parent);
x += offset_p.x;
y += offset_p.y;
}
offset_cache.x = x;
offset_cache.y = y;
}
return offset_cache;
},
get_node_point: function (node) {
var view_data = node._data.view;
var offset_p = this.get_node_offset(node);
//logger.debug(offset_p);
var p = {};
p.x = offset_p.x + view_data.width * (node._data.layout.direction - 1) / 2;
p.y = offset_p.y - view_data.height / 2;
//logger.debug(p);
return p;
},
get_node_point_in: function (node) {
var p = this.get_node_offset(node);
return p;
},
get_node_point_out: function (node) {
var layout_data = node._data.layout;
var pout_cache = null;
if (('_pout_' in layout_data) && this.cache_valid) {
pout_cache = layout_data._pout_;
} else {
pout_cache = { x: -1, y: -1 };
layout_data._pout_ = pout_cache;
}
if (pout_cache.x == -1 || pout_cache.y == -1) {
if (node.isroot) {
pout_cache.x = 0;
pout_cache.y = 0;
} else {
var view_data = node._data.view;
var offset_p = this.get_node_offset(node);
pout_cache.x = offset_p.x + (view_data.width + this.opts.pspace) * node._data.layout.direction;
pout_cache.y = offset_p.y;
//logger.debug('pout');
//logger.debug(pout_cache);
}
}
return pout_cache;
},
get_expander_point: function (node) {
var p = this.get_node_point_out(node);
var ex_p = {};
if (node._data.layout.direction == jm.direction.right) {
ex_p.x = p.x - this.opts.pspace;
} else {
ex_p.x = p.x;
}
ex_p.y = p.y - Math.ceil(this.opts.pspace / 2);
return ex_p;
},
get_min_size: function () {
var nodes = this.jm.mind.nodes;
var node = null;
var pout = null;
for (var nodeid in nodes) {
node = nodes[nodeid];
pout = this.get_node_point_out(node);
if (pout.x > this.bounds.e) { this.bounds.e = pout.x; }
if (pout.x < this.bounds.w) { this.bounds.w = pout.x; }
}
return {
w: this.bounds.e - this.bounds.w,
h: this.bounds.s - this.bounds.n
}
},
toggle_node: function (node) {
if (node.isroot) {
return;
}
if (node.expanded) {
this.collapse_node(node);
} else {
this.expand_node(node);
}
},
expand_node: function (node) {
node.expanded = true;
this.part_layout(node);
this.set_visible(node.children, true);
this.jm.invoke_event_handle(jm.event_type.show, { evt: 'expand_node', data: [], node: node.id });
},
collapse_node: function (node) {
node.expanded = false;
this.part_layout(node);
this.set_visible(node.children, false);
this.jm.invoke_event_handle(jm.event_type.show, { evt: 'collapse_node', data: [], node: node.id });
},
expand_all: function () {
var nodes = this.jm.mind.nodes;
var c = 0;
var node;
for (var nodeid in nodes) {
node = nodes[nodeid];
if (!node.expanded) {
node.expanded = true;
c++;
}
}
if (c > 0) {
var root = this.jm.mind.root;
this.part_layout(root);
this.set_visible(root.children, true);
}
},
collapse_all: function () {
var nodes = this.jm.mind.nodes;
var c = 0;
var node;
for (var nodeid in nodes) {
node = nodes[nodeid];
if (node.expanded && !node.isroot) {
node.expanded = false;
c++;
}
}
if (c > 0) {
var root = this.jm.mind.root;
this.part_layout(root);
this.set_visible(root.children, true);
}
},
expand_to_depth: function (target_depth, curr_nodes, curr_depth) {
if (target_depth < 1) { return; }
var nodes = curr_nodes || this.jm.mind.root.children;
var depth = curr_depth || 1;
var i = nodes.length;
var node = null;
while (i--) {
node = nodes[i];
if (depth < target_depth) {
if (!node.expanded) {
this.expand_node(node);
}
this.expand_to_depth(target_depth, node.children, depth + 1);
}
if (depth == target_depth) {
if (node.expanded) {
this.collapse_node(node);
}
}
}
},
part_layout: function (node) {
var root = this.jm.mind.root;
if (!!root) {
var root_layout_data = root._data.layout;
if (node.isroot) {
root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
} else {
if (node._data.layout.direction == jm.direction.right) {
root_layout_data.outer_height_right = this._layout_offset_subnodes_height(root_layout_data.right_nodes);
} else {
root_layout_data.outer_height_left = this._layout_offset_subnodes_height(root_layout_data.left_nodes);
}
}
this.bounds.s = Math.max(root_layout_data.outer_height_left, root_layout_data.outer_height_right);
this.cache_valid = false;
} else {
logger.warn('can not found root node');
}
},
set_visible: function (nodes, visible) {
var i = nodes.length;
var node = null;
var layout_data = null;
while (i--) {
node = nodes[i];
layout_data = node._data.layout;
if (node.expanded) {
this.set_visible(node.children, visible);
} else {
this.set_visible(node.children, false);
}
if (!node.isroot) {
node._data.layout.visible = visible;
}
}
},
is_expand: function (node) {
return node.expanded;
},
is_visible: function (node) {
var layout_data = node._data.layout;
if (('visible' in layout_data) && !layout_data.visible) {
return false;
} else {
return true;
}
}
};
jm.graph_canvas = function (view) {
this.opts = view.opts;
this.e_canvas = $c('canvas');
this.e_canvas.className = 'jsmind';
this.canvas_ctx = this.e_canvas.getContext('2d');
this.size = { w: 0, h: 0 };
};
jm.graph_canvas.prototype = {
element: function () {
return this.e_canvas;
},
set_size: function (w, h) {
this.size.w = w;
this.size.h = h;
this.e_canvas.width = w;
this.e_canvas.height = h;
},
clear: function () {
this.canvas_ctx.clearRect(0, 0, this.size.w, this.size.h);
},
draw_line: function (pout, pin, offset) {
var ctx = this.canvas_ctx;
ctx.strokeStyle = this.opts.line_color;
ctx.lineWidth = this.opts.line_width;
ctx.lineCap = 'round';
this._bezier_to(ctx,
pin.x + offset.x,
pin.y + offset.y,
pout.x + offset.x,
pout.y + offset.y);
},
copy_to: function (dest_canvas_ctx, callback) {
dest_canvas_ctx.drawImage(this.e_canvas, 0, 0);
!!callback && callback();
},
_bezier_to: function (ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(x1 + (x2 - x1) * 2 / 3, y1, x1, y2, x2, y2);
ctx.stroke();
},
_line_to: function (ctx, x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
};
jm.graph_svg = function (view) {
this.view = view;
this.opts = view.opts;
this.e_svg = jm.graph_svg.c('svg');
this.e_svg.setAttribute('class', 'jsmind');
this.size = { w: 0, h: 0 };
this.lines = [];
};
jm.graph_svg.c = function (tag) {
return $d.createElementNS('http://www.w3.org/2000/svg', tag);
};
jm.graph_svg.prototype = {
element: function () {
return this.e_svg;
},
set_size: function (w, h) {
this.size.w = w;
this.size.h = h;
this.e_svg.setAttribute('width', w);
this.e_svg.setAttribute('height', h);
},
clear: function () {
var len = this.lines.length;
while (len--) {
this.e_svg.removeChild(this.lines[len]);
}
this.lines.length = 0;
},
draw_line: function (pout, pin, offset) {
var line = jm.graph_svg.c('path');
line.setAttribute('stroke', this.opts.line_color);
line.setAttribute('stroke-width', this.opts.line_width);
line.setAttribute('fill', 'transparent');
this.lines.push(line);
this.e_svg.appendChild(line);
this._bezier_to(line, pin.x + offset.x, pin.y + offset.y, pout.x + offset.x, pout.y + offset.y);
},
copy_to: function (dest_canvas_ctx, callback) {
var img = new Image();
img.onload = function () {
dest_canvas_ctx.drawImage(img, 0, 0);
!!callback && callback();
}
img.src = 'data:image/svg+xml;base64,' + btoa(new XMLSerializer().serializeToString(this.e_svg));
},
_bezier_to: function (path, x1, y1, x2, y2) {
path.setAttribute('d', 'M' + x1 + ' ' + y1 + ' C ' + (x1 + (x2 - x1) * 2 / 3) + ' ' + y1 + ', ' + x1 + ' ' + y2 + ', ' + x2 + ' ' + y2);
},
_line_to: function (path, x1, y1, x2, y2) {
path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2);
}
};
// view provider
jm.view_provider = function (jm, options) {
this.opts = options;
this.jm = jm;
this.layout = jm.layout;
this.container = null;
this.e_panel = null;
this.e_nodes = null;
this.size = { w: 0, h: 0 };
this.selected_node = null;
this.editing_node = null;
this.graph = null;
};
jm.view_provider.prototype = {
init: function () {
logger.debug('view.init');
this.container = $i(this.opts.container) ? this.opts.container : $g(this.opts.container);
if (!this.container) {
logger.error('the options.view.container was not be found in dom');
return;
}
this.e_panel = $c('div');
this.e_nodes = $c('jmnodes');
this.e_editor = $c('input');
this.graph = this.opts.engine.toLowerCase() === 'svg' ? new jm.graph_svg(this) : new jm.graph_canvas(this);
this.e_panel.className = 'jsmind-inner jmnode-overflow-' + this.opts.node_overflow;
this.e_panel.tabIndex = 1;
this.e_panel.appendChild(this.graph.element());
this.e_panel.appendChild(this.e_nodes);
this.e_editor.className = 'jsmind-editor';
this.e_editor.type = 'text';
this.actualZoom = 1;
this.zoomStep = 0.1;
this.minZoom = 0.5;
this.maxZoom = 2;
var v = this;
jm.util.dom.add_event(this.e_editor, 'keydown', function (e) {
var evt = e || event;
if (evt.keyCode == 13) { v.edit_node_end(); evt.stopPropagation(); }
});
jm.util.dom.add_event(this.e_editor, 'blur', function (e) {
v.edit_node_end();
});
this.container.appendChild(this.e_panel);
// Used to avoid dragging, while editing node.
this.dragging_enabled = true
this.draggable_canvas()
},
add_event: function (obj, event_name, event_handle) {
jm.util.dom.add_event(this.e_nodes, event_name, function (e) {
var evt = e || event;
event_handle.call(obj, evt);
});
},
get_binded_nodeid: function (element) {
if (element == null) {
return null;
}
var tagName = element.tagName.toLowerCase();
if (tagName == 'jmnode' || tagName == 'jmexpander') {
return element.getAttribute('nodeid');
} else if (tagName == 'jmnodes' || tagName == 'body' || tagName == 'html') {
return null;
} else {
return this.get_binded_nodeid(element.parentElement);
}
},
is_node: function (element) {
if (element == null) {
return false;
}
var tagName = element.tagName.toLowerCase();
if (tagName == 'jmnode') {
return true;
} else if (tagName == 'jmnodes' || tagName == 'body' || tagName == 'html') {
return false;
} else {
return this.is_node(element.parentElement);
}
},
is_expander: function (element) {
return (element.tagName.toLowerCase() == 'jmexpander');
},
reset: function () {
logger.debug('view.reset');
this.selected_node = null;
this.clear_lines();
this.clear_nodes();
this.reset_theme();
},
reset_theme: function () {
var theme_name = this.jm.options.theme;
if (!!theme_name) {
this.e_nodes.className = 'theme-' + theme_name;
} else {
this.e_nodes.className = '';
}
},
reset_custom_style: function () {
var nodes = this.jm.mind.nodes;
for (var nodeid in nodes) {
this.reset_node_custom_style(nodes[nodeid]);
}
},
load: function () {
logger.debug('view.load');
this.init_nodes();
},
expand_size: function () {
var min_size = this.layout.get_min_size();
var min_width = min_size.w + this.opts.hmargin * 2;
var min_height = min_size.h + this.opts.vmargin * 2;
var client_w = this.e_panel.clientWidth;
var client_h = this.e_panel.clientHeight;
if (client_w < min_width) { client_w = min_width; }
if (client_h < min_height) { client_h = min_height; }
this.size.w = client_w;
this.size.h = client_h;
},
init_nodes_size: function (node) {
var view_data = node._data.view;
view_data.width = view_data.element.clientWidth;
view_data.height = view_data.element.clientHeight;
},
init_nodes: function () {
var nodes = this.jm.mind.nodes;
var doc_frag = $d.createDocumentFragment();
for (var nodeid in nodes) {
this.create_node_element(nodes[nodeid], doc_frag);
}
this.e_nodes.appendChild(doc_frag);
for (var nodeid in nodes) {
this.init_nodes_size(nodes[nodeid]);
}
},
add_node: function (node) {
this.create_node_element(node, this.e_nodes);
this.init_nodes_size(node);
},
create_node_element: function (node, parent_node) {
var view_data = null;
if ('view' in node._data) {
view_data = node._data.view;
} else {
view_data = {};
node._data.view = view_data;
}
var d = $c('jmnode');
if (node.isroot) {
d.className = 'root';
} else {
var d_e = $c('jmexpander');
$t(d_e, '-');
d_e.setAttribute('nodeid', node.id);
d_e.style.visibility = 'hidden';
parent_node.appendChild(d_e);
view_data.expander = d_e;
}
if (!!node.topic) {
if (this.opts.support_html) {
$h(d, node.topic);
} else {
$t(d, node.topic);
}
}
d.setAttribute('nodeid', node.id);
d.style.visibility = 'hidden';
this._reset_node_custom_style(d, node.data);
parent_node.appendChild(d);
view_data.element = d;
},
remove_node: function (node) {
if (this.selected_node != null && this.selected_node.id == node.id) {
this.selected_node = null;
}
if (this.editing_node != null && this.editing_node.id == node.id) {
node._data.view.element.removeChild(this.e_editor);
this.editing_node = null;
}
var children = node.children;
var i = children.length;
while (i--) {
this.remove_node(children[i]);
}
if (node._data.view) {
var element = node._data.view.element;
var expander = node._data.view.expander;
this.e_nodes.removeChild(element);
this.e_nodes.removeChild(expander);
node._data.view.element = null;
node._data.view.expander = null;
}
},
update_node: function (node) {
var view_data = node._data.view;
var element = view_data.element;
if (!!node.topic) {
if (this.opts.support_html) {
$h(element, node.topic);
} else {
$t(element, node.topic);
}
}
if (this.layout.is_visible(node)) {
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
} else {
let origin_style = element.getAttribute('style');
element.style = 'visibility: visible; left:0; top:0;';
view_data.width = element.clientWidth;
view_data.height = element.clientHeight;
element.style = origin_style;
}
},
select_node: function (node) {
if (!!this.selected_node) {
this.selected_node._data.view.element.className =
this.selected_node._data.view.element.className.replace(/\s*selected\b/i, '');
this.reset_node_custom_style(this.selected_node);
}
if (!!node) {
this.selected_node = node;
node._data.view.element.className += ' selected';
this.clear_node_custom_style(node);
}
},
select_clear: function () {
this.select_node(null);
},
get_editing_node: function () {
return this.editing_node;
},
is_editing: function () {
return (!!this.editing_node);
},
edit_node_begin: function (node) {
if (!node.topic) {
logger.warn("don't edit image nodes");
return;
}
if (this.editing_node != null) {
this.edit_node_end();
}
this.editing_node = node;
var view_data = node._data.view;
var element = view_data.element;
var topic = node.topic;
var ncs = getComputedStyle(element);
this.e_editor.value = topic;
this.e_editor.style.width = (element.clientWidth - parseInt(ncs.getPropertyValue('padding-left')) - parseInt(ncs.getPropertyValue('padding-right'))) + 'px';
element.innerHTML = '';
element.appendChild(this.e_editor);
element.style.zIndex = 5;
this.e_editor.focus();
this.e_editor.select();
},
edit_node_end: function () {
if (this.editing_node != null) {
var node = this.editing_node;
this.editing_node = null;
var view_data = node._data.view;
var element = view_data.element;
var topic = this.e_editor.value;
element.style.zIndex = 'auto';
element.removeChild(this.e_editor);
if (jm.util.text.is_empty(topic) || node.topic === topic) {
if (this.opts.support_html) {
$h(element, node.topic);
} else {
$t(element, node.topic);
}
} else {
this.jm.update_node(node.id, topic);
}
}
this.e_panel.focus();
},
get_view_offset: function () {
var bounds = this.layout.bounds;
var _x = (this.size.w - bounds.e - bounds.w) / 2;
var _y = this.size.h / 2;
return { x: _x, y: _y };
},
resize: function () {
this.graph.set_size(1, 1);
this.e_nodes.style.width = '1px';
this.e_nodes.style.height = '1px';
this.expand_size();
this._show();
},
_show: function () {
this.graph.set_size(this.size.w, this.size.h);
this.e_nodes.style.width = this.size.w + 'px';
this.e_nodes.style.height = this.size.h + 'px';
this.show_nodes();
this.show_lines();
//this.layout.cache_valid = true;
this.jm.invoke_event_handle(jm.event_type.resize, { data: [] });
},
zoomIn: function () {
return this.setZoom(this.actualZoom + this.zoomStep);
},
zoomOut: function () {
return this.setZoom(this.actualZoom - this.zoomStep);
},
setZoom: function (zoom) {
if ((zoom < this.minZoom) || (zoom > this.maxZoom)) {
return false;
}
this.actualZoom = zoom;
for (var i = 0; i < this.e_panel.children.length; i++) {
this.e_panel.children[i].style.zoom = zoom;
};
this.show(true);
return true;
},
_center_root: function () {
// center root node
var outer_w = this.e_panel.clientWidth;
var outer_h = this.e_panel.clientHeight;
if (this.size.w > outer_w) {
var _offset = this.get_view_offset();
this.e_panel.scrollLeft = _offset.x * this.actualZoom - outer_w / 2;
}
if (this.size.h > outer_h) {
this.e_panel.scrollTop = (this.size.h * this.actualZoom - outer_h) / 2;
}
},
show: function (keep_center) {
logger.debug('view.show');
this.expand_size();
this._show();
if (!!keep_center) {
this._center_root();
}
},
relayout: function () {
this.expand_size();
this._show();
},
save_location: function (node) {
var vd = node._data.view;
vd._saved_location = {
x: parseInt(vd.element.style.left) - this.e_panel.scrollLeft,
y: parseInt(vd.element.style.top) - this.e_panel.scrollTop,
};
},
restore_location: function (node) {
var vd = node._data.view;
this.e_panel.scrollLeft = parseInt(vd.element.style.left) - vd._saved_location.x;
this.e_panel.scrollTop = parseInt(vd.element.style.top) - vd._saved_location.y;
},
clear_nodes: function () {
var mind = this.jm.mind;
if (mind == null) {
return;
}
var nodes = mind.nodes;
var node = null;
for (var nodeid in nodes) {
node = nodes[nodeid];
node._data.view.element = null;
node._data.view.expander = null;
}
this.e_nodes.innerHTML = '';
},
show_nodes: function () {
var nodes = this.jm.mind.nodes;
var node = null;
var node_element = null;
var expander = null;
var p = null;
var p_expander = null;
var expander_text = '-';
var view_data = null;
var _offset = this.get_view_offset();
for (var nodeid in nodes) {
node = nodes[nodeid];
view_data = node._data.view;
node_element = view_data.element;
expander = view_data.expander;
if (!this.layout.is_visible(node)) {
node_element.style.display = 'none';
expander.style.display = 'none';
continue;
}
this.reset_node_custom_style(node);
p = this.layout.get_node_point(node);
view_data.abs_x = _offset.x + p.x;
view_data.abs_y = _offset.y + p.y;
node_element.style.left = (_offset.x + p.x) + 'px';
node_element.style.top = (_offset.y + p.y) + 'px';
node_element.style.display = '';
node_element.style.visibility = 'visible';
if (!node.isroot && node.children.length > 0) {
expander_text = node.expanded ? '-' : '+';
p_expander = this.layout.get_expander_point(node);
expander.style.left = (_offset.x + p_expander.x) + 'px';
expander.style.top = (_offset.y + p_expander.y) + 'px';
expander.style.display = '';
expander.style.visibility = 'visible';
$t(expander, expander_text);
}
// hide expander while all children have been removed
if (!node.isroot && node.children.length == 0) {
expander.style.display = 'none';
expander.style.visibility = 'hidden';
}
}
},
reset_node_custom_style: function (node) {
this._reset_node_custom_style(node._data.view.element, node.data);
},
_reset_node_custom_style: function (node_element, node_data) {
if ('background-color' in node_data) {
node_element.style.backgroundColor = node_data['background-color'];
}
if ('foreground-color' in node_data) {
node_element.style.color = node_data['foreground-color'];
}
if ('width' in node_data) {
node_element.style.width = node_data['width'] + 'px';
}
if ('height' in node_data) {
node_element.style.height = node_data['height'] + 'px';
}
if ('font-size' in node_data) {
node_element.style.fontSize = node_data['font-size'] + 'px';
}
if ('font-weight' in node_data) {
node_element.style.fontWeight = node_data['font-weight'];
}
if ('font-style' in node_data) {
node_element.style.fontStyle = node_data['font-style'];
}
if ('background-image' in node_data) {
var backgroundImage = node_data['background-image'];
if (backgroundImage.startsWith('data') && node_data['width'] && node_data['height']) {
var img = new Image();
img.onload = function () {
var c = $c('canvas');
c.width = node_element.clientWidth;
c.height = node_element.clientHeight;
var img = this;
if (c.getContext) {
var ctx = c.getContext('2d');
ctx.drawImage(img, 2, 2, node_element.clientWidth, node_element.clientHeight);
var scaledImageData = c.toDataURL();
node_element.style.backgroundImage = 'url(' + scaledImageData + ')';
}
};
img.src = backgroundImage;
} else {
node_element.style.backgroundImage = 'url(' + backgroundImage + ')';
}
node_element.style.backgroundSize = '99%';
if ('background-rotation' in node_data) {
node_element.style.transform = 'rotate(' + node_data['background-rotation'] + 'deg)';
}
}
},
clear_node_custom_style: function (node) {
var node_element = node._data.view.element;
node_element.style.backgroundColor = "";
node_element.style.color = "";
},
clear_lines: function () {
this.graph.clear();
},
show_lines: function () {
this.clear_lines();
var nodes = this.jm.mind.nodes;
var node = null;
var pin = null;
var pout = null;
var _offset = this.get_view_offset();
for (var nodeid in nodes) {
node = nodes[nodeid];
if (!!node.isroot) { continue; }
if (('visible' in node._data.layout) && !node._data.layout.visible) { continue; }
pin = this.layout.get_node_point_in(node);
pout = this.layout.get_node_point_out(node.parent);
this.graph.draw_line(pout, pin, _offset);
}
},
// Drag the whole mind map with your mouse (usefull when it's larger that the container).
draggable_canvas: function () {
// If draggable option is true.
if (this.opts.draggable) {
// Dragging disabled by default.
let dragging = false
let x, y
if (this.opts.hide_scrollbars_when_draggable) {
// Avoid scrollbars when mind map is larger than the container (e_panel = id jsmind-inner)
this.e_panel.style = 'overflow: hidden'
}
// Move the whole mind map with mouse moves, while button is down.
jm.util.dom.add_event(this.container, 'mousedown', (eventDown) => {
dragging = true
// Record current mouse position.
x = eventDown.clientX
y = eventDown.clientY
})
// Stop moving mind map once mouse button is released.
jm.util.dom.add_event(this.container, 'mouseup', () => {
dragging = false
})
// Follow current mouse position and move mind map accordingly.
jm.util.dom.add_event(this.container, 'mousemove', (eventMove) => {
if (this.dragging_enabled && dragging) {
this.e_panel.scrollBy(x - eventMove.clientX, y - eventMove.clientY)
// Record new current position.
x = eventMove.clientX
y = eventMove.clientY
}
})
}
},
get_draggable_canvas: function () {
return this.opts.draggable
},
enable_draggable_canvas: function () {
this.dragging_enabled = true
},
disable_draggable_canvas: function () {
this.dragging_enabled = false
},
};
// shortcut provider
jm.shortcut_provider = function (jm, options) {
this.jm = jm;
this.opts = options;
this.mapping = options.mapping;
this.handles = options.handles;
this._newid = null;
this._mapping = {};
};
jm.shortcut_provider.prototype = {
init: function () {
jm.util.dom.add_event(this.jm.view.e_panel, 'keydown', this.handler.bind(this));
this.handles['addchild'] = this.handle_addchild;
this.handles['addbrother'] = this.handle_addbrother;
this.handles['editnode'] = this.handle_editnode;
this.handles['delnode'] = this.handle_delnode;
this.handles['toggle'] = this.handle_toggle;
this.handles['up'] = this.handle_up;
this.handles['down'] = this.handle_down;
this.handles['left'] = this.handle_left;
this.handles['right'] = this.handle_right;
for (var handle in this.mapping) {
if (!!this.mapping[handle] && (handle in this.handles)) {
var keys = this.mapping[handle];
if(!Array.isArray(keys)){
keys = [keys]
}
for(let key of keys){
this._mapping[key] = this.handles[handle];
}
}
}
if (typeof this.opts.id_generator === 'function') {
this._newid = this.opts.id_generator;
} else {
this._newid = jm.util.uuid.newid;
}
},
enable_shortcut: function () {
this.opts.enable = true;
},
disable_shortcut: function () {
this.opts.enable = false;
},
handler: function (e) {
if (e.which == 9) { e.preventDefault(); } //prevent tab to change focus in browser
if (this.jm.view.is_editing()) { return; }
var evt = e || event;
if (!this.opts.enable) { return true; }
var kc = evt.keyCode + (evt.metaKey << 13) + (evt.ctrlKey << 12) + (evt.altKey << 11) + (evt.shiftKey << 10);
if (kc in this._mapping) {
this._mapping[kc].call(this, this.jm, e);
}
},
handle_addchild: function (_jm, e) {
var selected_node = _jm.get_selected_node();
if (!!selected_node) {
var nodeid = this._newid();
var node = _jm.add_node(selected_node, nodeid, 'New Node');
if (!!node) {
_jm.select_node(nodeid);
_jm.begin_edit(nodeid);
}
}
},
handle_addbrother: function (_jm, e) {
var selected_node = _jm.get_selected_node();
if (!!selected_node && !selected_node.isroot) {
var nodeid = this._newid();
var node = _jm.insert_node_after(selected_node, nodeid, 'New Node');
if (!!node) {
_jm.select_node(nodeid);
_jm.begin_edit(nodeid);
}
}
},
handle_editnode: function (_jm, e) {
var selected_node = _jm.get_selected_node();
if (!!selected_node) {
_jm.begin_edit(selected_node);
}
},
handle_delnode: function (_jm, e) {
var selected_node = _jm.get_selected_node();
if (!!selected_node && !selected_node.isroot) {
_jm.select_node(selected_node.parent);
_jm.remove_node(selected_node);
}
},
handle_toggle: function (_jm, e) {
var evt = e || event;
var selected_node = _jm.get_selected_node();
if (!!selected_node) {
_jm.toggle_node(selected_node.id);
evt.stopPropagation();
evt.preventDefault();
}
},
handle_up: function (_jm, e) {
var evt = e || event;
var selected_node = _jm.get_selected_node();
if (!!selected_node) {
var up_node = _jm.find_node_before(selected_node);
if (!up_node) {
var np = _jm.find_node_before(selected_node.parent);
if (!!np && np.children.length > 0) {
up_node = np.children[np.children.length - 1];
}
}
if (!!up_node) {
_jm.select_node(up_node);
}
evt.stopPropagation();
evt.preventDefault();
}
},
handle_down: function (_jm, e) {
var evt = e || event;
var selected_node = _jm.get_selected_node();
if (!!selected_node) {
var down_node = _jm.find_node_after(selected_node);
if (!down_node) {
var np = _jm.find_node_after(selected_node.parent);
if (!!np && np.children.length > 0) {
down_node = np.children[0];
}
}
if (!!down_node) {
_jm.select_node(down_node);
}
evt.stopPropagation();
evt.preventDefault();
}
},
handle_left: function (_jm, e) {
this._handle_direction(_jm, e, jm.direction.left);
},
handle_right: function (_jm, e) {
this._handle_direction(_jm, e, jm.direction.right);
},
_handle_direction: function (_jm, e, d) {
var evt = e || event;
var selected_node = _jm.get_selected_node();
var node = null;
if (!!selected_node) {
if (selected_node.isroot) {
var c = selected_node.children;
var children = [];
for (var i = 0; i < c.length; i++) {
if (c[i].direction === d) {
children.push(i);
}
}
node = c[children[Math.floor((children.length - 1) / 2)]];
}
else if (selected_node.direction === d) {
var children = selected_node.children;
var childrencount = children.length;
if (childrencount > 0) {
node = children[Math.floor((childrencount - 1) / 2)];
}
} else {
node = selected_node.parent;
}
if (!!node) {
_jm.select_node(node);
}
evt.stopPropagation();
evt.preventDefault();
}
},
};
// plugin
jm.plugin = function (name, init) {
this.name = name;
this.init = init;
};
jm.plugins = [];
jm.register_plugin = function (plugin) {
if (plugin instanceof jm.plugin) {
jm.plugins.push(plugin);
}
};
jm.init_plugins = function (sender) {
$w.setTimeout(function () {
jm._init_plugins(sender);
}, 0);
};
jm._init_plugins = function (sender) {
var l = jm.plugins.length;
var fn_init = null;
for (var i = 0; i < l; i++) {
fn_init = jm.plugins[i].init;
if (typeof fn_init === 'function') {
fn_init(sender);
}
}
};
jm.show = function (options, mind) {
logger.warn('`jsMind.show(options, mind)` is deprecated, please use `jm = new jsMind(options); jm.show(mind);` instead')
var _jm = new jm(options);
_jm.show(mind);
return _jm;
};
// export jsmind
if (typeof module !== 'undefined' && typeof exports === 'object') {
module.exports = jm;
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
define(function () { return jm; });
} else {
$w[__name__] = jm;
}
})(typeof window !== 'undefined' ? window : global);
//}}}
// // ''jsmind.draggable-node.js''
//{{{
/**
* @license BSD
* @copyright 2014-2023 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
*/
(function ($w) {
'use strict';
var $d = $w.document;
var __name__ = 'jsMind';
var jsMind = $w[__name__];
if (!jsMind) { return; }
if (typeof jsMind.draggable != 'undefined') { return; }
var jdom = jsMind.util.dom;
var clear_selection = 'getSelection' in $w ? function () {
$w.getSelection().removeAllRanges();
} : function () {
$d.selection.empty();
};
var options = {
line_width: 5,
line_color: 'rgba(0,0,0,0.3)',
lookup_delay: 500,
lookup_interval: 80,
scrolling_trigger_width: 20,
scrolling_step_length: 10
};
jsMind.draggable = function (jm) {
this.jm = jm;
this.e_canvas = null;
this.canvas_ctx = null;
this.shadow = null;
this.shadow_w = 0;
this.shadow_h = 0;
this.active_node = null;
this.target_node = null;
this.target_direct = null;
this.client_w = 0;
this.client_h = 0;
this.offset_x = 0;
this.offset_y = 0;
this.hlookup_delay = 0;
this.hlookup_timer = 0;
this.capture = false;
this.moved = false;
this.view_panel = jm.view.e_panel;
this.view_panel_rect = null
};
jsMind.draggable.prototype = {
init: function () {
this._create_canvas();
this._create_shadow();
this._event_bind();
},
resize: function () {
this.jm.view.e_nodes.appendChild(this.shadow);
this.e_canvas.width = this.jm.view.size.w;
this.e_canvas.height = this.jm.view.size.h;
},
_create_canvas: function () {
var c = $d.createElement('canvas');
this.jm.view.e_panel.appendChild(c);
var ctx = c.getContext('2d');
this.e_canvas = c;
this.canvas_ctx = ctx;
},
_create_shadow: function () {
var s = $d.createElement('jmnode');
s.style.visibility = 'hidden';
s.style.zIndex = '3';
s.style.cursor = 'move';
s.style.opacity = '0.7';
this.shadow = s;
},
reset_shadow: function (el) {
var s = this.shadow.style;
this.shadow.innerHTML = el.innerHTML;
s.left = el.style.left;
s.top = el.style.top;
s.width = el.style.width;
s.height = el.style.height;
s.backgroundImage = el.style.backgroundImage;
s.backgroundSize = el.style.backgroundSize;
s.transform = el.style.transform;
this.shadow_w = this.shadow.clientWidth;
this.shadow_h = this.shadow.clientHeight;
},
show_shadow: function () {
if (!this.moved) {
this.shadow.style.visibility = 'visible';
}
},
hide_shadow: function () {
this.shadow.style.visibility = 'hidden';
},
_magnet_shadow: function (node) {
if (!!node) {
this.canvas_ctx.lineWidth = options.line_width;
this.canvas_ctx.strokeStyle = options.line_color;
this.canvas_ctx.lineCap = 'round';
this._clear_lines();
this._canvas_lineto(node.sp.x, node.sp.y, node.np.x, node.np.y);
}
},
_clear_lines: function () {
this.canvas_ctx.clearRect(0, 0, this.jm.view.size.w, this.jm.view.size.h);
},
_canvas_lineto: function (x1, y1, x2, y2) {
this.canvas_ctx.beginPath();
this.canvas_ctx.moveTo(x1, y1);
this.canvas_ctx.lineTo(x2, y2);
this.canvas_ctx.stroke();
},
_lookup_close_node: function () {
var root = this.jm.get_root();
var root_location = root.get_location();
var root_size = root.get_size();
var root_x = root_location.x + root_size.w / 2;
var sw = this.shadow_w;
var sh = this.shadow_h;
var sx = this.shadow.offsetLeft;
var sy = this.shadow.offsetTop;
var ns, nl;
var direct = (sx + sw / 2) >= root_x ?
jsMind.direction.right : jsMind.direction.left;
var nodes = this.jm.mind.nodes;
var node = null;
var layout = this.jm.layout;
var min_distance = Number.MAX_VALUE;
var distance = 0;
var closest_node = null;
var closest_p = null;
var shadow_p = null;
for (var nodeid in nodes) {
var np, sp;
node = nodes[nodeid];
if (node.isroot || node.direction == direct) {
if (node.id == this.active_node.id) {
continue;
}
if (!layout.is_visible(node)) {
continue;
}
ns = node.get_size();
nl = node.get_location();
if (direct == jsMind.direction.right) {
if (sx - nl.x - ns.w <= 0) { continue; }
distance = Math.abs(sx - nl.x - ns.w) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2);
np = { x: nl.x + ns.w - options.line_width, y: nl.y + ns.h / 2 };
sp = { x: sx + options.line_width, y: sy + sh / 2 };
} else {
if (nl.x - sx - sw <= 0) { continue; }
distance = Math.abs(sx + sw - nl.x) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2);
np = { x: nl.x + options.line_width, y: nl.y + ns.h / 2 };
sp = { x: sx + sw - options.line_width, y: sy + sh / 2 };
}
if (distance < min_distance) {
closest_node = node;
closest_p = np;
shadow_p = sp;
min_distance = distance;
}
}
}
var result_node = null;
if (!!closest_node) {
result_node = {
node: closest_node,
direction: direct,
sp: shadow_p,
np: closest_p
};
}
return result_node;
},
lookup_close_node: function () {
var node_data = this._lookup_close_node();
if (!!node_data) {
this._magnet_shadow(node_data);
this.target_node = node_data.node;
this.target_direct = node_data.direction;
}
},
_event_bind: function () {
var jd = this;
var container = this.jm.view.container;
jdom.add_event(container, 'mousedown', function (e) {
var evt = e || event;
jd.dragstart.call(jd, evt);
});
jdom.add_event(container, 'mousemove', function (e) {
var evt = e || event;
jd.drag.call(jd, evt);
});
jdom.add_event(container, 'mouseup', function (e) {
var evt = e || event;
jd.dragend.call(jd, evt);
});
jdom.add_event(container, 'touchstart', function (e) {
var evt = e || event;
jd.dragstart.call(jd, evt);
});
jdom.add_event(container, 'touchmove', function (e) {
var evt = e || event;
jd.drag.call(jd, evt);
});
jdom.add_event(container, 'touchend', function (e) {
var evt = e || event;
jd.dragend.call(jd, evt);
});
},
dragstart: function (e) {
if (!this.jm.get_editable()) { return; }
if (this.capture) { return; }
this.active_node = null;
var jview = this.jm.view;
var el = e.target || event.srcElement;
if (el.tagName.toLowerCase() != 'jmnode') { return; }
if (jview.get_draggable_canvas()) { jview.disable_draggable_canvas() }
var nodeid = jview.get_binded_nodeid(el);
if (!!nodeid) {
var node = this.jm.get_node(nodeid);
if (!node.isroot) {
this.reset_shadow(el);
this.view_panel_rect = this.view_panel.getBoundingClientRect()
this.active_node = node;
this.offset_x = (e.clientX || e.touches[0].clientX) / jview.actualZoom - el.offsetLeft;
this.offset_y = (e.clientY || e.touches[0].clientY) / jview.actualZoom - el.offsetTop;
this.client_hw = Math.floor(el.clientWidth / 2);
this.client_hh = Math.floor(el.clientHeight / 2);
if (this.hlookup_delay != 0) {
$w.clearTimeout(this.hlookup_delay);
}
if (this.hlookup_timer != 0) {
$w.clearInterval(this.hlookup_timer);
}
var jd = this;
this.hlookup_delay = $w.setTimeout(function () {
jd.hlookup_delay = 0;
jd.hlookup_timer = $w.setInterval(function () {
jd.lookup_close_node.call(jd);
}, options.lookup_interval);
}, options.lookup_delay);
this.capture = true;
}
}
},
drag: function (e) {
if (!this.jm.get_editable()) { return; }
if (this.capture) {
e.preventDefault();
this.show_shadow();
this.moved = true;
clear_selection();
var jview = this.jm.view;
var px = (e.clientX || e.touches[0].clientX) / jview.actualZoom - this.offset_x;
var py = (e.clientY || e.touches[0].clientY) / jview.actualZoom - this.offset_y;
// scrolling container axisY if drag nodes exceeding container
if (
e.clientY - this.view_panel_rect.top < options.scrolling_trigger_width &&
this.view_panel.scrollTop > options.scrolling_step_length
) {
this.view_panel.scrollBy(0, -options.scrolling_step_length);
this.offset_y += options.scrolling_step_length / jview.actualZoom;
} else if (
this.view_panel_rect.bottom - e.clientY < options.scrolling_trigger_width &&
this.view_panel.scrollTop <
this.view_panel.scrollHeight - this.view_panel_rect.height - options.scrolling_step_length
) {
this.view_panel.scrollBy(0, options.scrolling_step_length);
this.offset_y -= options.scrolling_step_length / jview.actualZoom;
}
// scrolling container axisX if drag nodes exceeding container
if (e.clientX - this.view_panel_rect.left < options.scrolling_trigger_width && this.view_panel.scrollLeft > options.scrolling_step_length) {
this.view_panel.scrollBy(-options.scrolling_step_length, 0);
this.offset_x += options.scrolling_step_length / jview.actualZoom;
} else if (
this.view_panel_rect.right - e.clientX < options.scrolling_trigger_width &&
this.view_panel.scrollLeft < this.view_panel.scrollWidth - this.view_panel_rect.width - options.scrolling_step_length
) {
this.view_panel.scrollBy(options.scrolling_step_length, 0);
this.offset_x -= options.scrolling_step_length / jview.actualZoom;
}
this.shadow.style.left = px + 'px';
this.shadow.style.top = py + 'px';
clear_selection();
}
},
dragend: function (e) {
if (!this.jm.get_editable()) { return; }
if (this.jm.view.get_draggable_canvas()) { this.jm.view.enable_draggable_canvas() }
if (this.capture) {
if (this.hlookup_delay != 0) {
$w.clearTimeout(this.hlookup_delay);
this.hlookup_delay = 0;
this._clear_lines();
}
if (this.hlookup_timer != 0) {
$w.clearInterval(this.hlookup_timer);
this.hlookup_timer = 0;
this._clear_lines();
}
if (this.moved) {
var src_node = this.active_node;
var target_node = this.target_node;
var target_direct = this.target_direct;
this.move_node(src_node, target_node, target_direct);
}
this.hide_shadow();
}
this.view_panel_rect = null
this.moved = false;
this.capture = false;
},
move_node: function (src_node, target_node, target_direct) {
var shadow_h = this.shadow.offsetTop;
if (!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)) {
// lookup before_node
var sibling_nodes = target_node.children;
var sc = sibling_nodes.length;
var node = null;
var delta_y = Number.MAX_VALUE;
var node_before = null;
var beforeid = '_last_';
while (sc--) {
node = sibling_nodes[sc];
if (node.direction == target_direct && node.id != src_node.id) {
var dy = node.get_location().y - shadow_h;
if (dy > 0 && dy < delta_y) {
delta_y = dy;
node_before = node;
beforeid = '_first_';
}
}
}
if (!!node_before) { beforeid = node_before.id; }
this.jm.move_node(src_node.id, beforeid, target_node.id, target_direct);
}
this.active_node = null;
this.target_node = null;
this.target_direct = null;
},
jm_event_handle: function (type, data) {
if (type === jsMind.event_type.resize) {
this.resize();
}
}
};
var draggable_plugin = new jsMind.plugin('draggable', function (jm) {
var jd = new jsMind.draggable(jm);
jd.init();
jm.add_event_listener(function (type, data) {
jd.jm_event_handle.call(jd, type, data);
});
});
jsMind.register_plugin(draggable_plugin);
})(window);
//}}}
// // ''jsmind.screenshot.js''
//{{{
/**
* @license BSD
* @copyright 2014-2023 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
*/
(function ($w) {
'use strict';
var __name__ = 'jsMind';
var jsMind = $w[__name__];
if (!jsMind) { return; }
if (typeof jsMind.screenshot != 'undefined') { return; }
var $d = $w.document;
var $c = function (tag) { return $d.createElement(tag); };
var css = function (cstyle, property_name) {
return cstyle.getPropertyValue(property_name);
};
var is_visible = function (cstyle) {
var visibility = css(cstyle, 'visibility');
var display = css(cstyle, 'display');
return (visibility !== 'hidden' && display !== 'none');
};
var jcanvas = {};
jcanvas.rect = function (ctx, x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
};
jcanvas.text_multiline = function (ctx, text, x, y, w, h, lineheight) {
var line = '';
var text_len = text.length;
var chars = text.split('');
var test_line = null;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
for (var i = 0; i < text_len; i++) {
test_line = line + chars[i];
if (ctx.measureText(test_line).width > w && i > 0) {
ctx.fillText(line, x, y);
line = chars[i];
y += lineheight;
} else {
line = test_line;
}
}
ctx.fillText(line, x, y);
};
jcanvas.text_ellipsis = function (ctx, text, x, y, w, h) {
var center_y = y + h / 2;
var text = jcanvas.fittingString(ctx, text, w);
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText(text, x, center_y, w);
};
jcanvas.fittingString = function (ctx, text, max_width) {
var width = ctx.measureText(text).width;
var ellipsis = '…';
var ellipsis_width = ctx.measureText(ellipsis).width;
if (width <= max_width || width <= ellipsis_width) {
return text;
} else {
var len = text.length;
while (width >= max_width - ellipsis_width && len-- > 0) {
text = text.substring(0, len);
width = ctx.measureText(text).width;
}
return text + ellipsis;
}
};
jcanvas.image = function (ctx, url, x, y, w, h, r, rotation, callback) {
var img = new Image();
img.onload = function () {
ctx.save();
ctx.translate(x, y);
ctx.save();
ctx.beginPath();
jcanvas.rect(ctx, 0, 0, w, h, r);
ctx.closePath();
ctx.clip();
ctx.translate(w / 2, h / 2);
ctx.rotate(rotation * Math.PI / 180);
ctx.drawImage(img, -w / 2, -h / 2);
ctx.restore();
ctx.restore();
!!callback && callback();
}
img.src = url;
};
jsMind.screenshot = function (jm) {
this.jm = jm;
this.canvas_elem = null;
this.canvas_ctx = null;
this._inited = false;
};
jsMind.screenshot.prototype = {
init: function () {
if (this._inited) { return; }
console.log('init');
var c = $c('canvas');
var ctx = c.getContext('2d');
this.canvas_elem = c;
this.canvas_ctx = ctx;
this.jm.view.e_panel.appendChild(c);
this._inited = true;
this.resize();
},
shoot: function (callback) {
this.init();
this._draw(function () {
!!callback && callback();
this.clean();
}.bind(this));
this._watermark();
},
shootDownload: function () {
this.shoot(function () {
this._download();
}.bind(this));
},
shootAsDataURL: function (callback) {
this.shoot(function () {
!!callback && callback(this.canvas_elem.toDataURL());
}.bind(this));
},
resize: function () {
if (this._inited) {
this.canvas_elem.width = this.jm.view.size.w;
this.canvas_elem.height = this.jm.view.size.h;
}
},
clean: function () {
var c = this.canvas_elem;
this.canvas_ctx.clearRect(0, 0, c.width, c.height);
},
_draw: function (callback) {
var ctx = this.canvas_ctx;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
this._draw_lines(function () {
this._draw_nodes(callback);
}.bind(this));
},
_watermark: function () {
var c = this.canvas_elem;
var ctx = this.canvas_ctx;
ctx.textAlign = 'right';
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#000';
ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';
ctx.fillText('github.com/hizzgdev/jsmind', c.width - 5.5, c.height - 2.5);
ctx.textAlign = 'left';
ctx.fillText($w.location, 5.5, c.height - 2.5);
},
_draw_lines: function (callback) {
this.jm.view.graph.copy_to(this.canvas_ctx, callback);
},
_draw_nodes: function (callback) {
var nodes = this.jm.mind.nodes;
var node;
for (var nodeid in nodes) {
node = nodes[nodeid];
this._draw_node(node);
}
function check_nodes_ready() {
console.log('check_node_ready' + new Date());
var allOk = true;
for (var nodeid in nodes) {
node = nodes[nodeid];
allOk = allOk & node.ready;
}
if (!allOk) {
$w.setTimeout(check_nodes_ready, 200);
} else {
$w.setTimeout(callback, 200);
}
}
check_nodes_ready();
},
_draw_node: function (node) {
var ctx = this.canvas_ctx;
var view_data = node._data.view;
var node_element = view_data.element;
var ncs = getComputedStyle(node_element);
if (!is_visible(ncs)) {
node.ready = true;
return;
}
var bgcolor = css(ncs, 'background-color');
var round_radius = parseInt(css(ncs, 'border-top-left-radius'));
var color = css(ncs, 'color');
var padding_left = parseInt(css(ncs, 'padding-left'));
var padding_right = parseInt(css(ncs, 'padding-right'));
var padding_top = parseInt(css(ncs, 'padding-top'));
var padding_bottom = parseInt(css(ncs, 'padding-bottom'));
var text_overflow = css(ncs, 'text-overflow');
var font = css(ncs, 'font-style') + ' ' +
css(ncs, 'font-variant') + ' ' +
css(ncs, 'font-weight') + ' ' +
css(ncs, 'font-size') + '/' + css(ncs, 'line-height') + ' ' +
css(ncs, 'font-family');
var rb = {
x: view_data.abs_x,
y: view_data.abs_y,
w: view_data.width + 1,
h: view_data.height + 1
};
var tb = {
x: rb.x + padding_left,
y: rb.y + padding_top,
w: rb.w - padding_left - padding_right,
h: rb.h - padding_top - padding_bottom
};
ctx.font = font;
ctx.fillStyle = bgcolor;
ctx.beginPath();
jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
ctx.closePath();
ctx.fill();
ctx.fillStyle = color;
if ('background-image' in node.data) {
var backgroundUrl = css(ncs, 'background-image').slice(5, -2);
node.ready = false;
var rotation = 0;
if ('background-rotation' in node.data) {
rotation = node.data['background-rotation'];
}
jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
function () {
node.ready = true;
});
}
if (!!node.topic) {
if (text_overflow === 'ellipsis') {
jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
} else {
var line_height = parseInt(css(ncs, 'line-height'));
jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height);
}
}
if (!!view_data.expander) {
this._draw_expander(view_data.expander);
}
if (!('background-image' in node.data)) {
node.ready = true;
}
},
_draw_expander: function (expander) {
var ctx = this.canvas_ctx;
var ncs = getComputedStyle(expander);
if (!is_visible(ncs)) { return; }
var style_left = css(ncs, 'left');
var style_top = css(ncs, 'top');
var font = css(ncs, 'font');
var left = parseInt(style_left);
var top = parseInt(style_top);
var is_plus = expander.innerHTML === '+';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true);
ctx.moveTo(left + 10, top + 7);
ctx.lineTo(left + 4, top + 7);
if (is_plus) {
ctx.moveTo(left + 7, top + 4);
ctx.lineTo(left + 7, top + 10);
}
ctx.closePath();
ctx.stroke();
},
_download: function () {
var c = this.canvas_elem;
var name = this.jm.mind.name + '.png';
if (navigator.msSaveBlob && (!!c.msToBlob)) {
var blob = c.msToBlob();
navigator.msSaveBlob(blob, name);
} else {
var bloburl = this.canvas_elem.toDataURL();
var anchor = $c('a');
if ('download' in anchor) {
anchor.style.visibility = 'hidden';
anchor.href = bloburl;
anchor.download = name;
$d.body.appendChild(anchor);
var evt = $d.createEvent('MouseEvents');
evt.initEvent('click', true, true);
anchor.dispatchEvent(evt);
$d.body.removeChild(anchor);
} else {
location.href = bloburl;
}
}
},
jm_event_handle: function (type, data) {
if (type === jsMind.event_type.resize) {
this.resize();
}
}
};
var screenshot_plugin = new jsMind.plugin('screenshot', function (jm) {
var jss = new jsMind.screenshot(jm);
jm.screenshot = jss;
jm.shoot = function () {
jss.shoot();
};
jm.add_event_listener(function (type, data) {
jss.jm_event_handle.call(jss, type, data);
});
});
jsMind.register_plugin(screenshot_plugin);
})(window);
//}}}
//{{{
var __name__ = 'jsMind';
var $w = window || global;
if ($w && $w[__name__]) {
$w[__name__].logger = console;
}
})({
level: 0,
console: console || {
log: function () { },
error: function () {},
warn: function () {},
info: function () {},
debug: function () {}
},
log: function () { if (this.level > 0) this.console.log.apply(this.console, arguments) },
error: function () { if (this.level > 1) this.console.error.apply(this.console, arguments) },
warn: function () { if (this.level > 2) this.console.warn.apply(this.console, arguments) },
info: function () { if (this.level > 3) this.console.info.apply(this.console, arguments) },
debug: function () { if (this.level > 4) this.console.debug.apply(this.console, arguments) }
});
//}}}
//{{{
(function() {
var css = store.getTiddlerText("jsmind.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}"));
css = css.replace("max-width: 400px;", "");
setStylesheet(css, "jsmind.js-stylesheet");
})();
//}}}
/***
|Name|json2.js|
|Source|https://github.com/douglascrockford/JSON-js/blob/master/json2.js|
|Documentation|https://github.com/douglascrockford/JSON-js|
|Version|commit 2a76286e00cdc1e98fbc9e9ec6589563a3a4c3bb 2018-04-21 https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js|
|License|Public Domain|
|Description|JSON in JavaScript|
!!!!!Template
//{{{
// json2.js
// 2017-06-12
// Public Domain.
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
// NOT CONTROL.
// This file creates a global JSON object containing two methods: stringify
// and parse. This file provides the ES5 JSON capability to ES3 systems.
// If a project might run on IE8 or earlier, then this file should be included.
// This file does nothing on ES5 systems.
// JSON.stringify(value, replacer, space)
// value any JavaScript value, usually an object or array.
// replacer an optional parameter that determines how object
// values are stringified for objects. It can be a
// function or an array of strings.
// space an optional parameter that specifies the indentation
// of nested structures. If it is omitted, the text will
// be packed without extra whitespace. If it is a number,
// it will specify the number of spaces to indent at each
// level. If it is a string (such as "\t" or " "),
// it contains the characters used to indent at each level.
// This method produces a JSON text from a JavaScript value.
// When an object value is found, if the object contains a toJSON
// method, its toJSON method will be called and the result will be
// stringified. A toJSON method does not serialize: it returns the
// value represented by the name/value pair that should be serialized,
// or undefined if nothing should be serialized. The toJSON method
// will be passed the key associated with the value, and this will be
// bound to the value.
// For example, this would serialize Dates as ISO strings.
// Date.prototype.toJSON = function (key) {
// function f(n) {
// // Format integers to have at least two digits.
// return (n < 10)
// ? "0" + n
// : n;
// }
// return this.getUTCFullYear() + "-" +
// f(this.getUTCMonth() + 1) + "-" +
// f(this.getUTCDate()) + "T" +
// f(this.getUTCHours()) + ":" +
// f(this.getUTCMinutes()) + ":" +
// f(this.getUTCSeconds()) + "Z";
// };
// You can provide an optional replacer method. It will be passed the
// key and value of each member, with this bound to the containing
// object. The value that is returned from your method will be
// serialized. If your method returns undefined, then the member will
// be excluded from the serialization.
// If the replacer parameter is an array of strings, then it will be
// used to select the members to be serialized. It filters the results
// such that only members with keys listed in the replacer array are
// stringified.
// Values that do not have JSON representations, such as undefined or
// functions, will not be serialized. Such values in objects will be
// dropped; in arrays they will be replaced with null. You can use
// a replacer function to replace those with JSON values.
// JSON.stringify(undefined) returns undefined.
// The optional space parameter produces a stringification of the
// value that is filled with line breaks and indentation to make it
// easier to read.
// If the space parameter is a non-empty string, then that string will
// be used for indentation. If the space parameter is a number, then
// the indentation will be that many spaces.
// Example:
// text = JSON.stringify(["e", {pluribus: "unum"}]);
// // text is '["e",{"pluribus":"unum"}]'
// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
// text = JSON.stringify([new Date()], function (key, value) {
// return this[key] instanceof Date
// ? "Date(" + this[key] + ")"
// : value;
// });
// // text is '["Date(---current time---)"]'
// JSON.parse(text, reviver)
// This method parses a JSON text to produce an object or array.
// It can throw a SyntaxError exception.
// The optional reviver parameter is a function that can filter and
// transform the results. It receives each of the keys and values,
// and its return value is used instead of the original value.
// If it returns what it received, then the structure is not modified.
// If it returns undefined then the member is deleted.
// Example:
// // Parse the text. Values that look like ISO date strings will
// // be converted to Date objects.
// myData = JSON.parse(text, function (key, value) {
// var a;
// if (typeof value === "string") {
// a =
// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
// if (a) {
// return new Date(Date.UTC(
// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]
// ));
// }
// return value;
// }
// });
// myData = JSON.parse(
// "[\"Date(09/09/2001)\"]",
// function (key, value) {
// var d;
// if (
// typeof value === "string"
// && value.slice(0, 5) === "Date("
// && value.slice(-1) === ")"
// ) {
// d = new Date(value.slice(5, -1));
// if (d) {
// return d;
// }
// }
// return value;
// }
// );
// This is a reference implementation. You are free to copy, modify, or
// redistribute.
/*jslint
eval, for, this
* /
/*property
JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
* /
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (typeof JSON !== "object") {
JSON = {};
}
(function () {
"use strict";
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
function f(n) {
// Format integers to have at least two digits.
return (n < 10)
? "0" + n
: n;
}
function this_value() {
return this.valueOf();
}
if (typeof Date.prototype.toJSON !== "function") {
Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? (
this.getUTCFullYear()
+ "-"
+ f(this.getUTCMonth() + 1)
+ "-"
+ f(this.getUTCDate())
+ "T"
+ f(this.getUTCHours())
+ ":"
+ f(this.getUTCMinutes())
+ ":"
+ f(this.getUTCSeconds())
+ "Z"
)
: null;
};
Boolean.prototype.toJSON = this_value;
Number.prototype.toJSON = this_value;
String.prototype.toJSON = this_value;
}
var gap;
var indent;
var meta;
var rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
rx_escapable.lastIndex = 0;
return rx_escapable.test(string)
? "\"" + string.replace(rx_escapable, function (a) {
var c = meta[a];
return typeof c === "string"
? c
: "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
}) + "\""
: "\"" + string + "\"";
}
function str(key, holder) {
// Produce a string from holder[key].
var i; // The loop counter.
var k; // The member key.
var v; // The member value.
var length;
var mind = gap;
var partial;
var value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (
value
&& typeof value === "object"
&& typeof value.toJSON === "function"
) {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === "function") {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case "string":
return quote(value);
case "number":
// JSON numbers must be finite. Encode non-finite numbers as null.
return (isFinite(value))
? String(value)
: "null";
case "boolean":
case "null":
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce "null". The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is "object", we might be dealing with an object or an array or
// null.
case "object":
// Due to a specification blunder in ECMAScript, typeof null is "object",
// so watch out for that case.
if (!value) {
return "null";
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === "[object Array]") {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || "null";
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0
? "[]"
: gap
? (
"[\n"
+ gap
+ partial.join(",\n" + gap)
+ "\n"
+ mind
+ "]"
)
: "[" + partial.join(",") + "]";
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === "object") {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === "string") {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (
(gap)
? ": "
: ":"
) + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (
(gap)
? ": "
: ":"
) + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0
? "{}"
: gap
? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
: "{" + partial.join(",") + "}";
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== "function") {
meta = { // table of character substitutions
"\b": "\\b",
"\t": "\\t",
"\n": "\\n",
"\f": "\\f",
"\r": "\\r",
"\"": "\\\"",
"\\": "\\\\"
};
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = "";
indent = "";
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === "number") {
for (i = 0; i < space; i += 1) {
indent += " ";
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === "string") {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== "function" && (
typeof replacer !== "object"
|| typeof replacer.length !== "number"
)) {
throw new Error("JSON.stringify");
}
// Make a fake root object containing our value under the key of "".
// Return the result of stringifying the value.
return str("", {"": value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== "function") {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k;
var v;
var value = holder[key];
if (value && typeof value === "object") {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
rx_dangerous.lastIndex = 0;
if (rx_dangerous.test(text)) {
text = text.replace(rx_dangerous, function (a) {
return (
"\\u"
+ ("0000" + a.charCodeAt(0).toString(16)).slice(-4)
);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with "()" and "new"
// because they can cause invocation, and "=" because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
// replace all simple value tokens with "]" characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or "]" or
// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
if (
rx_one.test(
text
.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval("(" + text + ")");
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return (typeof reviver === "function")
? walk({"": j}, "")
: j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError("JSON.parse");
};
}
}());
//}}}
!!!!!Code
***/
//{{{
(function() {
var template = store.getTiddlerText("json2.js##Template").replace(/\* \//g, "*/");
template = template.replace('}());', 'JSON.quote = quote; }());');
template = template.replace('value.toJSON === "function"', 'value.toJSON === "function" && !isDate(value)')
template = template.replace('switch (typeof value) {', 'if (isDate(value)) return strDate(value);\nif (isError(value)) value = objError(value);\nswitch (typeof value) {\ncase "function":\nreturn strFunction(value);\n');
function isDate(o) {
return o && (o.constructor == (new Date()).constructor);
}
function strDate(o) {
if (isDate(o)) {
var val = '(function() { ' +
'var JSONDate = new Date(' +
o.getFullYear() + ',' +
o.getMonth() + ',' +
o.getDate() + ',' +
o.getHours() + ',' +
o.getMinutes() + ',' +
o.getSeconds() + ',' +
o.getMilliseconds() + '); ';
if (o._type) {
val = val + 'JSONDate._type=\'' + o._type + '\'; ';
}
val = val + 'return JSONDate; })()';
return val;
} else {
return o;
}
}
function isError(o) {
return o instanceof Error;
}
function objError(o) {
if (isError(o)) {
var val = {};
if (typeof o.message === "string") val.message = o.message;
if (typeof o.stack === "string") val.stack = o.stack;
return val;
} else {
return o;
}
}
function strFunction(o) {
if (typeof o === "function") {
return eval(o).toString();
}
}
var JSON2 = {};
eval(template.replace(/JSON/g, "JSON2"));
jQuery.toJSON = JSON2.stringify;
jQuery.evalJSON = JSON2.parse;
jQuery.secureEvalJSON = JSON2.parse;
jQuery.quoteString = JSON2.quote;
window.JSON2 = JSON2;
})();
//}}}
/***
|Name|jspreadsheet.js|
|Source|https://github.com/jspreadsheet/ce|
|Documentation|https://bossanova.uk/jspreadsheet/docs|
|Version|commit cb614d6e62423e3ed945959d5fa73c1ba5da700f 2025-01-08 https://raw.githubusercontent.com/jspreadsheet/ce/refs/heads/master/dist/jspreadsheet.css <br> commit 5c5eecd7bb59dd38dff67271341a73cb84b551e7 2025-01-07 https://raw.githubusercontent.com/jspreadsheet/ce/refs/heads/master/dist/jspreadsheet.themes.css <br> commit 9d7a6a5c16d22532f7f6ba1f2c50adb0c34d4592 2025-01-23 https://github.com/jspreadsheet/ce/tree/master/src|
|License|[[MIT license|https://opensource.org/license/mit/]]|
|Description|Jspreadsheet CE is an extensible framework for building sophisticated data-oriented interfaces with Excel-like controls|
|Requires|[[jsuites.js]]|
!!!!!Documentation
The native available types in jspreadsheet javascript spreadsheet are the following:
* text
* numeric
* hidden
* dropdown
** autocomplete
** multiple
* checkbox
* radio
* calendar
* image
* color
* html
!!!!!CSS
;jspreadsheet.css
//{{{
:root {
--jss-border-color:#000;
}
.jss_container {
display:inline-block;
padding-right:2px;
box-sizing: border-box;
overscroll-behavior: contain;
outline: none;
}
.fullscreen {
position:fixed !important;
top:0px;
left:0px;
width:100%;
height:100%;
z-index:21;
display: flex;
flex-direction: column;
background-color:#ffffff;
}
.fullscreen .jtabs-content {
flex: 1;
overflow: hidden;
}
.fullscreen .jss_content {
overflow:auto;
width: 100% !important;
height:100%;
max-height: 100% !important;
}
.fullscreen .jss_container {
height:100%;
}
.jss_content {
display:inline-block;
box-sizing: border-box;
padding-right:3px;
padding-bottom:3px;
position:relative;
scrollbar-width: thin;
scrollbar-color: #666 transparent;
}
@supports (-moz-appearance:none) {
.jss_content { padding-right:10px; }
}
.jss_content::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.jss_content::-webkit-scrollbar-track {
background: #eee;
}
.jss_content::-webkit-scrollbar-thumb {
background: #666;
}
.jss_worksheet {
border-collapse: separate;
table-layout: fixed;
white-space: nowrap;
empty-cells: show;
border: 0px;
background-color: #fff;
width: 0;
border-top: 1px solid transparent;
border-left: 1px solid transparent;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.jss_worksheet > thead > tr > td
{
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
background-color: #f3f3f3;
padding: 2px;
cursor: pointer;
box-sizing: border-box;
overflow: hidden;
position: -webkit-sticky;
position: sticky;
top: 0;
z-index:2;
}
.jss_worksheet > thead > tr > td.dragging
{
opacity:0.5;
}
.jss_worksheet > thead > tr > td.selected
{
background-color:#dcdcdc;
}
.jss_worksheet > thead > tr > td.arrow-up
{
background-repeat:no-repeat;
background-position:center right 5px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E");
text-decoration:underline;
}
.jss_worksheet > thead > tr > td.arrow-down
{
background-repeat:no-repeat;
background-position:center right 5px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E");
text-decoration:underline;
}
.jss_worksheet > tbody > tr > td:first-child
{
position:relative;
background-color:#f3f3f3;
text-align:center;
}
.jss_worksheet > tbody.resizable > tr > td:first-child::before
{
content:'\00a0';
width:100%;
height:3px;
position:absolute;
bottom:0px;
left:0px;
cursor:row-resize;
}
.jss_worksheet > tbody.draggable > tr > td:first-child::after
{
content:'\00a0';
width:3px;
height:100%;
position:absolute;
top:0px;
right:0px;
cursor:move;
}
.jss_worksheet > tbody > tr.dragging > td
{
background-color:#eee;
opacity:0.5;
}
.jss_worksheet > tbody > tr > td
{
border-top:1px solid #ccc;
border-left:1px solid #ccc;
border-right:1px solid transparent;
border-bottom:1px solid transparent;
padding:4px;
white-space: nowrap;
box-sizing: border-box;
line-height:1em;
}
.jss_overflow > tbody > tr > td {
overflow: hidden;
}
.jss_worksheet > tbody > tr > td:last-child
{
overflow: hidden;
}
.jss_worksheet > tbody > tr > td > img
{
display:inline-block;
max-width:100px;
}
.jss_worksheet > tbody > tr > td.readonly
{
color:rgba(0,0,0,0.3)
}
.jss_worksheet > tbody > tr.selected > td:first-child
{
background-color:#dcdcdc;
}
.jss_worksheet > tbody > tr > td > select,
.jss_worksheet > tbody > tr > td > input,
.jss_worksheet > tbody > tr > td > textarea
{
border:0px;
border-radius:0px;
outline:0px;
width:100%;
margin:0px;
padding:0px;
padding-right:2px;
background-color:transparent;
box-sizing: border-box;
}
.jss_worksheet > tbody > tr > td > textarea
{
resize: none;
padding-top:6px !important;
}
.jss_worksheet > tbody > tr > td > input[type=checkbox]
{
width:12px;
margin-top:2px;
}
.jss_worksheet > tbody > tr > td > input[type=radio]
{
width:12px;
margin-top:2px;
}
.jss_worksheet > tbody > tr > td > select
{
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-repeat: no-repeat;
background-position-x: 100%;
background-position-y: 40%;
background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+);
}
.jss_worksheet > tbody > tr > td.jss_dropdown
{
background-repeat: no-repeat;
background-position:top 50% right 5px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E");
text-overflow: ellipsis;
overflow-x:hidden;
}
.jss_worksheet > tbody > tr > td.jss_dropdown.jss_comments
{
background:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E") top 50% right 5px no-repeat, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=') top right no-repeat;
}
.jss_worksheet > tbody > tr > td > .color
{
width:90%;
height:10px;
margin:auto;
}
.jss_worksheet > tbody > tr > td > a {
text-decoration: underline;
}
.jss_worksheet > tbody > tr > td.highlight > a {
color: blue;
cursor: pointer;
}
.jss_worksheet > tfoot > tr > td
{
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
background-color: #f3f3f3;
padding: 2px;
cursor: pointer;
box-sizing: border-box;
overflow: hidden;
}
.jss_worksheet .highlight {
background-color:rgba(0,0,0,0.05);
}
.jss_worksheet .highlight-top {
border-top:1px solid #000;
box-shadow: 0px -1px #ccc;
}
.jss_worksheet .highlight-left {
border-left:1px solid #000;
box-shadow: -1px 0px #ccc;
}
.jss_worksheet .highlight-right {
border-right:1px solid #000;
}
.jss_worksheet .highlight-bottom {
border-bottom:1px solid #000;
}
.jss_worksheet .highlight-top.highlight-left {
box-shadow: -1px -1px #ccc;
-webkit-box-shadow: -1px -1px #ccc;
-moz-box-shadow: -1px -1px #ccc;
}
.jss_worksheet .highlight-selected
{
background-color:rgba(0,0,0,0.0);
}
.jss_worksheet .selection
{
background-color:rgba(0,0,0,0.05);
}
.jss_worksheet .selection-left
{
border-left:1px dotted #000;
}
.jss_worksheet .selection-right
{
border-right:1px dotted #000;
}
.jss_worksheet .selection-top
{
border-top:1px dotted #000;
}
.jss_worksheet .selection-bottom
{
border-bottom:1px dotted #000;
}
.jss_corner
{
position:absolute;
background-color: rgb(0, 0, 0);
height: 1px;
width: 1px;
border: 1px solid rgb(255, 255, 255);
top:-2000px;
left:-2000px;
cursor:crosshair;
box-sizing: initial;
z-index:20;
padding: 2px;
}
.jss_worksheet .editor
{
outline:0px solid transparent;
overflow:visible;
white-space: nowrap;
text-align:left;
padding:0px;
box-sizing: border-box;
overflow:visible !important;
}
.jss_worksheet .editor > input
{
padding-left:4px;
}
.jss_worksheet .editor .jupload
{
position:fixed;
top:100%;
z-index:40;
user-select:none;
-webkit-font-smoothing: antialiased;
font-size: .875rem;
letter-spacing: .2px;
-webkit-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
padding:10px;
background-color:#fff;
width:300px;
min-height:225px;
margin-top:2px;
}
.jss_worksheet .editor .jupload img
{
width:100%;
height:auto;
}
.jss_worksheet .editor .jss_richtext
{
position:fixed;
top:100%;
z-index:40;
user-select:none;
-webkit-font-smoothing: antialiased;
font-size: .875rem;
letter-spacing: .2px;
-webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
padding:10px;
background-color:#fff;
width:358px;
margin-top:2px;
text-align:left;
white-space: initial;
}
.jss_worksheet .editor .jclose:after
{
position:absolute;
top:0;
right:0;
margin:10px;
content:'close';
font-family:'Material icons';
font-size:24px;
width:24px;
height:24px;
line-height:24px;
cursor:pointer;
text-shadow: 0px 0px 5px #fff;
}
.jss_worksheet, .jss_worksheet td, .jss_corner
{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
}
.jss_textarea
{
position:absolute;
top:-999px;
left:-999px;
width:1px;
height:1px;
}
.jss_worksheet .dragline
{
position:absolute;
}
.jss_worksheet .dragline div
{
position:relative;
top:-6px;
height:5px;
width:22px;
}
.jss_worksheet .dragline div:hover
{
cursor:move;
}
.jss_worksheet .onDrag
{
background-color:rgba(0,0,0,0.6);
}
.jss_worksheet .error
{
border:1px solid red;
}
.jss_worksheet thead td.resizing
{
border-right-style:dotted !important;
border-right-color:red !important;
}
.jss_worksheet tbody tr.resizing > td
{
border-bottom-style:dotted !important;
border-bottom-color:red !important;
}
.jss_worksheet tbody td.resizing
{
border-right-style:dotted !important;
border-right-color:red !important;
}
.jss_worksheet .jdropdown-header
{
border:0px !important;
outline:none !important;
width:100% !important;
height:100% !important;
padding:0px !important;
padding-left:8px !important;
}
.jss_worksheet .jdropdown-container
{
margin-top:1px;
}
.jss_worksheet .jdropdown-container-header {
padding: 0px;
margin: 0px;
height: inherit;
}
.jss_worksheet .jdropdown-picker
{
border:0px !important;
padding:0px !important;
width:inherit;
height:inherit;
}
.jss_worksheet .jss_comments
{
background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: top right;
}
.jss_worksheet .sp-replacer
{
margin: 2px;
border:0px;
}
.jss_worksheet > thead > tr.jss_filter > td > input
{
border:0px;
width:100%;
outline:none;
}
.jss_about {
float: right;
font-size: 0.7em;
padding: 2px;
text-transform: uppercase;
letter-spacing: 1px;
display: none;
}
.jss_about a {
color: #ccc;
text-decoration: none;
}
.jss_about img {
display: none;
}
.jss_filter
{
display:flex;
justify-content:space-between;
margin-bottom:4px;
}
.jss_filter > div
{
padding:8px;
align-items:center;
}
.jss_pagination
{
display:flex;
justify-content:space-between;
align-items:center;
}
.jss_pagination > div
{
display:flex;
padding:10px;
}
.jss_pagination > div:last-child
{
padding-right:10px;
padding-top:10px;
}
.jss_pagination > div > div
{
text-align:center;
width:36px;
height:36px;
line-height:34px;
border:1px solid #ccc;
box-sizing: border-box;
margin-left:2px;
cursor:pointer;
}
.jss_page
{
font-size:0.8em;
}
.jss_page_selected
{
font-weight:bold;
background-color:#f3f3f3;
}
.jss_toolbar
{
display:flex;
background-color:#f3f3f3;
border:1px solid #ccc;
padding:4px;
margin:0px 2px 4px 1px;
}
.jss_toolbar:empty
{
display:none;
}
.jss_worksheet .dragging-left
{
background-repeat: no-repeat;
background-position:top 50% left 0px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E");
}
.jss_worksheet .dragging-right
{
background-repeat: no-repeat;
background-position:top 50% right 0px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E");
}
.jss_hidden_index > tbody > tr > td:first-child,
.jss_hidden_index > thead > tr > td:first-child,
.jss_hidden_index > tfoot > tr > td:first-child,
.jss_hidden_index > colgroup > col:first-child
{
display:none;
}
.jss_worksheet .jrating {
display: inline-flex;
}
.jss_worksheet .jrating > div {
zoom: 0.55;
}
.jss_worksheet .copying-top {
border-top:1px dashed #000;
}
.jss_worksheet .copying-left {
border-left:1px dashed #000;
}
.jss_worksheet .copying-right {
border-right:1px dashed #000;
}
.jss_worksheet .copying-bottom {
border-bottom:1px dashed #000;
}
.jss_worksheet .jss_column_filter {
background-repeat: no-repeat;
background-position: top 50% right 5px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='gray' width='18px' height='18px'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");
text-overflow: ellipsis;
overflow: hidden;
padding: 0px;
padding-left: 6px;
padding-right: 20px;
}
.jss_worksheet thead .jss_freezed, .jss_worksheet tfoot .jss_freezed {
left: 0px;
z-index: 3 !important;
box-shadow: 2px 0px 2px 0.2px #ccc !important;
-webkit-box-shadow: 2px 0px 2px 0.2px #ccc !important;
-moz-box-shadow: 2px 0px 2px 0.2px #ccc !important;
}
.jss_worksheet tbody .jss_freezed {
position: relative;
background-color: #fff;
box-shadow: 1px 1px 1px 1px #ccc !important;
-webkit-box-shadow: 2px 4px 4px 0.1px #ccc !important;
-moz-box-shadow: 2px 4px 4px 0.1px #ccc !important;
}
.red {
color: red;
}
.jss_worksheet > tbody > tr > td.readonly > input[type=checkbox],
.jss_worksheet > tbody > tr > td.readonly > input[type=radio] {
pointer-events: none;
opacity: 0.5;
}
//}}}
;jspreadsheet.themes.css
//{{{
.jss_worksheet > thead > tr > td
{
border-top: 1px solid var(--border_color, #ccc);
border-left: 1px solid var(--border_color, #ccc);
background-color: var(--header_background, #f3f3f3);
color: var(--header_color, #000);
}
.jss_worksheet > thead > tr > td.selected
{
background-color: var(--header_background_highlighted, #dcdcdc);
color: var(--header_color_highlighted, #000);
}
.jss_worksheet > tbody > tr > td:first-child
{
background-color: var(--header_background, #f3f3f3);
color: var(--header_color, #000);
}
.jss_worksheet > tbody > tr > td
{
background-color: var(--content_background, #fff);
color: var(--content_color, #000);
border-top: 1px solid var(--border_color, #ccc);
border-left: 1px solid var(--border_color, #ccc);
}
.jss_worksheet > tbody > tr.selected > td:first-child
{
background-color: var(--header_background_highlighted, #dcdcdc);
color: var(--header_color_highlighted, #000);
}
.jss_worksheet .highlight {
background-color: var(--selection, rgba(0,0,0,0.05));
}
.jss_worksheet .highlight-top {
border-top:1px solid var(--border_color_highlighted, #000);
}
.jss_worksheet .highlight-left {
border-left:1px solid var(--border_color_highlighted, #000);
}
.jss_worksheet .highlight-right {
border-right:1px solid var(--border_color_highlighted, #000);
}
.jss_worksheet .highlight-bottom {
border-bottom:1px solid var(--border_color_highlighted, #000);
}
.jss_worksheet .highlight-selected
{
background-color: var(--cursor, #eee);
}
.jss_pagination > div > div
{
color: var(--header_color, #000);
background: var(--header_background, #f3f3f3);
border: 1px solid var(--border_color, #ccc);
}
.jss_toolbar
{
background-color: var(--header_background, #f3f3f3);
color: var(--header_color, #000);
border: 1px solid var(--border_color, #ccc);
}
.jss_toolbar .jtoolbar-item i {
color: var(--content_color, #000);
}
.jss_toolbar .jtoolbar-item:not(.jtoolbar-divisor):hover,
.jss_toolbar .jtoolbar-item.jpicker:hover > .jpicker-header {
background-color: var(--content_background_highlighted, #f3f3f3);
color: var(--content_color_highlighted, #000);
}
.jss_toolbar .jtoolbar-divisor {
background: var(--header_color, #ddd);
}
.jss_contextmenu {
border: 1px solid var(--border_color, #ccc);
background: var(--menu_background, #fff);
color: var(--menu_color, #555);
box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));
-webkit-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));
-moz-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1))
}
.jss_contextmenu>div a {
color: var(--menu_color, #555);
}
.jss_contextmenu>div:not(.contextmenu-line):hover a {
color: var(--menu_color_highlighted, #555);
}
.jss_contextmenu>div:not(.contextmenu-line):hover {
background: var(--menu_background_highlighted, #ebebeb);
}
.jss_container input {
color: var(--header_color, #000);
background: var(--header_background, #f3f3f3);
}
//}}}
;fonts
https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400|Material+Icons&display=swap
//{{{
/* fallback * /
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(data:font/woff2;base64,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) format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
//}}}
!!!!!Code
//{{{
if (! jSuites && typeof(require) === 'function') {
var jSuites = require('jsuites');
}
if (! formula && typeof(require) === 'function') {
var formula = require('@jspreadsheet/formula');
}
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.jspreadsheet = factory();
}(this, (function () {
var jspreadsheet;
/****** / (function() { // webpackBootstrap
/****** / "use strict";
/****** / var __webpack_modules__ = ({
/*** / 94:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / My: function() { return /* binding * / orderBy; },
/* harmony export * / Th: function() { return /* binding * / updateOrderArrow; },
/* harmony export * / iY: function() { return /* binding * / updateOrder; }
/* harmony export * / });
/* harmony import * / var _history_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(911);
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(805);
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(530);
/* harmony import * / var _lazyLoading_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(497);
/* harmony import * / var _filter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(829);
/**
* Update order arrow
* /
const updateOrderArrow = function(column, order) {
const obj = this;
// Remove order
for (let i = 0; i < obj.headers.length; i++) {
obj.headers[i].classList.remove('arrow-up');
obj.headers[i].classList.remove('arrow-down');
}
// No order specified then toggle order
if (order) {
obj.headers[column].classList.add('arrow-up');
} else {
obj.headers[column].classList.add('arrow-down');
}
}
/**
* Update rows position
* /
const updateOrder = function(rows) {
const obj = this;
// History
let data = []
for (let j = 0; j < rows.length; j++) {
data[j] = obj.options.data[rows[j]];
}
obj.options.data = data;
data = []
for (let j = 0; j < rows.length; j++) {
data[j] = obj.records[rows[j]];
for (let i = 0; i < data[j].length; i++) {
data[j][i].y = j;
}
}
obj.records = data;
data = []
for (let j = 0; j < rows.length; j++) {
data[j] = obj.rows[rows[j]];
data[j].y = j;
}
obj.rows = data;
// Update references
_internal_js__WEBPACK_IMPORTED_MODULE_0__/* .updateTableReferences * / .o8.call(obj);
// Redo search
if (obj.results && obj.results.length) {
if (obj.searchInput.value) {
obj.search(obj.searchInput.value);
} else {
_filter_js__WEBPACK_IMPORTED_MODULE_1__/* .closeFilter * / .F8.call(obj);
}
} else {
// Create page
obj.results = null;
obj.pageNumber = 0;
if (obj.options.pagination > 0) {
obj.page(0);
} else if (obj.options.lazyLoading == true) {
_lazyLoading_js__WEBPACK_IMPORTED_MODULE_2__/* .loadPage * / .wu.call(obj, 0);
} else {
for (let j = 0; j < obj.rows.length; j++) {
obj.tbody.appendChild(obj.rows[j].element);
}
}
}
}
/**
* Sort data and reload table
* /
const orderBy = function(column, order) {
const obj = this;
if (column >= 0) {
// Merged cells
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
// Remove merged cells
obj.destroyMerge();
}
}
// Direction
if (order == null) {
order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0;
} else {
order = order ? 1 : 0;
}
// Test order
let temp = [];
if (
obj.options.columns &&
obj.options.columns[column] &&
(
obj.options.columns[column].type == 'number' ||
obj.options.columns[column].type == 'numeric' ||
obj.options.columns[column].type == 'percentage' ||
obj.options.columns[column].type == 'autonumber' ||
obj.options.columns[column].type == 'color'
)
) {
for (let j = 0; j < obj.options.data.length; j++) {
temp[j] = [ j, Number(obj.options.data[j][column]) ];
}
} else if (
obj.options.columns &&
obj.options.columns[column] &&
(
obj.options.columns[column].type == 'calendar' ||
obj.options.columns[column].type == 'checkbox' ||
obj.options.columns[column].type == 'radio'
)
) {
for (let j = 0; j < obj.options.data.length; j++) {
temp[j] = [ j, obj.options.data[j][column] ];
}
} else {
for (let j = 0; j < obj.options.data.length; j++) {
temp[j] = [ j, obj.records[j][column].element.textContent.toLowerCase() ];
}
}
// Default sorting method
if (typeof(obj.parent.config.sorting) !== 'function') {
obj.parent.config.sorting = function(direction) {
return function(a, b) {
const valueA = a[1];
const valueB = b[1];
if (! direction) {
return (valueA === '' && valueB !== '') ? 1 : (valueA !== '' && valueB === '') ? -1 : (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;
} else {
return (valueA === '' && valueB !== '') ? 1 : (valueA !== '' && valueB === '') ? -1 : (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;
}
}
}
}
temp = temp.sort(obj.parent.config.sorting(order));
// Save history
const newValue = [];
for (let j = 0; j < temp.length; j++) {
newValue[j] = temp[j][0];
}
// Save history
_history_js__WEBPACK_IMPORTED_MODULE_3__/* .setHistory * / .Dh.call(obj, {
action: 'orderBy',
rows: newValue,
column: column,
order: order,
});
// Update order
updateOrderArrow.call(obj, column, order);
updateOrder.call(obj, newValue);
// On sort event
_dispatch_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] * / .A.call(obj, 'onsort', obj, column, order, newValue.map((row) => row));
return true;
}
}
/*** / }),
/*** / 160:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / e: function() { return /* binding * / setFooter; }
/* harmony export * / });
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(530);
const setFooter = function(data) {
const obj = this;
if (data) {
obj.options.footers = data;
}
if (obj.options.footers) {
if (! obj.tfoot) {
obj.tfoot = document.createElement('tfoot');
obj.table.appendChild(obj.tfoot);
}
for (let j = 0; j < obj.options.footers.length; j++) {
let tr;
if (obj.tfoot.children[j]) {
tr = obj.tfoot.children[j];
} else {
tr = document.createElement('tr');
const td = document.createElement('td');
tr.appendChild(td);
obj.tfoot.appendChild(tr);
}
for (let i = 0; i < obj.headers.length; i++) {
if (! obj.options.footers[j][i]) {
obj.options.footers[j][i] = '';
}
let td;
if (obj.tfoot.children[j].children[i+1]) {
td = obj.tfoot.children[j].children[i+1];
} else {
td = document.createElement('td');
tr.appendChild(td);
// Text align
const colAlign = obj.options.columns[i].align || obj.options.defaultColAlign || 'center';
td.style.textAlign = colAlign;
}
td.textContent = _internal_js__WEBPACK_IMPORTED_MODULE_0__/* .parseValue * / .$x.call(obj, +obj.records.length + i, j, obj.options.footers[j][i]);
// Hide/Show with hideColumn()/showColumn()
td.style.display = obj.cols[i].colElement.style.display;
}
}
}
}
/*** / }),
/*** / 167:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / $f: function() { return /* binding * / quantiyOfPages; },
/* harmony export * / IV: function() { return /* binding * / updatePagination; },
/* harmony export * / MY: function() { return /* binding * / page; },
/* harmony export * / ho: function() { return /* binding * / whichPage; }
/* harmony export * / });
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(805);
/* harmony import * / var _selection_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(657);
/**
* Which page the row is
* /
const whichPage = function(row) {
const obj = this;
// Search
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
row = obj.results.indexOf(row);
}
return (Math.ceil((parseInt(row) + 1) / parseInt(obj.options.pagination))) - 1;
}
/**
* Update the pagination
* /
const updatePagination = function() {
const obj = this;
// Reset container
obj.pagination.children[0].innerHTML = '';
obj.pagination.children[1].innerHTML = '';
// Start pagination
if (obj.options.pagination) {
// Searchable
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results.length;
} else {
results = obj.rows.length;
}
if (! results) {
// No records found
obj.pagination.children[0].innerHTML = jSuites.translate('No records found');
} else {
// Pagination container
const quantyOfPages = Math.ceil(results / obj.options.pagination);
let startNumber, finalNumber;
if (obj.pageNumber < 6) {
startNumber = 1;
finalNumber = quantyOfPages < 10 ? quantyOfPages : 10;
} else if (quantyOfPages - obj.pageNumber < 5) {
startNumber = quantyOfPages - 9;
finalNumber = quantyOfPages;
if (startNumber < 1) {
startNumber = 1;
}
} else {
startNumber = obj.pageNumber - 4;
finalNumber = obj.pageNumber + 5;
}
// First
if (startNumber > 1) {
const paginationItem = document.createElement('div');
paginationItem.className = 'jss_page';
paginationItem.innerHTML = '<';
paginationItem.title = 1;
obj.pagination.children[1].appendChild(paginationItem);
}
// Get page links
for (let i = startNumber; i <= finalNumber; i++) {
const paginationItem = document.createElement('div');
paginationItem.className = 'jss_page';
paginationItem.innerHTML = i;
obj.pagination.children[1].appendChild(paginationItem);
if (obj.pageNumber == (i-1)) {
paginationItem.classList.add('jss_page_selected');
}
}
// Last
if (finalNumber < quantyOfPages) {
const paginationItem = document.createElement('div');
paginationItem.className = 'jss_page';
paginationItem.innerHTML = '>';
paginationItem.title = quantyOfPages;
obj.pagination.children[1].appendChild(paginationItem);
}
// Text
const format = function(format) {
const args = Array.prototype.slice.call(arguments, 1);
return format.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined'
? args[number]
: match
;
});
};
obj.pagination.children[0].innerHTML = format(jSuites.translate('Showing page {0} of {1} entries'), obj.pageNumber + 1, quantyOfPages)
}
}
}
/**
* Go to page
* /
const page = function(pageNumber) {
const obj = this;
const oldPage = obj.pageNumber;
// Search
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results;
} else {
results = obj.rows;
}
// Per page
const quantityPerPage = parseInt(obj.options.pagination);
// pageNumber
if (pageNumber == null || pageNumber == -1) {
// Last page
pageNumber = Math.ceil(results.length / quantityPerPage) - 1;
}
// Page number
obj.pageNumber = pageNumber;
let startRow = (pageNumber * quantityPerPage);
let finalRow = (pageNumber * quantityPerPage) + quantityPerPage;
if (finalRow > results.length) {
finalRow = results.length;
}
if (startRow < 0) {
startRow = 0;
}
// Reset container
while (obj.tbody.firstChild) {
obj.tbody.removeChild(obj.tbody.firstChild);
}
// Appeding items
for (let j = startRow; j < finalRow; j++) {
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
obj.tbody.appendChild(obj.rows[results[j]].element);
} else {
obj.tbody.appendChild(obj.rows[j].element);
}
}
if (obj.options.pagination > 0) {
updatePagination.call(obj);
}
// Update corner position
_selection_js__WEBPACK_IMPORTED_MODULE_0__/* .updateCornerPosition * / .Aq.call(obj);
// Events
_dispatch_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] * / .A.call(obj, 'onchangepage', obj, pageNumber, oldPage, obj.options.pagination);
}
const quantiyOfPages = function() {
const obj = this;
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results.length;
} else {
results = obj.rows.length;
}
return Math.ceil(results / obj.options.pagination);
}
/*** / }),
/*** / 296:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / w: function() { return /* binding * / getFreezeWidth; }
/* harmony export * / });
// Get width of all freezed cells together
const getFreezeWidth = function() {
const obj = this;
let width = 0;
if (obj.options.freezeColumns > 0) {
for (let i = 0; i < obj.options.freezeColumns; i++) {
let columnWidth;
if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].width !== undefined) {
columnWidth = parseInt(obj.options.columns[i].width);
} else {
columnWidth = obj.options.defaultColWidth !== undefined ? parseInt(obj.options.defaultColWidth) : 100;
}
width += columnWidth;
}
}
return width;
}
/*** / }),
/*** / 392:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / Ar: function() { return /* binding * / hideToolbar; },
/* harmony export * / ll: function() { return /* binding * / showToolbar; },
/* harmony export * / nK: function() { return /* binding * / updateToolbar; }
/* harmony export * / });
/* unused harmony exports getDefault, createToolbar * /
/* harmony import * / var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(978);
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(530);
const setItemStatus = function(toolbarItem, worksheet) {
if (worksheet.options.editable != false) {
toolbarItem.classList.remove('jtoolbar-disabled');
} else {
toolbarItem.classList.add('jtoolbar-disabled');
}
}
const getDefault = function() {
const items = [];
const spreadsheet = this;
const getActive = function() {
return _internal_js__WEBPACK_IMPORTED_MODULE_0__/* .getWorksheetInstance * / .eN.call(spreadsheet);
}
items.push({
content: 'undo',
onclick: function() {
const worksheet = getActive();
worksheet.undo();
}
});
items.push({
content: 'redo',
onclick: function() {
const worksheet = getActive();
worksheet.redo();
}
});
items.push({
content: 'save',
onclick: function () {
const worksheet = getActive();
if (worksheet) {
worksheet.download();
}
}
});
items.push({
type:'divisor',
});
items.push({
type:'select',
width: '120px',
options: [ 'Default', 'Verdana', 'Arial', 'Courier New' ],
render: function(e) {
return '<span style="font-family:' + e + '">' + e + '</span>';
},
onchange: function(a,b,c,d,e) {
const worksheet = getActive();
let cells = worksheet.getSelected(true);
if (cells) {
let value = (! e) ? '' : d;
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, 'font-family: ' + value ];
})));
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type: 'select',
width: '48px',
content: 'format_size',
options: ['x-small','small','medium','large','x-large'],
render: function(e) {
return '<span style="font-size:' + e + '">' + e + '</span>';
},
onchange: function(a, b, c, value) {
const worksheet = getActive();
let cells = worksheet.getSelected(true);
if (cells) {
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, 'font-size: ' + value ];
})));
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type: 'select',
options: ['left','center','right','justify'],
render: function(e) {
return '<i class="material-icons">format_align_' + e + '</i>';
},
onchange: function(a, b, c, value) {
const worksheet = getActive();
let cells = worksheet.getSelected(true);
if (cells) {
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, 'text-align: ' + value];
})));
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
content: 'format_bold',
onclick: function(a,b,c) {
const worksheet = getActive();
let cells = worksheet.getSelected(true);
if (cells) {
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, 'font-weight:bold'];
})));
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type: 'color',
content: 'format_color_text',
k: 'color',
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type: 'color',
content: 'format_color_fill',
k: 'background-color',
updateState: function(a, b, toolbarItem, d) {
setItemStatus(toolbarItem, getActive());
}
});
let verticalAlign = [ 'top','middle','bottom' ];
items.push({
type: 'select',
options: [ 'vertical_align_top', 'vertical_align_center', 'vertical_align_bottom' ],
render: function(e) {
return '<i class="material-icons">' + e + '</i>';
},
value: 1,
onchange: function(a, b, c, d, value) {
const worksheet = getActive();
let cells = worksheet.getSelected(true);
if (cells) {
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, 'vertical-align: ' + verticalAlign[value]];
})));
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
content: 'web',
tooltip: jSuites.translate('Merge the selected cells'),
onclick: function() {
const worksheet = getActive();
if (worksheet.selectedCell && confirm(jSuites.translate('The merged cells will retain the value of the top-left cell only. Are you sure?'))) {
const selectedRange = [
Math.min(worksheet.selectedCell[0], worksheet.selectedCell[2]),
Math.min(worksheet.selectedCell[1], worksheet.selectedCell[3]),
Math.max(worksheet.selectedCell[0], worksheet.selectedCell[2]),
Math.max(worksheet.selectedCell[1], worksheet.selectedCell[3]),
];
let cell = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getCellNameFromCoords)(selectedRange[0], selectedRange[1]);
if (worksheet.records[selectedRange[1]][selectedRange[0]].element.getAttribute('data-merged')) {
worksheet.removeMerge(cell);
} else {
let colspan = selectedRange[2] - selectedRange[0] + 1;
let rowspan = selectedRange[3] - selectedRange[1] + 1;
if (colspan !== 1 || rowspan !== 1) {
worksheet.setMerge(cell, colspan, rowspan);
}
}
}
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type: 'select',
options: [ 'border_all', 'border_outer', 'border_inner', 'border_horizontal', 'border_vertical', 'border_left', 'border_top', 'border_right', 'border_bottom', 'border_clear' ],
columns: 5,
render: function(e) {
return '<i class="material-icons">' + e + '</i>';
},
right: true,
onchange: function(a,b,c,d) {
const worksheet = getActive();
if (worksheet.selectedCell) {
const selectedRange = [
Math.min(worksheet.selectedCell[0], worksheet.selectedCell[2]),
Math.min(worksheet.selectedCell[1], worksheet.selectedCell[3]),
Math.max(worksheet.selectedCell[0], worksheet.selectedCell[2]),
Math.max(worksheet.selectedCell[1], worksheet.selectedCell[3]),
];
let type = d;
if (selectedRange) {
// Default options
let thickness = b.thickness || 1;
let color = b.color || 'black';
const borderStyle = b.style || 'solid';
if (borderStyle === 'double') {
thickness += 2;
}
let style = {};
// Matrix
let px = selectedRange[0];
let py = selectedRange[1];
let ux = selectedRange[2];
let uy = selectedRange[3];
const setBorder = function(columnName, i, j) {
let border = [ '','','','' ];
if (((type === 'border_top' || type === 'border_outer') && j === py) ||
((type === 'border_inner' || type === 'border_horizontal') && j > py) ||
(type === 'border_all')) {
border[0] = 'border-top: ' + thickness + 'px ' + borderStyle + ' ' + color;
} else {
border[0] = 'border-top: ';
}
if ((type === 'border_all' || type === 'border_right' || type === 'border_outer') && i === ux) {
border[1] = 'border-right: ' + thickness + 'px ' + borderStyle + ' ' + color;
} else {
border[1] = 'border-right: ';
}
if ((type === 'border_all' || type === 'border_bottom' || type === 'border_outer') && j === uy) {
border[2] = 'border-bottom: ' + thickness + 'px ' + borderStyle + ' ' + color;
} else {
border[2] = 'border-bottom: ';
}
if (((type === 'border_left' || type === 'border_outer') && i === px) ||
((type === 'border_inner' || type === 'border_vertical') && i > px) ||
(type === 'border_all')) {
border[3] = 'border-left: ' + thickness + 'px ' + borderStyle + ' ' + color;
} else {
border[3] = 'border-left: ';
}
style[columnName] = border.join(';');
}
for (let j = selectedRange[1]; j <= selectedRange[3]; j++) { // Row - py - uy
for (let i = selectedRange[0]; i <= selectedRange[2]; i++) { // Col - px - ux
setBorder((0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getCellNameFromCoords)(i, j), i, j);
if (worksheet.records[j][i].element.getAttribute('data-merged')) {
setBorder(
(0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.getCellNameFromCoords)(
selectedRange[0],
selectedRange[1],
),
i,
j
);
}
}
}
if (Object.keys(style)) {
worksheet.setStyle(style);
}
}
}
},
onload: function(a, b) {
// Border color
let container = document.createElement('div');
let div = document.createElement('div');
container.appendChild(div);
let colorPicker = jSuites.color(div, {
closeOnChange: false,
onchange: function(o, v) {
o.parentNode.children[1].style.color = v;
b.color = v;
},
});
let i = document.createElement('i');
i.classList.add('material-icons');
i.innerHTML = 'color_lens';
i.onclick = function() {
colorPicker.open();
}
container.appendChild(i);
a.children[1].appendChild(container);
div = document.createElement('div');
jSuites.picker(div, {
type: 'select',
data: [ 1, 2, 3, 4, 5 ],
render: function(e) {
return '<div style="height: ' + e + 'px; width: 30px; background-color: black;"></div>';
},
onchange: function(a, k, c, d) {
b.thickness = d;
},
width: '50px',
});
a.children[1].appendChild(div);
const borderStylePicker = document.createElement('div');
jSuites.picker(borderStylePicker, {
type: 'select',
data: ['solid', 'dotted', 'dashed', 'double'],
render: function(e) {
if (e === 'double') {
return '<div style="width: 30px; border-top: 3px ' + e + ' black;"></div>';
}
return '<div style="width: 30px; border-top: 2px ' + e + ' black;"></div>';
},
onchange: function(a, k, c, d) {
b.style = d;
},
width: '50px',
});
a.children[1].appendChild(borderStylePicker);
div = document.createElement('div');
div.style.flex = '1'
a.children[1].appendChild(div);
},
updateState: function(a, b, toolbarItem) {
setItemStatus(toolbarItem, getActive());
}
});
items.push({
type:'divisor',
});
items.push({
content: 'fullscreen',
tooltip: 'Toggle Fullscreen',
onclick: function(a,b,c) {
if (c.children[0].textContent === 'fullscreen') {
spreadsheet.fullscreen(true);
c.children[0].textContent = 'fullscreen_exit';
} else {
spreadsheet.fullscreen(false);
c.children[0].textContent = 'fullscreen';
}
},
updateState: function(a,b,c,d) {
if (d.parent.config.fullscreen === true) {
c.children[0].textContent = 'fullscreen_exit';
} else {
c.children[0].textContent = 'fullscreen';
}
}
});
return items;
}
const adjustToolbarSettingsForJSuites = function(toolbar) {
const spreadsheet = this;
const items = toolbar.items;
for (let i = 0; i < items.length; i++) {
// Tooltip
if (items[i].tooltip) {
items[i].title = items[i].tooltip;
delete items[i].tooltip;
}
if (items[i].type == 'select') {
if (items[i].options) {
items[i].data = items[i].options;
delete items[i].options;
} else {
items[i].data = items[i].v;
delete items[i].v;
if (items[i].k && !items[i].onchange) {
items[i].onchange = function(el, config, value) {
const worksheet = _internal_js__WEBPACK_IMPORTED_MODULE_0__/* .getWorksheetInstance * / .eN.call(spreadsheet);
const cells = worksheet.getSelected(true);
worksheet.setStyle(
Object.fromEntries(cells.map(function(cellName) {
return [cellName, items[i].k + ': ' + value]
}))
);
}
}
}
} else if (items[i].type == 'color') {
items[i].type = 'i';
items[i].onclick = function(a,b,c) {
if (! c.color) {
jSuites.color(c, {
onchange: function(o, v) {
const worksheet = _internal_js__WEBPACK_IMPORTED_MODULE_0__/* .getWorksheetInstance * / .eN.call(spreadsheet);
const cells = worksheet.getSelected(true);
worksheet.setStyle(Object.fromEntries(cells.map(function(cellName) {
return [cellName, items[i].k + ': ' + v];
})));
},
onopen: function(o) {
o.color.select('');
}
});
c.color.open();
}
}
}
}
}
/**
* Create toolbar
* /
const createToolbar = function(toolbar) {
const spreadsheet = this;
const toolbarElement = document.createElement('div');
toolbarElement.classList.add('jss_toolbar');
adjustToolbarSettingsForJSuites.call(spreadsheet, toolbar);
if (typeof spreadsheet.plugins === 'object') {
Object.entries(spreadsheet.plugins).forEach(function([, plugin]) {
if (typeof plugin.toolbar === 'function') {
const result = plugin.toolbar(toolbar);
if (result) {
toolbar = result;
}
}
});
}
jSuites.toolbar(toolbarElement, toolbar);
return toolbarElement;
}
const updateToolbar = function(worksheet) {
if (worksheet.parent.toolbar) {
worksheet.parent.toolbar.toolbar.update(worksheet);
}
}
const showToolbar = function() {
const spreadsheet = this;
if (spreadsheet.config.toolbar && !spreadsheet.toolbar) {
let toolbar;
if (Array.isArray(spreadsheet.config.toolbar)) {
toolbar = {
items: spreadsheet.config.toolbar,
};
} else if (typeof spreadsheet.config.toolbar === 'object') {
toolbar = spreadsheet.config.toolbar;
} else {
toolbar = {
items: getDefault.call(spreadsheet),
};
if (typeof spreadsheet.config.toolbar === 'function') {
toolbar = spreadsheet.config.toolbar(toolbar);
}
}
spreadsheet.toolbar = spreadsheet.element.insertBefore(
createToolbar.call(
spreadsheet,
toolbar,
),
spreadsheet.element.children[1],
);
}
}
const hideToolbar = function() {
const spreadsheet = this;
if (spreadsheet.toolbar) {
spreadsheet.toolbar.parentNode.removeChild(spreadsheet.toolbar);
delete spreadsheet.toolbar;
}
}
/*** / }),
/*** / 497:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / AG: function() { return /* binding * / loadValidation; },
/* harmony export * / G_: function() { return /* binding * / loadUp; },
/* harmony export * / p6: function() { return /* binding * / loadDown; },
/* harmony export * / wu: function() { return /* binding * / loadPage; }
/* harmony export * / });
/**
* Go to a page in a lazyLoading
* /
const loadPage = function(pageNumber) {
const obj = this;
// Search
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results;
} else {
results = obj.rows;
}
// Per page
const quantityPerPage = 100;
// pageNumber
if (pageNumber == null || pageNumber == -1) {
// Last page
pageNumber = Math.ceil(results.length / quantityPerPage) - 1;
}
let startRow = (pageNumber * quantityPerPage);
let finalRow = (pageNumber * quantityPerPage) + quantityPerPage;
if (finalRow > results.length) {
finalRow = results.length;
}
startRow = finalRow - 100;
if (startRow < 0) {
startRow = 0;
}
// Appeding items
for (let j = startRow; j < finalRow; j++) {
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
obj.tbody.appendChild(obj.rows[results[j]].element);
} else {
obj.tbody.appendChild(obj.rows[j].element);
}
if (obj.tbody.children.length > quantityPerPage) {
obj.tbody.removeChild(obj.tbody.firstChild);
}
}
}
const loadValidation = function() {
const obj = this;
if (obj.selectedCell) {
const currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100;
const selectedPage = parseInt(obj.selectedCell[3] / 100);
const totalPages = parseInt(obj.rows.length / 100);
if (currentPage != selectedPage && selectedPage <= totalPages) {
if (! Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]].element)) {
obj.loadPage(selectedPage);
return true;
}
}
}
return false;
}
const loadUp = function() {
const obj = this;
// Search
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results;
} else {
results = obj.rows;
}
let test = 0;
if (results.length > 100) {
// Get the first element in the page
let item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
item = results.indexOf(item);
}
if (item > 0) {
for (let j = 0; j < 30; j++) {
item = item - 1;
if (item > -1) {
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
obj.tbody.insertBefore(obj.rows[results[item]].element, obj.tbody.firstChild);
} else {
obj.tbody.insertBefore(obj.rows[item].element, obj.tbody.firstChild);
}
if (obj.tbody.children.length > 100) {
obj.tbody.removeChild(obj.tbody.lastChild);
test = 1;
}
}
}
}
}
return test;
}
const loadDown = function() {
const obj = this;
// Search
let results;
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
results = obj.results;
} else {
results = obj.rows;
}
let test = 0;
if (results.length > 100) {
// Get the last element in the page
let item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
item = results.indexOf(item);
}
if (item < obj.rows.length - 1) {
for (let j = 0; j <= 30; j++) {
if (item < results.length) {
if ((obj.options.search == true || obj.options.filters == true) && obj.results) {
obj.tbody.appendChild(obj.rows[results[item]].element);
} else {
obj.tbody.appendChild(obj.rows[item].element);
}
if (obj.tbody.children.length > 100) {
obj.tbody.removeChild(obj.tbody.firstChild);
test = 1;
}
}
item = item + 1;
}
}
}
return test;
}
/*** / }),
/*** / 530:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / $O: function() { return /* binding * / getWorksheetActive; },
/* harmony export * / $x: function() { return /* binding * / parseValue; },
/* harmony export * / C6: function() { return /* binding * / showIndex; },
/* harmony export * / Em: function() { return /* binding * / executeFormula; },
/* harmony export * / P9: function() { return /* binding * / createCell; },
/* harmony export * / Rs: function() { return /* binding * / updateScroll; },
/* harmony export * / TI: function() { return /* binding * / hideIndex; },
/* harmony export * / Xr: function() { return /* binding * / getCellFromCoords; },
/* harmony export * / Y5: function() { return /* binding * / fullscreen; },
/* harmony export * / am: function() { return /* binding * / updateTable; },
/* harmony export * / dw: function() { return /* binding * / isFormula; },
/* harmony export * / eN: function() { return /* binding * / getWorksheetInstance; },
/* harmony export * / hG: function() { return /* binding * / updateResult; },
/* harmony export * / ju: function() { return /* binding * / createNestedHeader; },
/* harmony export * / k9: function() { return /* binding * / updateCell; },
/* harmony export * / o8: function() { return /* binding * / updateTableReferences; },
/* harmony export * / p9: function() { return /* binding * / getLabel; },
/* harmony export * / rS: function() { return /* binding * / getMask; },
/* harmony export * / tT: function() { return /* binding * / getCell; },
/* harmony export * / xF: function() { return /* binding * / updateFormulaChain; },
/* harmony export * / yB: function() { return /* binding * / updateFormula; }
/* harmony export * / });
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(805);
/* harmony import * / var _selection_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(657);
/* harmony import * / var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(978);
/* harmony import * / var _meta_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(654);
/* harmony import * / var _freeze_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(296);
/* harmony import * / var _pagination_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(167);
/* harmony import * / var _footer_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(160);
/* harmony import * / var _internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(689);
const updateTable = function() {
const obj = this;
// Check for spare
if (obj.options.minSpareRows > 0) {
let numBlankRows = 0;
for (let j = obj.rows.length - 1; j >= 0; j--) {
let test = false;
for (let i = 0; i < obj.headers.length; i++) {
if (obj.options.data[j][i]) {
test = true;
}
}
if (test) {
break;
} else {
numBlankRows++;
}
}
if (obj.options.minSpareRows - numBlankRows > 0) {
obj.insertRow(obj.options.minSpareRows - numBlankRows)
}
}
if (obj.options.minSpareCols > 0) {
let numBlankCols = 0;
for (let i = obj.headers.length - 1; i >= 0 ; i--) {
let test = false;
for (let j = 0; j < obj.rows.length; j++) {
if (obj.options.data[j][i]) {
test = true;
}
}
if (test) {
break;
} else {
numBlankCols++;
}
}
if (obj.options.minSpareCols - numBlankCols > 0) {
obj.insertColumn(obj.options.minSpareCols - numBlankCols)
}
}
// Update footers
if (obj.options.footers) {
_footer_js__WEBPACK_IMPORTED_MODULE_0__/* .setFooter * / .e.call(obj);
}
// Update corner position
setTimeout(function() {
_selection_js__WEBPACK_IMPORTED_MODULE_1__/* .updateCornerPosition * / .Aq.call(obj);
},0);
}
/**
* Trying to extract a number from a string
* /
const parseNumber = function(value, columnNumber) {
const obj = this;
// Decimal point
const decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.';
// Parse both parts of the number
let number = ('' + value);
number = number.split(decimal);
number[0] = number[0].match(/[+-]?[0-9]/g);
if (number[0]) {
number[0] = number[0].join('');
}
if (number[1]) {
number[1] = number[1].match(/[0-9]* /g).join('');
}
// Is a valid number
if (number[0] && Number.isInteger(Number(number[0]))) {
if (! number[1]) {
value = Number(number[0] + '.00');
} else {
value = Number(number[0] + '.' + number[1]);
}
} else {
value = null;
}
return value;
}
/**
* Parse formulas
* /
const executeFormula = function(expression, x, y) {
const obj = this;
const formulaResults = [];
const formulaLoopProtection = [];
// Execute formula with loop protection
const execute = function(expression, x, y) {
// Parent column identification
const parentId = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([x, y]);
// Code protection
if (formulaLoopProtection[parentId]) {
console.error('Reference loop detected');
return '#ERROR';
}
formulaLoopProtection[parentId] = true;
// Convert range tokens
const tokensUpdate = function(tokens) {
for (let index = 0; index < tokens.length; index++) {
const f = [];
const token = tokens[index].split(':');
const e1 = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(token[0], true);
const e2 = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(token[1], true);
let x1, x2;
if (e1[0] <= e2[0]) {
x1 = e1[0];
x2 = e2[0];
} else {
x1 = e2[0];
x2 = e1[0];
}
let y1, y2;
if (e1[1] <= e2[1]) {
y1 = e1[1];
y2 = e2[1];
} else {
y1 = e2[1];
y2 = e1[1];
}
for (let j = y1; j <= y2; j++) {
for (let i = x1; i <= x2; i++) {
f.push((0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([i, j]));
}
}
expression = expression.replace(tokens[index], f.join(','));
}
}
// Range with $ remove $
expression = expression.replace(/\$?([A-Z]+)\$?([0-9]+)/g, "$1$2");
let tokens = expression.match(/([A-Z]+[0-9]+)\:([A-Z]+[0-9]+)/g);
if (tokens && tokens.length) {
tokensUpdate(tokens);
}
// Get tokens
tokens = expression.match(/([A-Z]+[0-9]+)/g);
// Direct self-reference protection
if (tokens && tokens.indexOf(parentId) > -1) {
console.error('Self Reference detected');
return '#ERROR';
} else {
// Expressions to be used in the parsing
const formulaExpressions = {};
if (tokens) {
for (let i = 0; i < tokens.length; i++) {
// Keep chain
if (! obj.formula[tokens[i]]) {
obj.formula[tokens[i]] = [];
}
// Is already in the register
if (obj.formula[tokens[i]].indexOf(parentId) < 0) {
obj.formula[tokens[i]].push(parentId);
}
// Do not calculate again
if (eval('typeof(' + tokens[i] + ') == "undefined"')) {
// Coords
const position = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(tokens[i], 1);
// Get value
let value;
if (typeof(obj.options.data[position[1]]) != 'undefined' && typeof(obj.options.data[position[1]][position[0]]) != 'undefined') {
value = obj.options.data[position[1]][position[0]];
} else {
value = '';
}
// Get column data
if ((''+value).substr(0,1) == '=') {
if (typeof formulaResults[tokens[i]] !== 'undefined') {
value = formulaResults[tokens[i]];
} else {
value = execute(value, position[0], position[1]);
formulaResults[tokens[i]] = value;
}
}
// Type!
if ((''+value).trim() == '') {
// Null
formulaExpressions[tokens[i]] = null;
} else {
if (value == Number(value) && obj.parent.config.autoCasting != false) {
// Number
formulaExpressions[tokens[i]] = Number(value);
} else {
// Trying any formatted number
const number = parseNumber.call(obj, value, position[0])
if (obj.parent.config.autoCasting != false && number) {
formulaExpressions[tokens[i]] = number;
} else {
formulaExpressions[tokens[i]] = '"' + value + '"';
}
}
}
}
}
}
const ret = _dispatch_js__WEBPACK_IMPORTED_MODULE_3__/* ["default"] * / .A.call(obj, 'onbeforeformula', obj, expression, x, y);
if (ret === false) {
return expression;
} else if (ret) {
expression = ret;
}
// Convert formula to javascript
let res;
try {
res = formula(expression.substr(1), formulaExpressions, x, y, obj);
if (typeof res === 'function') {
res = '#ERROR'
}
} catch (e) {
res = '#ERROR';
if (obj.parent.config.debugFormulas === true) {
console.log(expression.substr(1), formulaExpressions, e)
}
}
return res;
}
}
return execute(expression, x, y);
}
const parseValue = function(i, j, value, cell) {
const obj = this;
if ((''+value).substr(0,1) == '=' && obj.parent.config.parseFormulas != false) {
value = executeFormula.call(obj, value, i, j)
}
// Column options
const options = obj.options.columns && obj.options.columns[i];
if (options && ! isFormula(value)) {
// Mask options
let opt = null;
if (opt = getMask(options)) {
if (value && value == Number(value)) {
value = Number(value);
}
// Process the decimals to match the mask
let masked = jSuites.mask.render(value, opt, true);
// Negative indication
if (cell) {
if (opt.mask) {
const t = opt.mask.split(';');
if (t[1]) {
const t1 = t[1].match(new RegExp('\\[Red\\]', 'gi'));
if (t1) {
if (value < 0) {
cell.classList.add('red');
} else {
cell.classList.remove('red');
}
}
const t2 = t[1].match(new RegExp('\\(', 'gi'));
if (t2) {
if (value < 0) {
masked = '(' + masked + ')';
}
}
}
}
}
if (masked) {
value = masked;
}
}
}
return value;
}
/**
* Get dropdown value from key
* /
const getDropDownValue = function(column, key) {
const obj = this;
const value = [];
if (obj.options.columns && obj.options.columns[column] && obj.options.columns[column].source) {
// Create array from source
const combo = [];
const source = obj.options.columns[column].source;
for (let i = 0; i < source.length; i++) {
if (typeof(source[i]) == 'object') {
combo[source[i].id] = source[i].name;
} else {
combo[source[i]] = source[i];
}
}
// Guarantee single multiple compatibility
const keys = Array.isArray(key) ? key : ('' + key).split(';');
for (let i = 0; i < keys.length; i++) {
if (typeof(keys[i]) === 'object') {
value.push(combo[keys[i].id]);
} else {
if (combo[keys[i]]) {
value.push(combo[keys[i]]);
}
}
}
} else {
console.error('Invalid column');
}
return (value.length > 0) ? value.join('; ') : '';
}
const validDate = function(date) {
date = ''+date;
if (date.substr(4,1) == '-' && date.substr(7,1) == '-') {
return true;
} else {
date = date.split('-');
if ((date[0].length == 4 && date[0] == Number(date[0]) && date[1].length == 2 && date[1] == Number(date[1]))) {
return true;
}
}
return false;
}
/**
* Strip tags
* /
const stripScript = function(a) {
const b = new Option;
b.innerHTML = a;
let c = null;
for (a = b.getElementsByTagName('script'); c=a[0];) c.parentNode.removeChild(c);
return b.innerHTML;
}
const createCell = function(i, j, value) {
const obj = this;
// Create cell and properties
let td = document.createElement('td');
td.setAttribute('data-x', i);
td.setAttribute('data-y', j);
if( obj.headers[i].style.display === 'none' ){
td.style.display = 'none';
}
// Security
if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) {
const val = secureFormula(value);
if (val != value) {
// Update the data container
value = val;
}
}
// Custom column
if (obj.options.columns && obj.options.columns[i] && typeof obj.options.columns[i].type === 'object') {
if (obj.parent.config.parseHTML === true) {
td.innerHTML = value;
} else {
td.textContent = value;
}
if (typeof(obj.options.columns[i].type.createCell) == 'function') {
obj.options.columns[i].type.createCell(td, value, parseInt(i), parseInt(j), obj, obj.options.columns[i]);
}
} else {
// Hidden column
if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'hidden') {
td.style.display = 'none';
td.textContent = value;
} else if (obj.options.columns && obj.options.columns[i] && (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio')) {
// Create input
const element = document.createElement('input');
element.type = obj.options.columns[i].type;
element.name = 'c' + i;
element.checked = (value == 1 || value == true || value == 'true') ? true : false;
element.onclick = function() {
obj.setValue(td, this.checked);
}
if (obj.options.columns[i].readOnly == true || obj.options.editable == false) {
element.setAttribute('disabled', 'disabled');
}
// Append to the table
td.appendChild(element);
// Make sure the values are correct
obj.options.data[j][i] = element.checked;
} else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'calendar') {
// Try formatted date
let formatted = null;
if (! validDate(value)) {
const tmp = jSuites.calendar.extractDateFromString(value, (obj.options.columns[i].options && obj.options.columns[i].options.format) || 'YYYY-MM-DD');
if (tmp) {
formatted = tmp;
}
}
// Create calendar cell
td.textContent = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[i].options && obj.options.columns[i].options.format);
} else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'dropdown') {
// Create dropdown cell
td.classList.add('jss_dropdown');
td.textContent = getDropDownValue.call(obj, i, value);
} else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'color') {
if (obj.options.columns[i].render == 'square') {
const color = document.createElement('div');
color.className = 'color';
color.style.backgroundColor = value;
td.appendChild(color);
} else {
td.style.color = value;
td.textContent = value;
}
} else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'image') {
if (value && value.substr(0, 10) == 'data:image') {
const img = document.createElement('img');
img.src = value;
td.appendChild(img);
}
} else {
if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'html') {
td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));
} else {
if (obj.parent.config.parseHTML === true) {
td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));
} else {
td.textContent = parseValue.call(this, i, j, value, td);
}
}
}
}
// Readonly
if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].readOnly == true) {
td.className = 'readonly';
}
// Text align
const colAlign = (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].align) || obj.options.defaultColAlign || 'center';
td.style.textAlign = colAlign;
// Wrap option
if ((!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].wordWrap != false) && (obj.options.wordWrap == true || (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].wordWrap == true) || td.innerHTML.length > 200)) {
td.style.whiteSpace = 'pre-wrap';
}
// Overflow
if (i > 0) {
if (this.options.textOverflow == true) {
if (value || td.innerHTML) {
obj.records[j][i-1].element.style.overflow = 'hidden';
} else {
if (i == obj.options.columns.length - 1) {
td.style.overflow = 'hidden';
}
}
}
}
_dispatch_js__WEBPACK_IMPORTED_MODULE_3__/* ["default"] * / .A.call(obj, 'oncreatecell', obj, td, i, j, value);
return td;
}
/**
* Update cell content
*
* @param object cell
* @return void
* /
const updateCell = function(x, y, value, force) {
const obj = this;
let record;
// Changing value depending on the column type
if (obj.records[y][x].element.classList.contains('readonly') == true && ! force) {
// Do nothing
record = {
x: x,
y: y,
col: x,
row: y
}
} else {
// Security
if ((''+value).substr(0,1) == '=' && obj.options.secureFormulas == true) {
const val = secureFormula(value);
if (val != value) {
// Update the data container
value = val;
}
}
// On change
const val = _dispatch_js__WEBPACK_IMPORTED_MODULE_3__/* ["default"] * / .A.call(obj, 'onbeforechange', obj, obj.records[y][x].element, x, y, value);
// If you return something this will overwrite the value
if (val != undefined) {
value = val;
}
if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object' && typeof obj.options.columns[x].type.updateCell === 'function') {
const result = obj.options.columns[x].type.updateCell(obj.records[y][x].element, value, parseInt(x), parseInt(y), obj, obj.options.columns[x]);
if (result !== undefined) {
value = result;
}
}
// History format
record = {
x: x,
y: y,
col: x,
row: y,
value: value,
oldValue: obj.options.data[y][x],
}
let editor = obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object' ? obj.options.columns[x].type : null;
if (editor) {
// Update data and cell
obj.options.data[y][x] = value;
if (typeof(editor.setValue) === 'function') {
editor.setValue(obj.records[y][x].element, value);
}
} else {
// Native functions
if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio')) {
// Unchecked all options
if (obj.options.columns[x].type == 'radio') {
for (let j = 0; j < obj.options.data.length; j++) {
obj.options.data[j][x] = false;
}
}
// Update data and cell
obj.records[y][x].element.children[0].checked = (value == 1 || value == true || value == 'true' || value == 'TRUE') ? true : false;
obj.options.data[y][x] = obj.records[y][x].element.children[0].checked;
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {
// Update data and cell
obj.options.data[y][x] = value;
obj.records[y][x].element.textContent = getDropDownValue.call(obj, x, value);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {
// Try formatted date
let formatted = null;
if (! validDate(value)) {
const tmp = jSuites.calendar.extractDateFromString(value, (obj.options.columns[x].options && obj.options.columns[x].options.format) || 'YYYY-MM-DD');
if (tmp) {
formatted = tmp;
}
}
// Update data and cell
obj.options.data[y][x] = value;
obj.records[y][x].element.textContent = jSuites.calendar.getDateString(formatted ? formatted : value, obj.options.columns[x].options && obj.options.columns[x].options.format);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {
// Update color
obj.options.data[y][x] = value;
// Render
if (obj.options.columns[x].render == 'square') {
const color = document.createElement('div');
color.className = 'color';
color.style.backgroundColor = value;
obj.records[y][x].element.textContent = '';
obj.records[y][x].element.appendChild(color);
} else {
obj.records[y][x].element.style.color = value;
obj.records[y][x].element.textContent = value;
}
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {
value = ''+value;
obj.options.data[y][x] = value;
obj.records[y][x].element.innerHTML = '';
if (value && value.substr(0, 10) == 'data:image') {
const img = document.createElement('img');
img.src = value;
obj.records[y][x].element.appendChild(img);
}
} else {
// Update data and cell
obj.options.data[y][x] = value;
// Label
if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {
obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value));
} else {
if (obj.parent.config.parseHTML === true) {
obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value, obj.records[y][x].element));
} else {
obj.records[y][x].element.textContent = parseValue.call(obj, x, y, value, obj.records[y][x].element);
}
}
// Handle big text inside a cell
if ((!obj.options.columns || !obj.options.columns[x] || obj.options.columns[x].wordWrap != false) && (obj.options.wordWrap == true || (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].wordWrap == true) || obj.records[y][x].element.innerHTML.length > 200)) {
obj.records[y][x].element.style.whiteSpace = 'pre-wrap';
} else {
obj.records[y][x].element.style.whiteSpace = '';
}
}
}
// Overflow
if (x > 0) {
if (value) {
obj.records[y][x-1].element.style.overflow = 'hidden';
} else {
obj.records[y][x-1].element.style.overflow = '';
}
}
if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].render === 'function') {
obj.options.columns[x].render(
obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null,
value,
parseInt(x),
parseInt(y),
obj,
obj.options.columns[x],
);
}
// On change
_dispatch_js__WEBPACK_IMPORTED_MODULE_3__/* ["default"] * / .A.call(obj, 'onchange', obj, (obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null), x, y, value, record.oldValue);
}
return record;
}
/**
* The value is a formula
* /
const isFormula = function(value) {
const v = (''+value)[0];
return v == '=' || v == '#' ? true : false;
}
/**
* Get the mask in the jSuites.mask format
* /
const getMask = function(o) {
if (o.format || o.mask || o.locale) {
const opt = {};
if (o.mask) {
opt.mask = o.mask;
} else if (o.format) {
opt.mask = o.format;
} else {
opt.locale = o.locale;
opt.options = o.options;
}
if (o.decimal) {
if (! opt.options) {
opt.options = {};
}
opt.options = { decimal: o.decimal };
}
return opt;
}
return null;
}
/**
* Secure formula
* /
const secureFormula = function(oldValue) {
let newValue = '';
let inside = 0;
for (let i = 0; i < oldValue.length; i++) {
if (oldValue[i] == '"') {
if (inside == 0) {
inside = 1;
} else {
inside = 0;
}
}
if (inside == 1) {
newValue += oldValue[i];
} else {
newValue += oldValue[i].toUpperCase();
}
}
return newValue;
}
/**
* Update all related cells in the chain
* /
let chainLoopProtection = [];
const updateFormulaChain = function(x, y, records) {
const obj = this;
const cellId = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([x, y]);
if (obj.formula[cellId] && obj.formula[cellId].length > 0) {
if (chainLoopProtection[cellId]) {
obj.records[y][x].element.innerHTML = '#ERROR';
obj.formula[cellId] = '';
} else {
// Protection
chainLoopProtection[cellId] = true;
for (let i = 0; i < obj.formula[cellId].length; i++) {
const cell = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(obj.formula[cellId][i], true);
// Update cell
const value = ''+obj.options.data[cell[1]][cell[0]];
if (value.substr(0,1) == '=') {
records.push(updateCell.call(obj, cell[0], cell[1], value, true));
} else {
// No longer a formula, remove from the chain
Object.keys(obj.formula)[i] = null;
}
updateFormulaChain.call(obj, cell[0], cell[1], records);
}
}
}
chainLoopProtection = [];
}
/**
* Update formula
* /
const updateFormula = function(formula, referencesToUpdate) {
const testLetter = /[A-Z]/;
const testNumber = /[0-9]/;
let newFormula = '';
let letter = null;
let number = null;
let token = '';
for (let index = 0; index < formula.length; index++) {
if (testLetter.exec(formula[index])) {
letter = 1;
number = 0;
token += formula[index];
} else if (testNumber.exec(formula[index])) {
number = letter ? 1 : 0;
token += formula[index];
} else {
if (letter && number) {
token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
}
newFormula += token;
newFormula += formula[index];
letter = 0;
number = 0;
token = '';
}
}
if (token) {
if (letter && number) {
token = referencesToUpdate[token] ? referencesToUpdate[token] : token;
}
newFormula += token;
}
return newFormula;
}
/**
* Update formulas
* /
const updateFormulas = function(referencesToUpdate) {
const obj = this;
// Update formulas
for (let j = 0; j < obj.options.data.length; j++) {
for (let i = 0; i < obj.options.data[0].length; i++) {
const value = '' + obj.options.data[j][i];
// Is formula
if (value.substr(0,1) == '=') {
// Replace tokens
const newFormula = updateFormula(value, referencesToUpdate);
if (newFormula != value) {
obj.options.data[j][i] = newFormula;
}
}
}
}
// Update formula chain
const formula = [];
const keys = Object.keys(obj.formula);
for (let j = 0; j < keys.length; j++) {
// Current key and values
let key = keys[j];
const value = obj.formula[key];
// Update key
if (referencesToUpdate[key]) {
key = referencesToUpdate[key];
}
// Update values
formula[key] = [];
for (let i = 0; i < value.length; i++) {
let letter = value[i];
if (referencesToUpdate[letter]) {
letter = referencesToUpdate[letter];
}
formula[key].push(letter);
}
}
obj.formula = formula;
}
/**
* Update cell references
*
* @return void
* /
const updateTableReferences = function() {
const obj = this;
if( obj.skipUpdateTableReferences ){
return;
}
// Update headers
for (let i = 0; i < obj.headers.length; i++) {
const x = obj.headers[i].getAttribute('data-x');
if (x != i) {
// Update coords
obj.headers[i].setAttribute('data-x', i);
// Title
if (! obj.headers[i].getAttribute('title')) {
obj.headers[i].innerHTML = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_4__.getColumnName)(i);
}
}
}
// Update all rows
for (let j = 0; j < obj.rows.length; j++) {
if (obj.rows[j]) {
const y = obj.rows[j].element.getAttribute('data-y');
if (y != j) {
// Update coords
obj.rows[j].element.setAttribute('data-y', j);
obj.rows[j].element.children[0].setAttribute('data-y', j);
// Row number
obj.rows[j].element.children[0].innerHTML = j + 1;
}
}
}
// Regular cells affected by this change
const affectedTokens = [];
const mergeCellUpdates = [];
// Update cell
const updatePosition = function(x,y,i,j) {
if (x != i) {
obj.records[j][i].element.setAttribute('data-x', i);
}
if (y != j) {
obj.records[j][i].element.setAttribute('data-y', j);
}
// Other updates
if (x != i || y != j) {
const columnIdFrom = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([x, y]);
const columnIdTo = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([i, j]);
affectedTokens[columnIdFrom] = columnIdTo;
}
}
for (let j = 0; j < obj.records.length; j++) {
for (let i = 0; i < obj.records[0].length; i++) {
if (obj.records[j][i]) {
// Current values
const x = obj.records[j][i].element.getAttribute('data-x');
const y = obj.records[j][i].element.getAttribute('data-y');
// Update column
if (obj.records[j][i].element.getAttribute('data-merged')) {
const columnIdFrom = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([x, y]);
const columnIdTo = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getColumnNameFromId * / .t3)([i, j]);
if (mergeCellUpdates[columnIdFrom] == null) {
if (columnIdFrom == columnIdTo) {
mergeCellUpdates[columnIdFrom] = false;
} else {
const totalX = parseInt(i - x);
const totalY = parseInt(j - y);
mergeCellUpdates[columnIdFrom] = [ columnIdTo, totalX, totalY ];
}
}
} else {
updatePosition(x,y,i,j);
}
}
}
}
// Update merged if applicable
const keys = Object.keys(mergeCellUpdates);
if (keys.length) {
for (let i = 0; i < keys.length; i++) {
if (mergeCellUpdates[keys[i]]) {
const info = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(keys[i], true)
let x = info[0];
let y = info[1];
updatePosition(x,y,x + mergeCellUpdates[keys[i]][1],y + mergeCellUpdates[keys[i]][2]);
const columnIdFrom = keys[i];
const columnIdTo = mergeCellUpdates[keys[i]][0];
for (let j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) {
x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x'));
y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y'));
obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]);
obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]);
}
obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom];
delete(obj.options.mergeCells[columnIdFrom]);
}
}
}
// Update formulas
updateFormulas.call(obj, affectedTokens);
// Update meta data
_meta_js__WEBPACK_IMPORTED_MODULE_5__/* .updateMeta * / .hs.call(obj, affectedTokens);
// Refresh selection
_selection_js__WEBPACK_IMPORTED_MODULE_1__/* .refreshSelection * / .G9.call(obj);
// Update table with custom configuration if applicable
updateTable.call(obj);
}
/**
* Update scroll position based on the selection
* /
const updateScroll = function(direction) {
const obj = this;
// Jspreadsheet Container information
const contentRect = obj.content.getBoundingClientRect();
const x1 = contentRect.left;
const y1 = contentRect.top;
const w1 = contentRect.width;
const h1 = contentRect.height;
// Direction Left or Up
const reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]].element;
// Reference
const referenceRect = reference.getBoundingClientRect();
const x2 = referenceRect.left;
const y2 = referenceRect.top;
const w2 = referenceRect.width;
const h2 = referenceRect.height;
let x, y;
// Direction
if (direction == 0 || direction == 1) {
x = (x2 - x1) + obj.content.scrollLeft;
y = (y2 - y1) + obj.content.scrollTop - 2;
} else {
x = (x2 - x1) + obj.content.scrollLeft + w2;
y = (y2 - y1) + obj.content.scrollTop + h2;
}
// Top position check
if (y > (obj.content.scrollTop + 30) && y < (obj.content.scrollTop + h1)) {
// In the viewport
} else {
// Out of viewport
if (y < obj.content.scrollTop + 30) {
obj.content.scrollTop = y - h2;
} else {
obj.content.scrollTop = y - (h1 - 2);
}
}
// Freeze columns?
const freezed = _freeze_js__WEBPACK_IMPORTED_MODULE_6__/* .getFreezeWidth * / .w.call(obj);
// Left position check - TODO: change that to the bottom border of the element
if (x > (obj.content.scrollLeft + freezed) && x < (obj.content.scrollLeft + w1)) {
// In the viewport
} else {
// Out of viewport
if (x < obj.content.scrollLeft + 30) {
obj.content.scrollLeft = x;
if (obj.content.scrollLeft < 50) {
obj.content.scrollLeft = 0;
}
} else if (x < obj.content.scrollLeft + freezed) {
obj.content.scrollLeft = x - freezed - 1;
} else {
obj.content.scrollLeft = x - (w1 - 20);
}
}
}
const updateResult = function() {
const obj = this;
let total = 0;
let index = 0;
// Page 1
if (obj.options.lazyLoading == true) {
total = 100;
} else if (obj.options.pagination > 0) {
total = obj.options.pagination;
} else {
if (obj.results) {
total = obj.results.length;
} else {
total = obj.rows.length;
}
}
// Reset current nodes
while (obj.tbody.firstChild) {
obj.tbody.removeChild(obj.tbody.firstChild);
}
// Hide all records from the table
for (let j = 0; j < obj.rows.length; j++) {
if (! obj.results || obj.results.indexOf(j) > -1) {
if (index < total) {
obj.tbody.appendChild(obj.rows[j].element);
index++;
}
obj.rows[j].element.style.display = '';
} else {
obj.rows[j].element.style.display = 'none';
}
}
// Update pagination
if (obj.options.pagination > 0) {
_pagination_js__WEBPACK_IMPORTED_MODULE_7__/* .updatePagination * / .IV.call(obj);
}
_selection_js__WEBPACK_IMPORTED_MODULE_1__/* .updateCornerPosition * / .Aq.call(obj);
return total;
}
/**
* Get the cell object
*
* @param object cell
* @return string value
* /
const getCell = function(x, y) {
const obj = this;
if (typeof x === 'string') {
// Convert in case name is excel liked ex. A10, BB92
const cell = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(x, true);
x = cell[0];
y = cell[1];
}
return obj.records[y][x].element;
}
/**
* Get the cell object from coords
*
* @param object cell
* @return string value
* /
const getCellFromCoords = function(x, y) {
const obj = this;
return obj.records[y][x].element;
}
/**
* Get label
*
* @param object cell
* @return string value
* /
const getLabel = function(x, y) {
const obj = this;
if (typeof x === 'string') {
// Convert in case name is excel liked ex. A10, BB92
const cell = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__/* .getIdFromColumnName * / .vu)(x, true);
x = cell[0];
y = cell[1];
}
return obj.records[y][x].element.innerHTML;
}
/**
* Activate/Disable fullscreen
* use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false);
* @Param {boolean} activate
* /
const fullscreen = function(activate) {
const spreadsheet = this;
// If activate not defined, get reverse options.fullscreen
if (activate == null) {
activate = ! spreadsheet.config.fullscreen;
}
// If change
if (spreadsheet.config.fullscreen != activate) {
spreadsheet.config.fullscreen = activate;
// Test LazyLoading conflict
if (activate == true) {
spreadsheet.element.classList.add('fullscreen');
} else {
spreadsheet.element.classList.remove('fullscreen');
}
}
}
/**
* Show index column
* /
const showIndex = function() {
const obj = this;
obj.table.classList.remove('jss_hidden_index');
}
/**
* Hide index column
* /
const hideIndex = function() {
const obj = this;
obj.table.classList.add('jss_hidden_index');
}
/**
* Create a nested header object
* /
const createNestedHeader = function(nestedInformation) {
const obj = this;
const tr = document.createElement('tr');
tr.classList.add('jss_nested');
const td = document.createElement('td');
td.classList.add('jss_selectall');
tr.appendChild(td);
// Element
nestedInformation.element = tr;
let headerIndex = 0;
for (let i = 0; i < nestedInformation.length; i++) {
// Default values
if (! nestedInformation[i].colspan) {
nestedInformation[i].colspan = 1;
}
if (! nestedInformation[i].title) {
nestedInformation[i].title = '';
}
if (! nestedInformation[i].id) {
nestedInformation[i].id = '';
}
// Number of columns
let numberOfColumns = nestedInformation[i].colspan;
// Classes container
const column = [];
// Header classes for this cell
for (let x = 0; x < numberOfColumns; x++) {
if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') {
numberOfColumns++;
}
column.push(headerIndex);
headerIndex++;
}
// Created the nested cell
const td = document.createElement('td');
td.setAttribute('data-column', column.join(','));
td.setAttribute('colspan', nestedInformation[i].colspan);
td.setAttribute('align', nestedInformation[i].align || 'center');
td.setAttribute('id', nestedInformation[i].id);
td.textContent = nestedInformation[i].title;
tr.appendChild(td);
}
return tr;
}
const getWorksheetActive = function() {
const spreadsheet = this.parent ? this.parent : this;
return spreadsheet.element.tabs ? spreadsheet.element.tabs.getActive() : 0;
}
const getWorksheetInstance = function(index) {
const spreadsheet = this;
const worksheetIndex = typeof index !== 'undefined' ? index : getWorksheetActive.call(spreadsheet);
return spreadsheet.worksheets[worksheetIndex];
}
/*** / }),
/*** / 654:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / IQ: function() { return /* binding * / getMeta; },
/* harmony export * / hs: function() { return /* binding * / updateMeta; },
/* harmony export * / iZ: function() { return /* binding * / setMeta; }
/* harmony export * / });
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(805);
/**
* Get meta information from cell(s)
*
* @return integer
* /
const getMeta = function(cell, key) {
const obj = this;
if (! cell) {
return obj.options.meta;
} else {
if (key) {
return obj.options.meta && obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null;
} else {
return obj.options.meta && obj.options.meta[cell] ? obj.options.meta[cell] : null;
}
}
}
/**
* Update meta information
*
* @return integer
* /
const updateMeta = function(affectedCells) {
const obj = this;
if (obj.options.meta) {
const newMeta = {};
const keys = Object.keys(obj.options.meta);
for (let i = 0; i < keys.length; i++) {
if (affectedCells[keys[i]]) {
newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]];
} else {
newMeta[keys[i]] = obj.options.meta[keys[i]];
}
}
// Update meta information
obj.options.meta = newMeta;
}
}
/**
* Set meta information to cell(s)
*
* @return integer
* /
const setMeta = function(o, k, v) {
const obj = this;
if (! obj.options.meta) {
obj.options.meta = {}
}
if (k && v) {
// Set data value
if (! obj.options.meta[o]) {
obj.options.meta[o] = {};
}
obj.options.meta[o][k] = v;
_dispatch_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] * / .A.call(obj, 'onchangemeta', obj, { [o]: { [k]: v } });
} else {
// Apply that for all cells
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
if (! obj.options.meta[keys[i]]) {
obj.options.meta[keys[i]] = {};
}
const prop = Object.keys(o[keys[i]]);
for (let j = 0; j < prop.length; j++) {
obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]];
}
}
_dispatch_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] * / .A.call(obj, 'onchangemeta', obj, o);
}
}
/*** / }),
/*** / 657:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / AH: function() { return /* binding * / updateSelectionFromCoords; },
/* harmony export * / Aq: function() { return /* binding * / updateCornerPosition; },
/* harmony export * / G9: function() { return /* binding * / refreshSelection; },
/* harmony export * / Jg: function() { return /* binding * / getSelectedColumns; },
/* harmony export * / Lo: function() { return /* binding * / getSelection; },
/* harmony export * / R5: function() { return /* binding * / getSelectedRows; },
/* harmony export * / Ub: function() { return /* binding * / selectAll; },
/* harmony export * / at: function() { return /* binding * / conditionalSelectionUpdate; },
/* harmony export * / c6: function() { return /* binding * / updateSelection; },
/* harmony export * / eO: function() { return /* binding * / getRange; },
/* harmony export * / ef: function() { return /* binding * / getSelected; },
/* harmony export * / gE: function() { return /* binding * / resetSelection; },
/* harmony export * / gG: function() { return /* binding * / removeCopySelection; },
/* harmony export * / kA: function() { return /* binding * / removeCopyingSelection; },
/* harmony export * / kF: function() { return /* binding * / copyData; },
/* harmony export * / kV: function() { return /* binding * / getHighlighted; },
/* harmony export * / sp: function() { return /* binding * / isSelected; },
/* harmony export * / tW: function() { return /* binding * / hash; }
/* harmony export * / });
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(805);
/* harmony import * / var _freeze_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(296);
/* harmony import * / var _helpers_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(978);
/* harmony import * / var _history_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(911);
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(530);
/* harmony import * / var _internalHelpers_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(689);
/* harmony import * / var _toolbar_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(392);
const updateCornerPosition = function() {
const obj = this;
// If any selected cells
if (!obj.highlighted || !obj.highlighted.length) {
obj.corner.style.top = '-2000px';
obj.corner.style.left = '-2000px';
} else {
// Get last cell
const last = obj.highlighted[obj.highlighted.length-1].element;
const lastX = last.getAttribute('data-x');
const contentRect = obj.content.getBoundingClientRect();
const x1 = contentRect.left;
const y1 = contentRect.top;
const lastRect = last.getBoundingClientRect();
const x2 = lastRect.left;
const y2 = lastRect.top;
const w2 = lastRect.width;
const h2 = lastRect.height;
const x = (x2 - x1) + obj.content.scrollLeft + w2 - 4;
const y = (y2 - y1) + obj.content.scrollTop + h2 - 4;
// Place the corner in the correct place
obj.corner.style.top = y + 'px';
obj.corner.style.left = x + 'px';
if (obj.options.freezeColumns) {
const width = _freeze_js__WEBPACK_IMPORTED_MODULE_0__/* .getFreezeWidth * / .w.call(obj);
// Only check if the last column is not part of the merged cells
if (lastX > obj.options.freezeColumns-1 && x2 - x1 + w2 < width) {
obj.corner.style.display = 'none';
} else {
if (obj.options.selectionCopy != false) {
obj.corner.style.display = '';
}
}
} else {
if (obj.options.selectionCopy != false) {
obj.corner.style.display = '';
}
}
}
(0,_toolbar_js__WEBPACK_IMPORTED_MODULE_1__/* .updateToolbar * / .nK)(obj);
}
const resetSelection = function(blur) {
const obj = this;
let previousStatus;
// Remove style
if (!obj.highlighted || !obj.highlighted.length) {
previousStatus = 0;
} else {
previousStatus = 1;
for (let i = 0; i < obj.highlighted.length; i++) {
obj.highlighted[i].element.classList.remove('highlight');
obj.highlighted[i].element.classList.remove('highlight-left');
obj.highlighted[i].element.classList.remove('highlight-right');
obj.highlighted[i].element.classList.remove('highlight-top');
obj.highlighted[i].element.classList.remove('highlight-bottom');
obj.highlighted[i].element.classList.remove('highlight-selected');
const px = parseInt(obj.highlighted[i].element.getAttribute('data-x'));
const py = parseInt(obj.highlighted[i].element.getAttribute('data-y'));
// Check for merged cells
let ux, uy;
if (obj.highlighted[i].element.getAttribute('data-merged')) {
const colspan = parseInt(obj.highlighted[i].element.getAttribute('colspan'));
const rowspan = parseInt(obj.highlighted[i].element.getAttribute('rowspan'));
ux = colspan > 0 ? px + (colspan - 1) : px;
uy = rowspan > 0 ? py + (rowspan - 1): py;
} else {
ux = px;
uy = py;
}
// Remove selected from headers
for (let j = px; j <= ux; j++) {
if (obj.headers[j]) {
obj.headers[j].classList.remove('selected');
}
}
// Remove selected from rows
for (let j = py; j <= uy; j++) {
if (obj.rows[j]) {
obj.rows[j].element.classList.remove('selected');
}
}
}
}
// Reset highlighted cells
obj.highlighted = [];
// Reset
obj.selectedCell = null;
// Hide corner
obj.corner.style.top = '-2000px';
obj.corner.style.left = '-2000px';
if (blur == true && previousStatus == 1) {
_dispatch_js__WEBPACK_IMPORTED_MODULE_2__/* ["default"] * / .A.call(obj, 'onblur', obj);
}
return previousStatus;
}
/**
* Update selection based on two cells
* /
const updateSelection = function(el1, el2, origin) {
const obj = this;
const x1 = el1.getAttribute('data-x');
const y1 = el1.getAttribute('data-y');
let x2, y2;
if (el2) {
x2 = el2.getAttribute('data-x');
y2 = el2.getAttribute('data-y');
} else {
x2 = x1;
y2 = y1;
}
updateSelectionFromCoords.call(obj, x1, y1, x2, y2, origin);
}
const removeCopyingSelection = function() {
const copying = document.querySelectorAll('.jss_worksheet .copying');
for (let i = 0; i < copying.length; i++) {
copying[i].classList.remove('copying');
copying[i].classList.remove('copying-left');
copying[i].classList.remove('copying-right');
copying[i].classList.remove('copying-top');
copying[i].classList.remove('copying-bottom');
}
}
const updateSelectionFromCoords = function(x1, y1, x2, y2, origin) {
const obj = this;
// select column
if (y1 == null) {
y1 = 0;
y2 = obj.rows.length - 1;
if (x1 == null) {
return;
}
} else if (x1 == null) {
// select row
x1 = 0;
x2 = obj.options.data[0].length - 1;
}
// Same element
if (x2 == null) {
x2 = x1;
}
if (y2 == null) {
y2 = y1;
}
// Selection must be within the existing data
if (x1 >= obj.headers.length) {
x1 = obj.headers.length - 1;
}
if (y1 >= obj.rows.length) {
y1 = obj.rows.length - 1;
}
if (x2 >= obj.headers.length) {
x2 = obj.headers.length - 1;
}
if (y2 >= obj.rows.length) {
y2 = obj.rows.length - 1;
}
// Limits
let borderLeft = null;
let borderRight = null;
let borderTop = null;
let borderBottom = null;
// Origin & Destination
let px, ux;
if (parseInt(x1) < parseInt(x2)) {
px = parseInt(x1);
ux = parseInt(x2);
} else {
px = parseInt(x2);
ux = parseInt(x1);
}
let py, uy;
if (parseInt(y1) < parseInt(y2)) {
py = parseInt(y1);
uy = parseInt(y2);
} else {
py = parseInt(y2);
uy = parseInt(y1);
}
// Verify merged columns
for (let i = px; i <= ux; i++) {
for (let j = py; j <= uy; j++) {
if (obj.records[j][i] && obj.records[j][i].element.getAttribute('data-merged')) {
const x = parseInt(obj.records[j][i].element.getAttribute('data-x'));
const y = parseInt(obj.records[j][i].element.getAttribute('data-y'));
const colspan = parseInt(obj.records[j][i].element.getAttribute('colspan'));
const rowspan = parseInt(obj.records[j][i].element.getAttribute('rowspan'));
if (colspan > 1) {
if (x < px) {
px = x;
}
if (x + colspan > ux) {
ux = x + colspan - 1;
}
}
if (rowspan) {
if (y < py) {
py = y;
}
if (y + rowspan > uy) {
uy = y + rowspan - 1;
}
}
}
}
}
// Vertical limits
for (let j = py; j <= uy; j++) {
if (obj.rows[j].element.style.display != 'none') {
if (borderTop == null) {
borderTop = j;
}
borderBottom = j;
}
}
for (let i = px; i <= ux; i++) {
for (let j = py; j <= uy; j++) {
// Horizontal limits
if (!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].type != 'hidden') {
if (borderLeft == null) {
borderLeft = i;
}
borderRight = i;
}
}
}
// Create borders
if (! borderLeft) {
borderLeft = 0;
}
if (! borderRight) {
borderRight = 0;
}
const ret = _dispatch_js__WEBPACK_IMPORTED_MODULE_2__/* ["default"] * / .A.call(obj, 'onbeforeselection', obj, borderLeft, borderTop, borderRight, borderBottom, origin);
if (ret === false) {
return false;
}
// Reset Selection
const previousState = obj.resetSelection();
// Keep selected cell
obj.selectedCell = [x1, y1, x2, y2];
// Add selected cell
if (obj.records[y1][x1]) {
obj.records[y1][x1].element.classList.add('highlight-selected');
}
// Redefining styles
for (let i = px; i <= ux; i++) {
for (let j = py; j <= uy; j++) {
if (obj.rows[j].element.style.display != 'none' && obj.records[j][i].element.style.display != 'none') {
obj.records[j][i].element.classList.add('highlight');
obj.highlighted.push(obj.records[j][i]);
}
}
}
for (let i = borderLeft; i <= borderRight; i++) {
if ((!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].type != 'hidden') && obj.cols[i].colElement.style && obj.cols[i].colElement.style.display != 'none') {
// Top border
if (obj.records[borderTop] && obj.records[borderTop][i]) {
obj.records[borderTop][i].element.classList.add('highlight-top');
}
// Bottom border
if (obj.records[borderBottom] && obj.records[borderBottom][i]) {
obj.records[borderBottom][i].element.classList.add('highlight-bottom');
}
// Add selected from headers
obj.headers[i].classList.add('selected');
}
}
for (let j = borderTop; j <= borderBottom; j++) {
if (obj.rows[j] && obj.rows[j].element.style.display != 'none') {
// Left border
obj.records[j][borderLeft].element.classList.add('highlight-left');
// Right border
obj.records[j][borderRight].element.classList.add('highlight-right');
// Add selected from rows
obj.rows[j].element.classList.add('selected');
}
}
obj.selectedContainer = [ borderLeft, borderTop, borderRight, borderBottom ];
// Handle events
if (previousState == 0) {
_dispatch_js__WEBPACK_IMPORTED_MODULE_2__/* ["default"] * / .A.call(obj, 'onfocus', obj);
removeCopyingSelection();
}
_dispatch_js__WEBPACK_IMPORTED_MODULE_2__/* ["default"] * / .A.call(obj, 'onselection', obj, borderLeft, borderTop, borderRight, borderBottom, origin);
// Find corner cell
updateCornerPosition.call(obj);
}
/**
* Get selected column numbers
*
* @return array
* /
const getSelectedColumns = function(visibleOnly) {
const obj = this;
if (!obj.selectedCell) {
return [];
}
const result = [];
for (let i = Math.min(obj.selectedCell[0], obj.selectedCell[2]); i <= Math.max(obj.selectedCell[0], obj.selectedCell[2]); i++) {
if (!visibleOnly || obj.headers[i].style.display != 'none') {
result.push(i);
}
}
return result;
}
/**
* Refresh current selection
* /
const refreshSelection = function() {
const obj = this;
if (obj.selectedCell) {
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
}
}
/**
* Remove copy selection
*
* @return void
* /
const removeCopySelection = function() {
const obj = this;
// Remove current selection
for (let i = 0; i < obj.selection.length; i++) {
obj.selection[i].classList.remove('selection');
obj.selection[i].classList.remove('selection-left');
obj.selection[i].classList.remove('selection-right');
obj.selection[i].classList.remove('selection-top');
obj.selection[i].classList.remove('selection-bottom');
}
obj.selection = [];
}
const doubleDigitFormat = function(v) {
v = ''+v;
if (v.length == 1) {
v = '0'+v;
}
return v;
}
/**
* Helper function to copy data using the corner icon
* /
const copyData = function(o, d) {
const obj = this;
// Get data from all selected cells
const data = obj.getData(true, false);
// Selected cells
const h = obj.selectedContainer;
// Cells
const x1 = parseInt(o.getAttribute('data-x'));
const y1 = parseInt(o.getAttribute('data-y'));
const x2 = parseInt(d.getAttribute('data-x'));
const y2 = parseInt(d.getAttribute('data-y'));
// Records
const records = [];
let breakControl = false;
let rowNumber, colNumber;
if (h[0] == x1) {
// Vertical copy
if (y1 < h[1]) {
rowNumber = y1 - h[1];
} else {
rowNumber = 1;
}
colNumber = 0;
} else {
if (x1 < h[0]) {
colNumber = x1 - h[0];
} else {
colNumber = 1;
}
rowNumber = 0;
}
// Copy data procedure
let posx = 0;
let posy = 0;
for (let j = y1; j <= y2; j++) {
// Skip hidden rows
if (obj.rows[j] && obj.rows[j].element.style.display == 'none') {
continue;
}
// Controls
if (data[posy] == undefined) {
posy = 0;
}
posx = 0;
// Data columns
if (h[0] != x1) {
if (x1 < h[0]) {
colNumber = x1 - h[0];
} else {
colNumber = 1;
}
}
// Data columns
for (let i = x1; i <= x2; i++) {
// Update non-readonly
if (obj.records[j][i] && ! obj.records[j][i].element.classList.contains('readonly') && obj.records[j][i].element.style.display != 'none' && breakControl == false) {
// Stop if contains value
if (! obj.selection.length) {
if (obj.options.data[j][i] != '') {
breakControl = true;
continue;
}
}
// Column
if (data[posy] == undefined) {
posx = 0;
} else if (data[posy][posx] == undefined) {
posx = 0;
}
// Value
let value = data[posy][posx];
if (value && ! data[1] && obj.parent.config.autoIncrement != false) {
if (obj.options.columns && obj.options.columns[i] && (!obj.options.columns[i].type || obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number')) {
if ((''+value).substr(0,1) == '=') {
const tokens = value.match(/([A-Z]+[0-9]+)/g);
if (tokens) {
const affectedTokens = [];
for (let index = 0; index < tokens.length; index++) {
const position = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_3__/* .getIdFromColumnName * / .vu)(tokens[index], 1);
position[0] += colNumber;
position[1] += rowNumber;
if (position[1] < 0) {
position[1] = 0;
}
const token = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_3__/* .getColumnNameFromId * / .t3)([position[0], position[1]]);
if (token != tokens[index]) {
affectedTokens[tokens[index]] = token;
}
}
// Update formula
if (affectedTokens) {
value = (0,_internal_js__WEBPACK_IMPORTED_MODULE_4__/* .updateFormula * / .yB)(value, affectedTokens)
}
}
} else {
if (value == Number(value)) {
value = Number(value) + rowNumber;
}
}
} else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'calendar') {
const date = new Date(value);
date.setDate(date.getDate() + rowNumber);
value = date.getFullYear() + '-' + doubleDigitFormat(parseInt(date.getMonth() + 1)) + '-' + doubleDigitFormat(date.getDate()) + ' ' + '00:00:00';
}
}
records.push(_internal_js__WEBPACK_IMPORTED_MODULE_4__/* .updateCell * / .k9.call(obj, i, j, value));
// Update all formulas in the chain
_internal_js__WEBPACK_IMPORTED_MODULE_4__/* .updateFormulaChain * / .xF.call(obj, i, j, records);
}
posx++;
if (h[0] != x1) {
colNumber++;
}
}
posy++;
rowNumber++;
}
// Update history
_history_js__WEBPACK_IMPORTED_MODULE_5__/* .setHistory * / .Dh.call(obj, {
action:'setValue',
records:records,
selection:obj.selectedCell,
});
// Update table with custom configuration if applicable
_internal_js__WEBPACK_IMPORTED_MODULE_4__/* .updateTable * / .am.call(obj);
// On after changes
const onafterchangesRecords = records.map(function(record) {
return {
x: record.x,
y: record.y,
value: record.newValue,
oldValue: record.oldValue,
};
});
_dispatch_js__WEBPACK_IMPORTED_MODULE_2__/* ["default"] * / .A.call(obj, 'onafterchanges', obj, onafterchangesRecords);
}
const hash = function(str) {
let hash = 0, i, chr;
if (!str || str.length === 0) {
return hash;
} else {
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
}
return hash;
}
/**
* Move coords to A1 in case overlaps with an excluded cell
* /
const conditionalSelectionUpdate = function(type, o, d) {
const obj = this;
if (type == 1) {
if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) {
obj.resetSelection();
return;
}
} else {
if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) {
obj.resetSelection();
return;
}
}
}
/**
* Get selected rows numbers
*
* @return array
* /
const getSelectedRows = function(visibleOnly) {
const obj = this;
if (!obj.selectedCell) {
return [];
}
const result = [];
for (let i = Math.min(obj.selectedCell[1], obj.selectedCell[3]); i <= Math.max(obj.selectedCell[1], obj.selectedCell[3]); i++) {
if (!visibleOnly || obj.rows[i].element.style.display != 'none') {
result.push(i);
}
}
return result;
}
const selectAll = function() {
const obj = this;
if (! obj.selectedCell) {
obj.selectedCell = [];
}
obj.selectedCell[0] = 0;
obj.selectedCell[1] = 0;
obj.selectedCell[2] = obj.headers.length - 1;
obj.selectedCell[3] = obj.records.length - 1;
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
}
const getSelection = function() {
const obj = this;
if (!obj.selectedCell) {
return null;
}
return [
Math.min(obj.selectedCell[0], obj.selectedCell[2]),
Math.min(obj.selectedCell[1], obj.selectedCell[3]),
Math.max(obj.selectedCell[0], obj.selectedCell[2]),
Math.max(obj.selectedCell[1], obj.selectedCell[3]),
];
}
const getSelected = function(columnNameOnly) {
const obj = this;
const selectedRange = getSelection.call(obj);
if (!selectedRange) {
return [];
}
const cells = [];
for (let y = selectedRange[1]; y <= selectedRange[3]; y++) {
for (let x = selectedRange[0]; x <= selectedRange[2]; x++) {
if (columnNameOnly) {
cells.push((0,_helpers_js__WEBPACK_IMPORTED_MODULE_6__.getCellNameFromCoords)(x, y));
} else {
cells.push(obj.records[y][x]);
}
}
}
return cells;
}
const getRange = function() {
const obj = this;
const selectedRange = getSelection.call(obj);
if (!selectedRange) {
return '';
}
const start = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_6__.getCellNameFromCoords)(selectedRange[0], selectedRange[1]);
const end = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_6__.getCellNameFromCoords)(selectedRange[2], selectedRange[3]);
if (start === end) {
return obj.options.worksheetName + '!' + start;
}
return obj.options.worksheetName + '!' + start + ':' + end;
}
const isSelected = function(x, y) {
const obj = this;
const selection = getSelection.call(obj);
return x >= selection[0] && x <= selection[2] && y >= selection[1] && y <= selection[3];
}
const getHighlighted = function() {
const obj = this;
const selection = getSelection.call(obj);
if (selection) {
return [selection];
}
return [];
}
/*** / }),
/*** / 689:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / Hh: function() { return /* binding * / injectArray; },
/* harmony export * / t3: function() { return /* binding * / getColumnNameFromId; },
/* harmony export * / vu: function() { return /* binding * / getIdFromColumnName; }
/* harmony export * / });
/* harmony import * / var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(978);
/**
* Helper injectArray
* /
const injectArray = function(o, idx, arr) {
if (idx <= o.length) {
return o.slice(0, idx).concat(arr).concat(o.slice(idx));
}
const array = o.slice(0, o.length);
while (idx > array.length) {
array.push(undefined);
}
return array.concat(arr)
}
/**
* Convert excel like column to jss id
*
* @param string id
* @return string id
* /
const getIdFromColumnName = function (id, arr) {
// Get the letters
const t = /^[a-zA-Z]+/.exec(id);
if (t) {
// Base 26 calculation
let code = 0;
for (let i = 0; i < t[0].length; i++) {
code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i));
}
code--;
// Make sure jss starts on zero
if (code < 0) {
code = 0;
}
// Number
let number = parseInt(/[0-9]+$/.exec(id));
if (number > 0) {
number--;
}
if (arr == true) {
id = [ code, number ];
} else {
id = code + '-' + number;
}
}
return id;
}
/**
* Convert jss id to excel like column name
*
* @param string id
* @return string id
* /
const getColumnNameFromId = function (cellId) {
if (! Array.isArray(cellId)) {
cellId = cellId.split('-');
}
return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.getColumnName)(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1);
}
/*** / }),
/*** / 805:
/*** / (function(__unused_webpack_module, __webpack_exports__) {
/**
* Prepare JSON in the correct format
* /
const prepareJson = function(data) {
const obj = this;
const rows = [];
for (let i = 0; i < data.length; i++) {
const x = data[i].x;
const y = data[i].y;
const k = obj.options.columns[x].name ? obj.options.columns[x].name : x;
// Create row
if (! rows[y]) {
rows[y] = {
row: y,
data: {},
};
}
rows[y].data[k] = data[i].value;
}
// Filter rows
return rows.filter(function (el) {
return el != null;
});
}
/**
* Post json to a remote server
* /
const save = function(url, data) {
const obj = this;
// Parse anything in the data before sending to the server
const ret = dispatch.call(obj.parent, 'onbeforesave', obj.parent, obj, data);
if (ret) {
data = ret;
} else {
if (ret === false) {
return false;
}
}
// Remove update
jSuites.ajax({
url: url,
method: 'POST',
dataType: 'json',
data: { data: JSON.stringify(data) },
success: function(result) {
// Event
dispatch.call(obj, 'onsave', obj.parent, obj, data);
}
});
}
/**
* Trigger events
* /
const dispatch = function(event) {
const obj = this;
let ret = null;
let spreadsheet = obj.parent ? obj.parent : obj;
// Dispatch events
if (! spreadsheet.ignoreEvents) {
// Call global event
if (typeof(spreadsheet.config.onevent) == 'function') {
ret = spreadsheet.config.onevent.apply(this, arguments);
}
// Call specific events
if (typeof(spreadsheet.config[event]) == 'function') {
ret = spreadsheet.config[event].apply(this, Array.prototype.slice.call(arguments, 1));
}
if (typeof spreadsheet.plugins === 'object') {
const pluginKeys = Object.keys(spreadsheet.plugins);
for (let pluginKeyIndex = 0; pluginKeyIndex < pluginKeys.length; pluginKeyIndex++) {
const key = pluginKeys[pluginKeyIndex];
const plugin = spreadsheet.plugins[key];
if (typeof plugin.onevent === 'function') {
ret = plugin.onevent.apply(this, arguments);
}
}
}
}
if (event == 'onafterchanges') {
const scope = arguments;
if (typeof spreadsheet.plugins === 'object') {
Object.entries(spreadsheet.plugins).forEach(function([, plugin]) {
if (typeof plugin.persistence === 'function') {
plugin.persistence(obj, 'setValue', { data: scope[2] });
}
});
}
if (obj.options.persistence) {
const url = obj.options.persistence == true ? obj.options.url : obj.options.persistence;
const data = prepareJson.call(obj, arguments[2]);
save.call(obj, url, data);
}
}
return ret;
}
/* harmony default export * / __webpack_exports__.A = (dispatch);
/*** / }),
/*** / 829:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / F8: function() { return /* binding * / closeFilter; },
/* harmony export * / N$: function() { return /* binding * / openFilter; },
/* harmony export * / dr: function() { return /* binding * / resetFilters; }
/* harmony export * / });
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(530);
/* harmony import * / var _selection_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(657);
/**
* Open the column filter
* /
const openFilter = function(columnId) {
const obj = this;
if (! obj.options.filters) {
console.log('Jspreadsheet: filters not enabled.');
} else {
// Make sure is integer
columnId = parseInt(columnId);
// Reset selection
obj.resetSelection();
// Load options
let optionsFiltered = [];
if (obj.options.columns[columnId].type == 'checkbox') {
optionsFiltered.push({ id: 'true', name: 'True' });
optionsFiltered.push({ id: 'false', name: 'False' });
} else {
const options = [];
let hasBlanks = false;
for (let j = 0; j < obj.options.data.length; j++) {
const k = obj.options.data[j][columnId];
const v = obj.records[j][columnId].element.innerHTML;
if (k && v) {
options[k] = v;
} else {
hasBlanks = true;
}
}
const keys = Object.keys(options);
optionsFiltered = [];
for (let j = 0; j < keys.length; j++) {
optionsFiltered.push({ id: keys[j], name: options[keys[j]] });
}
// Has blank options
if (hasBlanks) {
optionsFiltered.push({ value: '', id: '', name: '(Blanks)' });
}
}
// Create dropdown
const div = document.createElement('div');
obj.filter.children[columnId + 1].innerHTML = '';
obj.filter.children[columnId + 1].appendChild(div);
obj.filter.children[columnId + 1].style.paddingLeft = '0px';
obj.filter.children[columnId + 1].style.paddingRight = '0px';
obj.filter.children[columnId + 1].style.overflow = 'initial';
const opt = {
data: optionsFiltered,
multiple: true,
autocomplete: true,
opened: true,
value: obj.filters[columnId] !== undefined ? obj.filters[columnId] : null,
width:'100%',
position: (obj.options.tableOverflow == true || obj.parent.config.fullscreen == true) ? true : false,
onclose: function(o) {
resetFilters.call(obj);
obj.filters[columnId] = o.dropdown.getValue(true);
obj.filter.children[columnId + 1].innerHTML = o.dropdown.getText();
obj.filter.children[columnId + 1].style.paddingLeft = '';
obj.filter.children[columnId + 1].style.paddingRight = '';
obj.filter.children[columnId + 1].style.overflow = '';
closeFilter.call(obj, columnId);
_selection_js__WEBPACK_IMPORTED_MODULE_0__/* .refreshSelection * / .G9.call(obj);
}
};
// Dynamic dropdown
jSuites.dropdown(div, opt);
}
}
const closeFilter = function(columnId) {
const obj = this;
if (! columnId) {
for (let i = 0; i < obj.filter.children.length; i++) {
if (obj.filters[i]) {
columnId = i;
}
}
}
// Search filter
const search = function(query, x, y) {
for (let i = 0; i < query.length; i++) {
const value = ''+obj.options.data[y][x];
const label = ''+obj.records[y][x].element.innerHTML;
if (query[i] == value || query[i] == label) {
return true;
}
}
return false;
}
const query = obj.filters[columnId];
obj.results = [];
for (let j = 0; j < obj.options.data.length; j++) {
if (search(query, columnId, j)) {
obj.results.push(j);
}
}
if (! obj.results.length) {
obj.results = null;
}
_internal_js__WEBPACK_IMPORTED_MODULE_1__/* .updateResult * / .hG.call(obj);
}
const resetFilters = function() {
const obj = this;
if (obj.options.filters) {
for (let i = 0; i < obj.filter.children.length; i++) {
obj.filter.children[i].innerHTML = ' ';
obj.filters[i] = null;
}
}
obj.results = null;
_internal_js__WEBPACK_IMPORTED_MODULE_1__/* .updateResult * / .hG.call(obj);
}
/*** / }),
/*** / 910:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / D0: function() { return /* binding * / isRowMerged; },
/* harmony export * / FU: function() { return /* binding * / setMerge; },
/* harmony export * / Lt: function() { return /* binding * / isColMerged; },
/* harmony export * / VP: function() { return /* binding * / destroyMerge; },
/* harmony export * / Zp: function() { return /* binding * / removeMerge; },
/* harmony export * / fd: function() { return /* binding * / getMerge; }
/* harmony export * / });
/* harmony import * / var _internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(689);
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(530);
/* harmony import * / var _history_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(911);
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(805);
/* harmony import * / var _selection_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(657);
/**
* Is column merged
* /
const isColMerged = function(x, insertBefore) {
const obj = this;
const cols = [];
// Remove any merged cells
if (obj.options.mergeCells) {
const keys = Object.keys(obj.options.mergeCells);
for (let i = 0; i < keys.length; i++) {
const info = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getIdFromColumnName * / .vu)(keys[i], true);
const colspan = obj.options.mergeCells[keys[i]][0];
const x1 = info[0];
const x2 = info[0] + (colspan > 1 ? colspan - 1 : 0);
if (insertBefore == null) {
if ((x1 <= x && x2 >= x)) {
cols.push(keys[i]);
}
} else {
if (insertBefore) {
if ((x1 < x && x2 >= x)) {
cols.push(keys[i]);
}
} else {
if ((x1 <= x && x2 > x)) {
cols.push(keys[i]);
}
}
}
}
}
return cols;
}
/**
* Is rows merged
* /
const isRowMerged = function(y, insertBefore) {
const obj = this;
const rows = [];
// Remove any merged cells
if (obj.options.mergeCells) {
const keys = Object.keys(obj.options.mergeCells);
for (let i = 0; i < keys.length; i++) {
const info = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getIdFromColumnName * / .vu)(keys[i], true);
const rowspan = obj.options.mergeCells[keys[i]][1];
const y1 = info[1];
const y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0);
if (insertBefore == null) {
if ((y1 <= y && y2 >= y)) {
rows.push(keys[i]);
}
} else {
if (insertBefore) {
if ((y1 < y && y2 >= y)) {
rows.push(keys[i]);
}
} else {
if ((y1 <= y && y2 > y)) {
rows.push(keys[i]);
}
}
}
}
}
return rows;
}
/**
* Merge cells
* @param cellName
* @param colspan
* @param rowspan
* @param ignoreHistoryAndEvents
* /
const getMerge = function(cellName) {
const obj = this;
let data = {};
if (cellName) {
if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {
data = [ obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1] ];
} else {
data = null;
}
} else {
if (obj.options.mergeCells) {
var mergedCells = obj.options.mergeCells;
const keys = Object.keys(obj.options.mergeCells);
for (let i = 0; i < keys.length; i++) {
data[keys[i]] = [ obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1] ];
}
}
}
return data;
}
/**
* Merge cells
* @param cellName
* @param colspan
* @param rowspan
* @param ignoreHistoryAndEvents
* /
const setMerge = function(cellName, colspan, rowspan, ignoreHistoryAndEvents) {
const obj = this;
let test = false;
if (! cellName) {
if (! obj.highlighted.length) {
alert(jSuites.translate('No cells selected'));
return null;
} else {
const x1 = parseInt(obj.highlighted[0].getAttribute('data-x'));
const y1 = parseInt(obj.highlighted[0].getAttribute('data-y'));
const x2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-x'));
const y2 = parseInt(obj.highlighted[obj.highlighted.length-1].getAttribute('data-y'));
cellName = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getColumnNameFromId * / .t3)([ x1, y1 ]);
colspan = (x2 - x1) + 1;
rowspan = (y2 - y1) + 1;
}
} else if (typeof cellName !== 'string') {
return null
}
const cell = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getIdFromColumnName * / .vu)(cellName, true);
if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {
if (obj.records[cell[1]][cell[0]].element.getAttribute('data-merged')) {
test = 'Cell already merged';
}
} else if ((! colspan || colspan < 2) && (! rowspan || rowspan < 2)) {
test = 'Invalid merged properties';
} else {
var cells = [];
for (let j = cell[1]; j < cell[1] + rowspan; j++) {
for (let i = cell[0]; i < cell[0] + colspan; i++) {
var columnName = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getColumnNameFromId * / .t3)([i, j]);
if (obj.records[j][i].element.getAttribute('data-merged')) {
test = 'There is a conflict with another merged cell';
}
}
}
}
if (test) {
alert(jSuites.translate(test));
} else {
// Add property
if (colspan > 1) {
obj.records[cell[1]][cell[0]].element.setAttribute('colspan', colspan);
} else {
colspan = 1;
}
if (rowspan > 1) {
obj.records[cell[1]][cell[0]].element.setAttribute('rowspan', rowspan);
} else {
rowspan = 1;
}
// Keep links to the existing nodes
if (!obj.options.mergeCells) {
obj.options.mergeCells = {};
}
obj.options.mergeCells[cellName] = [ colspan, rowspan, [] ];
// Mark cell as merged
obj.records[cell[1]][cell[0]].element.setAttribute('data-merged', 'true');
// Overflow
obj.records[cell[1]][cell[0]].element.style.overflow = 'hidden';
// History data
const data = [];
// Adjust the nodes
for (let y = cell[1]; y < cell[1] + rowspan; y++) {
for (let x = cell[0]; x < cell[0] + colspan; x++) {
if (! (cell[0] == x && cell[1] == y)) {
data.push(obj.options.data[y][x]);
_internal_js__WEBPACK_IMPORTED_MODULE_1__/* .updateCell * / .k9.call(obj, x, y, '', true);
obj.options.mergeCells[cellName][2].push(obj.records[y][x].element);
obj.records[y][x].element.style.display = 'none';
obj.records[y][x].element = obj.records[cell[1]][cell[0]].element;
}
}
}
// In the initialization is not necessary keep the history
_selection_js__WEBPACK_IMPORTED_MODULE_2__/* .updateSelection * / .c6.call(obj, obj.records[cell[1]][cell[0]].element);
if (! ignoreHistoryAndEvents) {
_history_js__WEBPACK_IMPORTED_MODULE_3__/* .setHistory * / .Dh.call(obj, {
action:'setMerge',
column:cellName,
colspan:colspan,
rowspan:rowspan,
data:data,
});
_dispatch_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] * / .A.call(obj, 'onmerge', obj, { [cellName]: [colspan, rowspan]});
}
}
}
/**
* Remove merge by cellname
* @param cellName
* /
const removeMerge = function(cellName, data, keepOptions) {
const obj = this;
if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {
const cell = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getIdFromColumnName * / .vu)(cellName, true);
obj.records[cell[1]][cell[0]].element.removeAttribute('colspan');
obj.records[cell[1]][cell[0]].element.removeAttribute('rowspan');
obj.records[cell[1]][cell[0]].element.removeAttribute('data-merged');
const info = obj.options.mergeCells[cellName];
let index = 0;
let j, i;
for (j = 0; j < info[1]; j++) {
for (i = 0; i < info[0]; i++) {
if (j > 0 || i > 0) {
obj.records[cell[1]+j][cell[0]+i].element = info[2][index];
obj.records[cell[1]+j][cell[0]+i].element.style.display = '';
// Recover data
if (data && data[index]) {
_internal_js__WEBPACK_IMPORTED_MODULE_1__/* .updateCell * / .k9.call(obj, cell[0]+i, cell[1]+j, data[index]);
}
index++;
}
}
}
// Update selection
_selection_js__WEBPACK_IMPORTED_MODULE_2__/* .updateSelection * / .c6.call(obj, obj.records[cell[1]][cell[0]].element, obj.records[cell[1]+j-1][cell[0]+i-1].element);
if (! keepOptions) {
delete(obj.options.mergeCells[cellName]);
}
}
}
/**
* Remove all merged cells
* /
const destroyMerge = function(keepOptions) {
const obj = this;
// Remove any merged cells
if (obj.options.mergeCells) {
var mergedCells = obj.options.mergeCells;
const keys = Object.keys(obj.options.mergeCells);
for (let i = 0; i < keys.length; i++) {
removeMerge.call(obj, keys[i], null, keepOptions);
}
}
}
/*** / }),
/*** / 911:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / Dh: function() { return /* binding * / setHistory; },
/* harmony export * / ZS: function() { return /* binding * / redo; },
/* harmony export * / tN: function() { return /* binding * / undo; }
/* harmony export * / });
/* harmony import * / var _dispatch_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(805);
/* harmony import * / var _internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(689);
/* harmony import * / var _internal_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(530);
/* harmony import * / var _merges_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(910);
/* harmony import * / var _orderBy_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(94);
/* harmony import * / var _selection_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(657);
/**
* Initializes a new history record for undo/redo
*
* @return null
* /
const setHistory = function(changes) {
const obj = this;
if (obj.ignoreHistory != true) {
// Increment and get the current history index
const index = ++obj.historyIndex;
// Slice the array to discard undone changes
obj.history = (obj.history = obj.history.slice(0, index + 1));
// Keep history
obj.history[index] = changes;
}
}
/**
* Process row
* /
const historyProcessRow = function(type, historyRecord) {
const obj = this;
const rowIndex = (! historyRecord.insertBefore) ? historyRecord.rowNumber + 1 : +historyRecord.rowNumber;
if (obj.options.search == true) {
if (obj.results && obj.results.length != obj.rows.length) {
obj.resetSearch();
}
}
// Remove row
if (type == 1) {
const numOfRows = historyRecord.numOfRows;
// Remove nodes
for (let j = rowIndex; j < (numOfRows + rowIndex); j++) {
obj.rows[j].element.parentNode.removeChild(obj.rows[j].element);
}
// Remove references
obj.records.splice(rowIndex, numOfRows);
obj.options.data.splice(rowIndex, numOfRows);
obj.rows.splice(rowIndex, numOfRows);
_selection_js__WEBPACK_IMPORTED_MODULE_0__/* .conditionalSelectionUpdate * / .at.call(obj, 1, rowIndex, (numOfRows + rowIndex) - 1);
} else {
// Insert data
obj.records = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.records, rowIndex, historyRecord.rowRecords);
obj.options.data = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.options.data, rowIndex, historyRecord.rowData);
obj.rows = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.rows, rowIndex, historyRecord.rowNode);
// Insert nodes
let index = 0
for (let j = rowIndex; j < (historyRecord.numOfRows + rowIndex); j++) {
obj.tbody.insertBefore(historyRecord.rowNode[index].element, obj.tbody.children[j]);
index++;
}
}
for (let j = rowIndex; j < obj.rows.length; j++) {
obj.rows[j].y = j;
}
for (let j = rowIndex; j < obj.records.length; j++) {
for (let i = 0; i < obj.records[j].length; i++) {
obj.records[j][i].y = j;
}
}
// Respect pagination
if (obj.options.pagination > 0) {
obj.page(obj.pageNumber);
}
_internal_js__WEBPACK_IMPORTED_MODULE_2__/* .updateTableReferences * / .o8.call(obj);
}
/**
* Process column
* /
const historyProcessColumn = function(type, historyRecord) {
const obj = this;
const columnIndex = (! historyRecord.insertBefore) ? historyRecord.columnNumber + 1 : historyRecord.columnNumber;
// Remove column
if (type == 1) {
const numOfColumns = historyRecord.numOfColumns;
obj.options.columns.splice(columnIndex, numOfColumns);
for (let i = columnIndex; i < (numOfColumns + columnIndex); i++) {
obj.headers[i].parentNode.removeChild(obj.headers[i]);
obj.cols[i].colElement.parentNode.removeChild(obj.cols[i].colElement);
}
obj.headers.splice(columnIndex, numOfColumns);
obj.cols.splice(columnIndex, numOfColumns);
for (let j = 0; j < historyRecord.data.length; j++) {
for (let i = columnIndex; i < (numOfColumns + columnIndex); i++) {
obj.records[j][i].element.parentNode.removeChild(obj.records[j][i].element);
}
obj.records[j].splice(columnIndex, numOfColumns);
obj.options.data[j].splice(columnIndex, numOfColumns);
}
// Process footers
if (obj.options.footers) {
for (let j = 0; j < obj.options.footers.length; j++) {
obj.options.footers[j].splice(columnIndex, numOfColumns);
}
}
} else {
// Insert data
obj.options.columns = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.options.columns, columnIndex, historyRecord.columns);
obj.headers = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.headers, columnIndex, historyRecord.headers);
obj.cols = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.cols, columnIndex, historyRecord.cols);
let index = 0
for (let i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) {
obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i+1]);
obj.colgroupContainer.insertBefore(historyRecord.cols[index].colElement, obj.colgroupContainer.children[i+1]);
index++;
}
for (let j = 0; j < historyRecord.data.length; j++) {
obj.options.data[j] = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.options.data[j], columnIndex, historyRecord.data[j]);
obj.records[j] = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.records[j], columnIndex, historyRecord.records[j]);
let index = 0
for (let i = columnIndex; i < (historyRecord.numOfColumns + columnIndex); i++) {
obj.rows[j].element.insertBefore(historyRecord.records[j][index].element, obj.rows[j].element.children[i+1]);
index++;
}
}
// Process footers
if (obj.options.footers) {
for (let j = 0; j < obj.options.footers.length; j++) {
obj.options.footers[j] = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_1__/* .injectArray * / .Hh)(obj.options.footers[j], columnIndex, historyRecord.footers[j]);
}
}
}
for (let i = columnIndex; i < obj.cols.length; i++) {
obj.cols[i].x = i;
}
for (let j = 0; j < obj.records.length; j++) {
for (let i = columnIndex; i < obj.records[j].length; i++) {
obj.records[j][i].x = i;
}
}
// Adjust nested headers
if (
obj.options.nestedHeaders &&
obj.options.nestedHeaders.length > 0 &&
obj.options.nestedHeaders[0] &&
obj.options.nestedHeaders[0][0]
) {
for (let j = 0; j < obj.options.nestedHeaders.length; j++) {
let colspan;
if (type == 1) {
colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - historyRecord.numOfColumns;
} else {
colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + historyRecord.numOfColumns;
}
obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
}
}
_internal_js__WEBPACK_IMPORTED_MODULE_2__/* .updateTableReferences * / .o8.call(obj);
}
/**
* Undo last action
* /
const undo = function() {
const obj = this;
// Ignore events and history
const ignoreEvents = obj.parent.ignoreEvents ? true : false;
const ignoreHistory = obj.ignoreHistory ? true : false;
obj.parent.ignoreEvents = true;
obj.ignoreHistory = true;
// Records
const records = [];
// Update cells
let historyRecord;
if (obj.historyIndex >= 0) {
// History
historyRecord = obj.history[obj.historyIndex--];
if (historyRecord.action == 'insertRow') {
historyProcessRow.call(obj, 1, historyRecord);
} else if (historyRecord.action == 'deleteRow') {
historyProcessRow.call(obj, 0, historyRecord);
} else if (historyRecord.action == 'insertColumn') {
historyProcessColumn.call(obj, 1, historyRecord);
} else if (historyRecord.action == 'deleteColumn') {
historyProcessColumn.call(obj, 0, historyRecord);
} else if (historyRecord.action == 'moveRow') {
obj.moveRow(historyRecord.newValue, historyRecord.oldValue);
} else if (historyRecord.action == 'moveColumn') {
obj.moveColumn(historyRecord.newValue, historyRecord.oldValue);
} else if (historyRecord.action == 'setMerge') {
obj.removeMerge(historyRecord.column, historyRecord.data);
} else if (historyRecord.action == 'setStyle') {
obj.setStyle(historyRecord.oldValue, null, null, 1);
} else if (historyRecord.action == 'setWidth') {
obj.setWidth(historyRecord.column, historyRecord.oldValue);
} else if (historyRecord.action == 'setHeight') {
obj.setHeight(historyRecord.row, historyRecord.oldValue);
} else if (historyRecord.action == 'setHeader') {
obj.setHeader(historyRecord.column, historyRecord.oldValue);
} else if (historyRecord.action == 'setComments') {
obj.setComments(historyRecord.oldValue);
} else if (historyRecord.action == 'orderBy') {
let rows = [];
for (let j = 0; j < historyRecord.rows.length; j++) {
rows[historyRecord.rows[j]] = j;
}
_orderBy_js__WEBPACK_IMPORTED_MODULE_3__/* .updateOrderArrow * / .Th.call(obj, historyRecord.column, historyRecord.order ? 0 : 1);
_orderBy_js__WEBPACK_IMPORTED_MODULE_3__/* .updateOrder * / .iY.call(obj, rows);
} else if (historyRecord.action == 'setValue') {
// Redo for changes in cells
for (let i = 0; i < historyRecord.records.length; i++) {
records.push({
x: historyRecord.records[i].x,
y: historyRecord.records[i].y,
value: historyRecord.records[i].oldValue,
});
if (historyRecord.oldStyle) {
obj.resetStyle(historyRecord.oldStyle);
}
}
// Update records
obj.setValue(records);
// Update selection
if (historyRecord.selection) {
obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
}
}
}
obj.parent.ignoreEvents = ignoreEvents;
obj.ignoreHistory = ignoreHistory;
// Events
_dispatch_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] * / .A.call(obj, 'onundo', obj, historyRecord);
}
/**
* Redo previously undone action
* /
const redo = function() {
const obj = this;
// Ignore events and history
const ignoreEvents = obj.parent.ignoreEvents ? true : false;
const ignoreHistory = obj.ignoreHistory ? true : false;
obj.parent.ignoreEvents = true;
obj.ignoreHistory = true;
// Records
var records = [];
// Update cells
let historyRecord;
if (obj.historyIndex < obj.history.length - 1) {
// History
historyRecord = obj.history[++obj.historyIndex];
if (historyRecord.action == 'insertRow') {
historyProcessRow.call(obj, 0, historyRecord);
} else if (historyRecord.action == 'deleteRow') {
historyProcessRow.call(obj, 1, historyRecord);
} else if (historyRecord.action == 'insertColumn') {
historyProcessColumn.call(obj, 0, historyRecord);
} else if (historyRecord.action == 'deleteColumn') {
historyProcessColumn.call(obj, 1, historyRecord);
} else if (historyRecord.action == 'moveRow') {
obj.moveRow(historyRecord.oldValue, historyRecord.newValue);
} else if (historyRecord.action == 'moveColumn') {
obj.moveColumn(historyRecord.oldValue, historyRecord.newValue);
} else if (historyRecord.action == 'setMerge') {
_merges_js__WEBPACK_IMPORTED_MODULE_5__/* .setMerge * / .FU.call(obj, historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1);
} else if (historyRecord.action == 'setStyle') {
obj.setStyle(historyRecord.newValue, null, null, 1);
} else if (historyRecord.action == 'setWidth') {
obj.setWidth(historyRecord.column, historyRecord.newValue);
} else if (historyRecord.action == 'setHeight') {
obj.setHeight(historyRecord.row, historyRecord.newValue);
} else if (historyRecord.action == 'setHeader') {
obj.setHeader(historyRecord.column, historyRecord.newValue);
} else if (historyRecord.action == 'setComments') {
obj.setComments(historyRecord.newValue);
} else if (historyRecord.action == 'orderBy') {
_orderBy_js__WEBPACK_IMPORTED_MODULE_3__/* .updateOrderArrow * / .Th.call(obj, historyRecord.column, historyRecord.order);
_orderBy_js__WEBPACK_IMPORTED_MODULE_3__/* .updateOrder * / .iY.call(obj, historyRecord.rows);
} else if (historyRecord.action == 'setValue') {
obj.setValue(historyRecord.records);
// Redo for changes in cells
for (let i = 0; i < historyRecord.records.length; i++) {
if (historyRecord.oldStyle) {
obj.resetStyle(historyRecord.newStyle);
}
}
// Update selection
if (historyRecord.selection) {
obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);
}
}
}
obj.parent.ignoreEvents = ignoreEvents;
obj.ignoreHistory = ignoreHistory;
// Events
_dispatch_js__WEBPACK_IMPORTED_MODULE_4__/* ["default"] * / .A.call(obj, 'onredo', obj, historyRecord);
}
/*** / }),
/*** / 978:
/*** / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export * / __webpack_require__.d(__webpack_exports__, {
/* harmony export * / createFromTable: function() { return /* binding * / createFromTable; },
/* harmony export * / getCaretIndex: function() { return /* binding * / getCaretIndex; },
/* harmony export * / getCellNameFromCoords: function() { return /* binding * / getCellNameFromCoords; },
/* harmony export * / getColumnName: function() { return /* binding * / getColumnName; },
/* harmony export * / getCoordsFromCellName: function() { return /* binding * / getCoordsFromCellName; },
/* harmony export * / getCoordsFromRange: function() { return /* binding * / getCoordsFromRange; },
/* harmony export * / invert: function() { return /* binding * / invert; },
/* harmony export * / parseCSV: function() { return /* binding * / parseCSV; }
/* harmony export * / });
/* harmony import * / var _internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(689);
/**
* Get carret position for one element
* /
const getCaretIndex = function (e) {
let d;
if (this.config.root) {
d = this.config.root;
} else {
d = window;
}
let pos = 0;
const s = d.getSelection();
if (s) {
if (s.rangeCount !== 0) {
const r = s.getRangeAt(0);
const p = r.cloneRange();
p.selectNodeContents(e);
p.setEnd(r.endContainer, r.endOffset);
pos = p.toString().length;
}
}
return pos;
}
/**
* Invert keys and values
* /
const invert = function (o) {
const d = [];
const k = Object.keys(o);
for (let i = 0; i < k.length; i++) {
d[o[k[i]]] = k[i];
}
return d;
}
/**
* Get letter based on a number
*
* @param {number} columnNumber
* @return string letter
* /
const getColumnName = function (columnNumber){
let dividend = columnNumber+1;
let columnName = "";
let modulo;
while (dividend > 0) {
modulo = (dividend - 1) % 26;
columnName = String.fromCharCode(65 + modulo).toString() + columnName;
dividend = parseInt((dividend - modulo) / 26);
}
return columnName;
}
/**
* Get column name from coords
* /
const getCellNameFromCoords = function (x, y) {
return getColumnName(parseInt(x)) + (parseInt(y) + 1);
}
const getCoordsFromCellName = function (columnName) {
// Get the letters
const t = /^[a-zA-Z]+/.exec(columnName);
if (t) {
// Base 26 calculation
let code = 0;
for (let i = 0; i < t[0].length; i++) {
code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i));
}
code--;
// Make sure jspreadsheet starts on zero
if (code < 0) {
code = 0;
}
// Number
let number = parseInt(/[0-9]+$/.exec(columnName)) || null;
if (number > 0) {
number--;
}
return [code, number];
}
}
const getCoordsFromRange = function (range) {
const [start, end] = range.split(':');
return [...getCoordsFromCellName(start), ...getCoordsFromCellName(end)];
}
/**
* From stack overflow contributions
* /
const parseCSV = function (str, delimiter) {
// user-supplied delimeter or default comma
delimiter = (delimiter || ",");
// Remove last line break
str = str.replace(/\r?\n$|\r$|\n$/g, "");
const arr = [];
let quote = false; // true means we're inside a quoted field
// iterate over each character, keep track of current row and column (of the returned array)
let maxCol = 0;
let row = 0, col = 0;
for (let c = 0; c < str.length; c++) {
const cc = str[c], nc = str[c + 1];
arr[row] = arr[row] || [];
arr[row][col] = arr[row][col] || '';
// If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character
if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; }
// If it's just one quotation mark, begin/end quoted field
if (cc == '"') { quote = !quote; continue; }
// If it's a comma and we're not in a quoted field, move on to the next column
if (cc == delimiter && !quote) { ++col; continue; }
// If it's a newline (CRLF) and we're not in a quoted field, skip the next character and move on to the next row and move to column 0 of that new row
if (cc == '\r' && nc == '\n' && !quote) { ++row; maxCol = Math.max(maxCol, col); col = 0; ++c; continue; }
// If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row and move to column 0 of that new row
if (cc == '\n' && !quote) { ++row; maxCol = Math.max(maxCol, col); col = 0; continue; }
if (cc == '\r' && !quote) { ++row; maxCol = Math.max(maxCol, col); col = 0; continue; }
// Otherwise, append the current character to the current column
arr[row][col] += cc;
}
// fix array length
arr.forEach((row, i) => {
for (let i = row.length; i <= maxCol; i++) {
row.push('');
}
});
return arr;
}
const createFromTable = function (el, options) {
if (el.tagName != 'TABLE') {
console.log('Element is not a table');
} else {
// Configuration
if (!options) {
options = {};
}
options.columns = [];
options.data = [];
// Colgroup
const colgroup = el.querySelectorAll('colgroup > col');
if (colgroup.length) {
// Get column width
for (let i = 0; i < colgroup.length; i++) {
let width = colgroup[i].style.width;
if (!width) {
width = colgroup[i].getAttribute('width');
}
// Set column width
if (width) {
if (!options.columns[i]) {
options.columns[i] = {}
}
options.columns[i].width = width;
}
}
}
// Parse header
const parseHeader = function (header, i) {
// Get width information
let info = header.getBoundingClientRect();
const width = info.width > 50 ? info.width : 50;
// Create column option
if (!options.columns[i]) {
options.columns[i] = {};
}
if (header.getAttribute('data-celltype')) {
options.columns[i].type = header.getAttribute('data-celltype');
} else {
options.columns[i].type = 'text';
}
options.columns[i].width = width + 'px';
options.columns[i].title = header.innerHTML;
if (header.style.textAlign) {
options.columns[i].align = header.style.textAlign;
}
if (info = header.getAttribute('name')) {
options.columns[i].name = info;
}
if (info = header.getAttribute('id')) {
options.columns[i].id = info;
}
if (info = header.getAttribute('data-mask')) {
options.columns[i].mask = info;
}
}
// Headers
const nested = [];
let headers = el.querySelectorAll(':scope > thead > tr');
if (headers.length) {
for (let j = 0; j < headers.length - 1; j++) {
const cells = [];
for (let i = 0; i < headers[j].children.length; i++) {
const row = {
title: headers[j].children[i].textContent,
colspan: headers[j].children[i].getAttribute('colspan') || 1,
};
cells.push(row);
}
nested.push(cells);
}
// Get the last row in the thead
headers = headers[headers.length - 1].children;
// Go though the headers
for (let i = 0; i < headers.length; i++) {
parseHeader(headers[i], i);
}
}
// Content
let rowNumber = 0;
const mergeCells = {};
const rows = {};
const style = {};
const classes = {};
let content = el.querySelectorAll(':scope > tr, :scope > tbody > tr');
for (let j = 0; j < content.length; j++) {
options.data[rowNumber] = [];
if (options.parseTableFirstRowAsHeader == true && !headers.length && j == 0) {
for (let i = 0; i < content[j].children.length; i++) {
parseHeader(content[j].children[i], i);
}
} else {
for (let i = 0; i < content[j].children.length; i++) {
// WickedGrid formula compatibility
let value = content[j].children[i].getAttribute('data-formula');
if (value) {
if (value.substr(0, 1) != '=') {
value = '=' + value;
}
} else {
value = content[j].children[i].innerHTML;
}
options.data[rowNumber].push(value);
// Key
const cellName = (0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_0__/* .getColumnNameFromId * / .t3)([i, j]);
// Classes
const tmp = content[j].children[i].getAttribute('class');
if (tmp) {
classes[cellName] = tmp;
}
// Merged cells
const mergedColspan = parseInt(content[j].children[i].getAttribute('colspan')) || 0;
const mergedRowspan = parseInt(content[j].children[i].getAttribute('rowspan')) || 0;
if (mergedColspan || mergedRowspan) {
mergeCells[cellName] = [mergedColspan || 1, mergedRowspan || 1];
}
// Avoid problems with hidden cells
if (content[j].children[i].style && content[j].children[i].style.display == 'none') {
content[j].children[i].style.display = '';
}
// Get style
const s = content[j].children[i].getAttribute('style');
if (s) {
style[cellName] = s;
}
// Bold
if (content[j].children[i].classList.contains('styleBold')) {
if (style[cellName]) {
style[cellName] += '; font-weight:bold;';
} else {
style[cellName] = 'font-weight:bold;';
}
}
}
// Row Height
if (content[j].style && content[j].style.height) {
rows[j] = { height: content[j].style.height };
}
// Index
rowNumber++;
}
}
// Nested
if (Object.keys(nested).length > 0) {
options.nestedHeaders = nested;
}
// Style
if (Object.keys(style).length > 0) {
options.style = style;
}
// Merged
if (Object.keys(mergeCells).length > 0) {
options.mergeCells = mergeCells;
}
// Row height
if (Object.keys(rows).length > 0) {
options.rows = rows;
}
// Classes
if (Object.keys(classes).length > 0) {
options.classes = classes;
}
content = el.querySelectorAll('tfoot tr');
if (content.length) {
const footers = [];
for (let j = 0; j < content.length; j++) {
let footer = [];
for (let i = 0; i < content[j].children.length; i++) {
footer.push(content[j].children[i].textContent);
}
footers.push(footer);
}
if (Object.keys(footers).length > 0) {
options.footers = footers;
}
}
// TODO: data-hiddencolumns="3,4"
// I guess in terms the better column type
if (options.parseTableAutoCellType == true) {
const pattern = [];
for (let i = 0; i < options.columns.length; i++) {
let test = true;
let testCalendar = true;
pattern[i] = [];
for (let j = 0; j < options.data.length; j++) {
const value = options.data[j][i];
if (!pattern[i][value]) {
pattern[i][value] = 0;
}
pattern[i][value]++;
if (value.length > 25) {
test = false;
}
if (value.length == 10) {
if (!(value.substr(4, 1) == '-' && value.substr(7, 1) == '-')) {
testCalendar = false;
}
} else {
testCalendar = false;
}
}
const keys = Object.keys(pattern[i]).length;
if (testCalendar) {
options.columns[i].type = 'calendar';
} else if (test == true && keys > 1 && keys <= parseInt(options.data.length * 0.1)) {
options.columns[i].type = 'dropdown';
options.columns[i].source = Object.keys(pattern[i]);
}
}
}
return options;
}
}
/*** / })
/****** / });
/************************************************************************ /
/****** / // The module cache
/****** / var __webpack_module_cache__ = {};
/****** /
/****** / // The require function
/****** / function __webpack_require__(moduleId) {
/****** / // Check if module is in cache
/****** / var cachedModule = __webpack_module_cache__[moduleId];
/****** / if (cachedModule !== undefined) {
/****** / return cachedModule.exports;
/****** / }
/****** / // Create a new module (and put it into the cache)
/****** / var module = __webpack_module_cache__[moduleId] = {
/****** / // no module.id needed
/****** / // no module.loaded needed
/****** / exports: {}
/****** / };
/****** /
/****** / // Execute the module function
/****** / __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/****** /
/****** / // Return the exports of the module
/****** / return module.exports;
/****** / }
/****** /
/************************************************************************ /
/****** / /* webpack/runtime/define property getters * /
/****** / !function() {
/****** / // define getter functions for harmony exports
/****** / __webpack_require__.d = function(exports, definition) {
/****** / for(var key in definition) {
/****** / if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/****** / Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/****** / }
/****** / }
/****** / };
/****** / }();
/****** /
/****** / /* webpack/runtime/hasOwnProperty shorthand * /
/****** / !function() {
/****** / __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/****** / }();
/****** /
/****** / /* webpack/runtime/make namespace object * /
/****** / !function() {
/****** / // define __esModule on exports
/****** / __webpack_require__.r = function(exports) {
/****** / if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/****** / Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/****** / }
/****** / Object.defineProperty(exports, '__esModule', { value: true });
/****** / };
/****** / }();
/****** /
/************************************************************************ /
var __webpack_exports__ = {};
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding * / src; }
});
;// ./src/utils/libraryBase.js
const lib = {
jspreadsheet: {}
};
/* harmony default export * / var libraryBase = (lib);
// EXTERNAL MODULE: ./src/utils/dispatch.js
var dispatch = __webpack_require__(805);
// EXTERNAL MODULE: ./src/utils/internal.js
var internal = __webpack_require__(530);
// EXTERNAL MODULE: ./src/utils/history.js
var utils_history = __webpack_require__(911);
;// ./src/utils/editor.js
/**
* Open the editor
*
* @param object cell
* @return void
* /
const openEditor = function(cell, empty, e) {
const obj = this;
// Get cell position
const y = cell.getAttribute('data-y');
const x = cell.getAttribute('data-x');
// On edition start
dispatch/* default * /.A.call(obj, 'oneditionstart', obj, cell, parseInt(x), parseInt(y));
// Overflow
if (x > 0) {
obj.records[y][x-1].element.style.overflow = 'hidden';
}
// Create editor
const createEditor = function(type) {
// Cell information
const info = cell.getBoundingClientRect();
// Create dropdown
const editor = document.createElement(type);
editor.style.width = (info.width) + 'px';
editor.style.height = (info.height - 2) + 'px';
editor.style.minHeight = (info.height - 2) + 'px';
// Edit cell
cell.classList.add('editor');
cell.innerHTML = '';
cell.appendChild(editor);
return editor;
}
// Readonly
if (cell.classList.contains('readonly') == true) {
// Do nothing
} else {
// Holder
obj.edition = [ obj.records[y][x].element, obj.records[y][x].element.innerHTML, x, y ];
// If there is a custom editor for it
if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {
// Custom editors
obj.options.columns[x].type.openEditor(cell, obj.options.data[y][x], parseInt(x), parseInt(y), obj, obj.options.columns[x], e);
// On edition start
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
} else {
// Native functions
if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'hidden') {
// Do nothing
} else if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio')) {
// Get value
const value = cell.children[0].checked ? false : true;
// Toogle value
obj.setValue(cell, value);
// Do not keep edition open
obj.edition = null;
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {
// Get current value
let value = obj.options.data[y][x];
if (obj.options.columns[x].multiple && !Array.isArray(value)) {
value = value.split(';');
}
// Create dropdown
let source;
if (typeof(obj.options.columns[x].filter) == 'function') {
source = obj.options.columns[x].filter(obj.element, cell, x, y, obj.options.columns[x].source);
} else {
source = obj.options.columns[x].source;
}
// Do not change the original source
const data = [];
if (source) {
for (let j = 0; j < source.length; j++) {
data.push(source[j]);
}
}
// Create editor
const editor = createEditor('div');
// On edition start
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
const options = {
data: data,
multiple: obj.options.columns[x].multiple ? true : false,
autocomplete: obj.options.columns[x].autocomplete ? true : false,
opened:true,
value: value,
width:'100%',
height:editor.style.minHeight,
position: (obj.options.tableOverflow == true || obj.parent.config.fullscreen == true) ? true : false,
onclose:function() {
closeEditor.call(obj, cell, true);
}
};
if (obj.options.columns[x].options && obj.options.columns[x].options.type) {
options.type = obj.options.columns[x].options.type;
}
jSuites.dropdown(editor, options);
} else if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color')) {
// Value
const value = obj.options.data[y][x];
// Create editor
const editor = createEditor('input');
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
editor.value = value;
const options = obj.options.columns[x].options ? { ...obj.options.columns[x].options } : {};
if (obj.options.tableOverflow == true || obj.parent.config.fullscreen == true) {
options.position = true;
}
options.value = obj.options.data[y][x];
options.opened = true;
options.onclose = function(el, value) {
closeEditor.call(obj, cell, true);
}
// Current value
if (obj.options.columns[x].type == 'color') {
jSuites.color(editor, options);
const rect = cell.getBoundingClientRect();
if (options.position) {
editor.nextSibling.children[1].style.top = (rect.top + rect.height) + 'px';
editor.nextSibling.children[1].style.left = rect.left + 'px';
}
} else {
if (!options.format) {
options.format = 'YYYY-MM-DD';
}
jSuites.calendar(editor, options);
}
// Focus on editor
editor.focus();
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {
const value = obj.options.data[y][x];
// Create editor
const editor = createEditor('div');
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
editor.style.position = 'relative';
const div = document.createElement('div');
div.classList.add('jss_richtext');
editor.appendChild(div);
jSuites.editor(div, {
focus: true,
value: value,
});
const rect = cell.getBoundingClientRect();
const rectContent = div.getBoundingClientRect();
if (window.innerHeight < rect.bottom + rectContent.height) {
div.style.top = (rect.bottom - (rectContent.height + 2)) + 'px';
} else {
div.style.top = (rect.top) + 'px';
}
if (window.innerWidth < rect.left + rectContent.width) {
div.style.left = (rect.right - (rectContent.width + 2)) + 'px';
} else {
div.style.left = rect.left + 'px';
}
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {
// Value
const img = cell.children[0];
// Create editor
const editor = createEditor('div');
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
editor.style.position = 'relative';
const div = document.createElement('div');
div.classList.add('jclose');
if (img && img.src) {
div.appendChild(img);
}
editor.appendChild(div);
jSuites.image(div, obj.options.columns[x]);
const rect = cell.getBoundingClientRect();
const rectContent = div.getBoundingClientRect();
if (window.innerHeight < rect.bottom + rectContent.height) {
div.style.top = (rect.top - (rectContent.height + 2)) + 'px';
} else {
div.style.top = (rect.top) + 'px';
}
div.style.left = rect.left + 'px';
} else {
// Value
const value = empty == true ? '' : obj.options.data[y][x];
// Basic editor
let editor;
if ((!obj.options.columns || !obj.options.columns[x] || obj.options.columns[x].wordWrap != false) && (obj.options.wordWrap == true || (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].wordWrap == true))) {
editor = createEditor('textarea');
} else {
editor = createEditor('input');
}
dispatch/* default * /.A.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);
editor.focus();
editor.value = value;
// Column options
const options = obj.options.columns && obj.options.columns[x];
// Apply format when is not a formula
if (! (0,internal/* isFormula * /.dw)(value)) {
if (options) {
// Format
const opt = (0,internal/* getMask * /.rS)(options);
if (opt) {
// Masking
if (! options.disabledMaskOnEdition) {
if (options.mask) {
const m = options.mask.split(';')
editor.setAttribute('data-mask', m[0]);
} else if (options.locale) {
editor.setAttribute('data-locale', options.locale);
}
}
// Input
opt.input = editor;
// Configuration
editor.mask = opt;
// Do not treat the decimals
jSuites.mask.render(value, opt, false);
}
}
}
editor.onblur = function() {
closeEditor.call(obj, cell, true);
};
editor.scrollLeft = editor.scrollWidth;
}
}
}
}
/**
* Close the editor and save the information
*
* @param object cell
* @param boolean save
* @return void
* /
const closeEditor = function(cell, save) {
const obj = this;
const x = parseInt(cell.getAttribute('data-x'));
const y = parseInt(cell.getAttribute('data-y'));
let value;
// Get cell properties
if (save == true) {
// If custom editor
if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {
// Custom editor
value = obj.options.columns[x].type.closeEditor(cell, save, parseInt(x), parseInt(y), obj, obj.options.columns[x]);
} else {
// Native functions
if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden')) {
// Do nothing
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {
value = cell.children[0].dropdown.close(true);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {
value = cell.children[0].calendar.close(true);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {
value = cell.children[0].color.close(true);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {
value = cell.children[0].children[0].editor.getData();
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {
const img = cell.children[0].children[0].children[0];
value = img && img.tagName == 'IMG' ? img.src : '';
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'numeric') {
value = cell.children[0].value;
if ((''+value).substr(0,1) != '=') {
if (value == '') {
value = obj.options.columns[x].allowEmpty ? '' : 0;
}
}
cell.children[0].onblur = null;
} else {
value = cell.children[0].value;
cell.children[0].onblur = null;
// Column options
const options = obj.options.columns && obj.options.columns[x];
if (options) {
// Format
const opt = (0,internal/* getMask * /.rS)(options);
if (opt) {
// Keep numeric in the raw data
if (value !== '' && ! (0,internal/* isFormula * /.dw)(value) && typeof(value) !== 'number') {
const t = jSuites.mask.extract(value, opt, true);
if (t && t.value !== '') {
value = t.value;
}
}
}
}
}
}
// Ignore changes if the value is the same
if (obj.options.data[y][x] == value) {
cell.innerHTML = obj.edition[1];
} else {
obj.setValue(cell, value);
}
} else {
if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {
// Custom editor
obj.options.columns[x].type.closeEditor(cell, save, parseInt(x), parseInt(y), obj, obj.options.columns[x]);
} else {
if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {
cell.children[0].dropdown.close(true);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {
cell.children[0].calendar.close(true);
} else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {
cell.children[0].color.close(true);
} else {
cell.children[0].onblur = null;
}
}
// Restore value
cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : '';
}
// On edition end
dispatch/* default * /.A.call(obj, 'oneditionend', obj, cell, x, y, value, save);
// Remove editor class
cell.classList.remove('editor');
// Finish edition
obj.edition = null;
}
/**
* Toogle
* /
const setCheckRadioValue = function() {
const obj = this;
const records = [];
const keys = Object.keys(obj.highlighted);
for (let i = 0; i < keys.length; i++) {
const x = obj.highlighted[i].element.getAttribute('data-x');
const y = obj.highlighted[i].element.getAttribute('data-y');
if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') {
// Update cell
records.push(internal/* updateCell * /.k9.call(obj, x, y, ! obj.options.data[y][x]));
}
}
if (records.length) {
// Update history
utils_history/* setHistory * /.Dh.call(obj, {
action:'setValue',
records:records,
selection:obj.selectedCell,
});
// On after changes
const onafterchangesRecords = records.map(function(record) {
return {
x: record.x,
y: record.y,
value: record.newValue,
oldValue: record.oldValue,
};
});
dispatch/* default * /.A.call(obj, 'onafterchanges', obj, onafterchangesRecords);
}
}
// EXTERNAL MODULE: ./src/utils/lazyLoading.js
var lazyLoading = __webpack_require__(497);
;// ./src/utils/keys.js
const upGet = function(x, y) {
const obj = this;
x = parseInt(x);
y = parseInt(y);
for (let j = (y - 1); j >= 0; j--) {
if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {
if (obj.records[j][x].element.getAttribute('data-merged')) {
if (obj.records[j][x].element == obj.records[y][x].element) {
continue;
}
}
y = j;
break;
}
}
return y;
}
const upVisible = function(group, direction) {
const obj = this;
let x, y;
if (group == 0) {
x = parseInt(obj.selectedCell[0]);
y = parseInt(obj.selectedCell[1]);
} else {
x = parseInt(obj.selectedCell[2]);
y = parseInt(obj.selectedCell[3]);
}
if (direction == 0) {
for (let j = 0; j < y; j++) {
if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {
y = j;
break;
}
}
} else {
y = upGet.call(obj, x, y);
}
if (group == 0) {
obj.selectedCell[0] = x;
obj.selectedCell[1] = y;
} else {
obj.selectedCell[2] = x;
obj.selectedCell[3] = y;
}
}
const up = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (obj.selectedCell[3] > 0) {
upVisible.call(obj, 1, ctrlKey ? 0 : 1)
}
} else {
if (obj.selectedCell[1] > 0) {
upVisible.call(obj, 0, ctrlKey ? 0 : 1)
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
// Update selection
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
// Change page
if (obj.options.lazyLoading == true) {
if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) {
lazyLoading/* loadPage * /.wu.call(obj, 0);
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
} else {
if (lazyLoading/* loadValidation * /.AG.call(obj)) {
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
} else {
const item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));
if (obj.selectedCell[1] - item < 30) {
lazyLoading/* loadUp * /.G_.call(obj);
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
}
}
}
} else if (obj.options.pagination > 0) {
const pageNumber = obj.whichPage(obj.selectedCell[3]);
if (pageNumber != obj.pageNumber) {
obj.page(pageNumber);
}
}
internal/* updateScroll * /.Rs.call(obj, 1);
}
const rightGet = function(x, y) {
const obj = this;
x = parseInt(x);
y = parseInt(y);
for (let i = (x + 1); i < obj.headers.length; i++) {
if (obj.records[y][i].element.style.display != 'none') {
if (obj.records[y][i].element.getAttribute('data-merged')) {
if (obj.records[y][i].element == obj.records[y][x].element) {
continue;
}
}
x = i;
break;
}
}
return x;
}
const rightVisible = function(group, direction) {
const obj = this;
let x, y;
if (group == 0) {
x = parseInt(obj.selectedCell[0]);
y = parseInt(obj.selectedCell[1]);
} else {
x = parseInt(obj.selectedCell[2]);
y = parseInt(obj.selectedCell[3]);
}
if (direction == 0) {
for (let i = obj.headers.length - 1; i > x; i--) {
if (obj.records[y][i].element.style.display != 'none') {
x = i;
break;
}
}
} else {
x = rightGet.call(obj, x, y);
}
if (group == 0) {
obj.selectedCell[0] = x;
obj.selectedCell[1] = y;
} else {
obj.selectedCell[2] = x;
obj.selectedCell[3] = y;
}
}
const right = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (obj.selectedCell[2] < obj.headers.length - 1) {
rightVisible.call(obj, 1, ctrlKey ? 0 : 1)
}
} else {
if (obj.selectedCell[0] < obj.headers.length - 1) {
rightVisible.call(obj, 0, ctrlKey ? 0 : 1)
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
internal/* updateScroll * /.Rs.call(obj, 2);
}
const downGet = function(x, y) {
const obj = this;
x = parseInt(x);
y = parseInt(y);
for (let j = (y + 1); j < obj.rows.length; j++) {
if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {
if (obj.records[j][x].element.getAttribute('data-merged')) {
if (obj.records[j][x].element == obj.records[y][x].element) {
continue;
}
}
y = j;
break;
}
}
return y;
}
const downVisible = function(group, direction) {
const obj = this;
let x, y;
if (group == 0) {
x = parseInt(obj.selectedCell[0]);
y = parseInt(obj.selectedCell[1]);
} else {
x = parseInt(obj.selectedCell[2]);
y = parseInt(obj.selectedCell[3]);
}
if (direction == 0) {
for (let j = obj.rows.length - 1; j > y; j--) {
if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {
y = j;
break;
}
}
} else {
y = downGet.call(obj, x, y);
}
if (group == 0) {
obj.selectedCell[0] = x;
obj.selectedCell[1] = y;
} else {
obj.selectedCell[2] = x;
obj.selectedCell[3] = y;
}
}
const down = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (obj.selectedCell[3] < obj.records.length - 1) {
downVisible.call(obj, 1, ctrlKey ? 0 : 1)
}
} else {
if (obj.selectedCell[1] < obj.records.length - 1) {
downVisible.call(obj, 0, ctrlKey ? 0 : 1)
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
// Change page
if (obj.options.lazyLoading == true) {
if ((obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) {
lazyLoading/* loadPage * /.wu.call(obj, -1);
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
} else {
if (lazyLoading/* loadValidation * /.AG.call(obj)) {
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
} else {
const item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));
if (item - obj.selectedCell[3] < 30) {
lazyLoading/* loadDown * /.p6.call(obj);
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
}
}
}
} else if (obj.options.pagination > 0) {
const pageNumber = obj.whichPage(obj.selectedCell[3]);
if (pageNumber != obj.pageNumber) {
obj.page(pageNumber);
}
}
internal/* updateScroll * /.Rs.call(obj, 3);
}
const leftGet = function(x, y) {
const obj = this;
x = parseInt(x);
y = parseInt(y);
for (let i = (x - 1); i >= 0; i--) {
if (obj.records[y][i].element.style.display != 'none') {
if (obj.records[y][i].element.getAttribute('data-merged')) {
if (obj.records[y][i].element == obj.records[y][x].element) {
continue;
}
}
x = i;
break;
}
}
return x;
}
const leftVisible = function(group, direction) {
const obj = this;
let x, y;
if (group == 0) {
x = parseInt(obj.selectedCell[0]);
y = parseInt(obj.selectedCell[1]);
} else {
x = parseInt(obj.selectedCell[2]);
y = parseInt(obj.selectedCell[3]);
}
if (direction == 0) {
for (let i = 0; i < x; i++) {
if (obj.records[y][i].element.style.display != 'none') {
x = i;
break;
}
}
} else {
x = leftGet.call(obj, x, y);
}
if (group == 0) {
obj.selectedCell[0] = x;
obj.selectedCell[1] = y;
} else {
obj.selectedCell[2] = x;
obj.selectedCell[3] = y;
}
}
const left = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (obj.selectedCell[2] > 0) {
leftVisible.call(obj, 1, ctrlKey ? 0 : 1)
}
} else {
if (obj.selectedCell[0] > 0) {
leftVisible.call(obj, 0, ctrlKey ? 0 : 1)
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
internal/* updateScroll * /.Rs.call(obj, 0);
}
const first = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (ctrlKey) {
obj.selectedCell[3] = 0;
} else {
leftVisible.call(obj, 1, 0);
}
} else {
if (ctrlKey) {
obj.selectedCell[1] = 0;
} else {
leftVisible.call(obj, 0, 0);
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
// Change page
if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) {
lazyLoading/* loadPage * /.wu.call(obj, 0);
} else if (obj.options.pagination > 0) {
const pageNumber = obj.whichPage(obj.selectedCell[3]);
if (pageNumber != obj.pageNumber) {
obj.page(pageNumber);
}
}
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
internal/* updateScroll * /.Rs.call(obj, 1);
}
const last = function(shiftKey, ctrlKey) {
const obj = this;
if (shiftKey) {
if (ctrlKey) {
obj.selectedCell[3] = obj.records.length - 1;
} else {
rightVisible.call(obj, 1, 0);
}
} else {
if (ctrlKey) {
obj.selectedCell[1] = obj.records.length - 1;
} else {
rightVisible.call(obj, 0, 0);
}
obj.selectedCell[2] = obj.selectedCell[0];
obj.selectedCell[3] = obj.selectedCell[1];
}
// Change page
if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) {
lazyLoading/* loadPage * /.wu.call(obj, -1);
} else if (obj.options.pagination > 0) {
const pageNumber = obj.whichPage(obj.selectedCell[3]);
if (pageNumber != obj.pageNumber) {
obj.page(pageNumber);
}
}
obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);
internal/* updateScroll * /.Rs.call(obj, 3);
}
// EXTERNAL MODULE: ./src/utils/merges.js
var merges = __webpack_require__(910);
// EXTERNAL MODULE: ./src/utils/selection.js
var selection = __webpack_require__(657);
// EXTERNAL MODULE: ./src/utils/helpers.js
var helpers = __webpack_require__(978);
// EXTERNAL MODULE: ./src/utils/internalHelpers.js
var internalHelpers = __webpack_require__(689);
;// ./src/utils/copyPaste.js
/**
* Copy method
*
* @param bool highlighted - Get only highlighted cells
* @param delimiter - \t default to keep compatibility with excel
* @return string value
* /
const copy = function(highlighted, delimiter, returnData, includeHeaders, download, isCut, processed) {
const obj = this;
if (! delimiter) {
delimiter = "\t";
}
const div = new RegExp(delimiter, 'ig');
// Controls
const header = [];
let col = [];
let colLabel = [];
const row = [];
const rowLabel = [];
const x = obj.options.data[0].length;
const y = obj.options.data.length;
let tmp = '';
let copyHeader = false;
let headers = '';
let nestedHeaders = '';
let numOfCols = 0;
let numOfRows = 0;
// Partial copy
let copyX = 0;
let copyY = 0;
let isPartialCopy = true;
// Go through the columns to get the data
for (let j = 0; j < y; j++) {
for (let i = 0; i < x; i++) {
// If cell is highlighted
if (! highlighted || obj.records[j][i].element.classList.contains('highlight')) {
if (copyX <= i) {
copyX = i;
}
if (copyY <= j) {
copyY = j;
}
}
}
}
if (x === copyX+1 && y === copyY+1) {
isPartialCopy = false;
}
if (
download &&
(obj.parent.config.includeHeadersOnDownload == true || includeHeaders)
) {
// Nested headers
if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {
tmp = obj.options.nestedHeaders;
for (let j = 0; j < tmp.length; j++) {
const nested = [];
for (let i = 0; i < tmp[j].length; i++) {
const colspan = parseInt(tmp[j][i].colspan);
nested.push(tmp[j][i].title);
for (let c = 0; c < colspan - 1; c++) {
nested.push('');
}
}
nestedHeaders += nested.join(delimiter) + "\r\n";
}
}
copyHeader = true;
}
// Reset container
obj.style = [];
// Go through the columns to get the data
for (let j = 0; j < y; j++) {
col = [];
colLabel = [];
for (let i = 0; i < x; i++) {
// If cell is highlighted
if (! highlighted || obj.records[j][i].element.classList.contains('highlight')) {
if (copyHeader == true) {
header.push(obj.headers[i].textContent);
}
// Values
let value = obj.options.data[j][i];
if (value.match && (value.match(div) || value.match(/,/g) || value.match(/\n/) || value.match(/\"/))) {
value = value.replace(new RegExp('"', 'g'), '""');
value = '"' + value + '"';
}
col.push(value);
// Labels
let label;
if (
obj.options.columns &&
obj.options.columns[i] &&
(
obj.options.columns[i].type == 'checkbox' ||
obj.options.columns[i].type == 'radio'
)
) {
label = value;
} else {
label = obj.records[j][i].element.innerHTML;
if (label.match && (label.match(div) || label.match(/,/g) || label.match(/\n/) || label.match(/\"/))) {
// Scape double quotes
label = label.replace(new RegExp('"', 'g'), '""');
label = '"' + label + '"';
}
}
colLabel.push(label);
// Get style
tmp = obj.records[j][i].element.getAttribute('style');
tmp = tmp.replace('display: none;', '');
obj.style.push(tmp ? tmp : '');
}
}
if (col.length) {
if (copyHeader) {
numOfCols = col.length;
row.push(header.join(delimiter));
}
row.push(col.join(delimiter));
}
if (colLabel.length) {
numOfRows++;
if (copyHeader) {
rowLabel.push(header.join(delimiter));
copyHeader = false;
}
rowLabel.push(colLabel.join(delimiter));
}
}
if (x == numOfCols && y == numOfRows) {
headers = nestedHeaders;
}
// Final string
const str = headers + row.join("\r\n");
let strLabel = headers + rowLabel.join("\r\n");
// Create a hidden textarea to copy the values
if (! returnData) {
// Paste event
const selectedRange = [
Math.min(obj.selectedCell[0], obj.selectedCell[2]),
Math.min(obj.selectedCell[1], obj.selectedCell[3]),
Math.max(obj.selectedCell[0], obj.selectedCell[2]),
Math.max(obj.selectedCell[1], obj.selectedCell[3]),
];
const result = dispatch/* default * /.A.call(obj, 'oncopy', obj, selectedRange, strLabel, isCut);
if (result) {
strLabel = result;
} else if (result === false) {
return false;
}
obj.textarea.value = strLabel;
obj.textarea.select();
document.execCommand("copy");
}
// Keep data
if (processed == true) {
obj.data = strLabel;
} else {
obj.data = str;
}
// Keep non visible information
obj.hashString = selection/* hash * /.tW.call(obj, obj.data);
// Any exiting border should go
if (! returnData) {
selection/* removeCopyingSelection * /.kA.call(obj);
// Border
if (obj.highlighted) {
for (let i = 0; i < obj.highlighted.length; i++) {
obj.highlighted[i].element.classList.add('copying');
if (obj.highlighted[i].element.classList.contains('highlight-left')) {
obj.highlighted[i].element.classList.add('copying-left');
}
if (obj.highlighted[i].element.classList.contains('highlight-right')) {
obj.highlighted[i].element.classList.add('copying-right');
}
if (obj.highlighted[i].element.classList.contains('highlight-top')) {
obj.highlighted[i].element.classList.add('copying-top');
}
if (obj.highlighted[i].element.classList.contains('highlight-bottom')) {
obj.highlighted[i].element.classList.add('copying-bottom');
}
}
}
}
return obj.data;
}
/**
* Jspreadsheet paste method
*
* @param x target column
* @param y target row
* @param data paste data. if data hash is the same as the copied data, apply style from copied cells
* @return string value
* /
const paste = function(x, y, data) {
const obj = this;
// Controls
const dataHash = (0,selection/* hash * /.tW)(data);
let style = (dataHash == obj.hashString) ? obj.style : null;
// Depending on the behavior
if (dataHash == obj.hashString) {
data = obj.data;
}
// Split new line
data = (0,helpers.parseCSV)(data, "\t");
const ex = obj.selectedCell[2];
const ey = obj.selectedCell[3];
const w = ex - x + 1;
const h = ey - y + 1;
// Modify data to allow wor extending paste range in multiples of input range
const srcW = data[0].length;
if ((w > 1) & Number.isInteger(w / srcW)) {
const repeats = w / srcW;
if( style ){
const newStyle = [];
for(let i=0 ;i <style.length ; i+=srcW ){
const chunk = style.slice(i,i+srcW);
for (let j = 0; j < repeats; j++) {
newStyle.push(...chunk);
}
}
style = newStyle;
};
const arrayB = data.map(function (row, i) {
const arrayC = Array.apply(null, { length: repeats * row.length }).map(
function (e, i) { return row[i % row.length]; }
);
return arrayC;
});
data = arrayB;
}
const srcH = data.length;
if ((h > 1) & Number.isInteger(h / srcH)) {
const repeats = h / srcH;
if( style ){
const newStyle = [];
for (let j = 0; j < repeats; j++) {
newStyle.push(...style);
}
style = newStyle;
};
const arrayB = Array.apply(null, { length: repeats * srcH }).map(
function (e, i) { return data[i % srcH]; }
);
data = arrayB;
}
// Paste filter
const ret = dispatch/* default * /.A.call(
obj,
'onbeforepaste',
obj,
data.map(function(row) {
return row.map(function(item) {
return { value: item }
});
}),
x,
y
);
if (ret === false) {
return false;
} else if (ret) {
data = ret;
}
if (x != null && y != null && data) {
// Records
let i = 0;
let j = 0;
const records = [];
const newStyle = {};
const oldStyle = {};
let styleIndex = 0;
// Index
let colIndex = parseInt(x);
let rowIndex = parseInt(y);
let row = null;
const hiddenColCount = obj.headers.slice(colIndex).filter(x=>x.style.display === 'none').length;
const expandedColCount = colIndex + hiddenColCount + data[0].length;
const currentColCount = obj.headers.length;
if( expandedColCount > currentColCount){
obj.skipUpdateTableReferences = true;
obj.insertColumn( expandedColCount - currentColCount);
}
const hiddenRowCount = obj.rows.slice(rowIndex).filter(x=>x.element.style.display === 'none').length;
const expandedRowCount = rowIndex + hiddenRowCount + data.length;
const currentRowCount = obj.rows.length;
if( expandedRowCount > currentRowCount){
obj.skipUpdateTableReferences = true;
obj.insertRow( expandedRowCount - currentRowCount);
}
if( obj.skipUpdateTableReferences ){
obj.skipUpdateTableReferences = false;
internal/* updateTableReferences * /.o8.call(obj);
}
// Go through the columns to get the data
while (row = data[j]) {
i = 0;
colIndex = parseInt(x);
while (row[i] != null) {
// Update and keep history
const record = internal/* updateCell * /.k9.call(obj, colIndex, rowIndex, row[i]);
// Keep history
records.push(record);
// Update all formulas in the chain
internal/* updateFormulaChain * /.xF.call(obj, colIndex, rowIndex, records);
// Style
if (style && style[styleIndex]) {
const columnName = (0,internalHelpers/* getColumnNameFromId * /.t3)([colIndex, rowIndex]);
newStyle[columnName] = style[styleIndex];
oldStyle[columnName] = obj.getStyle(columnName);
obj.records[rowIndex][colIndex].element.setAttribute('style', style[styleIndex]);
styleIndex++
}
i++;
if (row[i] != null) {
if (colIndex >= obj.headers.length - 1) {
// If the pasted column is out of range, create it if possible
if (obj.options.allowInsertColumn != false) {
obj.insertColumn();
// Otherwise skip the pasted data that overflows
} else {
break;
}
}
colIndex = rightGet.call(obj, colIndex, rowIndex);
}
}
j++;
if (data[j]) {
if (rowIndex >= obj.rows.length-1) {
// If the pasted row is out of range, create it if possible
if (obj.options.allowInsertRow != false) {
obj.insertRow();
// Otherwise skip the pasted data that overflows
} else {
break;
}
}
rowIndex = downGet.call(obj, x, rowIndex);
}
}
// Select the new cells
selection/* updateSelectionFromCoords * /.AH.call(obj, x, y, colIndex, rowIndex);
// Update history
utils_history/* setHistory * /.Dh.call(obj, {
action:'setValue',
records:records,
selection:obj.selectedCell,
newStyle:newStyle,
oldStyle:oldStyle,
});
// Update table
internal/* updateTable * /.am.call(obj);
// Paste event
const eventRecords = [];
for (let j = 0; j < data.length; j++) {
for (let i = 0; i < data[j].length; i++) {
eventRecords.push({
x: i + x,
y: j + y,
value: data[j][i],
});
}
}
dispatch/* default * /.A.call(obj, 'onpaste', obj, eventRecords);
// On after changes
const onafterchangesRecords = records.map(function(record) {
return {
x: record.x,
y: record.y,
value: record.newValue,
oldValue: record.oldValue,
};
});
dispatch/* default * /.A.call(obj, 'onafterchanges', obj, onafterchangesRecords);
}
(0,selection/* removeCopyingSelection * /.kA)();
}
// EXTERNAL MODULE: ./src/utils/filter.js
var filter = __webpack_require__(829);
// EXTERNAL MODULE: ./src/utils/footer.js
var footer = __webpack_require__(160);
;// ./src/utils/columns.js
const getNumberOfColumns = function() {
const obj = this;
let numberOfColumns = (obj.options.columns && obj.options.columns.length) || 0;
if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') {
// Data keys
const keys = Object.keys(obj.options.data[0]);
if (keys.length > numberOfColumns) {
numberOfColumns = keys.length;
}
}
if (obj.options.minDimensions && obj.options.minDimensions[0] > numberOfColumns) {
numberOfColumns = obj.options.minDimensions[0];
}
return numberOfColumns;
}
const createCellHeader = function(colNumber) {
const obj = this;
// Create col global control
const colWidth = (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].width) || obj.options.defaultColWidth || 100;
const colAlign = (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].align) || obj.options.defaultColAlign || 'center';
// Create header cell
obj.headers[colNumber] = document.createElement('td');
obj.headers[colNumber].textContent = (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].title) || (0,helpers.getColumnName)(colNumber);
obj.headers[colNumber].setAttribute('data-x', colNumber);
obj.headers[colNumber].style.textAlign = colAlign;
if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].title) {
obj.headers[colNumber].setAttribute('title', obj.headers[colNumber].innerText);
}
if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].id) {
obj.headers[colNumber].setAttribute('id', obj.options.columns[colNumber].id);
}
// Width control
const colElement = document.createElement('col');
colElement.setAttribute('width', colWidth);
obj.cols[colNumber] = {
colElement,
x: colNumber,
};
// Hidden column
if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].type == 'hidden') {
obj.headers[colNumber].style.display = 'none';
colElement.style.display = 'none';
}
}
/**
* Insert a new column
*
* @param mixed - num of columns to be added or data to be added in one single column
* @param int columnNumber - number of columns to be created
* @param bool insertBefore
* @param object properties - column properties
* @return void
* /
const insertColumn = function(mixed, columnNumber, insertBefore, properties) {
const obj = this;
// Configuration
if (obj.options.allowInsertColumn != false) {
// Records
var records = [];
// Data to be insert
let data = [];
// The insert could be lead by number of rows or the array of data
let numOfColumns;
if (!Array.isArray(mixed)) {
numOfColumns = typeof mixed === 'number' ? mixed : 1;
} else {
numOfColumns = 1;
if (mixed) {
data = mixed;
}
}
// Direction
insertBefore = insertBefore ? true : false;
// Current column number
const currentNumOfColumns = Math.max(
obj.options.columns.length,
...obj.options.data.map(function(row) {
return row.length;
})
);
const lastColumn = currentNumOfColumns - 1;
// Confirm position
if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) {
columnNumber = lastColumn;
}
// Create default properties
if (! properties) {
properties = [];
}
for (let i = 0; i < numOfColumns; i++) {
if (! properties[i]) {
properties[i] = {};
}
}
const columns = [];
if (!Array.isArray(mixed)) {
for (let i = 0; i < mixed; i++) {
const column = {
column: columnNumber + i + (insertBefore ? 0 : 1),
options: Object.assign({}, properties[i]),
};
columns.push(column);
}
} else {
const data = [];
for (let i = 0; i < obj.options.data.length; i++) {
data.push(i < mixed.length ? mixed[i] : '');
}
const column = {
column: columnNumber + (insertBefore ? 0 : 1),
options: Object.assign({}, properties[0]),
data,
};
columns.push(column);
}
// Onbeforeinsertcolumn
if (dispatch/* default * /.A.call(obj, 'onbeforeinsertcolumn', obj, columns) === false) {
return false;
}
// Merged cells
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
if (merges/* isColMerged * /.Lt.call(obj, columnNumber, insertBefore).length) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
}
// Insert before
const columnIndex = (! insertBefore) ? columnNumber + 1 : columnNumber;
obj.options.columns = (0,internalHelpers/* injectArray * /.Hh)(obj.options.columns, columnIndex, properties);
// Open space in the containers
const currentHeaders = obj.headers.splice(columnIndex);
const currentColgroup = obj.cols.splice(columnIndex);
// History
const historyHeaders = [];
const historyColgroup = [];
const historyRecords = [];
const historyData = [];
const historyFooters = [];
// Add new headers
for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
createCellHeader.call(obj, col);
obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col+1]);
obj.colgroupContainer.insertBefore(obj.cols[col].colElement, obj.colgroupContainer.children[col+1]);
historyHeaders.push(obj.headers[col]);
historyColgroup.push(obj.cols[col]);
}
// Add new footer cells
if (obj.options.footers) {
for (let j = 0; j < obj.options.footers.length; j++) {
historyFooters[j] = [];
for (let i = 0; i < numOfColumns; i++) {
historyFooters[j].push('');
}
obj.options.footers[j].splice(columnIndex, 0, historyFooters[j]);
}
}
// Adding visual columns
for (let row = 0; row < obj.options.data.length; row++) {
// Keep the current data
const currentData = obj.options.data[row].splice(columnIndex);
const currentRecord = obj.records[row].splice(columnIndex);
// History
historyData[row] = [];
historyRecords[row] = [];
for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
// New value
const value = data[row] ? data[row] : '';
obj.options.data[row][col] = value;
// New cell
const td = internal/* createCell * /.P9.call(obj, col, row, obj.options.data[row][col]);
obj.records[row][col] = {
element: td,
y: row,
};
// Add cell to the row
if (obj.rows[row]) {
obj.rows[row].element.insertBefore(td, obj.rows[row].element.children[col+1]);
}
if (obj.options.columns && obj.options.columns[col] && typeof obj.options.columns[col].render === 'function') {
obj.options.columns[col].render(
td,
value,
parseInt(col),
parseInt(row),
obj,
obj.options.columns[col],
);
}
// Record History
historyData[row].push(value);
historyRecords[row].push({ element: td, x: col, y: row });
}
// Copy the data back to the main data
Array.prototype.push.apply(obj.options.data[row], currentData);
Array.prototype.push.apply(obj.records[row], currentRecord);
}
Array.prototype.push.apply(obj.headers, currentHeaders);
Array.prototype.push.apply(obj.cols, currentColgroup);
for (let i = columnIndex; i < obj.cols.length; i++) {
obj.cols[i].x = i;
}
for (let j = 0; j < obj.records.length; j++) {
for (let i = 0; i < obj.records[j].length; i++) {
obj.records[j][i].x = i;
}
}
// Adjust nested headers
if (
obj.options.nestedHeaders &&
obj.options.nestedHeaders.length > 0 &&
obj.options.nestedHeaders[0] &&
obj.options.nestedHeaders[0][0]
) {
for (let j = 0; j < obj.options.nestedHeaders.length; j++) {
const colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) + numOfColumns;
obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
let o = obj.thead.children[j].children[obj.thead.children[j].children.length-1].getAttribute('data-column');
o = o.split(',');
for (let col = columnIndex; col < (numOfColumns + columnIndex); col++) {
o.push(col);
}
obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('data-column', o);
}
}
// Keep history
utils_history/* setHistory * /.Dh.call(obj, {
action: 'insertColumn',
columnNumber:columnNumber,
numOfColumns:numOfColumns,
insertBefore:insertBefore,
columns:properties,
headers:historyHeaders,
cols:historyColgroup,
records:historyRecords,
footers:historyFooters,
data:historyData,
});
// Remove table references
internal/* updateTableReferences * /.o8.call(obj);
// Events
dispatch/* default * /.A.call(obj, 'oninsertcolumn', obj, columns);
}
}
/**
* Move column
*
* @return void
* /
const moveColumn = function(o, d) {
const obj = this;
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
let insertBefore;
if (o > d) {
insertBefore = 1;
} else {
insertBefore = 0;
}
if (merges/* isColMerged * /.Lt.call(obj, o).length || merges/* isColMerged * /.Lt.call(obj, d, insertBefore).length) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
}
o = parseInt(o);
d = parseInt(d);
if (o > d) {
obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]);
obj.colgroupContainer.insertBefore(obj.cols[o].colElement, obj.cols[d].colElement);
for (let j = 0; j < obj.rows.length; j++) {
obj.rows[j].element.insertBefore(obj.records[j][o].element, obj.records[j][d].element);
}
} else {
obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling);
obj.colgroupContainer.insertBefore(obj.cols[o].colElement, obj.cols[d].colElement.nextSibling);
for (let j = 0; j < obj.rows.length; j++) {
obj.rows[j].element.insertBefore(obj.records[j][o].element, obj.records[j][d].element.nextSibling);
}
}
obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]);
obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]);
obj.cols.splice(d, 0, obj.cols.splice(o, 1)[0]);
const firstAffectedIndex = Math.min(o, d);
const lastAffectedIndex = Math.max(o, d);
for (let j = 0; j < obj.rows.length; j++) {
obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]);
obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]);
}
for (let i = firstAffectedIndex; i <= lastAffectedIndex; i++) {
obj.cols[i].x = i;
}
for (let j = 0; j < obj.records.length; j++) {
for (let i = firstAffectedIndex; i <= lastAffectedIndex; i++) {
obj.records[j][i].x = i;
}
}
// Update footers position
if (obj.options.footers) {
for (let j = 0; j < obj.options.footers.length; j++) {
obj.options.footers[j].splice(d, 0, obj.options.footers[j].splice(o, 1)[0]);
}
}
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action:'moveColumn',
oldValue: o,
newValue: d,
});
// Update table references
internal/* updateTableReferences * /.o8.call(obj);
// Events
dispatch/* default * /.A.call(obj, 'onmovecolumn', obj, o, d, 1);
}
/**
* Delete a column by number
*
* @param integer columnNumber - reference column to be excluded
* @param integer numOfColumns - number of columns to be excluded from the reference column
* @return void
* /
const deleteColumn = function(columnNumber, numOfColumns) {
const obj = this;
// Global Configuration
if (obj.options.allowDeleteColumn != false) {
if (obj.headers.length > 1) {
// Delete column definitions
if (columnNumber == undefined) {
const number = obj.getSelectedColumns(true);
if (! number.length) {
// Remove last column
columnNumber = obj.headers.length - 1;
numOfColumns = 1;
} else {
// Remove selected
columnNumber = parseInt(number[0]);
numOfColumns = parseInt(number.length);
}
}
// Lasat column
const lastColumn = obj.options.data[0].length - 1;
if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) {
columnNumber = lastColumn;
}
// Minimum of columns to be delete is 1
if (! numOfColumns) {
numOfColumns = 1;
}
// Can't delete more than the limit of the table
if (numOfColumns > obj.options.data[0].length - columnNumber) {
numOfColumns = obj.options.data[0].length - columnNumber;
}
const removedColumns = [];
for (let i = 0; i < numOfColumns; i++) {
removedColumns.push(i + columnNumber);
}
// onbeforedeletecolumn
if (dispatch/* default * /.A.call(obj, 'onbeforedeletecolumn', obj, removedColumns) === false) {
return false;
}
// Can't remove the last column
if (parseInt(columnNumber) > -1) {
// Merged cells
let mergeExists = false;
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {
if (merges/* isColMerged * /.Lt.call(obj, col, null).length) {
mergeExists = true;
}
}
}
if (mergeExists) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
// Delete the column properties
const columns = obj.options.columns ? obj.options.columns.splice(columnNumber, numOfColumns) : undefined;
for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {
obj.cols[col].colElement.className = '';
obj.headers[col].className = '';
obj.cols[col].colElement.parentNode.removeChild(obj.cols[col].colElement);
obj.headers[col].parentNode.removeChild(obj.headers[col]);
}
const historyHeaders = obj.headers.splice(columnNumber, numOfColumns);
const historyColgroup = obj.cols.splice(columnNumber, numOfColumns);
const historyRecords = [];
const historyData = [];
const historyFooters = [];
for (let row = 0; row < obj.options.data.length; row++) {
for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {
obj.records[row][col].element.className = '';
obj.records[row][col].element.parentNode.removeChild(obj.records[row][col].element);
}
}
// Delete headers
for (let row = 0; row < obj.options.data.length; row++) {
// History
historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns);
historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns);
}
for (let i = columnNumber; i < obj.cols.length; i++) {
obj.cols[i].x = i;
}
for (let j = 0; j < obj.records.length; j++) {
for (let i = columnNumber; i < obj.records[j].length; i++) {
obj.records[j][i].x = i;
}
}
// Delete footers
if (obj.options.footers) {
for (let row = 0; row < obj.options.footers.length; row++) {
historyFooters[row] = obj.options.footers[row].splice(columnNumber, numOfColumns);
}
}
// Remove selection
selection/* conditionalSelectionUpdate * /.at.call(obj, 0, columnNumber, (columnNumber + numOfColumns) - 1);
// Adjust nested headers
if (
obj.options.nestedHeaders &&
obj.options.nestedHeaders.length > 0 &&
obj.options.nestedHeaders[0] &&
obj.options.nestedHeaders[0][0]
) {
for (let j = 0; j < obj.options.nestedHeaders.length; j++) {
const colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan) - numOfColumns;
obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length-1].colspan = colspan;
obj.thead.children[j].children[obj.thead.children[j].children.length-1].setAttribute('colspan', colspan);
}
}
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action:'deleteColumn',
columnNumber:columnNumber,
numOfColumns:numOfColumns,
insertBefore: 1,
columns:columns,
headers:historyHeaders,
cols:historyColgroup,
records:historyRecords,
footers:historyFooters,
data:historyData,
});
// Update table references
internal/* updateTableReferences * /.o8.call(obj);
// Delete
dispatch/* default * /.A.call(obj, 'ondeletecolumn', obj, removedColumns);
}
} else {
console.error('Jspreadsheet: It is not possible to delete the last column');
}
}
}
/**
* Get the column width
*
* @param int column column number (first column is: 0)
* @return int current width
* /
const getWidth = function(column) {
const obj = this;
let data;
if (typeof column === 'undefined') {
// Get all headers
data = [];
for (let i = 0; i < obj.headers.length; i++) {
data.push((obj.options.columns && obj.options.columns[i] && obj.options.columns[i].width) || obj.options.defaultColWidth || 100);
}
} else {
data = parseInt(obj.cols[column].colElement.getAttribute('width'));
}
return data;
}
/**
* Set the column width
*
* @param int column number (first column is: 0)
* @param int new column width
* @param int old column width
* /
const setWidth = function (column, width, oldWidth) {
const obj = this;
if (width) {
if (Array.isArray(column)) {
// Oldwidth
if (! oldWidth) {
oldWidth = [];
}
// Set width
for (let i = 0; i < column.length; i++) {
if (! oldWidth[i]) {
oldWidth[i] = parseInt(obj.cols[column[i]].colElement.getAttribute('width'));
}
const w = Array.isArray(width) && width[i] ? width[i] : width;
obj.cols[column[i]].colElement.setAttribute('width', w);
if (!obj.options.columns) {
obj.options.columns = [];
}
if (!obj.options.columns[column[i]]) {
obj.options.columns[column[i]] = {};
}
obj.options.columns[column[i]].width = w;
}
} else {
// Oldwidth
if (! oldWidth) {
oldWidth = parseInt(obj.cols[column].colElement.getAttribute('width'));
}
// Set width
obj.cols[column].colElement.setAttribute('width', width);
if (!obj.options.columns) {
obj.options.columns = [];
}
if (!obj.options.columns[column]) {
obj.options.columns[column] = {};
}
obj.options.columns[column].width = width;
}
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action:'setWidth',
column:column,
oldValue:oldWidth,
newValue:width,
});
// On resize column
dispatch/* default * /.A.call(obj, 'onresizecolumn', obj, column, width, oldWidth);
// Update corner position
selection/* updateCornerPosition * /.Aq.call(obj);
}
}
/**
* Show column
* /
const showColumn = function(colNumber) {
const obj = this;
if (!Array.isArray(colNumber)) {
colNumber = [colNumber];
}
for (let i = 0; i < colNumber.length; i++) {
const columnIndex = colNumber[i];
obj.headers[columnIndex].style.display = '';
obj.cols[columnIndex].colElement.style.display = '';
if (obj.filter && obj.filter.children.length > columnIndex + 1) {
obj.filter.children[columnIndex + 1].style.display = '';
}
for (let j = 0; j < obj.options.data.length; j++) {
obj.records[j][columnIndex].element.style.display = '';
}
}
// Update footers
if (obj.options.footers) {
footer/* setFooter * /.e.call(obj);
}
obj.resetSelection();
}
/**
* Hide column
* /
const hideColumn = function(colNumber) {
const obj = this;
if (!Array.isArray(colNumber)) {
colNumber = [colNumber];
}
for (let i = 0; i < colNumber.length; i++) {
const columnIndex = colNumber[i];
obj.headers[columnIndex].style.display = 'none';
obj.cols[columnIndex].colElement.style.display = 'none';
if (obj.filter && obj.filter.children.length > columnIndex + 1) {
obj.filter.children[columnIndex + 1].style.display = 'none';
}
for (let j = 0; j < obj.options.data.length; j++) {
obj.records[j][columnIndex].element.style.display = 'none';
}
}
// Update footers
if (obj.options.footers) {
footer/* setFooter * /.e.call(obj);
}
obj.resetSelection();
}
/**
* Get a column data by columnNumber
* /
const getColumnData = function(columnNumber, processed) {
const obj = this;
const dataset = [];
// Go through the rows to get the data
for (let j = 0; j < obj.options.data.length; j++) {
if (processed) {
dataset.push(obj.records[j][columnNumber].element.innerHTML);
} else {
dataset.push(obj.options.data[j][columnNumber]);
}
}
return dataset;
}
/**
* Set a column data by colNumber
* /
const setColumnData = function(colNumber, data, force) {
const obj = this;
for (let j = 0; j < obj.rows.length; j++) {
// Update cell
const columnName = (0,internalHelpers/* getColumnNameFromId * /.t3)([ colNumber, j ]);
// Set value
if (data[j] != null) {
obj.setValue(columnName, data[j], force);
}
}
}
;// ./src/utils/rows.js
/**
* Create row
* /
const createRow = function(j, data) {
const obj = this;
// Create container
if (! obj.records[j]) {
obj.records[j] = [];
}
// Default data
if (! data) {
data = obj.options.data[j];
}
// New line of data to be append in the table
const row = {
element: document.createElement('tr'),
y: j,
};
obj.rows[j] = row;
row.element.setAttribute('data-y', j);
// Index
let index = null;
// Set default row height
if (obj.options.defaultRowHeight) {
row.element.style.height = obj.options.defaultRowHeight + 'px'
}
// Definitions
if (obj.options.rows && obj.options.rows[j]) {
if (obj.options.rows[j].height) {
row.element.style.height = obj.options.rows[j].height;
}
if (obj.options.rows[j].title) {
index = obj.options.rows[j].title;
}
}
if (! index) {
index = parseInt(j + 1);
}
// Row number label
const td = document.createElement('td');
td.innerHTML = index;
td.setAttribute('data-y', j);
td.className = 'jss_row';
row.element.appendChild(td);
const numberOfColumns = getNumberOfColumns.call(obj);
// Data columns
for (let i = 0; i < numberOfColumns; i++) {
// New column of data to be append in the line
obj.records[j][i] = {
element: internal/* createCell * /.P9.call(this, i, j, data[i]),
x: i,
y: j,
};
// Add column to the row
row.element.appendChild(obj.records[j][i].element);
if (obj.options.columns && obj.options.columns[i] && typeof obj.options.columns[i].render === 'function') {
obj.options.columns[i].render(
obj.records[j][i].element,
data[i],
parseInt(i),
parseInt(j),
obj,
obj.options.columns[i],
);
}
}
// Add row to the table body
return row;
}
/**
* Insert a new row
*
* @param mixed - number of blank lines to be insert or a single array with the data of the new row
* @param rowNumber
* @param insertBefore
* @return void
* /
const insertRow = function(mixed, rowNumber, insertBefore) {
const obj = this;
// Configuration
if (obj.options.allowInsertRow != false) {
// Records
var records = [];
// Data to be insert
let data = [];
// The insert could be lead by number of rows or the array of data
let numOfRows;
if (!Array.isArray(mixed)) {
numOfRows = typeof mixed !== 'undefined' ? mixed : 1;
} else {
numOfRows = 1;
if (mixed) {
data = mixed;
}
}
// Direction
insertBefore = insertBefore ? true : false;
// Current column number
const lastRow = obj.options.data.length - 1;
if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) {
rowNumber = lastRow;
}
const onbeforeinsertrowRecords = [];
for (let row = 0; row < numOfRows; row++) {
const newRow = [];
for (let col = 0; col < obj.options.columns.length; col++) {
newRow[col] = data[col] ? data[col] : '';
}
onbeforeinsertrowRecords.push({
row: row + rowNumber + (insertBefore ? 0 : 1),
data: newRow,
});
}
// Onbeforeinsertrow
if (dispatch/* default * /.A.call(obj, 'onbeforeinsertrow', obj, onbeforeinsertrowRecords) === false) {
return false;
}
// Merged cells
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
if (merges/* isRowMerged * /.D0.call(obj, rowNumber, insertBefore).length) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
}
// Clear any search
if (obj.options.search == true) {
if (obj.results && obj.results.length != obj.rows.length) {
if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {
obj.resetSearch();
} else {
return false;
}
}
obj.results = null;
}
// Insertbefore
const rowIndex = (! insertBefore) ? rowNumber + 1 : rowNumber;
// Keep the current data
const currentRecords = obj.records.splice(rowIndex);
const currentData = obj.options.data.splice(rowIndex);
const currentRows = obj.rows.splice(rowIndex);
// Adding lines
const rowRecords = [];
const rowData = [];
const rowNode = [];
for (let row = rowIndex; row < (numOfRows + rowIndex); row++) {
// Push data to the data container
obj.options.data[row] = [];
for (let col = 0; col < obj.options.columns.length; col++) {
obj.options.data[row][col] = data[col] ? data[col] : '';
}
// Create row
const newRow = createRow.call(obj, row, obj.options.data[row]);
// Append node
if (currentRows[0]) {
if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0].element) >= 0) {
obj.tbody.insertBefore(newRow.element, currentRows[0].element);
}
} else {
if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber].element) >= 0) {
obj.tbody.appendChild(newRow.element);
}
}
// Record History
rowRecords.push(obj.records[row]);
rowData.push(obj.options.data[row]);
rowNode.push(newRow);
}
// Copy the data back to the main data
Array.prototype.push.apply(obj.records, currentRecords);
Array.prototype.push.apply(obj.options.data, currentData);
Array.prototype.push.apply(obj.rows, currentRows);
for (let j = rowIndex; j < obj.rows.length; j++) {
obj.rows[j].y = j;
}
for (let j = rowIndex; j < obj.records.length; j++) {
for (let i = 0; i < obj.records[j].length; i++) {
obj.records[j][i].y = j;
}
}
// Respect pagination
if (obj.options.pagination > 0) {
obj.page(obj.pageNumber);
}
// Keep history
utils_history/* setHistory * /.Dh.call(obj, {
action: 'insertRow',
rowNumber: rowNumber,
numOfRows: numOfRows,
insertBefore: insertBefore,
rowRecords: rowRecords,
rowData: rowData,
rowNode: rowNode,
});
// Remove table references
internal/* updateTableReferences * /.o8.call(obj);
// Events
dispatch/* default * /.A.call(obj, 'oninsertrow', obj, onbeforeinsertrowRecords);
}
}
/**
* Move row
*
* @return void
* /
const moveRow = function(o, d, ignoreDom) {
const obj = this;
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
let insertBefore;
if (o > d) {
insertBefore = 1;
} else {
insertBefore = 0;
}
if (merges/* isRowMerged * /.D0.call(obj, o).length || merges/* isRowMerged * /.D0.call(obj, d, insertBefore).length) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
}
if (obj.options.search == true) {
if (obj.results && obj.results.length != obj.rows.length) {
if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {
obj.resetSearch();
} else {
return false;
}
}
obj.results = null;
}
if (! ignoreDom) {
if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d].element) >= 0) {
if (o > d) {
obj.tbody.insertBefore(obj.rows[o].element, obj.rows[d].element);
} else {
obj.tbody.insertBefore(obj.rows[o].element, obj.rows[d].element.nextSibling);
}
} else {
obj.tbody.removeChild(obj.rows[o].element);
}
}
// Place references in the correct position
obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]);
obj.records.splice(d, 0, obj.records.splice(o, 1)[0]);
obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]);
const firstAffectedIndex = Math.min(o, d);
const lastAffectedIndex = Math.max(o, d);
for (let j = firstAffectedIndex; j <= lastAffectedIndex; j++) {
obj.rows[j].y = j;
}
for (let j = firstAffectedIndex; j <= lastAffectedIndex; j++) {
for (let i = 0; i < obj.records[j].length; i++) {
obj.records[j][i].y = j;
}
}
// Respect pagination
if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {
obj.page(obj.pageNumber);
}
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action:'moveRow',
oldValue: o,
newValue: d,
});
// Update table references
internal/* updateTableReferences * /.o8.call(obj);
// Events
dispatch/* default * /.A.call(obj, 'onmoverow', obj, parseInt(o), parseInt(d), 1);
}
/**
* Delete a row by number
*
* @param integer rowNumber - row number to be excluded
* @param integer numOfRows - number of lines
* @return void
* /
const deleteRow = function(rowNumber, numOfRows) {
const obj = this;
// Global Configuration
if (obj.options.allowDeleteRow != false) {
if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) {
// Delete row definitions
if (rowNumber == undefined) {
const number = selection/* getSelectedRows * /.R5.call(obj);
if (number.length === 0) {
rowNumber = obj.options.data.length - 1;
numOfRows = 1;
} else {
rowNumber = number[0];
numOfRows = number.length;
}
}
// Last column
let lastRow = obj.options.data.length - 1;
if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) {
rowNumber = lastRow;
}
if (! numOfRows) {
numOfRows = 1;
}
// Do not delete more than the number of records
if (rowNumber + numOfRows >= obj.options.data.length) {
numOfRows = obj.options.data.length - rowNumber;
}
// Onbeforedeleterow
const onbeforedeleterowRecords = [];
for (let i = 0; i < numOfRows; i++) {
onbeforedeleterowRecords.push(i + rowNumber);
}
if (dispatch/* default * /.A.call(obj, 'onbeforedeleterow', obj, onbeforedeleterowRecords) === false) {
return false;
}
if (parseInt(rowNumber) > -1) {
// Merged cells
let mergeExists = false;
if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {
for (let row = rowNumber; row < rowNumber + numOfRows; row++) {
if (merges/* isRowMerged * /.D0.call(obj, row, false).length) {
mergeExists = true;
}
}
}
if (mergeExists) {
if (! confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {
return false;
} else {
obj.destroyMerge();
}
}
// Clear any search
if (obj.options.search == true) {
if (obj.results && obj.results.length != obj.rows.length) {
if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {
obj.resetSearch();
} else {
return false;
}
}
obj.results = null;
}
// If delete all rows, and set allowDeletingAllRows false, will stay one row
if (obj.options.allowDeletingAllRows != true && lastRow + 1 === numOfRows) {
numOfRows--;
console.error('Jspreadsheet: It is not possible to delete the last row');
}
// Remove node
for (let row = rowNumber; row < rowNumber + numOfRows; row++) {
if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row].element) >= 0) {
obj.rows[row].element.className = '';
obj.rows[row].element.parentNode.removeChild(obj.rows[row].element);
}
}
// Remove data
const rowRecords = obj.records.splice(rowNumber, numOfRows);
const rowData = obj.options.data.splice(rowNumber, numOfRows);
const rowNode = obj.rows.splice(rowNumber, numOfRows);
for (let j = rowNumber; j < obj.rows.length; j++) {
obj.rows[j].y = j;
}
for (let j = rowNumber; j < obj.records.length; j++) {
for (let i = 0; i < obj.records[j].length; i++) {
obj.records[j][i].y = j;
}
}
// Respect pagination
if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {
obj.page(obj.pageNumber);
}
// Remove selection
selection/* conditionalSelectionUpdate * /.at.call(obj, 1, rowNumber, (rowNumber + numOfRows) - 1);
// Keep history
utils_history/* setHistory * /.Dh.call(obj, {
action: 'deleteRow',
rowNumber: rowNumber,
numOfRows: numOfRows,
insertBefore: 1,
rowRecords: rowRecords,
rowData: rowData,
rowNode: rowNode
});
// Remove table references
internal/* updateTableReferences * /.o8.call(obj);
// Events
dispatch/* default * /.A.call(obj, 'ondeleterow', obj, onbeforedeleterowRecords);
}
} else {
console.error('Jspreadsheet: It is not possible to delete the last row');
}
}
}
/**
* Get the row height
*
* @param row - row number (first row is: 0)
* @return height - current row height
* /
const getHeight = function(row) {
const obj = this;
let data;
if (typeof row === 'undefined') {
// Get height of all rows
data = [];
for (let j = 0; j < obj.rows.length; j++) {
const h = obj.rows[j].element.style.height;
if (h) {
data[j] = h;
}
}
} else {
// In case the row is an object
if (typeof(row) == 'object') {
row = $(row).getAttribute('data-y');
}
data = obj.rows[row].element.style.height;
}
return data;
}
/**
* Set the row height
*
* @param row - row number (first row is: 0)
* @param height - new row height
* @param oldHeight - old row height
* /
const setHeight = function (row, height, oldHeight) {
const obj = this;
if (height > 0) {
// Oldwidth
if (! oldHeight) {
oldHeight = obj.rows[row].element.getAttribute('height');
if (! oldHeight) {
const rect = obj.rows[row].element.getBoundingClientRect();
oldHeight = rect.height;
}
}
// Integer
height = parseInt(height);
// Set width
obj.rows[row].element.style.height = height + 'px';
if (!obj.options.rows) {
obj.options.rows = [];
}
// Keep options updated
if (! obj.options.rows[row]) {
obj.options.rows[row] = {};
}
obj.options.rows[row].height = height;
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action:'setHeight',
row:row,
oldValue:oldHeight,
newValue:height,
});
// On resize column
dispatch/* default * /.A.call(obj, 'onresizerow', obj, row, height, oldHeight);
// Update corner position
selection/* updateCornerPosition * /.Aq.call(obj);
}
}
/**
* Show row
* /
const showRow = function(rowNumber) {
const obj = this;
if (!Array.isArray(rowNumber)) {
rowNumber = [rowNumber];
}
rowNumber.forEach(function(rowIndex) {
obj.rows[rowIndex].element.style.display = '';
});
}
/**
* Hide row
* /
const hideRow = function(rowNumber) {
const obj = this;
if (!Array.isArray(rowNumber)) {
rowNumber = [rowNumber];
}
rowNumber.forEach(function(rowIndex) {
obj.rows[rowIndex].element.style.display = 'none';
});
}
/**
* Get a row data by rowNumber
* /
const getRowData = function(rowNumber, processed) {
const obj = this;
if (processed) {
return obj.records[rowNumber].map(function(record) {
return record.element.innerHTML;
})
} else {
return obj.options.data[rowNumber];
}
}
/**
* Set a row data by rowNumber
* /
const setRowData = function(rowNumber, data, force) {
const obj = this;
for (let i = 0; i < obj.headers.length; i++) {
// Update cell
const columnName = (0,internalHelpers/* getColumnNameFromId * /.t3)([ i, rowNumber ]);
// Set value
if (data[i] != null) {
obj.setValue(columnName, data[i], force);
}
}
}
;// ./src/utils/version.js
// Basic version information
/* harmony default export * / var version = ({
version: '5.0.0',
host: 'https://bossanova.uk/jspreadsheet',
license: 'MIT',
print: function() {
return [[ 'Jspreadsheet CE', this.version, this.host, this.license ].join('\r\n')];
}
});
;// ./src/utils/events.js
const getElement = function(element) {
let jssSection = 0;
let jssElement = 0;
function path (element) {
if (element.className) {
if (element.classList.contains('jss_container')) {
jssElement = element;
}
if (element.classList.contains('jss_spreadsheet')) {
jssElement = element.querySelector(':scope > .jtabs-content > .jtabs-selected');
}
}
if (element.tagName == 'THEAD') {
jssSection = 1;
} else if (element.tagName == 'TBODY') {
jssSection = 2;
}
if (element.parentNode) {
if (! jssElement) {
path(element.parentNode);
}
}
}
path(element);
return [ jssElement, jssSection ];
}
const mouseUpControls = function(e) {
if (libraryBase.jspreadsheet.current) {
// Update cell size
if (libraryBase.jspreadsheet.current.resizing) {
// Columns to be updated
if (libraryBase.jspreadsheet.current.resizing.column) {
// New width
const newWidth = parseInt(libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.getAttribute('width'));
// Columns
const columns = libraryBase.jspreadsheet.current.getSelectedColumns();
if (columns.length > 1) {
const currentWidth = [];
for (let i = 0; i < columns.length; i++) {
currentWidth.push(parseInt(libraryBase.jspreadsheet.current.cols[columns[i]].colElement.getAttribute('width')));
}
// Previous width
const index = columns.indexOf(parseInt(libraryBase.jspreadsheet.current.resizing.column));
currentWidth[index] = libraryBase.jspreadsheet.current.resizing.width;
setWidth.call(libraryBase.jspreadsheet.current, columns, newWidth, currentWidth);
} else {
setWidth.call(libraryBase.jspreadsheet.current, parseInt(libraryBase.jspreadsheet.current.resizing.column), newWidth, libraryBase.jspreadsheet.current.resizing.width);
}
// Remove border
libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.resizing.column].classList.remove('resizing');
for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {
if (libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.resizing.column]) {
libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.resizing.column].element.classList.remove('resizing');
}
}
} else {
// Remove Class
libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.children[0].classList.remove('resizing');
let newHeight = libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.getAttribute('height');
setHeight.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.resizing.row, newHeight, libraryBase.jspreadsheet.current.resizing.height);
// Remove border
libraryBase.jspreadsheet.current.resizing.element.classList.remove('resizing');
}
// Reset resizing helper
libraryBase.jspreadsheet.current.resizing = null;
} else if (libraryBase.jspreadsheet.current.dragging) {
// Reset dragging helper
if (libraryBase.jspreadsheet.current.dragging) {
if (libraryBase.jspreadsheet.current.dragging.column) {
// Target
const columnId = e.target.getAttribute('data-x');
// Remove move style
libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.dragging.column].classList.remove('dragging');
for (let j = 0; j < libraryBase.jspreadsheet.current.rows.length; j++) {
if (libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.dragging.column]) {
libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.dragging.column].element.classList.remove('dragging');
}
}
for (let i = 0; i < libraryBase.jspreadsheet.current.headers.length; i++) {
libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-left');
libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-right');
}
// Update position
if (columnId) {
if (libraryBase.jspreadsheet.current.dragging.column != libraryBase.jspreadsheet.current.dragging.destination) {
libraryBase.jspreadsheet.current.moveColumn(libraryBase.jspreadsheet.current.dragging.column, libraryBase.jspreadsheet.current.dragging.destination);
}
}
} else {
let position;
if (libraryBase.jspreadsheet.current.dragging.element.nextSibling) {
position = parseInt(libraryBase.jspreadsheet.current.dragging.element.nextSibling.getAttribute('data-y'));
if (libraryBase.jspreadsheet.current.dragging.row < position) {
position -= 1;
}
} else {
position = parseInt(libraryBase.jspreadsheet.current.dragging.element.previousSibling.getAttribute('data-y'));
}
if (libraryBase.jspreadsheet.current.dragging.row != libraryBase.jspreadsheet.current.dragging.destination) {
moveRow.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.dragging.row, position, true);
}
libraryBase.jspreadsheet.current.dragging.element.classList.remove('dragging');
}
libraryBase.jspreadsheet.current.dragging = null;
}
} else {
// Close any corner selection
if (libraryBase.jspreadsheet.current.selectedCorner) {
libraryBase.jspreadsheet.current.selectedCorner = false;
// Data to be copied
if (libraryBase.jspreadsheet.current.selection.length > 0) {
// Copy data
selection/* copyData * /.kF.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.selection[0], libraryBase.jspreadsheet.current.selection[libraryBase.jspreadsheet.current.selection.length - 1]);
// Remove selection
selection/* removeCopySelection * /.gG.call(libraryBase.jspreadsheet.current);
}
}
}
}
// Clear any time control
if (libraryBase.jspreadsheet.timeControl) {
clearTimeout(libraryBase.jspreadsheet.timeControl);
libraryBase.jspreadsheet.timeControl = null;
}
// Mouse up
libraryBase.jspreadsheet.isMouseAction = false;
}
const mouseDownControls = function(e) {
e = e || window.event;
let mouseButton;
if (e.buttons) {
mouseButton = e.buttons;
} else if (e.button) {
mouseButton = e.button;
} else {
mouseButton = e.which;
}
// Get elements
const jssTable = getElement(e.target);
if (jssTable[0]) {
if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {
if (libraryBase.jspreadsheet.current) {
if (libraryBase.jspreadsheet.current.edition) {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
}
libraryBase.jspreadsheet.current.resetSelection();
}
libraryBase.jspreadsheet.current = jssTable[0].jssWorksheet;
}
} else {
if (libraryBase.jspreadsheet.current) {
if (libraryBase.jspreadsheet.current.edition) {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
}
if (!e.target.classList.contains('jss_object')) {
selection/* resetSelection * /.gE.call(libraryBase.jspreadsheet.current, true);
libraryBase.jspreadsheet.current = null;
}
}
}
if (libraryBase.jspreadsheet.current && mouseButton == 1) {
if (e.target.classList.contains('jss_selectall')) {
if (libraryBase.jspreadsheet.current) {
selection/* selectAll * /.Ub.call(libraryBase.jspreadsheet.current);
}
} else if (e.target.classList.contains('jss_corner')) {
if (libraryBase.jspreadsheet.current.options.editable != false) {
libraryBase.jspreadsheet.current.selectedCorner = true;
}
} else {
// Header found
if (jssTable[1] == 1) {
const columnId = e.target.getAttribute('data-x');
if (columnId) {
// Update cursor
const info = e.target.getBoundingClientRect();
if (libraryBase.jspreadsheet.current.options.columnResize != false && info.width - e.offsetX < 6) {
// Resize helper
libraryBase.jspreadsheet.current.resizing = {
mousePosition: e.pageX,
column: columnId,
width: info.width,
};
// Border indication
libraryBase.jspreadsheet.current.headers[columnId].classList.add('resizing');
for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {
if (libraryBase.jspreadsheet.current.records[j][columnId]) {
libraryBase.jspreadsheet.current.records[j][columnId].element.classList.add('resizing');
}
}
} else if (libraryBase.jspreadsheet.current.options.columnDrag != false && info.height - e.offsetY < 6) {
if (merges/* isColMerged * /.Lt.call(libraryBase.jspreadsheet.current, columnId).length) {
console.error('Jspreadsheet: This column is part of a merged cell.');
} else {
// Reset selection
libraryBase.jspreadsheet.current.resetSelection();
// Drag helper
libraryBase.jspreadsheet.current.dragging = {
element: e.target,
column: columnId,
destination: columnId,
};
// Border indication
libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging');
for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {
if (libraryBase.jspreadsheet.current.records[j][columnId]) {
libraryBase.jspreadsheet.current.records[j][columnId].element.classList.add('dragging');
}
}
}
} else {
let o, d;
if (libraryBase.jspreadsheet.current.selectedHeader && (e.shiftKey || e.ctrlKey)) {
o = libraryBase.jspreadsheet.current.selectedHeader;
d = columnId;
} else {
// Press to rename
if (libraryBase.jspreadsheet.current.selectedHeader == columnId && libraryBase.jspreadsheet.current.options.allowRenameColumn != false) {
libraryBase.jspreadsheet.timeControl = setTimeout(function() {
libraryBase.jspreadsheet.current.setHeader(columnId);
}, 800);
}
// Keep track of which header was selected first
libraryBase.jspreadsheet.current.selectedHeader = columnId;
// Update selection single column
o = columnId;
d = columnId;
}
// Update selection
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, o, 0, d, libraryBase.jspreadsheet.current.options.data.length - 1, e);
}
} else {
if (e.target.parentNode.classList.contains('jss_nested')) {
let c1, c2;
if (e.target.getAttribute('data-column')) {
const column = e.target.getAttribute('data-column').split(',');
c1 = parseInt(column[0]);
c2 = parseInt(column[column.length-1]);
} else {
c1 = 0;
c2 = libraryBase.jspreadsheet.current.options.columns.length - 1;
}
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, c1, 0, c2, libraryBase.jspreadsheet.current.options.data.length - 1, e);
}
}
} else {
libraryBase.jspreadsheet.current.selectedHeader = false;
}
// Body found
if (jssTable[1] == 2) {
const rowId = parseInt(e.target.getAttribute('data-y'));
if (e.target.classList.contains('jss_row')) {
const info = e.target.getBoundingClientRect();
if (libraryBase.jspreadsheet.current.options.rowResize != false && info.height - e.offsetY < 6) {
// Resize helper
libraryBase.jspreadsheet.current.resizing = {
element: e.target.parentNode,
mousePosition: e.pageY,
row: rowId,
height: info.height,
};
// Border indication
e.target.parentNode.classList.add('resizing');
} else if (libraryBase.jspreadsheet.current.options.rowDrag != false && info.width - e.offsetX < 6) {
if (merges/* isRowMerged * /.D0.call(libraryBase.jspreadsheet.current, rowId).length) {
console.error('Jspreadsheet: This row is part of a merged cell');
} else if (libraryBase.jspreadsheet.current.options.search == true && libraryBase.jspreadsheet.current.results) {
console.error('Jspreadsheet: Please clear your search before perform this action');
} else {
// Reset selection
libraryBase.jspreadsheet.current.resetSelection();
// Drag helper
libraryBase.jspreadsheet.current.dragging = {
element: e.target.parentNode,
row:rowId,
destination:rowId,
};
// Border indication
e.target.parentNode.classList.add('dragging');
}
} else {
let o, d;
if (libraryBase.jspreadsheet.current.selectedRow && (e.shiftKey || e.ctrlKey)) {
o = libraryBase.jspreadsheet.current.selectedRow;
d = rowId;
} else {
// Keep track of which header was selected first
libraryBase.jspreadsheet.current.selectedRow = rowId;
// Update selection single column
o = rowId;
d = rowId;
}
// Update selection
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, null, o, null, d, e);
}
} else {
// Jclose
if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
} else {
const getCellCoords = function(element) {
const x = element.getAttribute('data-x');
const y = element.getAttribute('data-y');
if (x && y) {
return [x, y];
} else {
if (element.parentNode) {
return getCellCoords(element.parentNode);
}
}
};
const position = getCellCoords(e.target);
if (position) {
const columnId = position[0];
const rowId = position[1];
// Close edition
if (libraryBase.jspreadsheet.current.edition) {
if (libraryBase.jspreadsheet.current.edition[2] != columnId || libraryBase.jspreadsheet.current.edition[3] != rowId) {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
}
}
if (! libraryBase.jspreadsheet.current.edition) {
// Update cell selection
if (e.shiftKey) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.selectedCell[0], libraryBase.jspreadsheet.current.selectedCell[1], columnId, rowId, e);
} else {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, columnId, rowId, undefined, undefined, e);
}
}
// No full row selected
libraryBase.jspreadsheet.current.selectedHeader = null;
libraryBase.jspreadsheet.current.selectedRow = null;
}
}
}
} else {
libraryBase.jspreadsheet.current.selectedRow = false;
}
// Pagination
if (e.target.classList.contains('jss_page')) {
if (e.target.textContent == '<') {
libraryBase.jspreadsheet.current.page(0);
} else if (e.target.textContent == '>') {
libraryBase.jspreadsheet.current.page(e.target.getAttribute('title') - 1);
} else {
libraryBase.jspreadsheet.current.page(e.target.textContent - 1);
}
}
}
if (libraryBase.jspreadsheet.current.edition) {
libraryBase.jspreadsheet.isMouseAction = false;
} else {
libraryBase.jspreadsheet.isMouseAction = true;
}
} else {
libraryBase.jspreadsheet.isMouseAction = false;
}
}
// Mouse move controls
const mouseMoveControls = function(e) {
e = e || window.event;
let mouseButton;
if (e.buttons) {
mouseButton = e.buttons;
} else if (e.button) {
mouseButton = e.button;
} else {
mouseButton = e.which;
}
if (! mouseButton) {
libraryBase.jspreadsheet.isMouseAction = false;
}
if (libraryBase.jspreadsheet.current) {
if (libraryBase.jspreadsheet.isMouseAction == true) {
// Resizing is ongoing
if (libraryBase.jspreadsheet.current.resizing) {
if (libraryBase.jspreadsheet.current.resizing.column) {
const width = e.pageX - libraryBase.jspreadsheet.current.resizing.mousePosition;
if (libraryBase.jspreadsheet.current.resizing.width + width > 0) {
const tempWidth = libraryBase.jspreadsheet.current.resizing.width + width;
libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.setAttribute('width', tempWidth);
selection/* updateCornerPosition * /.Aq.call(libraryBase.jspreadsheet.current);
}
} else {
const height = e.pageY - libraryBase.jspreadsheet.current.resizing.mousePosition;
if (libraryBase.jspreadsheet.current.resizing.height + height > 0) {
const tempHeight = libraryBase.jspreadsheet.current.resizing.height + height;
libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.setAttribute('height', tempHeight);
selection/* updateCornerPosition * /.Aq.call(libraryBase.jspreadsheet.current);
}
}
} else if (libraryBase.jspreadsheet.current.dragging) {
if (libraryBase.jspreadsheet.current.dragging.column) {
const columnId = e.target.getAttribute('data-x');
if (columnId) {
if (merges/* isColMerged * /.Lt.call(libraryBase.jspreadsheet.current, columnId).length) {
console.error('Jspreadsheet: This column is part of a merged cell.');
} else {
for (let i = 0; i < libraryBase.jspreadsheet.current.headers.length; i++) {
libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-left');
libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-right');
}
if (libraryBase.jspreadsheet.current.dragging.column == columnId) {
libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);
} else {
if (e.target.clientWidth / 2 > e.offsetX) {
if (libraryBase.jspreadsheet.current.dragging.column < columnId) {
libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) - 1;
} else {
libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);
}
libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-left');
} else {
if (libraryBase.jspreadsheet.current.dragging.column < columnId) {
libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);
} else {
libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) + 1;
}
libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-right');
}
}
}
}
} else {
const rowId = e.target.getAttribute('data-y');
if (rowId) {
if (merges/* isRowMerged * /.D0.call(libraryBase.jspreadsheet.current, rowId).length) {
console.error('Jspreadsheet: This row is part of a merged cell.');
} else {
const target = (e.target.clientHeight / 2 > e.offsetY) ? e.target.parentNode.nextSibling : e.target.parentNode;
if (libraryBase.jspreadsheet.current.dragging.element != target) {
e.target.parentNode.parentNode.insertBefore(libraryBase.jspreadsheet.current.dragging.element, target);
libraryBase.jspreadsheet.current.dragging.destination = Array.prototype.indexOf.call(libraryBase.jspreadsheet.current.dragging.element.parentNode.children, libraryBase.jspreadsheet.current.dragging.element);
}
}
}
}
}
} else {
const x = e.target.getAttribute('data-x');
const y = e.target.getAttribute('data-y');
const rect = e.target.getBoundingClientRect();
if (libraryBase.jspreadsheet.current.cursor) {
libraryBase.jspreadsheet.current.cursor.style.cursor = '';
libraryBase.jspreadsheet.current.cursor = null;
}
if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) {
if (e.target.parentNode.parentNode.classList.contains('resizable')) {
if (e.target && x && ! y && (rect.width - (e.clientX - rect.left) < 6)) {
libraryBase.jspreadsheet.current.cursor = e.target;
libraryBase.jspreadsheet.current.cursor.style.cursor = 'col-resize';
} else if (e.target && ! x && y && (rect.height - (e.clientY - rect.top) < 6)) {
libraryBase.jspreadsheet.current.cursor = e.target;
libraryBase.jspreadsheet.current.cursor.style.cursor = 'row-resize';
}
}
if (e.target.parentNode.parentNode.classList.contains('draggable')) {
if (e.target && ! x && y && (rect.width - (e.clientX - rect.left) < 6)) {
libraryBase.jspreadsheet.current.cursor = e.target;
libraryBase.jspreadsheet.current.cursor.style.cursor = 'move';
} else if (e.target && x && ! y && (rect.height - (e.clientY - rect.top) < 6)) {
libraryBase.jspreadsheet.current.cursor = e.target;
libraryBase.jspreadsheet.current.cursor.style.cursor = 'move';
}
}
}
}
}
}
/**
* Update copy selection
*
* @param int x, y
* @return void
* /
const updateCopySelection = function(x3, y3) {
const obj = this;
// Remove selection
selection/* removeCopySelection * /.gG.call(obj);
// Get elements first and last
const x1 = obj.selectedContainer[0];
const y1 = obj.selectedContainer[1];
const x2 = obj.selectedContainer[2];
const y2 = obj.selectedContainer[3];
if (x3 != null && y3 != null) {
let px, ux;
if (x3 - x2 > 0) {
px = parseInt(x2) + 1;
ux = parseInt(x3);
} else {
px = parseInt(x3);
ux = parseInt(x1) - 1;
}
let py, uy;
if (y3 - y2 > 0) {
py = parseInt(y2) + 1;
uy = parseInt(y3);
} else {
py = parseInt(y3);
uy = parseInt(y1) - 1;
}
if (ux - px <= uy - py) {
px = parseInt(x1);
ux = parseInt(x2);
} else {
py = parseInt(y1);
uy = parseInt(y2);
}
for (let j = py; j <= uy; j++) {
for (let i = px; i <= ux; i++) {
if (obj.records[j][i] && obj.rows[j].element.style.display != 'none' && obj.records[j][i].element.style.display != 'none') {
obj.records[j][i].element.classList.add('selection');
obj.records[py][i].element.classList.add('selection-top');
obj.records[uy][i].element.classList.add('selection-bottom');
obj.records[j][px].element.classList.add('selection-left');
obj.records[j][ux].element.classList.add('selection-right');
// Persist selected elements
obj.selection.push(obj.records[j][i].element);
}
}
}
}
}
const mouseOverControls = function(e) {
e = e || window.event;
let mouseButton;
if (e.buttons) {
mouseButton = e.buttons;
} else if (e.button) {
mouseButton = e.button;
} else {
mouseButton = e.which;
}
if (! mouseButton) {
libraryBase.jspreadsheet.isMouseAction = false;
}
if (libraryBase.jspreadsheet.current && libraryBase.jspreadsheet.isMouseAction == true) {
// Get elements
const jssTable = getElement(e.target);
if (jssTable[0]) {
// Avoid cross reference
if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {
if (libraryBase.jspreadsheet.current) {
return false;
}
}
let columnId = e.target.getAttribute('data-x');
const rowId = e.target.getAttribute('data-y');
if (libraryBase.jspreadsheet.current.resizing || libraryBase.jspreadsheet.current.dragging) {
} else {
// Header found
if (jssTable[1] == 1) {
if (libraryBase.jspreadsheet.current.selectedHeader) {
columnId = e.target.getAttribute('data-x');
const o = libraryBase.jspreadsheet.current.selectedHeader;
const d = columnId;
// Update selection
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, o, 0, d, libraryBase.jspreadsheet.current.options.data.length - 1, e);
}
}
// Body found
if (jssTable[1] == 2) {
if (e.target.classList.contains('jss_row')) {
if (libraryBase.jspreadsheet.current.selectedRow) {
const o = libraryBase.jspreadsheet.current.selectedRow;
const d = rowId;
// Update selection
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, 0, o, libraryBase.jspreadsheet.current.options.data[0].length - 1, d, e);
}
} else {
// Do not select edtion is in progress
if (! libraryBase.jspreadsheet.current.edition) {
if (columnId && rowId) {
if (libraryBase.jspreadsheet.current.selectedCorner) {
updateCopySelection.call(libraryBase.jspreadsheet.current, columnId, rowId);
} else {
if (libraryBase.jspreadsheet.current.selectedCell) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.selectedCell[0], libraryBase.jspreadsheet.current.selectedCell[1], columnId, rowId, e);
}
}
}
}
}
}
}
}
}
// Clear any time control
if (libraryBase.jspreadsheet.timeControl) {
clearTimeout(libraryBase.jspreadsheet.timeControl);
libraryBase.jspreadsheet.timeControl = null;
}
}
const doubleClickControls = function(e) {
// Jss is selected
if (libraryBase.jspreadsheet.current) {
// Corner action
if (e.target.classList.contains('jss_corner')) {
// Any selected cells
if (libraryBase.jspreadsheet.current.highlighted.length > 0) {
// Copy from this
const x1 = libraryBase.jspreadsheet.current.highlighted[0].element.getAttribute('data-x');
const y1 = parseInt(libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length - 1].element.getAttribute('data-y')) + 1;
// Until this
const x2 = libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length - 1].element.getAttribute('data-x');
const y2 = libraryBase.jspreadsheet.current.records.length - 1
// Execute copy
selection/* copyData * /.kF.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[y1][x1].element, libraryBase.jspreadsheet.current.records[y2][x2].element);
}
} else if (e.target.classList.contains('jss_column_filter')) {
// Column
const columnId = e.target.getAttribute('data-x');
// Open filter
filter/* openFilter * /.N$.call(libraryBase.jspreadsheet.current, columnId);
} else {
// Get table
const jssTable = getElement(e.target);
// Double click over header
if (jssTable[1] == 1 && libraryBase.jspreadsheet.current.options.columnSorting != false) {
// Check valid column header coords
const columnId = e.target.getAttribute('data-x');
if (columnId) {
libraryBase.jspreadsheet.current.orderBy(parseInt(columnId));
}
}
// Double click over body
if (jssTable[1] == 2 && libraryBase.jspreadsheet.current.options.editable != false) {
if (! libraryBase.jspreadsheet.current.edition) {
const getCellCoords = function(element) {
if (element.parentNode) {
const x = element.getAttribute('data-x');
const y = element.getAttribute('data-y');
if (x && y) {
return element;
} else {
return getCellCoords(element.parentNode);
}
}
}
const cell = getCellCoords(e.target);
if (cell && cell.classList.contains('highlight')) {
openEditor.call(libraryBase.jspreadsheet.current, cell, undefined, e);
}
}
}
}
}
}
const pasteControls = function(e) {
if (libraryBase.jspreadsheet.current && libraryBase.jspreadsheet.current.selectedCell) {
if (! libraryBase.jspreadsheet.current.edition) {
if (libraryBase.jspreadsheet.current.options.editable != false) {
if (e && e.clipboardData) {
paste.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.selectedCell[0], libraryBase.jspreadsheet.current.selectedCell[1], e.clipboardData.getData('text'));
e.preventDefault();
} else if (window.clipboardData) {
paste.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.selectedCell[0], libraryBase.jspreadsheet.current.selectedCell[1], window.clipboardData.getData('text'));
}
}
}
}
}
const getRole = function(element) {
if (element.classList.contains('jss_selectall')) {
return 'select-all';
}
if (element.classList.contains('jss_corner')) {
return 'fill-handle';
}
let tempElement = element;
while (!tempElement.classList.contains('jss_spreadsheet')) {
if (tempElement.classList.contains('jss_row')) {
return 'row';
}
if (tempElement.classList.contains('jss_nested')) {
return 'nested';
}
if (tempElement.classList.contains('jtabs-headers')) {
return 'tabs';
}
if (tempElement.classList.contains('jtoolbar')) {
return 'toolbar';
}
if (tempElement.classList.contains('jss_pagination')) {
return 'pagination';
}
if (tempElement.tagName === 'TBODY') {
return 'cell';
}
if (tempElement.tagName === 'TFOOT') {
return getElementIndex(element) === 0 ? 'grid' : 'footer';
}
if (tempElement.tagName === 'THEAD') {
return 'header';
}
tempElement = tempElement.parentElement;
}
return 'applications';
}
const defaultContextMenu = function(worksheet, x, y, role) {
const items = [];
if (role === 'header') {
// Insert a new column
if (worksheet.options.allowInsertColumn != false) {
items.push({
title: jSuites.translate('Insert a new column before'),
onclick:function() {
worksheet.insertColumn(1, parseInt(x), 1);
}
});
}
if (worksheet.options.allowInsertColumn != false) {
items.push({
title: jSuites.translate('Insert a new column after'),
onclick:function() {
worksheet.insertColumn(1, parseInt(x), 0);
}
});
}
// Delete a column
if (worksheet.options.allowDeleteColumn != false) {
items.push({
title: jSuites.translate('Delete selected columns'),
onclick:function() {
worksheet.deleteColumn(worksheet.getSelectedColumns().length ? undefined : parseInt(x));
}
});
}
// Rename column
if (worksheet.options.allowRenameColumn != false) {
items.push({
title: jSuites.translate('Rename this column'),
onclick:function() {
const oldValue = worksheet.getHeader(x);
const newValue = prompt(jSuites.translate('Column name'), oldValue);
worksheet.setHeader(x, newValue);
}
});
}
// Sorting
if (worksheet.options.columnSorting != false) {
// Line
items.push({ type:'line' });
items.push({
title: jSuites.translate('Order ascending'),
onclick:function() {
worksheet.orderBy(x, 0);
}
});
items.push({
title: jSuites.translate('Order descending'),
onclick:function() {
worksheet.orderBy(x, 1);
}
});
}
}
if (role === 'row' || role === 'cell') {
// Insert new row
if (worksheet.options.allowInsertRow != false) {
items.push({
title: jSuites.translate('Insert a new row before'),
onclick:function() {
worksheet.insertRow(1, parseInt(y), 1);
}
});
items.push({
title: jSuites.translate('Insert a new row after'),
onclick:function() {
worksheet.insertRow(1, parseInt(y));
}
});
}
if (worksheet.options.allowDeleteRow != false) {
items.push({
title: jSuites.translate('Delete selected rows'),
onclick:function() {
worksheet.deleteRow(worksheet.getSelectedRows().length ? undefined : parseInt(y));
}
});
}
}
if (role === 'cell') {
if (worksheet.options.allowComments != false) {
items.push({ type:'line' });
const title = worksheet.records[y][x].element.getAttribute('title') || '';
items.push({
title: jSuites.translate(title ? 'Edit comments' : 'Add comments'),
onclick:function() {
const comment = prompt(jSuites.translate('Comments'), title);
if (comment) {
worksheet.setComments((0,helpers.getCellNameFromCoords)(x, y), comment);
}
}
});
if (title) {
items.push({
title: jSuites.translate('Clear comments'),
onclick:function() {
worksheet.setComments((0,helpers.getCellNameFromCoords)(x, y), '');
}
});
}
}
}
// Line
if (items.length !== 0) {
items.push({ type:'line' });
}
// Copy
if (role === 'header' || role === 'row' || role === 'cell') {
items.push({
title: jSuites.translate('Copy') + '...',
shortcut:'Ctrl + C',
onclick:function() {
copy.call(worksheet, true);
}
});
// Paste
if (navigator && navigator.clipboard) {
items.push({
title: jSuites.translate('Paste') + '...',
shortcut:'Ctrl + V',
onclick:function() {
if (worksheet.selectedCell) {
navigator.clipboard.readText().then(function(text) {
if (text) {
paste.call(worksheet, worksheet.selectedCell[0], worksheet.selectedCell[1], text);
}
});
}
}
});
}
}
// Save
if (worksheet.parent.config.allowExport != false) {
items.push({
title: jSuites.translate('Save as') + '...',
shortcut: 'Ctrl + S',
onclick: function () {
worksheet.download();
}
});
}
// About
if (worksheet.parent.config.about != false) {
items.push({
title: jSuites.translate('About'),
onclick:function() {
if (typeof worksheet.parent.config.about === 'undefined' || worksheet.parent.config.about === true) {
alert(version.print());
} else {
alert(worksheet.parent.config.about);
}
}
});
}
return items;
}
const getElementIndex = function(element) {
const parentChildren = element.parentElement.children;
for (let i = 0; i < parentChildren.length; i++) {
const currentElement = parentChildren[i];
if (element === currentElement) {
return i;
}
}
return -1;
}
const contextMenuControls = function(e) {
e = e || window.event;
if ("buttons" in e) {
var mouseButton = e.buttons;
} else {
var mouseButton = e.which || e.button;
}
if (libraryBase.jspreadsheet.current) {
const spreadsheet = libraryBase.jspreadsheet.current.parent;
if (libraryBase.jspreadsheet.current.edition) {
e.preventDefault();
} else {
spreadsheet.contextMenu.contextmenu.close();
if (libraryBase.jspreadsheet.current) {
const role = getRole(e.target);
let x = null, y = null;
if (role === 'cell') {
let cellElement = e.target;
while (cellElement.tagName !== 'TD') {
cellElement = cellElement.parentNode;
}
y = cellElement.getAttribute('data-y');
x = cellElement.getAttribute('data-x');
if (
!libraryBase.jspreadsheet.current.selectedCell ||
(x < parseInt(libraryBase.jspreadsheet.current.selectedCell[0])) || (x > parseInt(libraryBase.jspreadsheet.current.selectedCell[2])) ||
(y < parseInt(libraryBase.jspreadsheet.current.selectedCell[1])) || (y > parseInt(libraryBase.jspreadsheet.current.selectedCell[3]))
) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, x, y, x, y, e);
}
} else if (role === 'row' || role === 'header') {
if (role === 'row') {
y = e.target.getAttribute('data-y');
} else {
x = e.target.getAttribute('data-x');
}
if (
!libraryBase.jspreadsheet.current.selectedCell ||
(x < parseInt(libraryBase.jspreadsheet.current.selectedCell[0])) || (x > parseInt(libraryBase.jspreadsheet.current.selectedCell[2])) ||
(y < parseInt(libraryBase.jspreadsheet.current.selectedCell[1])) || (y > parseInt(libraryBase.jspreadsheet.current.selectedCell[3]))
) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, x, y, x, y, e);
}
} else if (role === 'nested') {
const columns = e.target.getAttribute('data-column').split(',');
x = getElementIndex(e.target) - 1;
y = getElementIndex(e.target.parentElement);
if (
!libraryBase.jspreadsheet.current.selectedCell ||
(columns[0] != parseInt(libraryBase.jspreadsheet.current.selectedCell[0])) || (columns[columns.length - 1] != parseInt(libraryBase.jspreadsheet.current.selectedCell[2])) ||
(libraryBase.jspreadsheet.current.selectedCell[1] != null || libraryBase.jspreadsheet.current.selectedCell[3] != null)
) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, columns[0], null, columns[columns.length - 1], null, e);
}
} else if (role === 'select-all') {
selection/* selectAll * /.Ub.call(libraryBase.jspreadsheet.current);
} else if (role === 'tabs') {
x = getElementIndex(e.target);
} else if (role === 'footer') {
x = getElementIndex(e.target) - 1;
y = getElementIndex(e.target.parentElement);
}
// Table found
let items = defaultContextMenu(libraryBase.jspreadsheet.current,parseInt(x),parseInt(y), role);
if (typeof spreadsheet.config.contextMenu === 'function') {
const result = spreadsheet.config.contextMenu(libraryBase.jspreadsheet.current, x, y, e, items, role, x, y);
if (result) {
items = result;
} else if (result === false) {
return;
}
}
if (typeof spreadsheet.plugins === 'object') {
Object.entries(spreadsheet.plugins).forEach(function([, plugin]) {
if (typeof plugin.contextMenu === 'function') {
const result = plugin.contextMenu(
libraryBase.jspreadsheet.current,
x !== null ? parseInt(x) : null,
y !== null ? parseInt(y) : null,
e,
items,
role,
x !== null ? parseInt(x) : null,
y !== null ? parseInt(y) : null
);
if (result) {
items = result;
}
}
});
}
// The id is depending on header and body
spreadsheet.contextMenu.contextmenu.open(e, items);
// Avoid the real one
e.preventDefault();
}
}
}
}
const touchStartControls = function(e) {
const jssTable = getElement(e.target);
if (jssTable[0]) {
if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {
if (libraryBase.jspreadsheet.current) {
libraryBase.jspreadsheet.current.resetSelection();
}
libraryBase.jspreadsheet.current = jssTable[0].jssWorksheet;
}
} else {
if (libraryBase.jspreadsheet.current) {
libraryBase.jspreadsheet.current.resetSelection();
libraryBase.jspreadsheet.current = null;
}
}
if (libraryBase.jspreadsheet.current) {
if (! libraryBase.jspreadsheet.current.edition) {
const columnId = e.target.getAttribute('data-x');
const rowId = e.target.getAttribute('data-y');
if (columnId && rowId) {
selection/* updateSelectionFromCoords * /.AH.call(libraryBase.jspreadsheet.current, columnId, rowId, undefined, undefined, e);
libraryBase.jspreadsheet.timeControl = setTimeout(function() {
// Keep temporary reference to the element
if (libraryBase.jspreadsheet.current.options.columns[columnId].type == 'color') {
libraryBase.jspreadsheet.tmpElement = null;
} else {
libraryBase.jspreadsheet.tmpElement = e.target;
}
openEditor.call(libraryBase.jspreadsheet.current, e.target, false, e);
}, 500);
}
}
}
}
const touchEndControls = function(e) {
// Clear any time control
if (libraryBase.jspreadsheet.timeControl) {
clearTimeout(libraryBase.jspreadsheet.timeControl);
libraryBase.jspreadsheet.timeControl = null;
// Element
if (libraryBase.jspreadsheet.tmpElement && libraryBase.jspreadsheet.tmpElement.children[0].tagName == 'INPUT') {
libraryBase.jspreadsheet.tmpElement.children[0].focus();
}
libraryBase.jspreadsheet.tmpElement = null;
}
}
const cutControls = function(e) {
if (libraryBase.jspreadsheet.current) {
if (! libraryBase.jspreadsheet.current.edition) {
copy.call(libraryBase.jspreadsheet.current, true, undefined, undefined, undefined, undefined, true);
if (libraryBase.jspreadsheet.current.options.editable != false) {
libraryBase.jspreadsheet.current.setValue(
libraryBase.jspreadsheet.current.highlighted.map(function(record) {
return record.element;
}),
''
);
}
}
}
}
const copyControls = function(e) {
if (libraryBase.jspreadsheet.current && copyControls.enabled) {
if (! libraryBase.jspreadsheet.current.edition) {
copy.call(libraryBase.jspreadsheet.current, true);
}
}
}
/**
* Valid international letter
* /
const validLetter = function (text) {
const regex = /([\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC-\u0400-\u04FF']+)/g;
return text.match(regex) ? 1 : 0;
}
const keyDownControls = function(e) {
if (libraryBase.jspreadsheet.current) {
if (libraryBase.jspreadsheet.current.edition) {
if (e.which == 27) {
// Escape
if (libraryBase.jspreadsheet.current.edition) {
// Exit without saving
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], false);
}
e.preventDefault();
} else if (e.which == 13) {
// Enter
if (libraryBase.jspreadsheet.current.options.columns && libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] && libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type == 'calendar') {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
} else if (
libraryBase.jspreadsheet.current.options.columns &&
libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&
libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type == 'dropdown'
) {
// Do nothing
} else {
// Alt enter -> do not close editor
if (
(
libraryBase.jspreadsheet.current.options.wordWrap == true ||
(
libraryBase.jspreadsheet.current.options.columns &&
libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&
libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].wordWrap == true
) ||
(
libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][libraryBase.jspreadsheet.current.edition[2]] &&
libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][libraryBase.jspreadsheet.current.edition[2]].length > 200
)
) &&
e.altKey
) {
// Add new line to the editor
const editorTextarea = libraryBase.jspreadsheet.current.edition[0].children[0];
let editorValue = libraryBase.jspreadsheet.current.edition[0].children[0].value;
const editorIndexOf = editorTextarea.selectionStart;
editorValue = editorValue.slice(0, editorIndexOf) + "\n" + editorValue.slice(editorIndexOf);
editorTextarea.value = editorValue;
editorTextarea.focus();
editorTextarea.selectionStart = editorIndexOf + 1;
editorTextarea.selectionEnd = editorIndexOf + 1;
} else {
libraryBase.jspreadsheet.current.edition[0].children[0].blur();
}
}
} else if (e.which == 9) {
// Tab
if (
libraryBase.jspreadsheet.current.options.columns &&
libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&
['calendar', 'html'].includes(libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type)
) {
closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);
} else {
libraryBase.jspreadsheet.current.edition[0].children[0].blur();
}
}
}
if (! libraryBase.jspreadsheet.current.edition && libraryBase.jspreadsheet.current.selectedCell) {
// Which key
if (e.which == 37) {
left.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 39) {
right.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 38) {
up.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 40) {
down.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 36) {
first.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 35) {
last.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);
e.preventDefault();
} else if (e.which == 46 || e.which == 8) {
// Delete
if (libraryBase.jspreadsheet.current.options.editable != false) {
if (libraryBase.jspreadsheet.current.selectedRow) {
if (libraryBase.jspreadsheet.current.options.allowDeleteRow != false) {
if (confirm(jSuites.translate('Are you sure to delete the selected rows?'))) {
libraryBase.jspreadsheet.current.deleteRow();
}
}
} else if (libraryBase.jspreadsheet.current.selectedHeader) {
if (libraryBase.jspreadsheet.current.options.allowDeleteColumn != false) {
if (confirm(jSuites.translate('Are you sure to delete the selected columns?'))) {
libraryBase.jspreadsheet.current.deleteColumn();
}
}
} else {
// Change value
libraryBase.jspreadsheet.current.setValue(
libraryBase.jspreadsheet.current.highlighted.map(function(record) {
return record.element;
}),
''
);
}
}
} else if (e.which == 13) {
// Move cursor
if (e.shiftKey) {
up.call(libraryBase.jspreadsheet.current);
} else {
if (libraryBase.jspreadsheet.current.options.allowInsertRow != false) {
if (libraryBase.jspreadsheet.current.options.allowManualInsertRow != false) {
if (libraryBase.jspreadsheet.current.selectedCell[1] == libraryBase.jspreadsheet.current.options.data.length - 1) {
// New record in case selectedCell in the last row
libraryBase.jspreadsheet.current.insertRow();
}
}
}
down.call(libraryBase.jspreadsheet.current);
}
e.preventDefault();
} else if (e.which == 9) {
// Tab
if (e.shiftKey) {
left.call(libraryBase.jspreadsheet.current);
} else {
if (libraryBase.jspreadsheet.current.options.allowInsertColumn != false) {
if (libraryBase.jspreadsheet.current.options.allowManualInsertColumn != false) {
if (libraryBase.jspreadsheet.current.selectedCell[0] == libraryBase.jspreadsheet.current.options.data[0].length - 1) {
// New record in case selectedCell in the last column
libraryBase.jspreadsheet.current.insertColumn();
}
}
}
right.call(libraryBase.jspreadsheet.current);
}
e.preventDefault();
} else {
if ((e.ctrlKey || e.metaKey) && ! e.shiftKey) {
if (e.which == 65) {
// Ctrl + A
selection/* selectAll * /.Ub.call(libraryBase.jspreadsheet.current);
e.preventDefault();
} else if (e.which == 83) {
// Ctrl + S
libraryBase.jspreadsheet.current.download();
e.preventDefault();
} else if (e.which == 89) {
// Ctrl + Y
libraryBase.jspreadsheet.current.redo();
e.preventDefault();
} else if (e.which == 90) {
// Ctrl + Z
libraryBase.jspreadsheet.current.undo();
e.preventDefault();
} else if (e.which == 67) {
// Ctrl + C
copy.call(libraryBase.jspreadsheet.current, true);
e.preventDefault();
} else if (e.which == 88) {
// Ctrl + X
if (libraryBase.jspreadsheet.current.options.editable != false) {
cutControls();
} else {
copyControls();
}
e.preventDefault();
} else if (e.which == 86) {
// Ctrl + V
pasteControls();
}
} else {
if (libraryBase.jspreadsheet.current.selectedCell) {
if (libraryBase.jspreadsheet.current.options.editable != false) {
const rowId = libraryBase.jspreadsheet.current.selectedCell[1];
const columnId = libraryBase.jspreadsheet.current.selectedCell[0];
// Characters able to start a edition
if (e.keyCode == 32) {
// Space
e.preventDefault()
if (
libraryBase.jspreadsheet.current.options.columns[columnId].type == 'checkbox' ||
libraryBase.jspreadsheet.current.options.columns[columnId].type == 'radio'
) {
setCheckRadioValue.call(libraryBase.jspreadsheet.current);
} else {
// Start edition
openEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[rowId][columnId].element, true, e);
}
} else if (e.keyCode == 113) {
// Start edition with current content F2
openEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[rowId][columnId].element, false, e);
} else if (
(e.keyCode == 8) ||
(e.keyCode >= 48 && e.keyCode <= 57) ||
(e.keyCode >= 96 && e.keyCode <= 111) ||
(e.keyCode >= 187 && e.keyCode <= 190) ||
((String.fromCharCode(e.keyCode) == e.key || String.fromCharCode(e.keyCode).toLowerCase() == e.key.toLowerCase()) && validLetter(String.fromCharCode(e.keyCode)))
) {
// Start edition
openEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[rowId][columnId].element, true, e);
// Prevent entries in the calendar
if (libraryBase.jspreadsheet.current.options.columns && libraryBase.jspreadsheet.current.options.columns[columnId] && libraryBase.jspreadsheet.current.options.columns[columnId].type == 'calendar') {
e.preventDefault();
}
}
}
}
}
}
} else {
if (e.target.classList.contains('jss_search')) {
if (libraryBase.jspreadsheet.timeControl) {
clearTimeout(libraryBase.jspreadsheet.timeControl);
}
libraryBase.jspreadsheet.timeControl = setTimeout(function() {
libraryBase.jspreadsheet.current.search(e.target.value);
}, 200);
}
}
}
}
const wheelControls = function(e) {
const obj = this;
if (obj.options.lazyLoading == true) {
if (libraryBase.jspreadsheet.timeControlLoading == null) {
libraryBase.jspreadsheet.timeControlLoading = setTimeout(function() {
if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight - 10) {
if (lazyLoading/* loadDown * /.p6.call(obj)) {
if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) {
obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight;
}
selection/* updateCornerPosition * /.Aq.call(obj);
}
} else if (obj.content.scrollTop <= obj.content.clientHeight) {
if (lazyLoading/* loadUp * /.G_.call(obj)) {
if (obj.content.scrollTop < 10) {
obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight;
}
selection/* updateCornerPosition * /.Aq.call(obj);
}
}
libraryBase.jspreadsheet.timeControlLoading = null;
}, 100);
}
}
}
let scrollLeft = 0;
const updateFreezePosition = function() {
const obj = this;
scrollLeft = obj.content.scrollLeft;
let width = 0;
if (scrollLeft > 50) {
for (let i = 0; i < obj.options.freezeColumns; i++) {
if (i > 0) {
// Must check if the previous column is hidden or not to determin whether the width shoule be added or not!
if (!obj.options.columns || !obj.options.columns[i-1] || obj.options.columns[i-1].type !== "hidden") {
let columnWidth;
if (obj.options.columns && obj.options.columns[i-1] && obj.options.columns[i-1].width !== undefined) {
columnWidth = parseInt(obj.options.columns[i-1].width);
} else {
columnWidth = obj.options.defaultColWidth !== undefined ? parseInt(obj.options.defaultColWidth) : 100;
}
width += parseInt(columnWidth);
}
}
obj.headers[i].classList.add('jss_freezed');
obj.headers[i].style.left = width + 'px';
for (let j = 0; j < obj.rows.length; j++) {
if (obj.rows[j] && obj.records[j][i]) {
const shifted = (scrollLeft + (i > 0 ? obj.records[j][i-1].element.style.width : 0)) - 51 + 'px';
obj.records[j][i].element.classList.add('jss_freezed');
obj.records[j][i].element.style.left = shifted;
}
}
}
} else {
for (let i = 0; i < obj.options.freezeColumns; i++) {
obj.headers[i].classList.remove('jss_freezed');
obj.headers[i].style.left = '';
for (let j = 0; j < obj.rows.length; j++) {
if (obj.records[j][i]) {
obj.records[j][i].element.classList.remove('jss_freezed');
obj.records[j][i].element.style.left = '';
}
}
}
}
// Place the corner in the correct place
selection/* updateCornerPosition * /.Aq.call(obj);
}
const scrollControls = function(e) {
const obj = this;
wheelControls.call(obj);
if (obj.options.freezeColumns > 0 && obj.content.scrollLeft != scrollLeft) {
updateFreezePosition.call(obj);
}
// Close editor
if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) {
if (obj.edition && e.target.className.substr(0,9) != 'jdropdown') {
closeEditor.call(obj, obj.edition[0], true);
}
}
}
const setEvents = function(root) {
destroyEvents(root);
root.addEventListener("mouseup", mouseUpControls);
root.addEventListener("mousedown", mouseDownControls);
root.addEventListener("mousemove", mouseMoveControls);
root.addEventListener("mouseover", mouseOverControls);
root.addEventListener("dblclick", doubleClickControls);
root.addEventListener("paste", pasteControls);
root.addEventListener("contextmenu", contextMenuControls);
root.addEventListener("touchstart", touchStartControls);
root.addEventListener("touchend", touchEndControls);
root.addEventListener("touchcancel", touchEndControls);
root.addEventListener("touchmove", touchEndControls);
document.addEventListener("keydown", keyDownControls);
}
const destroyEvents = function(root) {
root.removeEventListener("mouseup", mouseUpControls);
root.removeEventListener("mousedown", mouseDownControls);
root.removeEventListener("mousemove", mouseMoveControls);
root.removeEventListener("mouseover", mouseOverControls);
root.removeEventListener("dblclick", doubleClickControls);
root.removeEventListener("paste", pasteControls);
root.removeEventListener("contextmenu", contextMenuControls);
root.removeEventListener("touchstart", touchStartControls);
root.removeEventListener("touchend", touchEndControls);
root.removeEventListener("touchcancel", touchEndControls);
document.removeEventListener("keydown", keyDownControls);
}
// EXTERNAL MODULE: ./src/utils/toolbar.js
var toolbar = __webpack_require__(392);
// EXTERNAL MODULE: ./src/utils/pagination.js
var pagination = __webpack_require__(167);
;// ./src/utils/data.js
const setData = function(data) {
const obj = this;
// Update data
if (data) {
obj.options.data = data;
}
// Data
if (! obj.options.data) {
obj.options.data = [];
}
// Prepare data
if (obj.options.data && obj.options.data[0]) {
if (! Array.isArray(obj.options.data[0])) {
data = [];
for (let j = 0; j < obj.options.data.length; j++) {
const row = [];
for (let i = 0; i < obj.options.columns.length; i++) {
row[i] = obj.options.data[j][obj.options.columns[i].name];
}
data.push(row);
}
obj.options.data = data;
}
}
// Adjust minimal dimensions
let j = 0;
let i = 0;
const size_i = obj.options.columns && obj.options.columns.length || 0;
const size_j = obj.options.data.length;
const min_i = obj.options.minDimensions[0];
const min_j = obj.options.minDimensions[1];
const max_i = min_i > size_i ? min_i : size_i;
const max_j = min_j > size_j ? min_j : size_j;
for (j = 0; j < max_j; j++) {
for (i = 0; i < max_i; i++) {
if (obj.options.data[j] == undefined) {
obj.options.data[j] = [];
}
if (obj.options.data[j][i] == undefined) {
obj.options.data[j][i] = '';
}
}
}
// Reset containers
obj.rows = [];
obj.results = null;
obj.records = [];
obj.history = [];
// Reset internal controllers
obj.historyIndex = -1;
// Reset data
obj.tbody.innerHTML = '';
let startNumber;
let finalNumber;
// Lazy loading
if (obj.options.lazyLoading == true) {
// Load only 100 records
startNumber = 0
finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100;
if (obj.options.pagination) {
obj.options.pagination = false;
console.error('Jspreadsheet: Pagination will be disable due the lazyLoading');
}
} else if (obj.options.pagination) {
// Pagination
if (! obj.pageNumber) {
obj.pageNumber = 0;
}
var quantityPerPage = obj.options.pagination;
startNumber = (obj.options.pagination * obj.pageNumber);
finalNumber = (obj.options.pagination * obj.pageNumber) + obj.options.pagination;
if (obj.options.data.length < finalNumber) {
finalNumber = obj.options.data.length;
}
} else {
startNumber = 0;
finalNumber = obj.options.data.length;
}
// Append nodes to the HTML
for (j = 0; j < obj.options.data.length; j++) {
// Create row
const row = createRow.call(obj, j, obj.options.data[j]);
// Append line to the table
if (j >= startNumber && j < finalNumber) {
obj.tbody.appendChild(row.element);
}
}
if (obj.options.lazyLoading == true) {
// Do not create pagination with lazyloading activated
} else if (obj.options.pagination) {
pagination/* updatePagination * /.IV.call(obj);
}
// Merge cells
if (obj.options.mergeCells) {
const keys = Object.keys(obj.options.mergeCells);
for (let i = 0; i < keys.length; i++) {
const num = obj.options.mergeCells[keys[i]];
merges/* setMerge * /.FU.call(obj, keys[i], num[0], num[1], 1);
}
}
// Updata table with custom configurations if applicable
internal/* updateTable * /.am.call(obj);
}
/**
* Get the value from a cell
*
* @param object cell
* @return string value
* /
const getValue = function(cell, processedValue) {
const obj = this;
let x;
let y;
if (typeof cell !== 'string') {
return null;
}
cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(cell, true);
x = cell[0];
y = cell[1];
let value = null;
if (x != null && y != null) {
if (obj.records[y] && obj.records[y][x] && processedValue) {
value = obj.records[y][x].element.innerHTML;
} else {
if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {
value = obj.options.data[y][x];
}
}
}
return value;
}
/**
* Get the value from a coords
*
* @param int x
* @param int y
* @return string value
* /
const getValueFromCoords = function(x, y, processedValue) {
const obj = this;
let value = null;
if (x != null && y != null) {
if ((obj.records[y] && obj.records[y][x]) && processedValue) {
value = obj.records[y][x].element.innerHTML;
} else {
if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {
value = obj.options.data[y][x];
}
}
}
return value;
}
/**
* Set a cell value
*
* @param mixed cell destination cell
* @param string value value
* @return void
* /
const setValue = function(cell, value, force) {
const obj = this;
const records = [];
if (typeof(cell) == 'string') {
const columnId = (0,internalHelpers/* getIdFromColumnName * /.vu)(cell, true);
const x = columnId[0];
const y = columnId[1];
// Update cell
records.push(internal/* updateCell * /.k9.call(obj, x, y, value, force));
// Update all formulas in the chain
internal/* updateFormulaChain * /.xF.call(obj, x, y, records);
} else {
let x = null;
let y = null;
if (cell && cell.getAttribute) {
x = cell.getAttribute('data-x');
y = cell.getAttribute('data-y');
}
// Update cell
if (x != null && y != null) {
records.push(internal/* updateCell * /.k9.call(obj, x, y, value, force));
// Update all formulas in the chain
internal/* updateFormulaChain * /.xF.call(obj, x, y, records);
} else {
const keys = Object.keys(cell);
if (keys.length > 0) {
for (let i = 0; i < keys.length; i++) {
let x, y;
if (typeof(cell[i]) == 'string') {
const columnId = (0,internalHelpers/* getIdFromColumnName * /.vu)(cell[i], true);
x = columnId[0];
y = columnId[1];
} else {
if (cell[i].x != null && cell[i].y != null) {
x = cell[i].x;
y = cell[i].y;
// Flexible setup
if (cell[i].value != null) {
value = cell[i].value;
}
} else {
x = cell[i].getAttribute('data-x');
y = cell[i].getAttribute('data-y');
}
}
// Update cell
if (x != null && y != null) {
records.push(internal/* updateCell * /.k9.call(obj, x, y, value, force));
// Update all formulas in the chain
internal/* updateFormulaChain * /.xF.call(obj, x, y, records);
}
}
}
}
}
// Update history
utils_history/* setHistory * /.Dh.call(obj, {
action:'setValue',
records:records,
selection:obj.selectedCell,
});
// Update table with custom configurations if applicable
internal/* updateTable * /.am.call(obj);
// On after changes
const onafterchangesRecords = records.map(function(record) {
return {
x: record.x,
y: record.y,
value: record.newValue,
oldValue: record.oldValue,
};
});
dispatch/* default * /.A.call(obj, 'onafterchanges', obj, onafterchangesRecords);
}
/**
* Set a cell value based on coordinates
*
* @param int x destination cell
* @param int y destination cell
* @param string value
* @return void
* /
const setValueFromCoords = function(x, y, value, force) {
const obj = this;
const records = [];
records.push(internal/* updateCell * /.k9.call(obj, x, y, value, force));
// Update all formulas in the chain
internal/* updateFormulaChain * /.xF.call(obj, x, y, records);
// Update history
utils_history/* setHistory * /.Dh.call(obj, {
action:'setValue',
records:records,
selection:obj.selectedCell,
});
// Update table with custom configurations if applicable
internal/* updateTable * /.am.call(obj);
// On after changes
const onafterchangesRecords = records.map(function(record) {
return {
x: record.x,
y: record.y,
value: record.newValue,
oldValue: record.oldValue,
};
});
dispatch/* default * /.A.call(obj, 'onafterchanges', obj, onafterchangesRecords);
}
/**
* Get the whole table data
*
* @param bool get highlighted cells only
* @return array data
* /
const getData = function(highlighted, processed, delimiter, asJson) {
const obj = this;
// Control vars
const dataset = [];
let px = 0;
let py = 0;
// Column and row length
const x = Math.max(...obj.options.data.map(function(row) {
return row.length;
}));
const y = obj.options.data.length
// Go through the columns to get the data
for (let j = 0; j < y; j++) {
px = 0;
for (let i = 0; i < x; i++) {
// Cell selected or fullset
if (! highlighted || obj.records[j][i].element.classList.contains('highlight')) {
// Get value
if (! dataset[py]) {
dataset[py] = [];
}
if (processed) {
dataset[py][px] = obj.records[j][i].element.innerHTML;
} else {
dataset[py][px] = obj.options.data[j][i];
}
px++;
}
}
if (px > 0) {
py++;
}
}
if (delimiter) {
return dataset.map(function(row) {
return row.join(delimiter);
}).join("\r\n") + "\r\n";
}
if (asJson) {
return dataset.map(function(row) {
const resultRow = {};
row.forEach(function(item, index) {
resultRow[index] = item;
});
return resultRow;
})
}
return dataset;
}
const getDataFromRange = function(range, processed) {
const obj = this;
const coords = (0,helpers.getCoordsFromRange)(range);
const dataset = [];
for (let y = coords[1]; y <= coords[3]; y++) {
dataset.push([]);
for (let x = coords[0]; x <= coords[2]; x++) {
if (processed) {
dataset[dataset.length - 1].push(obj.records[y][x].element.innerHTML);
} else {
dataset[dataset.length - 1].push(obj.options.data[y][x]);
}
}
}
return dataset;
}
;// ./src/utils/search.js
/**
* Search
* /
const search = function(query) {
const obj = this;
// Reset any filter
if (obj.options.filters) {
filter/* resetFilters * /.dr.call(obj);
}
// Reset selection
obj.resetSelection();
// Total of results
obj.pageNumber = 0;
obj.results = [];
if (query) {
if (obj.searchInput.value !== query) {
obj.searchInput.value = query;
}
// Search filter
const search = function(item, query, index) {
for (let i = 0; i < item.length; i++) {
if ((''+item[i]).toLowerCase().search(query) >= 0 ||
(''+obj.records[index][i].element.innerHTML).toLowerCase().search(query) >= 0) {
return true;
}
}
return false;
}
// Result
const addToResult = function(k) {
if (obj.results.indexOf(k) == -1) {
obj.results.push(k);
}
}
let parsedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
parsedQuery = new RegExp(parsedQuery, "i");
// Filter
obj.options.data.forEach(function(v, k) {
if (search(v, parsedQuery, k)) {
// Merged rows found
const rows = merges/* isRowMerged * /.D0.call(obj, k);
if (rows.length) {
for (let i = 0; i < rows.length; i++) {
const row = (0,internalHelpers/* getIdFromColumnName * /.vu)(rows[i], true);
for (let j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) {
addToResult(row[1]+j);
}
}
} else {
// Normal row found
addToResult(k);
}
}
});
} else {
obj.results = null;
}
internal/* updateResult * /.hG.call(obj);
}
/**
* Reset search
* /
const resetSearch = function() {
const obj = this;
obj.searchInput.value = '';
obj.search('');
obj.results = null;
}
;// ./src/utils/headers.js
/**
* Get the column title
*
* @param column - column number (first column is: 0)
* @param title - new column title
* /
const getHeader = function(column) {
const obj = this;
return obj.headers[column].textContent;
}
/**
* Get the headers
*
* @param asArray
* @return mixed
* /
const getHeaders = function (asArray) {
const obj = this;
const title = [];
for (let i = 0; i < obj.headers.length; i++) {
title.push(obj.getHeader(i));
}
return asArray ? title : title.join(obj.options.csvDelimiter);
}
/**
* Set the column title
*
* @param column - column number (first column is: 0)
* @param title - new column title
* /
const setHeader = function(column, newValue) {
const obj = this;
if (obj.headers[column]) {
const oldValue = obj.headers[column].textContent;
const onchangeheaderOldValue = (obj.options.columns && obj.options.columns[column] && obj.options.columns[column].title) || '';
if (! newValue) {
newValue = (0,helpers.getColumnName)(column);
}
obj.headers[column].textContent = newValue;
// Keep the title property
obj.headers[column].setAttribute('title', newValue);
// Update title
if (!obj.options.columns) {
obj.options.columns = [];
}
if (!obj.options.columns[column]) {
obj.options.columns[column] = {};
}
obj.options.columns[column].title = newValue;
utils_history/* setHistory * /.Dh.call(obj, {
action: 'setHeader',
column: column,
oldValue: oldValue,
newValue: newValue
});
// On onchange header
dispatch/* default * /.A.call(obj, 'onchangeheader', obj, parseInt(column), newValue, onchangeheaderOldValue);
}
}
;// ./src/utils/style.js
/**
* Get style information from cell(s)
*
* @return integer
* /
const getStyle = function(cell, key) {
const obj = this;
// Cell
if (! cell) {
// Control vars
const data = {};
// Column and row length
const x = obj.options.data[0].length;
const y = obj.options.data.length;
// Go through the columns to get the data
for (let j = 0; j < y; j++) {
for (let i = 0; i < x; i++) {
// Value
const v = key ? obj.records[j][i].element.style[key] : obj.records[j][i].element.getAttribute('style');
// Any meta data for this column?
if (v) {
// Column name
const k = (0,internalHelpers/* getColumnNameFromId * /.t3)([i, j]);
// Value
data[k] = v;
}
}
}
return data;
} else {
cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(cell, true);
return key ? obj.records[cell[1]][cell[0]].element.style[key] : obj.records[cell[1]][cell[0]].element.getAttribute('style');
}
}
/**
* Set meta information to cell(s)
*
* @return integer
* /
const setStyle = function(o, k, v, force, ignoreHistoryAndEvents) {
const obj = this;
const newValue = {};
const oldValue = {};
// Apply style
const applyStyle = function(cellId, key, value) {
// Position
const cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(cellId, true);
if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]] && (obj.records[cell[1]][cell[0]].element.classList.contains('readonly')==false || force)) {
// Current value
const currentValue = obj.records[cell[1]][cell[0]].element.style[key];
// Change layout
if (currentValue == value && ! force) {
value = '';
obj.records[cell[1]][cell[0]].element.style[key] = '';
} else {
obj.records[cell[1]][cell[0]].element.style[key] = value;
}
// History
if (! oldValue[cellId]) {
oldValue[cellId] = [];
}
if (! newValue[cellId]) {
newValue[cellId] = [];
}
oldValue[cellId].push([key + ':' + currentValue]);
newValue[cellId].push([key + ':' + value]);
}
}
if (k && v) {
// Get object from string
if (typeof(o) == 'string') {
applyStyle(o, k, v);
}
} else {
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
let style = o[keys[i]];
if (typeof(style) == 'string') {
style = style.split(';');
}
for (let j = 0; j < style.length; j++) {
if (typeof(style[j]) == 'string') {
style[j] = style[j].split(':');
}
// Apply value
if (style[j][0].trim()) {
applyStyle(keys[i], style[j][0].trim(), style[j][1]);
}
}
}
}
let keys = Object.keys(oldValue);
for (let i = 0; i < keys.length; i++) {
oldValue[keys[i]] = oldValue[keys[i]].join(';');
}
keys = Object.keys(newValue);
for (let i = 0; i < keys.length; i++) {
newValue[keys[i]] = newValue[keys[i]].join(';');
}
if (! ignoreHistoryAndEvents) {
// Keeping history of changes
utils_history/* setHistory * /.Dh.call(obj, {
action: 'setStyle',
oldValue: oldValue,
newValue: newValue,
});
}
dispatch/* default * /.A.call(obj, 'onchangestyle', obj, newValue);
}
const resetStyle = function(o, ignoreHistoryAndEvents) {
const obj = this;
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
// Position
const cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(keys[i], true);
if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) {
obj.records[cell[1]][cell[0]].element.setAttribute('style', '');
}
}
obj.setStyle(o, null, null, null, ignoreHistoryAndEvents);
}
;// ./src/utils/download.js
/**
* Download CSV table
*
* @return null
* /
const download = function(includeHeaders, processed) {
const obj = this;
if (obj.parent.config.allowExport == false) {
console.error('Export not allowed');
} else {
// Data
let data = '';
// Get data
data += copy.call(obj, false, obj.options.csvDelimiter, true, includeHeaders, true, undefined, processed);
// Download element
const blob = new Blob(["\uFEFF"+data], {type: 'text/csv;charset=utf-8;'});
// IE Compatibility
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, (obj.options.csvFileName || obj.options.worksheetName) + '.csv');
} else {
// Download element
const pom = document.createElement('a');
const url = URL.createObjectURL(blob);
pom.href = url;
pom.setAttribute('download', (obj.options.csvFileName || obj.options.worksheetName) + '.csv');
document.body.appendChild(pom);
pom.click();
pom.parentNode.removeChild(pom);
}
}
}
;// ./src/utils/comments.js
/**
* Get cell comments, null cell for all
* /
const getComments = function(cell) {
const obj = this;
if (cell) {
if (typeof(cell) !== 'string') {
return getComments.call(obj);
}
cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(cell, true);
return obj.records[cell[1]][cell[0]].element.getAttribute('title') || '';
} else {
const data = {};
for (let j = 0; j < obj.options.data.length; j++) {
for (let i = 0; i < obj.options.columns.length; i++) {
const comments = obj.records[j][i].element.getAttribute('title');
if (comments) {
const cell = (0,internalHelpers/* getColumnNameFromId * /.t3)([i, j]);
data[cell] = comments;
}
}
}
return data;
}
}
/**
* Set cell comments
* /
const setComments = function(cellId, comments) {
const obj = this;
let commentsObj;
if (typeof cellId == 'string') {
commentsObj = { [cellId]: comments };
} else {
commentsObj = cellId;
}
const oldValue = {};
Object.entries(commentsObj).forEach(function([cellName, comment]) {
const cellCoords = (0,helpers.getCoordsFromCellName)(cellName);
// Keep old value
oldValue[cellName] = obj.records[cellCoords[1]][cellCoords[0]].element.getAttribute('title');
// Set new values
obj.records[cellCoords[1]][cellCoords[0]].element.setAttribute('title', comment ? comment : '');
// Remove class if there is no comment
if (comment) {
obj.records[cellCoords[1]][cellCoords[0]].element.classList.add('jss_comments');
if (!obj.options.comments) {
obj.options.comments = {};
}
obj.options.comments[cellName] = comment;
} else {
obj.records[cellCoords[1]][cellCoords[0]].element.classList.remove('jss_comments');
if (obj.options.comments && obj.options.comments[cellName]) {
delete obj.options.comments[cellName];
}
}
});
// Save history
utils_history/* setHistory * /.Dh.call(obj, {
action:'setComments',
newValue: commentsObj,
oldValue: oldValue,
});
// Set comments
dispatch/* default * /.A.call(obj, 'oncomments', obj, commentsObj, oldValue);
}
// EXTERNAL MODULE: ./src/utils/orderBy.js
var orderBy = __webpack_require__(94);
;// ./src/utils/config.js
/**
* Get table config information
* /
const getWorksheetConfig = function() {
const obj = this;
return obj.options;
}
const getSpreadsheetConfig = function() {
const spreadsheet = this;
return spreadsheet.config;
}
const setConfig = function(config, spreadsheetLevel) {
const obj = this;
const keys = Object.keys(config);
let spreadsheet;
if (!obj.parent) {
spreadsheetLevel = true;
spreadsheet = obj;
} else {
spreadsheet = obj.parent;
}
keys.forEach(function(key) {
if (spreadsheetLevel) {
spreadsheet.config[key] = config[key];
if (key === 'toolbar') {
if (config[key] === true) {
spreadsheet.showToolbar();
} else if (config[key] === false) {
spreadsheet.hideToolbar();
}
}
} else {
obj.options[key] = config[key];
}
});
}
// EXTERNAL MODULE: ./src/utils/meta.js
var meta = __webpack_require__(654);
;// ./src/utils/cells.js
const setReadOnly = function(cell, state) {
const obj = this;
let record;
if (typeof cell === 'string') {
const coords = (0,helpers.getCoordsFromCellName)(cell);
record = obj.records[coords[1]][coords[0]];
} else {
const x = parseInt(cell.getAttribute('data-x'));
const y = parseInt(cell.getAttribute('data-y'));
record = obj.records[y][x];
}
if (state) {
record.element.classList.add('readonly');
} else {
record.element.classList.remove('readonly');
}
}
const isReadOnly = function(x, y) {
const obj = this;
if (typeof x === 'string' && typeof y === 'undefined') {
const coords = (0,helpers.getCoordsFromCellName)(x);
[x, y] = coords;
}
return obj.records[y][x].element.classList.contains('readonly');
}
;// ./src/utils/worksheets.js
const setWorksheetFunctions = function(worksheet) {
for (let i = 0; i < worksheetPublicMethodsLength; i++) {
const [methodName, method] = worksheetPublicMethods[i];
worksheet[methodName] = method.bind(worksheet);
}
}
const createTable = function() {
let obj = this;
setWorksheetFunctions(obj);
// Elements
obj.table = document.createElement('table');
obj.thead = document.createElement('thead');
obj.tbody = document.createElement('tbody');
// Create headers controllers
obj.headers = [];
obj.cols = [];
// Create table container
obj.content = document.createElement('div');
obj.content.classList.add('jss_content');
obj.content.onscroll = function(e) {
scrollControls.call(obj, e);
}
obj.content.onwheel = function(e) {
wheelControls.call(obj, e);
}
// Search
const searchContainer = document.createElement('div');
const searchLabel = document.createElement('label');
searchLabel.innerHTML = jSuites.translate('Search') + ': ';
searchContainer.appendChild(searchLabel);
obj.searchInput = document.createElement('input');
obj.searchInput.classList.add('jss_search');
searchLabel.appendChild(obj.searchInput);
obj.searchInput.onfocus = function() {
obj.resetSelection();
}
// Pagination select option
const paginationUpdateContainer = document.createElement('div');
if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) {
obj.paginationDropdown = document.createElement('select');
obj.paginationDropdown.classList.add('jss_pagination_dropdown');
obj.paginationDropdown.onchange = function() {
obj.options.pagination = parseInt(this.value);
obj.page(0);
}
for (let i = 0; i < obj.options.paginationOptions.length; i++) {
const temp = document.createElement('option');
temp.value = obj.options.paginationOptions[i];
temp.innerHTML = obj.options.paginationOptions[i];
obj.paginationDropdown.appendChild(temp);
}
// Set initial pagination value
obj.paginationDropdown.value = obj.options.pagination;
paginationUpdateContainer.appendChild(document.createTextNode(jSuites.translate('Show ')));
paginationUpdateContainer.appendChild(obj.paginationDropdown);
paginationUpdateContainer.appendChild(document.createTextNode(jSuites.translate('entries')));
}
// Filter and pagination container
const filter = document.createElement('div');
filter.classList.add('jss_filter');
filter.appendChild(paginationUpdateContainer);
filter.appendChild(searchContainer);
// Colsgroup
obj.colgroupContainer = document.createElement('colgroup');
let tempCol = document.createElement('col');
tempCol.setAttribute('width', '50');
obj.colgroupContainer.appendChild(tempCol);
// Nested
if (
obj.options.nestedHeaders &&
obj.options.nestedHeaders.length > 0 &&
obj.options.nestedHeaders[0] &&
obj.options.nestedHeaders[0][0]
) {
for (let j = 0; j < obj.options.nestedHeaders.length; j++) {
obj.thead.appendChild(internal/* createNestedHeader * /.ju.call(obj, obj.options.nestedHeaders[j]));
}
}
// Row
obj.headerContainer = document.createElement('tr');
tempCol = document.createElement('td');
tempCol.classList.add('jss_selectall');
obj.headerContainer.appendChild(tempCol);
const numberOfColumns = getNumberOfColumns.call(obj);
for (let i = 0; i < numberOfColumns; i++) {
// Create header
createCellHeader.call(obj, i);
// Append cell to the container
obj.headerContainer.appendChild(obj.headers[i]);
obj.colgroupContainer.appendChild(obj.cols[i].colElement);
}
obj.thead.appendChild(obj.headerContainer);
// Filters
if (obj.options.filters == true) {
obj.filter = document.createElement('tr');
const td = document.createElement('td');
obj.filter.appendChild(td);
for (let i = 0; i < obj.options.columns.length; i++) {
const td = document.createElement('td');
td.innerHTML = ' ';
td.setAttribute('data-x', i);
td.className = 'jss_column_filter';
if (obj.options.columns[i].type == 'hidden') {
td.style.display = 'none';
}
obj.filter.appendChild(td);
}
obj.thead.appendChild(obj.filter);
}
// Content table
obj.table = document.createElement('table');
obj.table.classList.add('jss_worksheet');
obj.table.setAttribute('cellpadding', '0');
obj.table.setAttribute('cellspacing', '0');
obj.table.setAttribute('unselectable', 'yes');
//obj.table.setAttribute('onselectstart', 'return false');
obj.table.appendChild(obj.colgroupContainer);
obj.table.appendChild(obj.thead);
obj.table.appendChild(obj.tbody);
if (! obj.options.textOverflow) {
obj.table.classList.add('jss_overflow');
}
// Spreadsheet corner
obj.corner = document.createElement('div');
obj.corner.className = 'jss_corner';
obj.corner.setAttribute('unselectable', 'on');
obj.corner.setAttribute('onselectstart', 'return false');
if (obj.options.selectionCopy == false) {
obj.corner.style.display = 'none';
}
// Textarea helper
obj.textarea = document.createElement('textarea');
obj.textarea.className = 'jss_textarea';
obj.textarea.id = 'jss_textarea';
obj.textarea.tabIndex = '-1';
obj.textarea.ariaHidden = 'true';
// Powered by Jspreadsheet
const ads = document.createElement('a');
ads.setAttribute('href', 'https://bossanova.uk/jspreadsheet/');
obj.ads = document.createElement('div');
obj.ads.className = 'jss_about';
const span = document.createElement('span');
span.innerHTML = 'Jspreadsheet CE';
ads.appendChild(span);
obj.ads.appendChild(ads);
// Create table container TODO: frozen columns
const container = document.createElement('div');
container.classList.add('jss_table');
// Pagination
obj.pagination = document.createElement('div');
obj.pagination.classList.add('jss_pagination');
const paginationInfo = document.createElement('div');
const paginationPages = document.createElement('div');
obj.pagination.appendChild(paginationInfo);
obj.pagination.appendChild(paginationPages);
// Hide pagination if not in use
if (! obj.options.pagination) {
obj.pagination.style.display = 'none';
}
// Append containers to the table
if (obj.options.search == true) {
obj.element.appendChild(filter);
}
// Elements
obj.content.appendChild(obj.table);
obj.content.appendChild(obj.corner);
obj.content.appendChild(obj.textarea);
obj.element.appendChild(obj.content);
obj.element.appendChild(obj.pagination);
obj.element.appendChild(obj.ads);
obj.element.classList.add('jss_container');
obj.element.jssWorksheet = obj;
obj.element.jspreadsheet = obj;
// Overflow
if (obj.options.tableOverflow == true) {
if (obj.options.tableHeight) {
obj.content.style['overflow-y'] = 'auto';
obj.content.style['box-shadow'] = 'rgb(221 221 221) 2px 2px 5px 0.1px';
obj.content.style.maxHeight = typeof obj.options.tableHeight === 'string' ? obj.options.tableHeight : obj.options.tableHeight + 'px';
}
if (obj.options.tableWidth) {
obj.content.style['overflow-x'] = 'auto';
obj.content.style.width = typeof obj.options.tableWidth === 'string' ? obj.options.tableWidth : obj.options.tableWidth + 'px';
}
}
// With toolbars
if (obj.options.tableOverflow != true && obj.parent.config.toolbar) {
obj.element.classList.add('with-toolbar');
}
// Actions
if (obj.options.columnDrag != false) {
obj.thead.classList.add('draggable');
}
if (obj.options.columnResize != false) {
obj.thead.classList.add('resizable');
}
if (obj.options.rowDrag != false) {
obj.tbody.classList.add('draggable');
}
if (obj.options.rowResize != false) {
obj.tbody.classList.add('resizable');
}
// Load data
obj.setData.call(obj);
// Style
if (obj.options.style) {
obj.setStyle(obj.options.style, null, null, 1, 1);
delete obj.options.style;
}
Object.defineProperty(obj.options, 'style', {
enumerable: true,
configurable: true,
get() {
return obj.getStyle();
},
});
if (obj.options.comments) {
obj.setComments(obj.options.comments);
}
// Classes
if (obj.options.classes) {
const k = Object.keys(obj.options.classes);
for (let i = 0; i < k.length; i++) {
const cell = (0,internalHelpers/* getIdFromColumnName * /.vu)(k[i], true);
obj.records[cell[1]][cell[0]].element.classList.add(obj.options.classes[k[i]]);
}
}
}
/**
* Prepare the jspreadsheet table
*
* @Param config
* /
const prepareTable = function() {
const obj = this;
// Lazy loading
if (obj.options.lazyLoading == true && (obj.options.tableOverflow != true && obj.parent.config.fullscreen != true)) {
console.error('Jspreadsheet: The lazyloading only works when tableOverflow = yes or fullscreen = yes');
obj.options.lazyLoading = false;
}
if (!obj.options.columns) {
obj.options.columns = [];
}
// Number of columns
let size = obj.options.columns.length;
let keys;
if (obj.options.data && typeof(obj.options.data[0]) !== 'undefined') {
if (!Array.isArray(obj.options.data[0])) {
// Data keys
keys = Object.keys(obj.options.data[0]);
if (keys.length > size) {
size = keys.length;
}
} else {
const numOfColumns = obj.options.data[0].length;
if (numOfColumns > size) {
size = numOfColumns;
}
}
}
// Minimal dimensions
if (!obj.options.minDimensions) {
obj.options.minDimensions = [0, 0];
}
if (obj.options.minDimensions[0] > size) {
size = obj.options.minDimensions[0];
}
// Requests
const multiple = [];
// Preparations
for (let i = 0; i < size; i++) {
// Default column description
if (! obj.options.columns[i]) {
obj.options.columns[i] = {};
}
if (! obj.options.columns[i].name && keys && keys[i]) {
obj.options.columns[i].name = keys[i];
}
// Pre-load initial source for json dropdown
if (obj.options.columns[i].type == 'dropdown') {
// if remote content
if (obj.options.columns[i].url) {
multiple.push({
url: obj.options.columns[i].url,
index: i,
method: 'GET',
dataType: 'json',
success: function(data) {
if (!obj.options.columns[this.index].source) {
obj.options.columns[this.index].source = [];
}
for (let i = 0; i < data.length; i++) {
obj.options.columns[this.index].source.push(data[i]);
}
}
});
}
}
}
// Create the table when is ready
if (! multiple.length) {
createTable.call(obj);
} else {
jSuites.ajax(multiple, function() {
createTable.call(obj);
});
}
}
const getNextDefaultWorksheetName = function(spreadsheet) {
const defaultWorksheetNameRegex = /^Sheet(\d+)$/;
let largestWorksheetNumber = 0;
spreadsheet.worksheets.forEach(function(worksheet) {
const regexResult = defaultWorksheetNameRegex.exec(worksheet.options.worksheetName);
if (regexResult) {
largestWorksheetNumber = Math.max(largestWorksheetNumber, parseInt(regexResult[1]));
}
});
return 'Sheet' + (largestWorksheetNumber + 1);
}
const buildWorksheet = async function() {
const obj = this;
const el = obj.element;
const spreadsheet = obj.parent;
if (typeof spreadsheet.plugins === 'object') {
Object.entries(spreadsheet.plugins).forEach(function([, plugin]) {
if (typeof plugin.beforeinit === 'function') {
plugin.beforeinit(obj);
}
});
}
libraryBase.jspreadsheet.current = obj;
const promises = [];
// Load the table data based on an CSV file
if (obj.options.csv) {
const promise = new Promise((resolve) => {
// Load CSV file
jSuites.ajax({
url: obj.options.csv,
method: 'GET',
dataType: 'text',
success: function(result) {
// Convert data
const newData = (0,helpers.parseCSV)(result, obj.options.csvDelimiter)
// Headers
if (obj.options.csvHeaders == true && newData.length > 0) {
const headers = newData.shift();
if (headers.length > 0) {
if (!obj.options.columns) {
obj.options.columns = [];
}
for(let i = 0; i < headers.length; i++) {
if (! obj.options.columns[i]) {
obj.options.columns[i] = {};
}
// Precedence over pre-configurated titles
if (typeof obj.options.columns[i].title === 'undefined') {
obj.options.columns[i].title = headers[i];
}
}
}
}
// Data
obj.options.data = newData;
// Prepare table
prepareTable.call(obj);
resolve();
}
});
});
promises.push(promise);
} else if (obj.options.url) {
const promise = new Promise((resolve) => {
jSuites.ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
success: function(result) {
// Data
obj.options.data = (result.data) ? result.data : result;
// Prepare table
prepareTable.call(obj);
resolve();
}
});
})
promises.push(promise);
} else {
// Prepare table
prepareTable.call(obj);
}
await Promise.all(promises);
if (typeof spreadsheet.plugins === 'object') {
Object.entries(spreadsheet.plugins).forEach(function([, plugin]) {
if (typeof plugin.init === 'function') {
plugin.init(obj);
}
});
}
}
const createWorksheetObj = function(options) {
const obj = this;
const spreadsheet = obj.parent;
if (!options.worksheetName) {
options.worksheetName = getNextDefaultWorksheetName(obj.parent);
}
const newWorksheet = {
parent: spreadsheet,
options: options,
filters: [],
formula: [],
history: [],
selection: [],
historyIndex: -1,
};
spreadsheet.config.worksheets.push(newWorksheet.options);
spreadsheet.worksheets.push(newWorksheet);
return newWorksheet;
}
const createWorksheet = function(options) {
const obj = this;
const spreadsheet = obj.parent;
spreadsheet.creationThroughJss = true;
createWorksheetObj.call(obj, options);
spreadsheet.element.tabs.create(options.worksheetName);
}
const openWorksheet = function(position) {
const obj = this;
const spreadsheet = obj.parent;
spreadsheet.element.tabs.open(position);
}
const deleteWorksheet = function(position) {
const obj = this;
obj.parent.element.tabs.remove(position);
const removedWorksheet = obj.parent.worksheets.splice(position, 1)[0];
dispatch/* default * /.A.call(obj.parent, 'ondeleteworksheet', removedWorksheet, position);
}
const worksheetPublicMethods = [
['selectAll', selection/* selectAll * /.Ub],
['updateSelectionFromCoords', function(x1, y1, x2, y2) {
return selection/* updateSelectionFromCoords * /.AH.call(this, x1, y1, x2, y2);
}],
['resetSelection', function() {
return selection/* resetSelection * /.gE.call(this);
}],
['getSelection', selection/* getSelection * /.Lo],
['getSelected', selection/* getSelected * /.ef],
['getSelectedColumns', selection/* getSelectedColumns * /.Jg],
['getSelectedRows', selection/* getSelectedRows * /.R5],
['getData', getData],
['setData', setData],
['getValue', getValue],
['getValueFromCoords', getValueFromCoords],
['setValue', setValue],
['setValueFromCoords', setValueFromCoords],
['getWidth', getWidth],
['setWidth', function(column, width) {
return setWidth.call(this, column, width);
}],
['insertRow', insertRow],
['moveRow', function(rowNumber, newPositionNumber) {
return moveRow.call(this, rowNumber, newPositionNumber);
}],
['deleteRow', deleteRow],
['hideRow', hideRow],
['showRow', showRow],
['getRowData', getRowData],
['setRowData', setRowData],
['getHeight', getHeight],
['setHeight', function(row, height) {
return setHeight.call(this, row, height);
}],
['getMerge', merges/* getMerge * /.fd],
['setMerge', function(cellName, colspan, rowspan) {
return merges/* setMerge * /.FU.call(this, cellName, colspan, rowspan);
}],
['destroyMerge', function() {
return merges/* destroyMerge * /.VP.call(this);
}],
['removeMerge', function(cellName, data) {
return merges/* removeMerge * /.Zp.call(this, cellName, data);
}],
['search', search],
['resetSearch', resetSearch],
['getHeader', getHeader],
['getHeaders', getHeaders],
['setHeader', setHeader],
['getStyle', getStyle],
['setStyle', function(cell, property, value, forceOverwrite) {
return setStyle.call(this, cell, property, value, forceOverwrite);
}],
['resetStyle', resetStyle],
['insertColumn', insertColumn],
['moveColumn', moveColumn],
['deleteColumn', deleteColumn],
['getColumnData', getColumnData],
['setColumnData', setColumnData],
['whichPage', pagination/* whichPage * /.ho],
['page', pagination/* page * /.MY],
['download', download],
['getComments', getComments],
['setComments', setComments],
['orderBy', orderBy/* orderBy * /.My],
['undo', utils_history/* undo * /.tN],
['redo', utils_history/* redo * /.ZS],
['getCell', internal/* getCell * /.tT],
['getCellFromCoords', internal/* getCellFromCoords * /.Xr],
['getLabel', internal/* getLabel * /.p9],
['getConfig', getWorksheetConfig],
['setConfig', setConfig],
['getMeta', function(cell) {
return meta/* getMeta * /.IQ.call(this, cell);
}],
['setMeta', meta/* setMeta * /.iZ],
['showColumn', showColumn],
['hideColumn', hideColumn],
['showIndex', internal/* showIndex * /.C6],
['hideIndex', internal/* hideIndex * /.TI],
['getWorksheetActive', internal/* getWorksheetActive * /.$O],
['openEditor', openEditor],
['closeEditor', closeEditor],
['createWorksheet', createWorksheet],
['openWorksheet', openWorksheet],
['deleteWorksheet', deleteWorksheet],
['copy', function(cut) {
if (cut) {
cutControls();
} else {
copy.call(this, true);
}
}],
['paste', paste],
['executeFormula', internal/* executeFormula * /.Em],
['getDataFromRange', getDataFromRange],
['quantiyOfPages', pagination/* quantiyOfPages * /.$f],
['getRange', selection/* getRange * /.eO],
['isSelected', selection/* isSelected * /.sp],
['setReadOnly', setReadOnly],
['isReadOnly', isReadOnly],
['getHighlighted', selection/* getHighlighted * /.kV],
['dispatch', dispatch/* default * /.A],
['down', down],
['first', first],
['last', last],
['left', left],
['right', right],
['up', up],
['openFilter', filter/* openFilter * /.N$],
['resetFilters', filter/* resetFilters * /.dr],
];
const worksheetPublicMethodsLength = worksheetPublicMethods.length;
;// ./src/utils/factory.js
const factory = function() {};
const createWorksheets = async function(spreadsheet, options, el) {
// Create worksheets
let o = options.worksheets;
if (o) {
let tabsOptions = {
animation: true,
onbeforecreate: function(element, title) {
if (title) {
return title;
} else {
return getNextDefaultWorksheetName(spreadsheet);
}
},
oncreate: function(element, newTabContent) {
if (!spreadsheet.creationThroughJss) {
const worksheetName = element.tabs.headers.children[element.tabs.headers.children.length - 2].innerHTML;
createWorksheetObj.call(
spreadsheet.worksheets[0],
{
minDimensions: [10, 15],
worksheetName: worksheetName,
}
)
} else {
spreadsheet.creationThroughJss = false;
}
const newWorksheet = spreadsheet.worksheets[spreadsheet.worksheets.length - 1];
newWorksheet.element = newTabContent;
buildWorksheet.call(newWorksheet)
.then(function() {
(0,toolbar/* updateToolbar * /.nK)(newWorksheet);
dispatch/* default * /.A.call(newWorksheet, 'oncreateworksheet', newWorksheet, options, spreadsheet.worksheets.length - 1);
});
},
onchange: function(element, instance, tabIndex) {
if (spreadsheet.worksheets.length != 0 && spreadsheet.worksheets[tabIndex]) {
(0,toolbar/* updateToolbar * /.nK)(spreadsheet.worksheets[tabIndex]);
}
}
}
if (options.tabs == true) {
tabsOptions.allowCreate = true;
} else {
tabsOptions.hideHeaders = true;
}
tabsOptions.data = [];
let sheetNumber = 1;
for (let i = 0; i < o.length; i++) {
if (!o[i].worksheetName) {
o[i].worksheetName = 'Sheet' + sheetNumber++;
}
tabsOptions.data.push({
title: o[i].worksheetName,
content: ''
});
}
el.classList.add('jss_spreadsheet');
const tabs = jSuites.tabs(el, tabsOptions);
const spreadsheetStyles = options.style;
delete options.style;
for (let i = 0; i < o.length; i++) {
if (o[i].style) {
Object.entries(o[i].style).forEach(function([cellName, value]) {
if (typeof value === 'number') {
o[i].style[cellName] = spreadsheetStyles[value];
}
})
}
spreadsheet.worksheets.push({
parent: spreadsheet,
element: tabs.content.children[i],
options: o[i],
filters: [],
formula: [],
history: [],
selection: [],
historyIndex: -1,
});
await buildWorksheet.call(spreadsheet.worksheets[i]);
}
} else {
throw new Error('JSS: worksheets are not defined');
}
}
factory.spreadsheet = async function(el, options, worksheets) {
if (el.tagName == 'TABLE') {
if (!options) {
options = {};
}
if (!options.worksheets) {
options.worksheets = [];
}
const tableOptions = (0,helpers.createFromTable)(el, options.worksheets[0]);
options.worksheets[0] = tableOptions;
const div = document.createElement('div');
el.parentNode.insertBefore(div, el);
el.remove();
el = div;
}
let spreadsheet = {
worksheets: worksheets,
config: options,
element: el,
el,
};
// Contextmenu container
spreadsheet.contextMenu = document.createElement('div');
spreadsheet.contextMenu.className = 'jss_contextmenu';
spreadsheet.getWorksheetActive = internal/* getWorksheetActive * /.$O.bind(spreadsheet);
spreadsheet.fullscreen = internal/* fullscreen * /.Y5.bind(spreadsheet);
spreadsheet.showToolbar = toolbar/* showToolbar * /.ll.bind(spreadsheet);
spreadsheet.hideToolbar = toolbar/* hideToolbar * /.Ar.bind(spreadsheet);
spreadsheet.getConfig = getSpreadsheetConfig.bind(spreadsheet);
spreadsheet.setConfig = setConfig.bind(spreadsheet);
spreadsheet.setPlugins = function(newPlugins) {
if (!spreadsheet.plugins) {
spreadsheet.plugins = {};
}
if (typeof newPlugins == 'object') {
Object.entries(newPlugins).forEach(function([pluginName, plugin]) {
spreadsheet.plugins[pluginName] = plugin.call(
libraryBase.jspreadsheet,
spreadsheet,
{},
spreadsheet.config,
);
})
}
}
spreadsheet.setPlugins(options.plugins);
// Create as worksheets
await createWorksheets(spreadsheet, options, el);
spreadsheet.element.appendChild(spreadsheet.contextMenu);
// Create element
jSuites.contextmenu(spreadsheet.contextMenu, {
onclick:function() {
spreadsheet.contextMenu.contextmenu.close(false);
}
});
// Fullscreen
if (spreadsheet.config.fullscreen == true) {
spreadsheet.element.classList.add('fullscreen');
}
toolbar/* showToolbar * /.ll.call(spreadsheet);
// Build handlers
if (options.root) {
setEvents(options.root);
} else {
setEvents(document);
}
el.spreadsheet = spreadsheet;
return spreadsheet;
}
factory.worksheet = function(spreadsheet, options, position) {
// Worksheet object
let w = {
// Parent of a worksheet is always the spreadsheet
parent: spreadsheet,
// Options for this worksheet
options: {},
};
// Create the worksheets object
if (typeof(position) === 'undefined') {
spreadsheet.worksheets.push(w);
} else {
spreadsheet.worksheets.splice(position, 0, w);
}
// Keep configuration used
Object.assign(w.options, options);
return w;
}
/* harmony default export * / var utils_factory = (factory);
;// ./src/index.js
libraryBase.jspreadsheet = function(el, options) {
try {
let worksheets = [];
// Create spreadsheet
utils_factory.spreadsheet(el, options, worksheets)
.then((spreadsheet) => {
libraryBase.jspreadsheet.spreadsheet.push(spreadsheet);
// Global onload event
dispatch/* default * /.A.call(spreadsheet, 'onload', spreadsheet);
});
return worksheets;
} catch (e) {
console.error(e);
}
}
libraryBase.jspreadsheet.getWorksheetInstanceByName = function(worksheetName, namespace) {
const targetSpreadsheet = libraryBase.jspreadsheet.spreadsheet.find((spreadsheet) => {
return spreadsheet.config.namespace === namespace;
});
if (targetSpreadsheet) {
return {};
}
if (typeof worksheetName === 'undefined' || worksheetName === null) {
const namespaceEntries = targetSpreadsheet.worksheets.map((worksheet) => {
return [worksheet.options.worksheetName, worksheet];
})
return Object.fromEntries(namespaceEntries);
}
return targetSpreadsheet.worksheets.find((worksheet) => {
return worksheet.options.worksheetName === worksheetName;
});
}
// Define dictionary
libraryBase.jspreadsheet.setDictionary = function(o) {
jSuites.setDictionary(o);
}
libraryBase.jspreadsheet.destroy = function(element, destroyEventHandlers) {
if (element.spreadsheet) {
const spreadsheetIndex = libraryBase.jspreadsheet.spreadsheet.indexOf(element.spreadsheet);
libraryBase.jspreadsheet.spreadsheet.splice(spreadsheetIndex, 1);
const root = element.spreadsheet.config.root || document;
element.spreadsheet = null;
element.innerHTML = '';
if (destroyEventHandlers) {
destroyEvents(root);
}
}
}
libraryBase.jspreadsheet.destroyAll = function() {
for (let spreadsheetIndex = 0; spreadsheetIndex < libraryBase.jspreadsheet.spreadsheet.length; spreadsheetIndex++) {
const spreadsheet = libraryBase.jspreadsheet.spreadsheet[spreadsheetIndex];
libraryBase.jspreadsheet.destroy(spreadsheet.element);
}
}
libraryBase.jspreadsheet.current = null;
libraryBase.jspreadsheet.spreadsheet = [];
libraryBase.jspreadsheet.helpers = {};
libraryBase.jspreadsheet.version = function() {
return version;
};
Object.entries(helpers).forEach(([key, value]) => {
libraryBase.jspreadsheet.helpers[key] = value;
})
/* harmony default export * / var src = (libraryBase.jspreadsheet);
jspreadsheet = __webpack_exports__["default"];
/****** / })()
;
return jspreadsheet;
})));
//}}}
!!!!!Formula
//{{{
var formula = (function() {
// Based on sutoiku work (https://github.com/sutoiku)
var error = (function() {
var exports = {};
exports.nil = new Error('#NULL!');
exports.div0 = new Error('#DIV/0!');
exports.value = new Error('#VALUE!');
exports.ref = new Error('#REF!');
exports.name = new Error('#NAME?');
exports.num = new Error('#NUM!');
exports.na = new Error('#N/A');
exports.error = new Error('#ERROR!');
exports.data = new Error('#GETTING_DATA');
return exports;
})();
var utils = (function() {
var exports = {};
exports.flattenShallow = function(array) {
if (!array || !array.reduce) {
return array;
}
return array.reduce(function(a, b) {
var aIsArray = Array.isArray(a);
var bIsArray = Array.isArray(b);
if (aIsArray && bIsArray) {
return a.concat(b);
}
if (aIsArray) {
a.push(b);
return a;
}
if (bIsArray) {
return [ a ].concat(b);
}
return [ a, b ];
});
};
exports.isFlat = function(array) {
if (!array) {
return false;
}
for (var i = 0; i < array.length; ++i) {
if (Array.isArray(array[i])) {
return false;
}
}
return true;
};
exports.flatten = function() {
var result = exports.argsToArray.apply(null, arguments);
while (!exports.isFlat(result)) {
result = exports.flattenShallow(result);
}
return result;
};
exports.argsToArray = function(args) {
var result = [];
exports.arrayEach(args, function(value) {
result.push(value);
});
return result;
};
exports.numbers = function() {
var possibleNumbers = this.flatten.apply(null, arguments);
return possibleNumbers.filter(function(el) {
return typeof el === 'number';
});
};
exports.cleanFloat = function(number) {
var power = 1e14;
return Math.round(number * power) / power;
};
exports.parseBool = function(bool) {
if (typeof bool === 'boolean') {
return bool;
}
if (bool instanceof Error) {
return bool;
}
if (typeof bool === 'number') {
return bool !== 0;
}
if (typeof bool === 'string') {
var up = bool.toUpperCase();
if (up === 'TRUE') {
return true;
}
if (up === 'FALSE') {
return false;
}
}
if (bool instanceof Date && !isNaN(bool)) {
return true;
}
return error.value;
};
exports.parseNumber = function(string) {
if (string === undefined || string === '') {
return error.value;
}
if (!isNaN(string)) {
return parseFloat(string);
}
return error.value;
};
exports.parseNumberArray = function(arr) {
var len;
if (!arr || (len = arr.length) === 0) {
return error.value;
}
var parsed;
while (len--) {
parsed = exports.parseNumber(arr[len]);
if (parsed === error.value) {
return parsed;
}
arr[len] = parsed;
}
return arr;
};
exports.parseMatrix = function(matrix) {
var n;
if (!matrix || (n = matrix.length) === 0) {
return error.value;
}
var pnarr;
for (var i = 0; i < matrix.length; i++) {
pnarr = exports.parseNumberArray(matrix[i]);
matrix[i] = pnarr;
if (pnarr instanceof Error) {
return pnarr;
}
}
return matrix;
};
var d1900 = new Date(Date.UTC(1900, 0, 1));
exports.parseDate = function(date) {
if (!isNaN(date)) {
if (date instanceof Date) {
return new Date(date);
}
var d = parseInt(date, 10);
if (d < 0) {
return error.num;
}
if (d <= 60) {
return new Date(d1900.getTime() + (d - 1) * 86400000);
}
return new Date(d1900.getTime() + (d - 2) * 86400000);
}
if (typeof date === 'string') {
date = new Date(date);
if (!isNaN(date)) {
return date;
}
}
return error.value;
};
exports.parseDateArray = function(arr) {
var len = arr.length;
var parsed;
while (len--) {
parsed = this.parseDate(arr[len]);
if (parsed === error.value) {
return parsed;
}
arr[len] = parsed;
}
return arr;
};
exports.anyIsError = function() {
var n = arguments.length;
while (n--) {
if (arguments[n] instanceof Error) {
return true;
}
}
return false;
};
exports.arrayValuesToNumbers = function(arr) {
var n = arr.length;
var el;
while (n--) {
el = arr[n];
if (typeof el === 'number') {
continue;
}
if (el === true) {
arr[n] = 1;
continue;
}
if (el === false) {
arr[n] = 0;
continue;
}
if (typeof el === 'string') {
var number = this.parseNumber(el);
if (number instanceof Error) {
arr[n] = 0;
} else {
arr[n] = number;
}
}
}
return arr;
};
exports.rest = function(array, idx) {
idx = idx || 1;
if (!array || typeof array.slice !== 'function') {
return array;
}
return array.slice(idx);
};
exports.initial = function(array, idx) {
idx = idx || 1;
if (!array || typeof array.slice !== 'function') {
return array;
}
return array.slice(0, array.length - idx);
};
exports.arrayEach = function(array, iteratee) {
var index = -1, length = array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
};
exports.transpose = function(matrix) {
if (!matrix) {
return error.value;
}
return matrix[0].map(function(col, i) {
return matrix.map(function(row) {
return row[i];
});
});
};
return exports;
})();
var met = {};
met.datetime = (function() {
var exports = {};
var d1900 = new Date(1900, 0, 1);
var WEEK_STARTS = [
undefined,
0,
1,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
1,
2,
3,
4,
5,
6,
0
];
var WEEK_TYPES = [
[],
[1, 2, 3, 4, 5, 6, 7],
[7, 1, 2, 3, 4, 5, 6],
[6, 0, 1, 2, 3, 4, 5],
[],
[],
[],
[],
[],
[],
[],
[7, 1, 2, 3, 4, 5, 6],
[6, 7, 1, 2, 3, 4, 5],
[5, 6, 7, 1, 2, 3, 4],
[4, 5, 6, 7, 1, 2, 3],
[3, 4, 5, 6, 7, 1, 2],
[2, 3, 4, 5, 6, 7, 1],
[1, 2, 3, 4, 5, 6, 7]
];
var WEEKEND_TYPES = [
[],
[6, 0],
[0, 1],
[1, 2],
[2, 3],
[3, 4],
[4, 5],
[5, 6],
undefined,
undefined,
undefined, [0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6]
];
exports.DATE = function(year, month, day) {
year = utils.parseNumber(year);
month = utils.parseNumber(month);
day = utils.parseNumber(day);
if (utils.anyIsError(year, month, day)) {
return error.value;
}
if (year < 0 || month < 0 || day < 0) {
return error.num;
}
var date = new Date(year, month - 1, day);
return date;
};
exports.DATEVALUE = function(date_text) {
if (typeof date_text !== 'string') {
return error.value;
}
var date = Date.parse(date_text);
if (isNaN(date)) {
return error.value;
}
if (date <= -2203891200000) {
return (date - d1900) / 86400000 + 1;
}
return (date - d1900) / 86400000 + 2;
};
exports.DAY = function(serial_number) {
var date = utils.parseDate(serial_number);
if (date instanceof Error) {
return date;
}
return date.getDate();
};
exports.DAYS = function(end_date, start_date) {
end_date = utils.parseDate(end_date);
start_date = utils.parseDate(start_date);
if (end_date instanceof Error) {
return end_date;
}
if (start_date instanceof Error) {
return start_date;
}
return serial(end_date) - serial(start_date);
};
exports.DAYS360 = function(start_date, end_date, method) {
};
exports.EDATE = function(start_date, months) {
start_date = utils.parseDate(start_date);
if (start_date instanceof Error) {
return start_date;
}
if (isNaN(months)) {
return error.value;
}
months = parseInt(months, 10);
start_date.setMonth(start_date.getMonth() + months);
return serial(start_date);
};
exports.EOMONTH = function(start_date, months) {
start_date = utils.parseDate(start_date);
if (start_date instanceof Error) {
return start_date;
}
if (isNaN(months)) {
return error.value;
}
months = parseInt(months, 10);
return serial(new Date(start_date.getFullYear(), start_date.getMonth() + months + 1, 0));
};
exports.HOUR = function(serial_number) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
return serial_number.getHours();
};
exports.INTERVAL = function(second) {
if (typeof second !== 'number' && typeof second !== 'string') {
return error.value;
} else {
second = parseInt(second, 10);
}
var year = Math.floor(second/946080000);
second = second%946080000;
var month = Math.floor(second/2592000);
second = second%2592000;
var day = Math.floor(second/86400);
second = second%86400;
var hour = Math.floor(second/3600);
second = second%3600;
var min = Math.floor(second/60);
second = second%60;
var sec = second;
year = (year > 0) ? year + 'Y' : '';
month = (month > 0) ? month + 'M' : '';
day = (day > 0) ? day + 'D' : '';
hour = (hour > 0) ? hour + 'H' : '';
min = (min > 0) ? min + 'M' : '';
sec = (sec > 0) ? sec + 'S' : '';
return 'P' + year + month + day + 'T' + hour + min + sec;
};
exports.ISOWEEKNUM = function(date) {
date = utils.parseDate(date);
if (date instanceof Error) {
return date;
}
date.setHours(0, 0, 0);
date.setDate(date.getDate() + 4 - (date.getDay() || 7));
var yearStart = new Date(date.getFullYear(), 0, 1);
return Math.ceil((((date - yearStart) / 86400000) + 1) / 7);
};
exports.MINUTE = function(serial_number) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
return serial_number.getMinutes();
};
exports.MONTH = function(serial_number) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
return serial_number.getMonth() + 1;
};
exports.NETWORKDAYS = function(start_date, end_date, holidays) {
};
exports.NETWORKDAYS.INTL = function(start_date, end_date, weekend, holidays) {
};
exports.NOW = function() {
return new Date();
};
exports.SECOND = function(serial_number) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
return serial_number.getSeconds();
};
exports.TIME = function(hour, minute, second) {
hour = utils.parseNumber(hour);
minute = utils.parseNumber(minute);
second = utils.parseNumber(second);
if (utils.anyIsError(hour, minute, second)) {
return error.value;
}
if (hour < 0 || minute < 0 || second < 0) {
return error.num;
}
return (3600 * hour + 60 * minute + second) / 86400;
};
exports.TIMEVALUE = function(time_text) {
time_text = utils.parseDate(time_text);
if (time_text instanceof Error) {
return time_text;
}
return (3600 * time_text.getHours() + 60 * time_text.getMinutes() + time_text.getSeconds()) / 86400;
};
exports.TODAY = function() {
return new Date();
};
exports.WEEKDAY = function(serial_number, return_type) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
if (return_type === undefined) {
return_type = 1;
}
var day = serial_number.getDay();
return WEEK_TYPES[return_type][day];
};
exports.WEEKNUM = function(serial_number, return_type) {
};
exports.WORKDAY = function(start_date, days, holidays) {
};
exports.WORKDAY.INTL = function(start_date, days, weekend, holidays) {
};
exports.YEAR = function(serial_number) {
serial_number = utils.parseDate(serial_number);
if (serial_number instanceof Error) {
return serial_number;
}
return serial_number.getFullYear();
};
function isLeapYear(year) {
return new Date(year, 1, 29).getMonth() === 1;
}
exports.YEARFRAC = function(start_date, end_date, basis) {
};
function serial(date) {
var addOn = (date > -2203891200000)?2:1;
return (date - d1900) / 86400000 + addOn;
}
return exports;
})();
met.database = (function() {
var exports = {};
function compact(array) {
if (!array) {
return array;
}
var result = [];
for (var i = 0; i < array.length; ++i) {
if (!array[i]) {
continue;
}
result.push(array[i]);
}
return result;
}
exports.FINDFIELD = function(database, title) {
var index = null;
for (var i = 0; i < database.length; i++) {
if (database[i][0] === title) {
index = i;
break;
}
}
// Return error if the input field title is incorrect
if (index == null) {
return error.value;
}
return index;
};
function findResultIndex(database, criterias) {
var matches = {};
for (var i = 1; i < database[0].length; ++i) {
matches[i] = true;
}
var maxCriteriaLength = criterias[0].length;
for (i = 1; i < criterias.length; ++i) {
if (criterias[i].length > maxCriteriaLength) {
maxCriteriaLength = criterias[i].length;
}
}
for (var k = 1; k < database.length; ++k) {
for (var l = 1; l < database[k].length; ++l) {
var currentCriteriaResult = false;
var hasMatchingCriteria = false;
for (var j = 0; j < criterias.length; ++j) {
var criteria = criterias[j];
if (criteria.length < maxCriteriaLength) {
continue;
}
var criteriaField = criteria[0];
if (database[k][0] !== criteriaField) {
continue;
}
hasMatchingCriteria = true;
for (var p = 1; p < criteria.length; ++p) {
currentCriteriaResult = currentCriteriaResult
|| eval(database[k][l] + criteria[p]); // jshint
// ignore:line
}
}
if (hasMatchingCriteria) {
matches[l] = matches[l] && currentCriteriaResult;
}
}
}
var result = [];
for (var n = 0; n < database[0].length; ++n) {
if (matches[n]) {
result.push(n - 1);
}
}
return result;
}
// Database functions
exports.DAVERAGE = function(database, field, criteria) {
// Return error if field is not a number and not a string
if (isNaN(field) && (typeof field !== "string")) {
return error.value;
}
var resultIndexes = findResultIndex(database, criteria);
var targetFields = [];
if (typeof field === "string") {
var index = exports.FINDFIELD(database, field);
targetFields = utils.rest(database[index]);
} else {
targetFields = utils.rest(database[field]);
}
var sum = 0;
for (var i = 0; i < resultIndexes.length; i++) {
sum += targetFields[resultIndexes[i]];
}
return resultIndexes.length === 0 ? error.div0 : sum / resultIndexes.length;
};
exports.DCOUNT = function(database, field, criteria) {
};
exports.DCOUNTA = function(database, field, criteria) {
};
exports.DGET = function(database, field, criteria) {
// Return error if field is not a number and not a string
if (isNaN(field) && (typeof field !== "string")) {
return error.value;
}
var resultIndexes = findResultIndex(database, criteria);
var targetFields = [];
if (typeof field === "string") {
var index = exports.FINDFIELD(database, field);
targetFields = utils.rest(database[index]);
} else {
targetFields = utils.rest(database[field]);
}
// Return error if no record meets the criteria
if (resultIndexes.length === 0) {
return error.value;
}
// Returns the #NUM! error value because more than one record meets the
// criteria
if (resultIndexes.length > 1) {
return error.num;
}
return targetFields[resultIndexes[0]];
};
exports.DMAX = function(database, field, criteria) {
// Return error if field is not a number and not a string
if (isNaN(field) && (typeof field !== "string")) {
return error.value;
}
var resultIndexes = findResultIndex(database, criteria);
var targetFields = [];
if (typeof field === "string") {
var index = exports.FINDFIELD(database, field);
targetFields = utils.rest(database[index]);
} else {
targetFields = utils.rest(database[field]);
}
var maxValue = targetFields[resultIndexes[0]];
for (var i = 1; i < resultIndexes.length; i++) {
if (maxValue < targetFields[resultIndexes[i]]) {
maxValue = targetFields[resultIndexes[i]];
}
}
return maxValue;
};
exports.DMIN = function(database, field, criteria) {
// Return error if field is not a number and not a string
if (isNaN(field) && (typeof field !== "string")) {
return error.value;
}
var resultIndexes = findResultIndex(database, criteria);
var targetFields = [];
if (typeof field === "string") {
var index = exports.FINDFIELD(database, field);
targetFields = utils.rest(database[index]);
} else {
targetFields = utils.rest(database[field]);
}
var minValue = targetFields[resultIndexes[0]];
for (var i = 1; i < resultIndexes.length; i++) {
if (minValue > targetFields[resultIndexes[i]]) {
minValue = targetFields[resultIndexes[i]];
}
}
return minValue;
};
exports.DPRODUCT = function(database, field, criteria) {
// Return error if field is not a number and not a string
if (isNaN(field) && (typeof field !== "string")) {
return error.value;
}
var resultIndexes = findResultIndex(database, criteria);
var targetFields = [];
if (typeof field === "string") {
var index = exports.FINDFIELD(database, field);
targetFields = utils.rest(database[index]);
} else {
targetFields = utils.rest(database[field]);
}
var targetValues = [];
for (var i = 0; i < resultIndexes.length; i++) {
targetValues[i] = targetFields[resultIndexes[i]];
}
targetValues = compact(targetValues);
var result = 1;
for (i = 0; i < targetValues.length; i++) {
result *= targetValues[i];
}
return result;
};
exports.DSTDEV = function(database, field, criteria) {
};
exports.DSTDEVP = function(database, field, criteria) {
};
exports.DSUM = function(database, field, criteria) {
};
exports.DVAR = function(database, field, criteria) {
};
exports.DVARP = function(database, field, criteria) {
};
exports.MATCH = function(lookupValue, lookupArray, matchType) {
if (!lookupValue && !lookupArray) {
return error.na;
}
if (arguments.length === 2) {
matchType = 1;
}
if (!(lookupArray instanceof Array)) {
return error.na;
}
if (matchType !== -1 && matchType !== 0 && matchType !== 1) {
return error.na;
}
var index;
var indexValue;
for (var idx = 0; idx < lookupArray.length; idx++) {
if (matchType === 1) {
if (lookupArray[idx] === lookupValue) {
return idx + 1;
} else if (lookupArray[idx] < lookupValue) {
if (!indexValue) {
index = idx + 1;
indexValue = lookupArray[idx];
} else if (lookupArray[idx] > indexValue) {
index = idx + 1;
indexValue = lookupArray[idx];
}
}
} else if (matchType === 0) {
if (typeof lookupValue === 'string') {
lookupValue = lookupValue.replace(/\?/g, '.');
if (lookupArray[idx].toLowerCase().match(lookupValue.toLowerCase())) {
return idx + 1;
}
} else {
if (lookupArray[idx] === lookupValue) {
return idx + 1;
}
}
} else if (matchType === -1) {
if (lookupArray[idx] === lookupValue) {
return idx + 1;
} else if (lookupArray[idx] > lookupValue) {
if (!indexValue) {
index = idx + 1;
indexValue = lookupArray[idx];
} else if (lookupArray[idx] < indexValue) {
index = idx + 1;
indexValue = lookupArray[idx];
}
}
}
}
return index ? index : error.na;
};
return exports;
})();
met.engineering = (function() {
var exports = {};
function isValidBinaryNumber(number) {
return (/^[01]{1,10}$/).test(number);
}
exports.BESSELI = function(x, n) {
};
exports.BESSELJ = function(x, n) {
};
exports.BESSELK = function(x, n) {
};
exports.BESSELY = function(x, n) {
};
exports.BIN2DEC = function(number) {
// Return error if number is not binary or contains more than 10
// characters (10 digits)
if (!isValidBinaryNumber(number)) {
return error.num;
}
// Convert binary number to decimal
var result = parseInt(number, 2);
// Handle negative numbers
var stringified = number.toString();
if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
return parseInt(stringified.substring(1), 2) - 512;
} else {
return result;
}
};
exports.BIN2HEX = function(number, places) {
// Return error if number is not binary or contains more than 10
// characters (10 digits)
if (!isValidBinaryNumber(number)) {
return error.num;
}
// Ignore places and return a 10-character hexadecimal number if number
// is negative
var stringified = number.toString();
if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
return (1099511627264 + parseInt(stringified.substring(1), 2)).toString(16);
}
// Convert binary number to hexadecimal
var result = parseInt(number, 2).toString(16);
// Return hexadecimal number using the minimum number of characters
// necessary if places is undefined
if (places === undefined) {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.BIN2OCT = function(number, places) {
// Return error if number is not binary or contains more than 10
// characters (10 digits)
if (!isValidBinaryNumber(number)) {
return error.num;
}
// Ignore places and return a 10-character octal number if number is
// negative
var stringified = number.toString();
if (stringified.length === 10 && stringified.substring(0, 1) === '1') {
return (1073741312 + parseInt(stringified.substring(1), 2)).toString(8);
}
// Convert binary number to octal
var result = parseInt(number, 2).toString(8);
// Return octal number using the minimum number of characters necessary
// if places is undefined
if (places === undefined) {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.BITAND = function(number1, number2) {
// Return error if either number is a non-numeric value
number1 = utils.parseNumber(number1);
number2 = utils.parseNumber(number2);
if (utils.anyIsError(number1, number2)) {
return error.value;
}
// Return error if either number is less than 0
if (number1 < 0 || number2 < 0) {
return error.num;
}
// Return error if either number is a non-integer
if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
return error.num;
}
// Return error if either number is greater than (2^48)-1
if (number1 > 281474976710655 || number2 > 281474976710655) {
return error.num;
}
// Return bitwise AND of two numbers
return number1 & number2;
};
exports.BITLSHIFT = function(number, shift) {
number = utils.parseNumber(number);
shift = utils.parseNumber(shift);
if (utils.anyIsError(number, shift)) {
return error.value;
}
// Return error if number is less than 0
if (number < 0) {
return error.num;
}
// Return error if number is a non-integer
if (Math.floor(number) !== number) {
return error.num;
}
// Return error if number is greater than (2^48)-1
if (number > 281474976710655) {
return error.num;
}
// Return error if the absolute value of shift is greater than 53
if (Math.abs(shift) > 53) {
return error.num;
}
// Return number shifted by shift bits to the left or to the right if
// shift is negative
return (shift >= 0) ? number << shift : number >> -shift;
};
exports.BITOR = function(number1, number2) {
number1 = utils.parseNumber(number1);
number2 = utils.parseNumber(number2);
if (utils.anyIsError(number1, number2)) {
return error.value;
}
// Return error if either number is less than 0
if (number1 < 0 || number2 < 0) {
return error.num;
}
// Return error if either number is a non-integer
if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
return error.num;
}
// Return error if either number is greater than (2^48)-1
if (number1 > 281474976710655 || number2 > 281474976710655) {
return error.num;
}
// Return bitwise OR of two numbers
return number1 | number2;
};
exports.BITRSHIFT = function(number, shift) {
number = utils.parseNumber(number);
shift = utils.parseNumber(shift);
if (utils.anyIsError(number, shift)) {
return error.value;
}
// Return error if number is less than 0
if (number < 0) {
return error.num;
}
// Return error if number is a non-integer
if (Math.floor(number) !== number) {
return error.num;
}
// Return error if number is greater than (2^48)-1
if (number > 281474976710655) {
return error.num;
}
// Return error if the absolute value of shift is greater than 53
if (Math.abs(shift) > 53) {
return error.num;
}
// Return number shifted by shift bits to the right or to the left if
// shift is negative
return (shift >= 0) ? number >> shift : number << -shift;
};
exports.BITXOR = function(number1, number2) {
number1 = utils.parseNumber(number1);
number2 = utils.parseNumber(number2);
if (utils.anyIsError(number1, number2)) {
return error.value;
}
// Return error if either number is less than 0
if (number1 < 0 || number2 < 0) {
return error.num;
}
// Return error if either number is a non-integer
if (Math.floor(number1) !== number1 || Math.floor(number2) !== number2) {
return error.num;
}
// Return error if either number is greater than (2^48)-1
if (number1 > 281474976710655 || number2 > 281474976710655) {
return error.num;
}
// Return bitwise XOR of two numbers
return number1 ^ number2;
};
exports.COMPLEX = function(real, imaginary, suffix) {
real = utils.parseNumber(real);
imaginary = utils.parseNumber(imaginary);
if (utils.anyIsError(real, imaginary)) {
return real;
}
// Set suffix
suffix = (suffix === undefined) ? 'i' : suffix;
// Return error if suffix is neither "i" nor "j"
if (suffix !== 'i' && suffix !== 'j') {
return error.value;
}
// Return complex number
if (real === 0 && imaginary === 0) {
return 0;
} else if (real === 0) {
return (imaginary === 1) ? suffix : imaginary.toString() + suffix;
} else if (imaginary === 0) {
return real.toString();
} else {
var sign = (imaginary > 0) ? '+' : '';
return real.toString() + sign + ((imaginary === 1) ? suffix : imaginary.toString() + suffix);
}
};
exports.CONVERT = function(number, from_unit, to_unit) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
// List of units supported by CONVERT and units defined by the
// International System of Units
// [Name, Symbol, Alternate symbols, Quantity, ISU, CONVERT, Conversion
// ratio]
var units = [
["a.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
["a.u. of charge", "e", null, "electric_charge", false, false, 1.60217653141414e-19],
["a.u. of energy", "Eh", null, "energy", false, false, 4.35974417757576e-18],
["a.u. of length", "a?", null, "length", false, false, 5.29177210818182e-11],
["a.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
["a.u. of time", "?/Eh", null, "time", false, false, 2.41888432650516e-17],
["admiralty knot", "admkn", null, "speed", false, true, 0.514773333],
["ampere", "A", null, "electric_current", true, false, 1],
["ampere per meter", "A/m", null, "magnetic_field_intensity", true, false, 1],
["ångström", "Å", ["ang"], "length", false, true, 1e-10],
["are", "ar", null, "area", false, true, 100],
["astronomical unit", "ua", null, "length", false, false, 1.49597870691667e-11],
["bar", "bar", null, "pressure", false, false, 100000],
["barn", "b", null, "area", false, false, 1e-28],
["becquerel", "Bq", null, "radioactivity", true, false, 1],
["bit", "bit", ["b"], "information", false, true, 1],
["btu", "BTU", ["btu"], "energy", false, true, 1055.05585262],
["byte", "byte", null, "information", false, true, 8],
["candela", "cd", null, "luminous_intensity", true, false, 1],
["candela per square metre", "cd/m?", null, "luminance", true, false, 1],
["coulomb", "C", null, "electric_charge", true, false, 1],
["cubic ångström", "ang3", ["ang^3"], "volume", false, true, 1e-30],
["cubic foot", "ft3", ["ft^3"], "volume", false, true, 0.028316846592],
["cubic inch", "in3", ["in^3"], "volume", false, true, 0.000016387064],
["cubic light-year", "ly3", ["ly^3"], "volume", false, true, 8.46786664623715e-47],
["cubic metre", "m?", null, "volume", true, true, 1],
["cubic mile", "mi3", ["mi^3"], "volume", false, true, 4168181825.44058],
["cubic nautical mile", "Nmi3", ["Nmi^3"], "volume", false, true, 6352182208],
["cubic Pica", "Pica3", ["Picapt3", "Pica^3", "Picapt^3"], "volume", false, true, 7.58660370370369e-8],
["cubic yard", "yd3", ["yd^3"], "volume", false, true, 0.764554857984],
["cup", "cup", null, "volume", false, true, 0.0002365882365],
["dalton", "Da", ["u"], "mass", false, false, 1.66053886282828e-27],
["day", "d", ["day"], "time", false, true, 86400],
["degree", "°", null, "angle", false, false, 0.0174532925199433],
["degrees Rankine", "Rank", null, "temperature", false, true, 0.555555555555556],
["dyne", "dyn", ["dy"], "force", false, true, 0.00001],
["electronvolt", "eV", ["ev"], "energy", false, true, 1.60217656514141],
["ell", "ell", null, "length", false, true, 1.143],
["erg", "erg", ["e"], "energy", false, true, 1e-7],
["farad", "F", null, "electric_capacitance", true, false, 1],
["fluid ounce", "oz", null, "volume", false, true, 0.0000295735295625],
["foot", "ft", null, "length", false, true, 0.3048],
["foot-pound", "flb", null, "energy", false, true, 1.3558179483314],
["gal", "Gal", null, "acceleration", false, false, 0.01],
["gallon", "gal", null, "volume", false, true, 0.003785411784],
["gauss", "G", ["ga"], "magnetic_flux_density", false, true, 1],
["grain", "grain", null, "mass", false, true, 0.0000647989],
["gram", "g", null, "mass", false, true, 0.001],
["gray", "Gy", null, "absorbed_dose", true, false, 1],
["gross registered ton", "GRT", ["regton"], "volume", false, true, 2.8316846592],
["hectare", "ha", null, "area", false, true, 10000],
["henry", "H", null, "inductance", true, false, 1],
["hertz", "Hz", null, "frequency", true, false, 1],
["horsepower", "HP", ["h"], "power", false, true, 745.69987158227],
["horsepower-hour", "HPh", ["hh", "hph"], "energy", false, true, 2684519.538],
["hour", "h", ["hr"], "time", false, true, 3600],
["imperial gallon (U.K.)", "uk_gal", null, "volume", false, true, 0.00454609],
["imperial hundredweight", "lcwt", ["uk_cwt", "hweight"], "mass", false, true, 50.802345],
["imperial quart (U.K)", "uk_qt", null, "volume", false, true, 0.0011365225],
["imperial ton", "brton", ["uk_ton", "LTON"], "mass", false, true, 1016.046909],
["inch", "in", null, "length", false, true, 0.0254],
["international acre", "uk_acre", null, "area", false, true, 4046.8564224],
["IT calorie", "cal", null, "energy", false, true, 4.1868],
["joule", "J", null, "energy", true, true, 1],
["katal", "kat", null, "catalytic_activity", true, false, 1],
["kelvin", "K", ["kel"], "temperature", true, true, 1],
["kilogram", "kg", null, "mass", true, true, 1],
["knot", "kn", null, "speed", false, true, 0.514444444444444],
["light-year", "ly", null, "length", false, true, 9460730472580800],
["litre", "L", ["l", "lt"], "volume", false, true, 0.001],
["lumen", "lm", null, "luminous_flux", true, false, 1],
["lux", "lx", null, "illuminance", true, false, 1],
["maxwell", "Mx", null, "magnetic_flux", false, false, 1e-18],
["measurement ton", "MTON", null, "volume", false, true, 1.13267386368],
["meter per hour", "m/h", ["m/hr"], "speed", false, true, 0.00027777777777778],
["meter per second", "m/s", ["m/sec"], "speed", true, true, 1],
["meter per second squared", "m?s??", null, "acceleration", true, false, 1],
["parsec", "pc", ["parsec"], "length", false, true, 30856775814671900],
["meter squared per second", "m?/s", null, "kinematic_viscosity", true, false, 1],
["metre", "m", null, "length", true, true, 1],
["miles per hour", "mph", null, "speed", false, true, 0.44704],
["millimetre of mercury", "mmHg", null, "pressure", false, false, 133.322],
["minute", "?", null, "angle", false, false, 0.000290888208665722],
["minute", "min", ["mn"], "time", false, true, 60],
["modern teaspoon", "tspm", null, "volume", false, true, 0.000005],
["mole", "mol", null, "amount_of_substance", true, false, 1],
["morgen", "Morgen", null, "area", false, true, 2500],
["n.u. of action", "?", null, "action", false, false, 1.05457168181818e-34],
["n.u. of mass", "m?", null, "mass", false, false, 9.10938261616162e-31],
["n.u. of speed", "c?", null, "speed", false, false, 299792458],
["n.u. of time", "?/(me?c??)", null, "time", false, false, 1.28808866778687e-21],
["nautical mile", "M", ["Nmi"], "length", false, true, 1852],
["newton", "N", null, "force", true, true, 1],
["œrsted", "Oe ", null, "magnetic_field_intensity", false, false, 79.5774715459477],
["ohm", "Ω", null, "electric_resistance", true, false, 1],
["ounce mass", "ozm", null, "mass", false, true, 0.028349523125],
["pascal", "Pa", null, "pressure", true, false, 1],
["pascal second", "Pa?s", null, "dynamic_viscosity", true, false, 1],
["pferdestärke", "PS", null, "power", false, true, 735.49875],
["phot", "ph", null, "illuminance", false, false, 0.0001],
["pica (1/6 inch)", "pica", null, "length", false, true, 0.00035277777777778],
["pica (1/72 inch)", "Pica", ["Picapt"], "length", false, true, 0.00423333333333333],
["poise", "P", null, "dynamic_viscosity", false, false, 0.1],
["pond", "pond", null, "force", false, true, 0.00980665],
["pound force", "lbf", null, "force", false, true, 4.4482216152605],
["pound mass", "lbm", null, "mass", false, true, 0.45359237],
["quart", "qt", null, "volume", false, true, 0.000946352946],
["radian", "rad", null, "angle", true, false, 1],
["second", "?", null, "angle", false, false, 0.00000484813681109536],
["second", "s", ["sec"], "time", true, true, 1],
["short hundredweight", "cwt", ["shweight"], "mass", false, true, 45.359237],
["siemens", "S", null, "electrical_conductance", true, false, 1],
["sievert", "Sv", null, "equivalent_dose", true, false, 1],
["slug", "sg", null, "mass", false, true, 14.59390294],
["square ångström", "ang2", ["ang^2"], "area", false, true, 1e-20],
["square foot", "ft2", ["ft^2"], "area", false, true, 0.09290304],
["square inch", "in2", ["in^2"], "area", false, true, 0.00064516],
["square light-year", "ly2", ["ly^2"], "area", false, true, 8.95054210748189e+31],
["square meter", "m?", null, "area", true, true, 1],
["square mile", "mi2", ["mi^2"], "area", false, true, 2589988.110336],
["square nautical mile", "Nmi2", ["Nmi^2"], "area", false, true, 3429904],
["square Pica", "Pica2", ["Picapt2", "Pica^2", "Picapt^2"], "area", false, true, 0.00001792111111111],
["square yard", "yd2", ["yd^2"], "area", false, true, 0.83612736],
["statute mile", "mi", null, "length", false, true, 1609.344],
["steradian", "sr", null, "solid_angle", true, false, 1],
["stilb", "sb", null, "luminance", false, false, 0.0001],
["stokes", "St", null, "kinematic_viscosity", false, false, 0.0001],
["stone", "stone", null, "mass", false, true, 6.35029318],
["tablespoon", "tbs", null, "volume", false, true, 0.0000147868],
["teaspoon", "tsp", null, "volume", false, true, 0.00000492892],
["tesla", "T", null, "magnetic_flux_density", true, true, 1],
["thermodynamic calorie", "c", null, "energy", false, true, 4.184],
["ton", "ton", null, "mass", false, true, 907.18474],
["tonne", "t", null, "mass", false, false, 1000],
["U.K. pint", "uk_pt", null, "volume", false, true, 0.00056826125],
["U.S. bushel", "bushel", null, "volume", false, true, 0.03523907],
["U.S. oil barrel", "barrel", null, "volume", false, true, 0.158987295],
["U.S. pint", "pt", ["us_pt"], "volume", false, true, 0.000473176473],
["U.S. survey mile", "survey_mi", null, "length", false, true, 1609.347219],
["U.S. survey/statute acre", "us_acre", null, "area", false, true, 4046.87261],
["volt", "V", null, "voltage", true, false, 1],
["watt", "W", null, "power", true, true, 1],
["watt-hour", "Wh", ["wh"], "energy", false, true, 3600],
["weber", "Wb", null, "magnetic_flux", true, false, 1],
["yard", "yd", null, "length", false, true, 0.9144],
["year", "yr", null, "time", false, true, 31557600]
];
// Binary prefixes
// [Name, Prefix power of 2 value, Previx value, Abbreviation, Derived
// from]
var binary_prefixes = {
Yi: ["yobi", 80, 1208925819614629174706176, "Yi", "yotta"],
Zi: ["zebi", 70, 1180591620717411303424, "Zi", "zetta"],
Ei: ["exbi", 60, 1152921504606846976, "Ei", "exa"],
Pi: ["pebi", 50, 1125899906842624, "Pi", "peta"],
Ti: ["tebi", 40, 1099511627776, "Ti", "tera"],
Gi: ["gibi", 30, 1073741824, "Gi", "giga"],
Mi: ["mebi", 20, 1048576, "Mi", "mega"],
ki: ["kibi", 10, 1024, "ki", "kilo"]
};
// Unit prefixes
// [Name, Multiplier, Abbreviation]
var unit_prefixes = {
Y: ["yotta", 1e+24, "Y"],
Z: ["zetta", 1e+21, "Z"],
E: ["exa", 1e+18, "E"],
P: ["peta", 1e+15, "P"],
T: ["tera", 1e+12, "T"],
G: ["giga", 1e+09, "G"],
M: ["mega", 1e+06, "M"],
k: ["kilo", 1e+03, "k"],
h: ["hecto", 1e+02, "h"],
e: ["dekao", 1e+01, "e"],
d: ["deci", 1e-01, "d"],
c: ["centi", 1e-02, "c"],
m: ["milli", 1e-03, "m"],
u: ["micro", 1e-06, "u"],
n: ["nano", 1e-09, "n"],
p: ["pico", 1e-12, "p"],
f: ["femto", 1e-15, "f"],
a: ["atto", 1e-18, "a"],
z: ["zepto", 1e-21, "z"],
y: ["yocto", 1e-24, "y"]
};
// Initialize units and multipliers
var from = null;
var to = null;
var base_from_unit = from_unit;
var base_to_unit = to_unit;
var from_multiplier = 1;
var to_multiplier = 1;
var alt;
// Lookup from and to units
for (var i = 0; i < units.length; i++) {
alt = (units[i][2] === null) ? [] : units[i][2];
if (units[i][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
from = units[i];
}
if (units[i][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
to = units[i];
}
}
// Lookup from prefix
if (from === null) {
var from_binary_prefix = binary_prefixes[from_unit.substring(0, 2)];
var from_unit_prefix = unit_prefixes[from_unit.substring(0, 1)];
// Handle dekao unit prefix (only unit prefix with two characters)
if (from_unit.substring(0, 2) === 'da') {
from_unit_prefix = ["dekao", 1e+01, "da"];
}
// Handle binary prefixes first (so that 'Yi' is processed before
// 'Y')
if (from_binary_prefix) {
from_multiplier = from_binary_prefix[2];
base_from_unit = from_unit.substring(2);
} else if (from_unit_prefix) {
from_multiplier = from_unit_prefix[1];
base_from_unit = from_unit.substring(from_unit_prefix[2].length);
}
// Lookup from unit
for (var j = 0; j < units.length; j++) {
alt = (units[j][2] === null) ? [] : units[j][2];
if (units[j][1] === base_from_unit || alt.indexOf(base_from_unit) >= 0) {
from = units[j];
}
}
}
// Lookup to prefix
if (to === null) {
var to_binary_prefix = binary_prefixes[to_unit.substring(0, 2)];
var to_unit_prefix = unit_prefixes[to_unit.substring(0, 1)];
// Handle dekao unit prefix (only unit prefix with two characters)
if (to_unit.substring(0, 2) === 'da') {
to_unit_prefix = ["dekao", 1e+01, "da"];
}
// Handle binary prefixes first (so that 'Yi' is processed before
// 'Y')
if (to_binary_prefix) {
to_multiplier = to_binary_prefix[2];
base_to_unit = to_unit.substring(2);
} else if (to_unit_prefix) {
to_multiplier = to_unit_prefix[1];
base_to_unit = to_unit.substring(to_unit_prefix[2].length);
}
// Lookup to unit
for (var k = 0; k < units.length; k++) {
alt = (units[k][2] === null) ? [] : units[k][2];
if (units[k][1] === base_to_unit || alt.indexOf(base_to_unit) >= 0) {
to = units[k];
}
}
}
// Return error if a unit does not exist
if (from === null || to === null) {
return error.na;
}
// Return error if units represent different quantities
if (from[3] !== to[3]) {
return error.na;
}
// Return converted number
return number * from[6] * from_multiplier / (to[6] * to_multiplier);
};
exports.DEC2BIN = function(number, places) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
// Return error if number is not decimal, is lower than -512, or is
// greater than 511
if (!/^-?[0-9]{1,3}$/.test(number) || number < -512 || number > 511) {
return error.num;
}
// Ignore places and return a 10-character binary number if number is
// negative
if (number < 0) {
return '1' + REPT('0', 9 - (512 + number).toString(2).length) + (512 + number).toString(2);
}
// Convert decimal number to binary
var result = parseInt(number, 10).toString(2);
// Return binary number using the minimum number of characters necessary
// if places is undefined
if (typeof places === 'undefined') {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.DEC2HEX = function(number, places) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
// Return error if number is not decimal, is lower than -549755813888,
// or is greater than 549755813887
if (!/^-?[0-9]{1,12}$/.test(number) || number < -549755813888 || number > 549755813887) {
return error.num;
}
// Ignore places and return a 10-character hexadecimal number if number
// is negative
if (number < 0) {
return (1099511627776 + number).toString(16);
}
// Convert decimal number to hexadecimal
var result = parseInt(number, 10).toString(16);
// Return hexadecimal number using the minimum number of characters
// necessary if places is undefined
if (typeof places === 'undefined') {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.DEC2OCT = function(number, places) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
// Return error if number is not decimal, is lower than -549755813888,
// or is greater than 549755813887
if (!/^-?[0-9]{1,9}$/.test(number) || number < -536870912 || number > 536870911) {
return error.num;
}
// Ignore places and return a 10-character octal number if number is
// negative
if (number < 0) {
return (1073741824 + number).toString(8);
}
// Convert decimal number to octal
var result = parseInt(number, 10).toString(8);
// Return octal number using the minimum number of characters necessary
// if places is undefined
if (typeof places === 'undefined') {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.DELTA = function(number1, number2) {
// Set number2 to zero if undefined
number2 = (number2 === undefined) ? 0 : number2;
number1 = utils.parseNumber(number1);
number2 = utils.parseNumber(number2);
if (utils.anyIsError(number1, number2)) {
return error.value;
}
// Return delta
return (number1 === number2) ? 1 : 0;
};
exports.ERF = function(lower_bound, upper_bound) {
};
exports.ERF.PRECISE = function() {
};
exports.ERFC = function(x) {
};
exports.ERFC.PRECISE = function() {
};
exports.GESTEP = function(number, step) {
step = step || 0;
number = utils.parseNumber(number);
if (utils.anyIsError(step, number)) {
return number;
}
// Return delta
return (number >= step) ? 1 : 0;
};
exports.HEX2BIN = function(number, places) {
// Return error if number is not hexadecimal or contains more than ten
// characters (10 digits)
if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
return error.num;
}
// Check if number is negative
var negative = (number.length === 10 && number.substring(0, 1).toLowerCase() === 'f') ? true : false;
// Convert hexadecimal number to decimal
var decimal = (negative) ? parseInt(number, 16) - 1099511627776 : parseInt(number, 16);
// Return error if number is lower than -512 or greater than 511
if (decimal < -512 || decimal > 511) {
return error.num;
}
// Ignore places and return a 10-character binary number if number is
// negative
if (negative) {
return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
}
// Convert decimal number to binary
var result = decimal.toString(2);
// Return binary number using the minimum number of characters necessary
// if places is undefined
if (places === undefined) {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.HEX2DEC = function(number) {
// Return error if number is not hexadecimal or contains more than ten
// characters (10 digits)
if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
return error.num;
}
// Convert hexadecimal number to decimal
var decimal = parseInt(number, 16);
// Return decimal number
return (decimal >= 549755813888) ? decimal - 1099511627776 : decimal;
};
exports.HEX2OCT = function(number, places) {
// Return error if number is not hexadecimal or contains more than ten
// characters (10 digits)
if (!/^[0-9A-Fa-f]{1,10}$/.test(number)) {
return error.num;
}
// Convert hexadecimal number to decimal
var decimal = parseInt(number, 16);
// Return error if number is positive and greater than 0x1fffffff
// (536870911)
if (decimal > 536870911 && decimal < 1098974756864) {
return error.num;
}
// Ignore places and return a 10-character octal number if number is
// negative
if (decimal >= 1098974756864) {
return (decimal - 1098437885952).toString(8);
}
// Convert decimal number to octal
var result = decimal.toString(8);
// Return octal number using the minimum number of characters necessary
// if places is undefined
if (places === undefined) {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.IMABS = function(inumber) {
// Lookup real and imaginary coefficients using exports.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
// Return error if either coefficient is not a number
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return absolute value of complex number
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
};
exports.IMAGINARY = function(inumber) {
if (inumber === undefined || inumber === true || inumber === false) {
return error.value;
}
// Return 0 if inumber is equal to 0
if (inumber === 0 || inumber === '0') {
return 0;
}
// Handle special cases
if (['i', 'j'].indexOf(inumber) >= 0) {
return 1;
}
// Normalize imaginary coefficient
inumber = inumber.replace('+i', '+1i').replace('-i', '-1i').replace('+j', '+1j').replace('-j', '-1j');
// Lookup sign
var plus = inumber.indexOf('+');
var minus = inumber.indexOf('-');
if (plus === 0) {
plus = inumber.indexOf('+', 1);
}
if (minus === 0) {
minus = inumber.indexOf('-', 1);
}
// Lookup imaginary unit
var last = inumber.substring(inumber.length - 1, inumber.length);
var unit = (last === 'i' || last === 'j');
if (plus >= 0 || minus >= 0) {
// Return error if imaginary unit is neither i nor j
if (!unit) {
return error.num;
}
// Return imaginary coefficient of complex number
if (plus >= 0) {
return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
error.num :
Number(inumber.substring(plus + 1, inumber.length - 1));
} else {
return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
error.num :
-Number(inumber.substring(minus + 1, inumber.length - 1));
}
} else {
if (unit) {
return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : inumber.substring(0, inumber.length - 1);
} else {
return (isNaN(inumber)) ? error.num : 0;
}
}
};
exports.IMARGUMENT = function(inumber) {
// Lookup real and imaginary coefficients using exports.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
// Return error if either coefficient is not a number
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return error if inumber is equal to zero
if (x === 0 && y === 0) {
return error.div0;
}
// Return PI/2 if x is equal to zero and y is positive
if (x === 0 && y > 0) {
return Math.PI / 2;
}
// Return -PI/2 if x is equal to zero and y is negative
if (x === 0 && y < 0) {
return -Math.PI / 2;
}
// Return zero if x is negative and y is equal to zero
if (y === 0 && x > 0) {
return 0;
}
// Return zero if x is negative and y is equal to zero
if (y === 0 && x < 0) {
return -Math.PI;
}
// Return argument of complex number
if (x > 0) {
return Math.atan(y / x);
} else if (x < 0 && y >= 0) {
return Math.atan(y / x) + Math.PI;
} else {
return Math.atan(y / x) - Math.PI;
}
};
exports.IMCONJUGATE = function(inumber) {
// Lookup real and imaginary coefficients using exports.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return conjugate of complex number
return (y !== 0) ? exports.COMPLEX(x, -y, unit) : inumber;
};
exports.IMCOS = function(inumber) {
// Lookup real and imaginary coefficients using exports.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return cosine of complex number
return exports.COMPLEX(Math.cos(x) * (Math.exp(y) + Math.exp(-y)) / 2, -Math.sin(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
};
exports.IMCOSH = function(inumber) {
// Lookup real and imaginary coefficients using exports.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return hyperbolic cosine of complex number
return exports.COMPLEX(Math.cos(y) * (Math.exp(x) + Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) - Math.exp(-x)) / 2, unit);
};
exports.IMCOT = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return cotangent of complex number
return exports.IMDIV(exports.IMCOS(inumber), exports.IMSIN(inumber));
};
exports.IMDIV = function(inumber1, inumber2) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var a = exports.IMREAL(inumber1);
var b = exports.IMAGINARY(inumber1);
var c = exports.IMREAL(inumber2);
var d = exports.IMAGINARY(inumber2);
if (utils.anyIsError(a, b, c, d)) {
return error.value;
}
// Lookup imaginary unit
var unit1 = inumber1.substring(inumber1.length - 1);
var unit2 = inumber2.substring(inumber2.length - 1);
var unit = 'i';
if (unit1 === 'j') {
unit = 'j';
} else if (unit2 === 'j') {
unit = 'j';
}
// Return error if inumber2 is null
if (c === 0 && d === 0) {
return error.num;
}
// Return exponential of complex number
var den = c * c + d * d;
return exports.COMPLEX((a * c + b * d) / den, (b * c - a * d) / den, unit);
};
exports.IMEXP = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return exponential of complex number
var e = Math.exp(x);
return exports.COMPLEX(e * Math.cos(y), e * Math.sin(y), unit);
};
exports.IMLN = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return exponential of complex number
return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)), Math.atan(y / x), unit);
};
exports.IMLOG10 = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return exponential of complex number
return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(10), Math.atan(y / x) / Math.log(10), unit);
};
exports.IMLOG2 = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return exponential of complex number
return exports.COMPLEX(Math.log(Math.sqrt(x * x + y * y)) / Math.log(2), Math.atan(y / x) / Math.log(2), unit);
};
exports.IMPOWER = function(inumber, number) {
number = utils.parseNumber(number);
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(number, x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Calculate power of modulus
var p = Math.pow(exports.IMABS(inumber), number);
// Calculate argument
var t = exports.IMARGUMENT(inumber);
// Return exponential of complex number
return exports.COMPLEX(p * Math.cos(number * t), p * Math.sin(number * t), unit);
};
exports.IMPRODUCT = function() {
// Initialize result
var result = arguments[0];
// Loop on all numbers
for (var i = 1; i < arguments.length; i++) {
// Lookup coefficients of two complex numbers
var a = exports.IMREAL(result);
var b = exports.IMAGINARY(result);
var c = exports.IMREAL(arguments[i]);
var d = exports.IMAGINARY(arguments[i]);
if (utils.anyIsError(a, b, c, d)) {
return error.value;
}
// Complute product of two complex numbers
result = exports.COMPLEX(a * c - b * d, a * d + b * c);
}
// Return product of complex numbers
return result;
};
exports.IMREAL = function(inumber) {
if (inumber === undefined || inumber === true || inumber === false) {
return error.value;
}
// Return 0 if inumber is equal to 0
if (inumber === 0 || inumber === '0') {
return 0;
}
// Handle special cases
if (['i', '+i', '1i', '+1i', '-i', '-1i', 'j', '+j', '1j', '+1j', '-j', '-1j'].indexOf(inumber) >= 0) {
return 0;
}
// Lookup sign
var plus = inumber.indexOf('+');
var minus = inumber.indexOf('-');
if (plus === 0) {
plus = inumber.indexOf('+', 1);
}
if (minus === 0) {
minus = inumber.indexOf('-', 1);
}
// Lookup imaginary unit
var last = inumber.substring(inumber.length - 1, inumber.length);
var unit = (last === 'i' || last === 'j');
if (plus >= 0 || minus >= 0) {
// Return error if imaginary unit is neither i nor j
if (!unit) {
return error.num;
}
// Return real coefficient of complex number
if (plus >= 0) {
return (isNaN(inumber.substring(0, plus)) || isNaN(inumber.substring(plus + 1, inumber.length - 1))) ?
error.num :
Number(inumber.substring(0, plus));
} else {
return (isNaN(inumber.substring(0, minus)) || isNaN(inumber.substring(minus + 1, inumber.length - 1))) ?
error.num :
Number(inumber.substring(0, minus));
}
} else {
if (unit) {
return (isNaN(inumber.substring(0, inumber.length - 1))) ? error.num : 0;
} else {
return (isNaN(inumber)) ? error.num : inumber;
}
}
};
exports.IMSEC = function(inumber) {
// Return error if inumber is a logical value
if (inumber === true || inumber === false) {
return error.value;
}
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return secant of complex number
return exports.IMDIV('1', exports.IMCOS(inumber));
};
exports.IMSECH = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return hyperbolic secant of complex number
return exports.IMDIV('1', exports.IMCOSH(inumber));
};
exports.IMSIN = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return sine of complex number
return exports.COMPLEX(Math.sin(x) * (Math.exp(y) + Math.exp(-y)) / 2, Math.cos(x) * (Math.exp(y) - Math.exp(-y)) / 2, unit);
};
exports.IMSINH = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Return hyperbolic sine of complex number
return exports.COMPLEX(Math.cos(y) * (Math.exp(x) - Math.exp(-x)) / 2, Math.sin(y) * (Math.exp(x) + Math.exp(-x)) / 2, unit);
};
exports.IMSQRT = function(inumber) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Lookup imaginary unit
var unit = inumber.substring(inumber.length - 1);
unit = (unit === 'i' || unit === 'j') ? unit : 'i';
// Calculate power of modulus
var s = Math.sqrt(exports.IMABS(inumber));
// Calculate argument
var t = exports.IMARGUMENT(inumber);
// Return exponential of complex number
return exports.COMPLEX(s * Math.cos(t / 2), s * Math.sin(t / 2), unit);
};
exports.IMCSC = function (inumber) {
// Return error if inumber is a logical value
if (inumber === true || inumber === false) {
return error.value;
}
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
// Return error if either coefficient is not a number
if (utils.anyIsError(x, y)) {
return error.num;
}
// Return cosecant of complex number
return exports.IMDIV('1', exports.IMSIN(inumber));
};
exports.IMCSCH = function (inumber) {
// Return error if inumber is a logical value
if (inumber === true || inumber === false) {
return error.value;
}
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
// Return error if either coefficient is not a number
if (utils.anyIsError(x, y)) {
return error.num;
}
// Return hyperbolic cosecant of complex number
return exports.IMDIV('1', exports.IMSINH(inumber));
};
exports.IMSUB = function(inumber1, inumber2) {
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var a = this.IMREAL(inumber1);
var b = this.IMAGINARY(inumber1);
var c = this.IMREAL(inumber2);
var d = this.IMAGINARY(inumber2);
if (utils.anyIsError(a, b, c, d)) {
return error.value;
}
// Lookup imaginary unit
var unit1 = inumber1.substring(inumber1.length - 1);
var unit2 = inumber2.substring(inumber2.length - 1);
var unit = 'i';
if (unit1 === 'j') {
unit = 'j';
} else if (unit2 === 'j') {
unit = 'j';
}
// Return _ of two complex numbers
return this.COMPLEX(a - c, b - d, unit);
};
exports.IMSUM = function() {
var args = utils.flatten(arguments);
// Initialize result
var result = args[0];
// Loop on all numbers
for (var i = 1; i < args.length; i++) {
// Lookup coefficients of two complex numbers
var a = this.IMREAL(result);
var b = this.IMAGINARY(result);
var c = this.IMREAL(args[i]);
var d = this.IMAGINARY(args[i]);
if (utils.anyIsError(a, b, c, d)) {
return error.value;
}
// Complute product of two complex numbers
result = this.COMPLEX(a + c, b + d);
}
// Return sum of complex numbers
return result;
};
exports.IMTAN = function(inumber) {
// Return error if inumber is a logical value
if (inumber === true || inumber === false) {
return error.value;
}
// Lookup real and imaginary coefficients using Formula.js
// [http://formulajs.org]
var x = exports.IMREAL(inumber);
var y = exports.IMAGINARY(inumber);
if (utils.anyIsError(x, y)) {
return error.value;
}
// Return tangent of complex number
return this.IMDIV(this.IMSIN(inumber), this.IMCOS(inumber));
};
exports.OCT2BIN = function(number, places) {
// Return error if number is not hexadecimal or contains more than ten
// characters (10 digits)
if (!/^[0-7]{1,10}$/.test(number)) {
return error.num;
}
// Check if number is negative
var negative = (number.length === 10 && number.substring(0, 1) === '7') ? true : false;
// Convert octal number to decimal
var decimal = (negative) ? parseInt(number, 8) - 1073741824 : parseInt(number, 8);
// Return error if number is lower than -512 or greater than 511
if (decimal < -512 || decimal > 511) {
return error.num;
}
// Ignore places and return a 10-character binary number if number is
// negative
if (negative) {
return '1' + REPT('0', 9 - (512 + decimal).toString(2).length) + (512 + decimal).toString(2);
}
// Convert decimal number to binary
var result = decimal.toString(2);
// Return binary number using the minimum number of characters necessary
// if places is undefined
if (typeof places === 'undefined') {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
exports.OCT2DEC = function(number) {
// Return error if number is not octal or contains more than ten
// characters (10 digits)
if (!/^[0-7]{1,10}$/.test(number)) {
return error.num;
}
// Convert octal number to decimal
var decimal = parseInt(number, 8);
// Return decimal number
return (decimal >= 536870912) ? decimal - 1073741824 : decimal;
};
exports.OCT2HEX = function(number, places) {
// Return error if number is not octal or contains more than ten
// characters (10 digits)
if (!/^[0-7]{1,10}$/.test(number)) {
return error.num;
}
// Convert octal number to decimal
var decimal = parseInt(number, 8);
// Ignore places and return a 10-character octal number if number is
// negative
if (decimal >= 536870912) {
return 'ff' + (decimal + 3221225472).toString(16);
}
// Convert decimal number to hexadecimal
var result = decimal.toString(16);
// Return hexadecimal number using the minimum number of characters
// necessary if places is undefined
if (places === undefined) {
return result;
} else {
// Return error if places is nonnumeric
if (isNaN(places)) {
return error.value;
}
// Return error if places is negative
if (places < 0) {
return error.num;
}
// Truncate places in case it is not an integer
places = Math.floor(places);
// Pad return value with leading 0s (zeros) if necessary (using
// Underscore.string)
return (places >= result.length) ? REPT('0', places - result.length) + result : error.num;
}
};
return exports;
})();
met.financial = (function() {
var exports = {};
function validDate(d) {
return d && d.getTime && !isNaN(d.getTime());
}
function ensureDate(d) {
return (d instanceof Date)?d:new Date(d);
}
exports.ACCRINT = function(issue, first, settlement, rate, par, frequency, basis) {
// Return error if either date is invalid
issue = ensureDate(issue);
first = ensureDate(first);
settlement = ensureDate(settlement);
if (!validDate(issue) || !validDate(first) || !validDate(settlement)) {
return '#VALUE!';
}
// Return error if either rate or par are lower than or equal to zero
if (rate <= 0 || par <= 0) {
return '#NUM!';
}
// Return error if frequency is neither 1, 2, or 4
if ([1, 2, 4].indexOf(frequency) === -1) {
return '#NUM!';
}
// Return error if basis is neither 0, 1, 2, 3, or 4
if ([0, 1, 2, 3, 4].indexOf(basis) === -1) {
return '#NUM!';
}
// Return error if settlement is before or equal to issue
if (settlement <= issue) {
return '#NUM!';
}
// Set default values
par = par || 0;
basis = basis || 0;
// Compute accrued interest
return par * rate * YEARFRAC(issue, settlement, basis);
};
exports.ACCRINTM = null;
exports.AMORDEGRC = null;
exports.AMORLINC = null;
exports.COUPDAYBS = null;
exports.COUPDAYS = null;
exports.COUPDAYSNC = null;
exports.COUPNCD = null;
exports.COUPNUM = null;
exports.COUPPCD = null;
exports.CUMIPMT = function(rate, periods, value, start, end, type) {
// Credits: algorithm inspired by Apache OpenOffice
// Credits: Hannes Stiebitzhofer for the translations of function and
// variable names
// Requires exports.FV() and exports.PMT() from exports.js
// [http://stoic.com/exports/]
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
value = utils.parseNumber(value);
if (utils.anyIsError(rate, periods, value)) {
return error.value;
}
// Return error if either rate, periods, or value are lower than or
// equal to zero
if (rate <= 0 || periods <= 0 || value <= 0) {
return error.num;
}
// Return error if start < 1, end < 1, or start > end
if (start < 1 || end < 1 || start > end) {
return error.num;
}
// Return error if type is neither 0 nor 1
if (type !== 0 && type !== 1) {
return error.num;
}
// Compute cumulative interest
var payment = exports.PMT(rate, periods, value, 0, type);
var interest = 0;
if (start === 1) {
if (type === 0) {
interest = -value;
start++;
}
}
for (var i = start; i <= end; i++) {
if (type === 1) {
interest += exports.FV(rate, i - 2, payment, value, 1) - payment;
} else {
interest += exports.FV(rate, i - 1, payment, value, 0);
}
}
interest *= rate;
// Return cumulative interest
return interest;
};
exports.CUMPRINC = function(rate, periods, value, start, end, type) {
// Credits: algorithm inspired by Apache OpenOffice
// Credits: Hannes Stiebitzhofer for the translations of function and
// variable names
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
value = utils.parseNumber(value);
if (utils.anyIsError(rate, periods, value)) {
return error.value;
}
// Return error if either rate, periods, or value are lower than or
// equal to zero
if (rate <= 0 || periods <= 0 || value <= 0) {
return error.num;
}
// Return error if start < 1, end < 1, or start > end
if (start < 1 || end < 1 || start > end) {
return error.num;
}
// Return error if type is neither 0 nor 1
if (type !== 0 && type !== 1) {
return error.num;
}
// Compute cumulative principal
var payment = exports.PMT(rate, periods, value, 0, type);
var principal = 0;
if (start === 1) {
if (type === 0) {
principal = payment + value * rate;
} else {
principal = payment;
}
start++;
}
for (var i = start; i <= end; i++) {
if (type > 0) {
principal += payment - (exports.FV(rate, i - 2, payment, value, 1) - payment) * rate;
} else {
principal += payment - exports.FV(rate, i - 1, payment, value, 0) * rate;
}
}
// Return cumulative principal
return principal;
};
exports.DB = function(cost, salvage, life, period, month) {
// Initialize month
month = (month === undefined) ? 12 : month;
cost = utils.parseNumber(cost);
salvage = utils.parseNumber(salvage);
life = utils.parseNumber(life);
period = utils.parseNumber(period);
month = utils.parseNumber(month);
if (utils.anyIsError(cost, salvage, life, period, month)) {
return error.value;
}
// Return error if any of the parameters is negative
if (cost < 0 || salvage < 0 || life < 0 || period < 0) {
return error.num;
}
// Return error if month is not an integer between 1 and 12
if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].indexOf(month) === -1) {
return error.num;
}
// Return error if period is greater than life
if (period > life) {
return error.num;
}
// Return 0 (zero) if salvage is greater than or equal to cost
if (salvage >= cost) {
return 0;
}
// Rate is rounded to three decimals places
var rate = (1 - Math.pow(salvage / cost, 1 / life)).toFixed(3);
// Compute initial depreciation
var initial = cost * rate * month / 12;
// Compute total depreciation
var total = initial;
var current = 0;
var ceiling = (period === life) ? life - 1 : period;
for (var i = 2; i <= ceiling; i++) {
current = (cost - total) * rate;
total += current;
}
// Depreciation for the first and last periods are special cases
if (period === 1) {
// First period
return initial;
} else if (period === life) {
// Last period
return (cost - total) * rate;
} else {
return current;
}
};
exports.DDB = function(cost, salvage, life, period, factor) {
// Initialize factor
factor = (factor === undefined) ? 2 : factor;
cost = utils.parseNumber(cost);
salvage = utils.parseNumber(salvage);
life = utils.parseNumber(life);
period = utils.parseNumber(period);
factor = utils.parseNumber(factor);
if (utils.anyIsError(cost, salvage, life, period, factor)) {
return error.value;
}
// Return error if any of the parameters is negative or if factor is
// null
if (cost < 0 || salvage < 0 || life < 0 || period < 0 || factor <= 0) {
return error.num;
}
// Return error if period is greater than life
if (period > life) {
return error.num;
}
// Return 0 (zero) if salvage is greater than or equal to cost
if (salvage >= cost) {
return 0;
}
// Compute depreciation
var total = 0;
var current = 0;
for (var i = 1; i <= period; i++) {
current = Math.min((cost - total) * (factor / life), (cost - salvage - total));
total += current;
}
// Return depreciation
return current;
};
exports.DISC = null;
exports.DOLLARDE = function(dollar, fraction) {
// Credits: algorithm inspired by Apache OpenOffice
dollar = utils.parseNumber(dollar);
fraction = utils.parseNumber(fraction);
if (utils.anyIsError(dollar, fraction)) {
return error.value;
}
// Return error if fraction is negative
if (fraction < 0) {
return error.num;
}
// Return error if fraction is greater than or equal to 0 and less than
// 1
if (fraction >= 0 && fraction < 1) {
return error.div0;
}
// Truncate fraction if it is not an integer
fraction = parseInt(fraction, 10);
// Compute integer part
var result = parseInt(dollar, 10);
// Add decimal part
result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
// Round result
var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
result = Math.round(result * power) / power;
// Return converted dollar price
return result;
};
exports.DOLLARFR = function(dollar, fraction) {
// Credits: algorithm inspired by Apache OpenOffice
dollar = utils.parseNumber(dollar);
fraction = utils.parseNumber(fraction);
if (utils.anyIsError(dollar, fraction)) {
return error.value;
}
// Return error if fraction is negative
if (fraction < 0) {
return error.num;
}
// Return error if fraction is greater than or equal to 0 and less than
// 1
if (fraction >= 0 && fraction < 1) {
return error.div0;
}
// Truncate fraction if it is not an integer
fraction = parseInt(fraction, 10);
// Compute integer part
var result = parseInt(dollar, 10);
// Add decimal part
result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(fraction) / Math.LN10)) * fraction;
// Return converted dollar price
return result;
};
exports.DURATION = null;
exports.EFFECT = function(rate, periods) {
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
if (utils.anyIsError(rate, periods)) {
return error.value;
}
// Return error if rate <=0 or periods < 1
if (rate <= 0 || periods < 1) {
return error.num;
}
// Truncate periods if it is not an integer
periods = parseInt(periods, 10);
// Return effective annual interest rate
return Math.pow(1 + rate / periods, periods) - 1;
};
exports.FV = function(rate, periods, payment, value, type) {
// Credits: algorithm inspired by Apache OpenOffice
value = value || 0;
type = type || 0;
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
payment = utils.parseNumber(payment);
value = utils.parseNumber(value);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, periods, payment, value, type)) {
return error.value;
}
// Return future value
var result;
if (rate === 0) {
result = value + payment * periods;
} else {
var term = Math.pow(1 + rate, periods);
if (type === 1) {
result = value * term + payment * (1 + rate) * (term - 1) / rate;
} else {
result = value * term + payment * (term - 1) / rate;
}
}
return -result;
};
exports.FVSCHEDULE = function(principal, schedule) {
principal = utils.parseNumber(principal);
schedule = utils.parseNumberArray(utils.flatten(schedule));
if (utils.anyIsError(principal, schedule)) {
return error.value;
}
var n = schedule.length;
var future = principal;
// Apply all interests in schedule
for (var i = 0; i < n; i++) {
// Apply scheduled interest
future *= 1 + schedule[i];
}
// Return future value
return future;
};
exports.INTRATE = null;
exports.IPMT = function(rate, period, periods, present, future, type) {
// Credits: algorithm inspired by Apache OpenOffice
future = future || 0;
type = type || 0;
rate = utils.parseNumber(rate);
period = utils.parseNumber(period);
periods = utils.parseNumber(periods);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, period, periods, present, future, type)) {
return error.value;
}
// Compute payment
var payment = exports.PMT(rate, periods, present, future, type);
// Compute interest
var interest;
if (period === 1) {
if (type === 1) {
interest = 0;
} else {
interest = -present;
}
} else {
if (type === 1) {
interest = exports.FV(rate, period - 2, payment, present, 1) - payment;
} else {
interest = exports.FV(rate, period - 1, payment, present, 0);
}
}
// Return interest
return interest * rate;
};
exports.IRR = function(values, guess) {
// Credits: algorithm inspired by Apache OpenOffice
guess = guess || 0;
values = utils.parseNumberArray(utils.flatten(values));
guess = utils.parseNumber(guess);
if (utils.anyIsError(values, guess)) {
return error.value;
}
// Calculates the resulting amount
var irrResult = function(values, dates, rate) {
var r = rate + 1;
var result = values[0];
for (var i = 1; i < values.length; i++) {
result += values[i] / Math.pow(r, (dates[i] - dates[0]) / 365);
}
return result;
};
// Calculates the first derivation
var irrResultDeriv = function(values, dates, rate) {
var r = rate + 1;
var result = 0;
for (var i = 1; i < values.length; i++) {
var frac = (dates[i] - dates[0]) / 365;
result -= frac * values[i] / Math.pow(r, frac + 1);
}
return result;
};
// Initialize dates and check that values contains at least one positive
// value and one negative value
var dates = [];
var positive = false;
var negative = false;
for (var i = 0; i < values.length; i++) {
dates[i] = (i === 0) ? 0 : dates[i - 1] + 365;
if (values[i] > 0) {
positive = true;
}
if (values[i] < 0) {
negative = true;
}
}
// Return error if values does not contain at least one positive value
// and one negative value
if (!positive || !negative) {
return error.num;
}
// Initialize guess and resultRate
guess = (guess === undefined) ? 0.1 : guess;
var resultRate = guess;
// Set maximum epsilon for end of iteration
var epsMax = 1e-10;
// Implement Newton's method
var newRate, epsRate, resultValue;
var contLoop = true;
do {
resultValue = irrResult(values, dates, resultRate);
newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
epsRate = Math.abs(newRate - resultRate);
resultRate = newRate;
contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
} while (contLoop);
// Return internal rate of return
return resultRate;
};
exports.ISPMT = function(rate, period, periods, value) {
rate = utils.parseNumber(rate);
period = utils.parseNumber(period);
periods = utils.parseNumber(periods);
value = utils.parseNumber(value);
if (utils.anyIsError(rate, period, periods, value)) {
return error.value;
}
// Return interest
return value * rate * (period / periods - 1);
};
exports.MDURATION = null;
exports.MIRR = function(values, finance_rate, reinvest_rate) {
values = utils.parseNumberArray(utils.flatten(values));
finance_rate = utils.parseNumber(finance_rate);
reinvest_rate = utils.parseNumber(reinvest_rate);
if (utils.anyIsError(values, finance_rate, reinvest_rate)) {
return error.value;
}
// Initialize number of values
var n = values.length;
// Lookup payments (negative values) and incomes (positive values)
var payments = [];
var incomes = [];
for (var i = 0; i < n; i++) {
if (values[i] < 0) {
payments.push(values[i]);
} else {
incomes.push(values[i]);
}
}
// Return modified internal rate of return
var num = -exports.NPV(reinvest_rate, incomes) * Math.pow(1 + reinvest_rate, n - 1);
var den = exports.NPV(finance_rate, payments) * (1 + finance_rate);
return Math.pow(num / den, 1 / (n - 1)) - 1;
};
exports.NOMINAL = function(rate, periods) {
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
if (utils.anyIsError(rate, periods)) {
return error.value;
}
// Return error if rate <=0 or periods < 1
if (rate <= 0 || periods < 1) {
return error.num;
}
// Truncate periods if it is not an integer
periods = parseInt(periods, 10);
// Return nominal annual interest rate
return (Math.pow(rate + 1, 1 / periods) - 1) * periods;
};
exports.NPER = function(rate, payment, present, future, type) {
type = (type === undefined) ? 0 : type;
future = (future === undefined) ? 0 : future;
rate = utils.parseNumber(rate);
payment = utils.parseNumber(payment);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, payment, present, future, type)) {
return error.value;
}
// Return number of periods
var num = payment * (1 + rate * type) - future * rate;
var den = (present * rate + payment * (1 + rate * type));
return Math.log(num / den) / Math.log(1 + rate);
};
exports.NPV = function() {
var args = utils.parseNumberArray(utils.flatten(arguments));
if (args instanceof Error) {
return args;
}
// Lookup rate
var rate = args[0];
// Initialize net present value
var value = 0;
// Loop on all values
for (var j = 1; j < args.length; j++) {
value += args[j] / Math.pow(1 + rate, j);
}
// Return net present value
return value;
};
exports.ODDFPRICE = null;
exports.ODDFYIELD = null;
exports.ODDLPRICE = null;
exports.ODDLYIELD = null;
exports.PDURATION = function(rate, present, future) {
rate = utils.parseNumber(rate);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
if (utils.anyIsError(rate, present, future)) {
return error.value;
}
// Return error if rate <=0
if (rate <= 0) {
return error.num;
}
// Return number of periods
return (Math.log(future) - Math.log(present)) / Math.log(1 + rate);
};
exports.PMT = function(rate, periods, present, future, type) {
// Credits: algorithm inspired by Apache OpenOffice
future = future || 0;
type = type || 0;
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, periods, present, future, type)) {
return error.value;
}
// Return payment
var result;
if (rate === 0) {
result = (present + future) / periods;
} else {
var term = Math.pow(1 + rate, periods);
if (type === 1) {
result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate);
} else {
result = future * rate / (term - 1) + present * rate / (1 - 1 / term);
}
}
return -result;
};
exports.PPMT = function(rate, period, periods, present, future, type) {
future = future || 0;
type = type || 0;
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, periods, present, future, type)) {
return error.value;
}
return exports.PMT(rate, periods, present, future, type) - exports.IPMT(rate, period, periods, present, future, type);
};
exports.PRICE = null;
exports.PRICEDISC = null;
exports.PRICEMAT = null;
exports.PV = function(rate, periods, payment, future, type) {
future = future || 0;
type = type || 0;
rate = utils.parseNumber(rate);
periods = utils.parseNumber(periods);
payment = utils.parseNumber(payment);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
if (utils.anyIsError(rate, periods, payment, future, type)) {
return error.value;
}
// Return present value
if (rate === 0) {
return -payment * periods - future;
} else {
return (((1 - Math.pow(1 + rate, periods)) / rate) * payment * (1 + rate * type) - future) / Math.pow(1 + rate, periods);
}
};
exports.RATE = function(periods, payment, present, future, type, guess) {
// Credits: rabugento
guess = (guess === undefined) ? 0.01 : guess;
future = (future === undefined) ? 0 : future;
type = (type === undefined) ? 0 : type;
periods = utils.parseNumber(periods);
payment = utils.parseNumber(payment);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
type = utils.parseNumber(type);
guess = utils.parseNumber(guess);
if (utils.anyIsError(periods, payment, present, future, type, guess)) {
return error.value;
}
// Set maximum epsilon for end of iteration
var epsMax = 1e-6;
// Set maximum number of iterations
var iterMax = 100;
var iter = 0;
var close = false;
var rate = guess;
while (iter < iterMax && !close) {
var t1 = Math.pow(rate + 1, periods);
var t2 = Math.pow(rate + 1, periods - 1);
var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate;
var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2);
var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate;
var newRate = rate - f1 / (f2 + f3);
if (Math.abs(newRate - rate) < epsMax) close = true;
iter++
rate = newRate;
}
if (!close) return Number.NaN + rate;
return rate;
};
// TODO
exports.RECEIVED = null;
exports.RRI = function(periods, present, future) {
periods = utils.parseNumber(periods);
present = utils.parseNumber(present);
future = utils.parseNumber(future);
if (utils.anyIsError(periods, present, future)) {
return error.value;
}
// Return error if periods or present is equal to 0 (zero)
if (periods === 0 || present === 0) {
return error.num;
}
// Return equivalent interest rate
return Math.pow(future / present, 1 / periods) - 1;
};
exports.SLN = function(cost, salvage, life) {
cost = utils.parseNumber(cost);
salvage = utils.parseNumber(salvage);
life = utils.parseNumber(life);
if (utils.anyIsError(cost, salvage, life)) {
return error.value;
}
// Return error if life equal to 0 (zero)
if (life === 0) {
return error.num;
}
// Return straight-line depreciation
return (cost - salvage) / life;
};
exports.SYD = function(cost, salvage, life, period) {
// Return error if any of the parameters is not a number
cost = utils.parseNumber(cost);
salvage = utils.parseNumber(salvage);
life = utils.parseNumber(life);
period = utils.parseNumber(period);
if (utils.anyIsError(cost, salvage, life, period)) {
return error.value;
}
// Return error if life equal to 0 (zero)
if (life === 0) {
return error.num;
}
// Return error if period is lower than 1 or greater than life
if (period < 1 || period > life) {
return error.num;
}
// Truncate period if it is not an integer
period = parseInt(period, 10);
// Return straight-line depreciation
return ((cost - salvage) * (life - period + 1) * 2) / (life * (life + 1));
};
exports.TBILLEQ = function(settlement, maturity, discount) {
settlement = utils.parseDate(settlement);
maturity = utils.parseDate(maturity);
discount = utils.parseNumber(discount);
if (utils.anyIsError(settlement, maturity, discount)) {
return error.value;
}
// Return error if discount is lower than or equal to zero
if (discount <= 0) {
return error.num;
}
// Return error if settlement is greater than maturity
if (settlement > maturity) {
return error.num;
}
// Return error if maturity is more than one year after settlement
if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
return error.num;
}
// Return bond-equivalent yield
return (365 * discount) / (360 - discount * DAYS360(settlement, maturity, false));
};
exports.TBILLPRICE = function(settlement, maturity, discount) {
settlement = utils.parseDate(settlement);
maturity = utils.parseDate(maturity);
discount = utils.parseNumber(discount);
if (utils.anyIsError(settlement, maturity, discount)) {
return error.value;
}
// Return error if discount is lower than or equal to zero
if (discount <= 0) {
return error.num;
}
// Return error if settlement is greater than maturity
if (settlement > maturity) {
return error.num;
}
// Return error if maturity is more than one year after settlement
if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
return error.num;
}
// Return bond-equivalent yield
return 100 * (1 - discount * DAYS360(settlement, maturity, false) / 360);
};
exports.TBILLYIELD = function(settlement, maturity, price) {
settlement = utils.parseDate(settlement);
maturity = utils.parseDate(maturity);
price = utils.parseNumber(price);
if (utils.anyIsError(settlement, maturity, price)) {
return error.value;
}
// Return error if price is lower than or equal to zero
if (price <= 0) {
return error.num;
}
// Return error if settlement is greater than maturity
if (settlement > maturity) {
return error.num;
}
// Return error if maturity is more than one year after settlement
if (maturity - settlement > 365 * 24 * 60 * 60 * 1000) {
return error.num;
}
// Return bond-equivalent yield
return (100 - price) * 360 / (price * DAYS360(settlement, maturity, false));
};
exports.VDB = null;
exports.XIRR = function(values, dates, guess) {
// Credits: algorithm inspired by Apache OpenOffice
values = utils.parseNumberArray(utils.flatten(values));
dates = utils.parseDateArray(utils.flatten(dates));
guess = utils.parseNumber(guess);
if (utils.anyIsError(values, dates, guess)) {
return error.value;
}
// Calculates the resulting amount
var irrResult = function(values, dates, rate) {
var r = rate + 1;
var result = values[0];
for (var i = 1; i < values.length; i++) {
result += values[i] / Math.pow(r, DAYS(dates[i], dates[0]) / 365);
}
return result;
};
// Calculates the first derivation
var irrResultDeriv = function(values, dates, rate) {
var r = rate + 1;
var result = 0;
for (var i = 1; i < values.length; i++) {
var frac = DAYS(dates[i], dates[0]) / 365;
result -= frac * values[i] / Math.pow(r, frac + 1);
}
return result;
};
// Check that values contains at least one positive value and one
// negative value
var positive = false;
var negative = false;
for (var i = 0; i < values.length; i++) {
if (values[i] > 0) {
positive = true;
}
if (values[i] < 0) {
negative = true;
}
}
// Return error if values does not contain at least one positive value
// and one negative value
if (!positive || !negative) {
return error.num;
}
// Initialize guess and resultRate
guess = guess || 0.1;
var resultRate = guess;
// Set maximum epsilon for end of iteration
var epsMax = 1e-10;
// Implement Newton's method
var newRate, epsRate, resultValue;
var contLoop = true;
do {
resultValue = irrResult(values, dates, resultRate);
newRate = resultRate - resultValue / irrResultDeriv(values, dates, resultRate);
epsRate = Math.abs(newRate - resultRate);
resultRate = newRate;
contLoop = (epsRate > epsMax) && (Math.abs(resultValue) > epsMax);
} while (contLoop);
// Return internal rate of return
return resultRate;
};
exports.XNPV = function(rate, values, dates) {
rate = utils.parseNumber(rate);
values = utils.parseNumberArray(utils.flatten(values));
dates = utils.parseDateArray(utils.flatten(dates));
if (utils.anyIsError(rate, values, dates)) {
return error.value;
}
var result = 0;
for (var i = 0; i < values.length; i++) {
result += values[i] / Math.pow(1 + rate, DAYS(dates[i], dates[0]) / 365);
}
return result;
};
exports.YIELD = null;
exports.YIELDDISC = null;
exports.YIELDMAT = null;
return exports;
})();
met.information = (function() {
var exports = {};
exports.CELL = null;
exports.ERROR = {};
exports.ERROR.TYPE = function(error_val) {
switch (error_val) {
case error.nil: return 1;
case error.div0: return 2;
case error.value: return 3;
case error.ref: return 4;
case error.name: return 5;
case error.num: return 6;
case error.na: return 7;
case error.data: return 8;
}
return error.na;
};
exports.INFO = null;
exports.ISBLANK = function(value) {
return value === null;
};
exports.ISBINARY = function (number) {
return (/^[01]{1,10}$/).test(number);
};
exports.ISERR = function(value) {
return ([error.value, error.ref, error.div0, error.num, error.name, error.nil]).indexOf(value) >= 0 ||
(typeof value === 'number' && (isNaN(value) || !isFinite(value)));
};
exports.ISERROR = function(value) {
return exports.ISERR(value) || value === error.na;
};
exports.ISEVEN = function(number) {
return (Math.floor(Math.abs(number)) & 1) ? false : true;
};
// TODO
exports.ISFORMULA = null;
exports.ISLOGICAL = function(value) {
return value === true || value === false;
};
exports.ISNA = function(value) {
return value === error.na;
};
exports.ISNONTEXT = function(value) {
return typeof(value) !== 'string';
};
exports.ISNUMBER = function(value) {
return typeof(value) === 'number' && !isNaN(value) && isFinite(value);
};
exports.ISODD = function(number) {
return (Math.floor(Math.abs(number)) & 1) ? true : false;
};
exports.ISREF = null;
exports.ISTEXT = function(value) {
return typeof(value) === 'string';
};
exports.N = function(value) {
if (this.ISNUMBER(value)) {
return value;
}
if (value instanceof Date) {
return value.getTime();
}
if (value === true) {
return 1;
}
if (value === false) {
return 0;
}
if (this.ISERROR(value)) {
return value;
}
return 0;
};
exports.NA = function() {
return error.na;
};
exports.SHEET = null;
exports.SHEETS = null;
exports.TYPE = function(value) {
if (this.ISNUMBER(value)) {
return 1;
}
if (this.ISTEXT(value)) {
return 2;
}
if (this.ISLOGICAL(value)) {
return 4;
}
if (this.ISERROR(value)) {
return 16;
}
if (Array.isArray(value)) {
return 64;
}
};
return exports;
})();
met.logical = (function() {
var exports = {};
exports.AND = function() {
var args = utils.flatten(arguments);
var result = true;
for (var i = 0; i < args.length; i++) {
if (!args[i]) {
result = false;
}
}
return result;
};
exports.CHOOSE = function() {
if (arguments.length < 2) {
return error.na;
}
var index = arguments[0];
if (index < 1 || index > 254) {
return error.value;
}
if (arguments.length < index + 1) {
return error.value;
}
return arguments[index];
};
exports.FALSE = function() {
return false;
};
exports.IF = function(test, then_value, otherwise_value) {
return test ? then_value : otherwise_value;
};
exports.IFERROR = function(value, valueIfError) {
if (ISERROR(value)) {
return valueIfError;
}
return value;
};
exports.IFNA = function(value, value_if_na) {
return value === error.na ? value_if_na : value;
};
exports.NOT = function(logical) {
return !logical;
};
exports.OR = function() {
var args = utils.flatten(arguments);
var result = false;
for (var i = 0; i < args.length; i++) {
if (args[i]) {
result = true;
}
}
return result;
};
exports.TRUE = function() {
return true;
};
exports.XOR = function() {
var args = utils.flatten(arguments);
var result = 0;
for (var i = 0; i < args.length; i++) {
if (args[i]) {
result++;
}
}
return (Math.floor(Math.abs(result)) & 1) ? true : false;
};
exports.SWITCH = function() {
var result;
if (arguments.length > 0) {
var targetValue = arguments[0];
var argc = arguments.length - 1;
var switchCount = Math.floor(argc / 2);
var switchSatisfied = false;
var defaultClause = argc % 2 === 0 ? null : arguments[arguments.length - 1];
if (switchCount) {
for (var index = 0; index < switchCount; index++) {
if (targetValue === arguments[index * 2 + 1]) {
result = arguments[index * 2 + 2];
switchSatisfied = true;
break;
}
}
}
if (!switchSatisfied && defaultClause) {
result = defaultClause;
}
}
return result;
};
return exports;
})();
met.math = (function() {
var exports = {};
exports.ABS = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.abs(utils.parseNumber(number));
};
exports.ACOS = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.acos(number);
};
exports.ACOSH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.log(number + Math.sqrt(number * number - 1));
};
exports.ACOT = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.atan(1 / number);
};
exports.ACOTH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 0.5 * Math.log((number + 1) / (number - 1));
};
exports.AGGREGATE = null
exports.ARABIC = function(text) {
// Credits: Rafa? Kukawski
if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) {
return error.value;
}
var r = 0;
text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) {
r += {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
}[i];
});
return r;
};
exports.ASIN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.asin(number);
};
exports.ASINH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.log(number + Math.sqrt(number * number + 1));
};
exports.ATAN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.atan(number);
};
exports.ATAN2 = function(number_x, number_y) {
number_x = utils.parseNumber(number_x);
number_y = utils.parseNumber(number_y);
if (utils.anyIsError(number_x, number_y)) {
return error.value;
}
return Math.atan2(number_x, number_y);
};
exports.ATANH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.log((1 + number) / (1 - number)) / 2;
};
exports.BASE = function(number, radix, min_length) {
min_length = min_length || 0;
number = utils.parseNumber(number);
radix = utils.parseNumber(radix);
min_length = utils.parseNumber(min_length);
if (utils.anyIsError(number, radix, min_length)) {
return error.value;
}
min_length = (min_length === undefined) ? 0 : min_length;
var result = number.toString(radix);
return new Array(Math.max(min_length + 1 - result.length, 0)).join('0') + result;
};
exports.CEILING = function(number, significance, mode) {
significance = (significance === undefined) ? 1 : significance;
mode = (mode === undefined) ? 0 : mode;
number = utils.parseNumber(number);
significance = utils.parseNumber(significance);
mode = utils.parseNumber(mode);
if (utils.anyIsError(number, significance, mode)) {
return error.value;
}
if (significance === 0) {
return 0;
}
significance = Math.abs(significance);
if (number >= 0) {
return Math.ceil(number / significance) * significance;
} else {
if (mode === 0) {
return -1 * Math.floor(Math.abs(number) / significance) * significance;
} else {
return -1 * Math.ceil(Math.abs(number) / significance) * significance;
}
}
};
exports.CEILING.MATH = exports.CEILING;
exports.CEILING.PRECISE = exports.CEILING;
exports.COMBIN = function(number, number_chosen) {
number = utils.parseNumber(number);
number_chosen = utils.parseNumber(number_chosen);
if (utils.anyIsError(number, number_chosen)) {
return error.value;
}
return exports.FACT(number) / (exports.FACT(number_chosen) * exports.FACT(number - number_chosen));
};
exports.COMBINA = function(number, number_chosen) {
number = utils.parseNumber(number);
number_chosen = utils.parseNumber(number_chosen);
if (utils.anyIsError(number, number_chosen)) {
return error.value;
}
return (number === 0 && number_chosen === 0) ? 1 : exports.COMBIN(number + number_chosen - 1, number - 1);
};
exports.COS = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.cos(number);
};
exports.COSH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return (Math.exp(number) + Math.exp(-number)) / 2;
};
exports.COT = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 1 / Math.tan(number);
};
exports.COTH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
var e2 = Math.exp(2 * number);
return (e2 + 1) / (e2 - 1);
};
exports.CSC = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 1 / Math.sin(number);
};
exports.CSCH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 2 / (Math.exp(number) - Math.exp(-number));
};
exports.DECIMAL = function(number, radix) {
if (arguments.length < 1) {
return error.value;
}
return parseInt(number, radix);
};
exports.DEGREES = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return number * 180 / Math.PI;
};
exports.EVEN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return exports.CEILING(number, -2, -1);
};
exports.EXP = Math.exp;
var MEMOIZED_FACT = [];
exports.FACT = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
var n = Math.floor(number);
if (n === 0 || n === 1) {
return 1;
} else if (MEMOIZED_FACT[n] > 0) {
return MEMOIZED_FACT[n];
} else {
MEMOIZED_FACT[n] = exports.FACT(n - 1) * n;
return MEMOIZED_FACT[n];
}
};
exports.FACTDOUBLE = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
var n = Math.floor(number);
if (n <= 0) {
return 1;
} else {
return n * exports.FACTDOUBLE(n - 2);
}
};
exports.FLOOR = function(number, significance, mode) {
significance = (significance === undefined) ? 1 : significance;
mode = (mode === undefined) ? 0 : mode;
number = utils.parseNumber(number);
significance = utils.parseNumber(significance);
mode = utils.parseNumber(mode);
if (utils.anyIsError(number, significance, mode)) {
return error.value;
}
if (significance === 0) {
return 0;
}
significance = Math.abs(significance);
if (number >= 0) {
return Math.floor(number / significance) * significance;
} else {
if (mode === 0) {
return -1 * Math.ceil(Math.abs(number) / significance) * significance;
} else {
return -1 * Math.floor(Math.abs(number) / significance) * significance;
}
}
};
exports.FLOOR.MATH = exports.FLOOR;
exports.GCD = null;
exports.INT = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.floor(number);
};
exports.LCM = function() {
// Credits: Jonas Raoni Soares Silva
var o = utils.parseNumberArray(utils.flatten(arguments));
if (o instanceof Error) {
return o;
}
for (var i, j, n, d, r = 1;
(n = o.pop()) !== undefined;) {
while (n > 1) {
if (n % 2) {
for (i = 3, j = Math.floor(Math.sqrt(n)); i <= j && n % i; i += 2) {
// empty
}
d = (i <= j) ? i : n;
} else {
d = 2;
}
for (n /= d, r *= d, i = o.length; i;
(o[--i] % d) === 0 && (o[i] /= d) === 1 && o.splice(i, 1)) {
// empty
}
}
}
return r;
};
exports.LN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.log(number);
};
exports.LOG = function(number, base) {
number = utils.parseNumber(number);
base = (base === undefined) ? 10 : utils.parseNumber(base);
if (utils.anyIsError(number, base)) {
return error.value;
}
return Math.log(number) / Math.log(base);
};
exports.LOG10 = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.log(number) / Math.log(10);
};
exports.MDETERM = null;
exports.MINVERSE = null;
exports.MMULT = null;
exports.MOD = function(dividend, divisor) {
dividend = utils.parseNumber(dividend);
divisor = utils.parseNumber(divisor);
if (utils.anyIsError(dividend, divisor)) {
return error.value;
}
if (divisor === 0) {
return error.div0;
}
var modulus = Math.abs(dividend % divisor);
return (divisor > 0) ? modulus : -modulus;
};
exports.MROUND = function(number, multiple) {
number = utils.parseNumber(number);
multiple = utils.parseNumber(multiple);
if (utils.anyIsError(number, multiple)) {
return error.value;
}
if (number * multiple < 0) {
return error.num;
}
return Math.round(number / multiple) * multiple;
};
exports.MULTINOMIAL = function() {
var args = utils.parseNumberArray(utils.flatten(arguments));
if (args instanceof Error) {
return args;
}
var sum = 0;
var divisor = 1;
for (var i = 0; i < args.length; i++) {
sum += args[i];
divisor *= exports.FACT(args[i]);
}
return exports.FACT(sum) / divisor;
};
exports.MUNIT = null;
exports.ODD = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
var temp = Math.ceil(Math.abs(number));
temp = (temp & 1) ? temp : temp + 1;
return (number > 0) ? temp : -temp;
};
exports.PI = function() {
return Math.PI;
};
exports.POWER = function(number, power) {
number = utils.parseNumber(number);
power = utils.parseNumber(power);
if (utils.anyIsError(number, power)) {
return error.value;
}
var result = Math.pow(number, power);
if (isNaN(result)) {
return error.num;
}
return result;
};
exports.PRODUCT = function() {
var args = utils.parseNumberArray(utils.flatten(arguments));
if (args instanceof Error) {
return args;
}
var result = 1;
for (var i = 0; i < args.length; i++) {
result *= args[i];
}
return result;
};
exports.QUOTIENT = function(numerator, denominator) {
numerator = utils.parseNumber(numerator);
denominator = utils.parseNumber(denominator);
if (utils.anyIsError(numerator, denominator)) {
return error.value;
}
return parseInt(numerator / denominator, 10);
};
exports.RADIANS = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return number * Math.PI / 180;
};
exports.RAND = function() {
return Math.random();
};
exports.RANDBETWEEN = function(bottom, top) {
bottom = utils.parseNumber(bottom);
top = utils.parseNumber(top);
if (utils.anyIsError(bottom, top)) {
return error.value;
}
// Creative Commons Attribution 3.0 License
// Copyright (c) 2012 eqcode
return bottom + Math.ceil((top - bottom + 1) * Math.random()) - 1;
};
exports.ROMAN = null;
exports.ROUND = function(number, digits) {
number = utils.parseNumber(number);
digits = utils.parseNumber(digits);
if (utils.anyIsError(number, digits)) {
return error.value;
}
return Math.round(number * Math.pow(10, digits)) / Math.pow(10, digits);
};
exports.ROUNDDOWN = function(number, digits) {
number = utils.parseNumber(number);
digits = utils.parseNumber(digits);
if (utils.anyIsError(number, digits)) {
return error.value;
}
var sign = (number > 0) ? 1 : -1;
return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
};
exports.ROUNDUP = function(number, digits) {
number = utils.parseNumber(number);
digits = utils.parseNumber(digits);
if (utils.anyIsError(number, digits)) {
return error.value;
}
var sign = (number > 0) ? 1 : -1;
return sign * (Math.ceil(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
};
exports.SEC = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 1 / Math.cos(number);
};
exports.SECH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return 2 / (Math.exp(number) + Math.exp(-number));
};
exports.SERIESSUM = function(x, n, m, coefficients) {
x = utils.parseNumber(x);
n = utils.parseNumber(n);
m = utils.parseNumber(m);
coefficients = utils.parseNumberArray(coefficients);
if (utils.anyIsError(x, n, m, coefficients)) {
return error.value;
}
var result = coefficients[0] * Math.pow(x, n);
for (var i = 1; i < coefficients.length; i++) {
result += coefficients[i] * Math.pow(x, n + i * m);
}
return result;
};
exports.SIGN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
if (number < 0) {
return -1;
} else if (number === 0) {
return 0;
} else {
return 1;
}
};
exports.SIN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.sin(number);
};
exports.SINH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return (Math.exp(number) - Math.exp(-number)) / 2;
};
exports.SQRT = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
if (number < 0) {
return error.num;
}
return Math.sqrt(number);
};
exports.SQRTPI = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.sqrt(number * Math.PI);
};
exports.SUBTOTAL = null;
exports.ADD = function (num1, num2) {
if (arguments.length !== 2) {
return error.na;
}
num1 = utils.parseNumber(num1);
num2 = utils.parseNumber(num2);
if (utils.anyIsError(num1, num2)) {
return error.value;
}
return num1 + num2;
};
exports.MINUS = function (num1, num2) {
if (arguments.length !== 2) {
return error.na;
}
num1 = utils.parseNumber(num1);
num2 = utils.parseNumber(num2);
if (utils.anyIsError(num1, num2)) {
return error.value;
}
return num1 - num2;
};
exports.DIVIDE = function (dividend, divisor) {
if (arguments.length !== 2) {
return error.na;
}
dividend = utils.parseNumber(dividend);
divisor = utils.parseNumber(divisor);
if (utils.anyIsError(dividend, divisor)) {
return error.value;
}
if (divisor === 0) {
return error.div0;
}
return dividend / divisor;
};
exports.MULTIPLY = function (factor1, factor2) {
if (arguments.length !== 2) {
return error.na;
}
factor1 = utils.parseNumber(factor1);
factor2 = utils.parseNumber(factor2);
if (utils.anyIsError(factor1, factor2)) {
return error.value;
}
return factor1 * factor2;
};
exports.GTE = function (num1, num2) {
if (arguments.length !== 2) {
return error.na;
}
num1 = utils.parseNumber(num1);
num2 = utils.parseNumber(num2);
if (utils.anyIsError(num1, num2)) {
return error.error;
}
return num1 >= num2;
};
exports.LT = function (num1, num2) {
if (arguments.length !== 2) {
return error.na;
}
num1 = utils.parseNumber(num1);
num2 = utils.parseNumber(num2);
if (utils.anyIsError(num1, num2)) {
return error.error;
}
return num1 < num2;
};
exports.LTE = function (num1, num2) {
if (arguments.length !== 2) {
return error.na;
}
num1 = utils.parseNumber(num1);
num2 = utils.parseNumber(num2);
if (utils.anyIsError(num1, num2)) {
return error.error;
}
return num1 <= num2;
};
exports.EQ = function (value1, value2) {
if (arguments.length !== 2) {
return error.na;
}
return value1 === value2;
};
exports.NE = function (value1, value2) {
if (arguments.length !== 2) {
return error.na;
}
return value1 !== value2;
};
exports.POW = function (base, exponent) {
if (arguments.length !== 2) {
return error.na;
}
base = utils.parseNumber(base);
exponent = utils.parseNumber(exponent);
if (utils.anyIsError(base, exponent)) {
return error.error;
}
return exports.POWER(base, exponent);
};
exports.SUM = function() {
var result = 0;
var argsKeys = Object.keys(arguments);
for (var i = 0; i < argsKeys.length; ++i) {
var elt = arguments[argsKeys[i]];
if (typeof elt === 'number') {
result += elt;
} else if (typeof elt === 'string') {
var parsed = parseFloat(elt);
!isNaN(parsed) && (result += parsed);
} else if (Array.isArray(elt)) {
result += exports.SUM.apply(null, elt);
}
}
return result;
};
exports.SUMIF = function() {
var args = utils.argsToArray(arguments);
var criteria = args.pop();
var range = utils.parseNumberArray(utils.flatten(args));
if (range instanceof Error) {
return range;
}
var result = 0;
for (var i = 0; i < range.length; i++) {
result += (eval(range[i] + criteria)) ? range[i] : 0; // jshint ignore:line
}
return result;
};
exports.SUMIFS = function () {
var args = utils.argsToArray(arguments);
var range = utils.parseNumberArray(utils.flatten(args.shift()));
if (range instanceof Error) {
return range;
}
var criteria = args;
var n_range_elements = range.length;
var n_criterias = criteria.length;
var result = 0;
for (var i = 0; i < n_range_elements; i++) {
var el = range[i];
var condition = '';
for (var c = 0; c < n_criterias; c+=2) {
if(isNaN(criteria[c][i])){
condition += '"' + criteria[c][i] + '"' + criteria[c+1];
}
else {
condition += criteria[c][i] + criteria[c + 1];
}
if (c !== n_criterias - 1) {
condition += ' && ';
}
}
condition = condition.slice(0,-4)
if (eval(condition)) { // jshint ignore:line
result += el;
}
}
return result;
};
exports.SUMPRODUCT = null;
exports.SUMSQ = function() {
var numbers = utils.parseNumberArray(utils.flatten(arguments));
if (numbers instanceof Error) {
return numbers;
}
var result = 0;
var length = numbers.length;
for (var i = 0; i < length; i++) {
result += (ISNUMBER(numbers[i])) ? numbers[i] * numbers[i] : 0;
}
return result;
};
exports.SUMX2MY2 = function(array_x, array_y) {
array_x = utils.parseNumberArray(utils.flatten(array_x));
array_y = utils.parseNumberArray(utils.flatten(array_y));
if (utils.anyIsError(array_x, array_y)) {
return error.value;
}
var result = 0;
for (var i = 0; i < array_x.length; i++) {
result += array_x[i] * array_x[i] - array_y[i] * array_y[i];
}
return result;
};
exports.SUMX2PY2 = function(array_x, array_y) {
array_x = utils.parseNumberArray(utils.flatten(array_x));
array_y = utils.parseNumberArray(utils.flatten(array_y));
if (utils.anyIsError(array_x, array_y)) {
return error.value;
}
var result = 0;
array_x = utils.parseNumberArray(utils.flatten(array_x));
array_y = utils.parseNumberArray(utils.flatten(array_y));
for (var i = 0; i < array_x.length; i++) {
result += array_x[i] * array_x[i] + array_y[i] * array_y[i];
}
return result;
};
exports.SUMXMY2 = function(array_x, array_y) {
array_x = utils.parseNumberArray(utils.flatten(array_x));
array_y = utils.parseNumberArray(utils.flatten(array_y));
if (utils.anyIsError(array_x, array_y)) {
return error.value;
}
var result = 0;
array_x = utils.flatten(array_x);
array_y = utils.flatten(array_y);
for (var i = 0; i < array_x.length; i++) {
result += Math.pow(array_x[i] - array_y[i], 2);
}
return result;
};
exports.TAN = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return Math.tan(number);
};
exports.TANH = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
var e2 = Math.exp(2 * number);
return (e2 - 1) / (e2 + 1);
};
exports.TRUNC = function(number, digits) {
digits = (digits === undefined) ? 0 : digits;
number = utils.parseNumber(number);
digits = utils.parseNumber(digits);
if (utils.anyIsError(number, digits)) {
return error.value;
}
var sign = (number > 0) ? 1 : -1;
return sign * (Math.floor(Math.abs(number) * Math.pow(10, digits))) / Math.pow(10, digits);
};
return exports;
})();
met.misc = (function() {
var exports = {};
exports.UNIQUE = function () {
var result = [];
for (var i = 0; i < arguments.length; ++i) {
var hasElement = false;
var element = arguments[i];
// Check if we've already seen this element.
for (var j = 0; j < result.length; ++j) {
hasElement = result[j] === element;
if (hasElement) { break; }
}
// If we did not find it, add it to the result.
if (!hasElement) {
result.push(element);
}
}
return result;
};
exports.FLATTEN = utils.flatten;
exports.ARGS2ARRAY = function () {
return Array.prototype.slice.call(arguments, 0);
};
exports.REFERENCE = function (context, reference) {
try {
var path = reference.split('.');
var result = context;
for (var i = 0; i < path.length; ++i) {
var step = path[i];
if (step[step.length - 1] === ']') {
var opening = step.indexOf('[');
var index = step.substring(opening + 1, step.length - 1);
result = result[step.substring(0, opening)][index];
} else {
result = result[step];
}
}
return result;
} catch (error) {}
};
exports.JOIN = function (array, separator) {
return array.join(separator);
};
exports.NUMBERS = function () {
var possibleNumbers = utils.flatten(arguments);
return possibleNumbers.filter(function (el) {
return typeof el === 'number';
});
};
exports.NUMERAL = null;
return exports;
})();
met.text = (function() {
var exports = {};
exports.ASC = null;
exports.BAHTTEXT = null;
exports.CHAR = function(number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return String.fromCharCode(number);
};
exports.CLEAN = function(text) {
text = text || '';
var re = /[\0-\x1F]/g;
return text.replace(re, "");
};
exports.CODE = function(text) {
text = text || '';
return text.charCodeAt(0);
};
exports.CONCATENATE = function() {
var args = utils.flatten(arguments);
var trueFound = 0;
while ((trueFound = args.indexOf(true)) > -1) {
args[trueFound] = 'TRUE';
}
var falseFound = 0;
while ((falseFound = args.indexOf(false)) > -1) {
args[falseFound] = 'FALSE';
}
return args.join('');
};
exports.DBCS = null;
exports.DOLLAR = null;
exports.EXACT = function(text1, text2) {
return text1 === text2;
};
exports.FIND = function(find_text, within_text, position) {
position = (position === undefined) ? 0 : position;
return within_text ? within_text.indexOf(find_text, position - 1) + 1 : null;
};
exports.FIXED = null;
exports.HTML2TEXT = function (value) {
var result = '';
if (value) {
if (value instanceof Array) {
value.forEach(function (line) {
if (result !== '') {
result += '\n';
}
result += (line.replace(/<(?:.|\n)*?>/gm, ''));
});
} else {
result = value.replace(/<(?:.|\n)*?>/gm, '');
}
}
return result;
};
exports.LEFT = function(text, number) {
number = (number === undefined) ? 1 : number;
number = utils.parseNumber(number);
if (number instanceof Error || typeof text !== 'string') {
return error.value;
}
return text ? text.substring(0, number) : null;
};
exports.LEN = function(text) {
if (arguments.length === 0) {
return error.error;
}
if (typeof text === 'string') {
return text ? text.length : 0;
}
if (text.length) {
return text.length;
}
if (text == null) {
return 0;
}
return error.value;
};
exports.LOWER = function(text) {
if (typeof text !== 'string') {
return error.value;
}
return text ? text.toLowerCase() : text;
};
exports.MID = function(text, start, number) {
start = utils.parseNumber(start);
number = utils.parseNumber(number);
if (utils.anyIsError(start, number) || typeof text !== 'string') {
return number;
}
var begin = start - 1;
var end = begin + number;
return text.substring(begin, end);
};
exports.NUMBERVALUE = null;
exports.PRONETIC = null;
exports.PROPER = function(text) {
if (text === undefined || text.length === 0) {
return error.value;
}
if (text === true) {
text = 'TRUE';
}
if (text === false) {
text = 'FALSE';
}
if (isNaN(text) && typeof text === 'number') {
return error.value;
}
if (typeof text === 'number') {
text = '' + text;
}
return text.replace(/\w\S* /g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
exports.REGEXEXTRACT = function (text, regular_expression) {
var match = text.match(new RegExp(regular_expression));
return match ? (match[match.length > 1 ? match.length - 1 : 0]) : null;
};
exports.REGEXMATCH = function (text, regular_expression, full) {
var match = text.match(new RegExp(regular_expression));
return full ? match : !!match;
};
exports.REGEXREPLACE = function (text, regular_expression, replacement) {
return text.replace(new RegExp(regular_expression), replacement);
};
exports.REPLACE = function(text, position, length, new_text) {
position = utils.parseNumber(position);
length = utils.parseNumber(length);
if (utils.anyIsError(position, length) ||
typeof text !== 'string' ||
typeof new_text !== 'string') {
return error.value;
}
return text.substr(0, position - 1) + new_text + text.substr(position - 1 + length);
};
exports.REPT = function(text, number) {
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return new Array(number + 1).join(text);
};
exports.RIGHT = function(text, number) {
number = (number === undefined) ? 1 : number;
number = utils.parseNumber(number);
if (number instanceof Error) {
return number;
}
return text ? text.substring(text.length - number) : null;
};
exports.SEARCH = function(find_text, within_text, position) {
var foundAt;
if (typeof find_text !== 'string' || typeof within_text !== 'string') {
return error.value;
}
position = (position === undefined) ? 0 : position;
foundAt = within_text.toLowerCase().indexOf(find_text.toLowerCase(), position - 1)+1;
return (foundAt === 0)?error.value:foundAt;
};
exports.SPLIT = function (text, separator) {
return text.split(separator);
};
exports.SUBSTITUTE = function(text, old_text, new_text, occurrence) {
if (!text || !old_text || !new_text) {
return text;
} else if (occurrence === undefined) {
return text.replace(new RegExp(old_text, 'g'), new_text);
} else {
var index = 0;
var i = 0;
while (text.indexOf(old_text, index) > 0) {
index = text.indexOf(old_text, index + 1);
i++;
if (i === occurrence) {
return text.substring(0, index) + new_text + text.substring(index + old_text.length);
}
}
}
};
exports.T = function(value) {
return (typeof value === "string") ? value : '';
};
exports.TEXT = null;
exports.TRIM = function(text) {
if (typeof text !== 'string') {
return error.value;
}
return text.replace(/ +/g, ' ').trim();
};
exports.UNICHAR = exports.CHAR;
exports.UNICODE = exports.CODE;
exports.UPPER = function(text) {
if (typeof text !== 'string') {
return error.value;
}
return text.toUpperCase();
};
exports.VALUE = null;
return exports;
})();
met.stats = (function() {
var exports = {};
var SQRT2PI = 2.5066282746310002;
exports.AVEDEV = null;
exports.AVERAGE = function() {
var range = utils.numbers(utils.flatten(arguments));
var n = range.length;
var sum = 0;
var count = 0;
for (var i = 0; i < n; i++) {
sum += range[i];
count += 1;
}
return sum / count;
};
exports.AVERAGEA = function() {
var range = utils.flatten(arguments);
var n = range.length;
var sum = 0;
var count = 0;
for (var i = 0; i < n; i++) {
var el = range[i];
if (typeof el === 'number') {
sum += el;
}
if (el === true) {
sum++;
}
if (el !== null) {
count++;
}
}
return sum / count;
};
exports.AVERAGEIF = function(range, criteria, average_range) {
average_range = average_range || range;
range = utils.flatten(range);
average_range = utils.parseNumberArray(utils.flatten(average_range));
if (average_range instanceof Error) {
return average_range;
}
var average_count = 0;
var result = 0;
for (var i = 0; i < range.length; i++) {
if (eval(range[i] + criteria)) { // jshint ignore:line
result += average_range[i];
average_count++;
}
}
return result / average_count;
};
exports.AVERAGEIFS = null;
exports.COUNT = function() {
return utils.numbers(utils.flatten(arguments)).length;
};
exports.COUNTA = function() {
var range = utils.flatten(arguments);
return range.length - exports.COUNTBLANK(range);
};
exports.COUNTIN = function (range, value) {
var result = 0;
for (var i = 0; i < range.length; i++) {
if (range[i] === value) {
result++;
}
}
return result;
};
exports.COUNTBLANK = function() {
var range = utils.flatten(arguments);
var blanks = 0;
var element;
for (var i = 0; i < range.length; i++) {
element = range[i];
if (element === null || element === '') {
blanks++;
}
}
return blanks;
};
exports.COUNTIF = function() {
var args = utils.argsToArray(arguments);
var criteria = args.pop();
var range = utils.flatten(args);
if (!/[<>=!]/.test(criteria)) {
criteria = '=="' + criteria + '"';
}
var matches = 0;
for (var i = 0; i < range.length; i++) {
if (typeof range[i] !== 'string') {
if (eval(range[i] + criteria)) { // jshint ignore:line
matches++;
}
} else {
if (eval('"' + range[i] + '"' + criteria)) { // jshint ignore:line
matches++;
}
}
}
return matches;
};
exports.COUNTIFS = function() {
var args = utils.argsToArray(arguments);
var results = new Array(utils.flatten(args[0]).length);
for (var i = 0; i < results.length; i++) {
results[i] = true;
}
for (i = 0; i < args.length; i += 2) {
var range = utils.flatten(args[i]);
var criteria = args[i + 1];
if (!/[<>=!]/.test(criteria)) {
criteria = '=="' + criteria + '"';
}
for (var j = 0; j < range.length; j++) {
if (typeof range[j] !== 'string') {
results[j] = results[j] && eval(range[j] + criteria); // jshint ignore:line
} else {
results[j] = results[j] && eval('"' + range[j] + '"' + criteria); // jshint ignore:line
}
}
}
var result = 0;
for (i = 0; i < results.length; i++) {
if (results[i]) {
result++;
}
}
return result;
};
exports.COUNTUNIQUE = function () {
return UNIQUE.apply(null, utils.flatten(arguments)).length;
};
exports.FISHER = function(x) {
x = utils.parseNumber(x);
if (x instanceof Error) {
return x;
}
return Math.log((1 + x) / (1 - x)) / 2;
};
exports.FISHERINV = function(y) {
y = utils.parseNumber(y);
if (y instanceof Error) {
return y;
}
var e2y = Math.exp(2 * y);
return (e2y - 1) / (e2y + 1);
};
exports.FREQUENCY = function(data, bins) {
data = utils.parseNumberArray(utils.flatten(data));
bins = utils.parseNumberArray(utils.flatten(bins));
if (utils.anyIsError(data, bins)) {
return error.value;
}
var n = data.length;
var b = bins.length;
var r = [];
for (var i = 0; i <= b; i++) {
r[i] = 0;
for (var j = 0; j < n; j++) {
if (i === 0) {
if (data[j] <= bins[0]) {
r[0] += 1;
}
} else if (i < b) {
if (data[j] > bins[i - 1] && data[j] <= bins[i]) {
r[i] += 1;
}
} else if (i === b) {
if (data[j] > bins[b - 1]) {
r[b] += 1;
}
}
}
}
return r;
};
exports.LARGE = function(range, k) {
range = utils.parseNumberArray(utils.flatten(range));
k = utils.parseNumber(k);
if (utils.anyIsError(range, k)) {
return range;
}
return range.sort(function(a, b) {
return b - a;
})[k - 1];
};
exports.MAX = function() {
var range = utils.numbers(utils.flatten(arguments));
return (range.length === 0) ? 0 : Math.max.apply(Math, range);
};
exports.MAXA = function() {
var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
return (range.length === 0) ? 0 : Math.max.apply(Math, range);
};
exports.MIN = function() {
var range = utils.numbers(utils.flatten(arguments));
return (range.length === 0) ? 0 : Math.min.apply(Math, range);
};
exports.MINA = function() {
var range = utils.arrayValuesToNumbers(utils.flatten(arguments));
return (range.length === 0) ? 0 : Math.min.apply(Math, range);
};
exports.MODE = {};
exports.MODE.MULT = function() {
// Credits: Roönaän
var range = utils.parseNumberArray(utils.flatten(arguments));
if (range instanceof Error) {
return range;
}
var n = range.length;
var count = {};
var maxItems = [];
var max = 0;
var currentItem;
for (var i = 0; i < n; i++) {
currentItem = range[i];
count[currentItem] = count[currentItem] ? count[currentItem] + 1 : 1;
if (count[currentItem] > max) {
max = count[currentItem];
maxItems = [];
}
if (count[currentItem] === max) {
maxItems[maxItems.length] = currentItem;
}
}
return maxItems;
};
exports.MODE.SNGL = function() {
var range = utils.parseNumberArray(utils.flatten(arguments));
if (range instanceof Error) {
return range;
}
return exports.MODE.MULT(range).sort(function(a, b) {
return a - b;
})[0];
};
exports.PERCENTILE = {};
exports.PERCENTILE.EXC = function(array, k) {
array = utils.parseNumberArray(utils.flatten(array));
k = utils.parseNumber(k);
if (utils.anyIsError(array, k)) {
return error.value;
}
array = array.sort(function(a, b) {
{
return a - b;
}
});
var n = array.length;
if (k < 1 / (n + 1) || k > 1 - 1 / (n + 1)) {
return error.num;
}
var l = k * (n + 1) - 1;
var fl = Math.floor(l);
return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
};
exports.PERCENTILE.INC = function(array, k) {
array = utils.parseNumberArray(utils.flatten(array));
k = utils.parseNumber(k);
if (utils.anyIsError(array, k)) {
return error.value;
}
array = array.sort(function(a, b) {
return a - b;
});
var n = array.length;
var l = k * (n - 1);
var fl = Math.floor(l);
return utils.cleanFloat((l === fl) ? array[l] : array[fl] + (l - fl) * (array[fl + 1] - array[fl]));
};
exports.PERCENTRANK = {};
exports.PERCENTRANK.EXC = function(array, x, significance) {
significance = (significance === undefined) ? 3 : significance;
array = utils.parseNumberArray(utils.flatten(array));
x = utils.parseNumber(x);
significance = utils.parseNumber(significance);
if (utils.anyIsError(array, x, significance)) {
return error.value;
}
array = array.sort(function(a, b) {
return a - b;
});
var uniques = UNIQUE.apply(null, array);
var n = array.length;
var m = uniques.length;
var power = Math.pow(10, significance);
var result = 0;
var match = false;
var i = 0;
while (!match && i < m) {
if (x === uniques[i]) {
result = (array.indexOf(uniques[i]) + 1) / (n + 1);
match = true;
} else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
result = (array.indexOf(uniques[i]) + 1 + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n + 1);
match = true;
}
i++;
}
return Math.floor(result * power) / power;
};
exports.PERCENTRANK.INC = function(array, x, significance) {
significance = (significance === undefined) ? 3 : significance;
array = utils.parseNumberArray(utils.flatten(array));
x = utils.parseNumber(x);
significance = utils.parseNumber(significance);
if (utils.anyIsError(array, x, significance)) {
return error.value;
}
array = array.sort(function(a, b) {
return a - b;
});
var uniques = UNIQUE.apply(null, array);
var n = array.length;
var m = uniques.length;
var power = Math.pow(10, significance);
var result = 0;
var match = false;
var i = 0;
while (!match && i < m) {
if (x === uniques[i]) {
result = array.indexOf(uniques[i]) / (n - 1);
match = true;
} else if (x >= uniques[i] && (x < uniques[i + 1] || i === m - 1)) {
result = (array.indexOf(uniques[i]) + (x - uniques[i]) / (uniques[i + 1] - uniques[i])) / (n - 1);
match = true;
}
i++;
}
return Math.floor(result * power) / power;
};
exports.PERMUT = function(number, number_chosen) {
number = utils.parseNumber(number);
number_chosen = utils.parseNumber(number_chosen);
if (utils.anyIsError(number, number_chosen)) {
return error.value;
}
return FACT(number) / FACT(number - number_chosen);
};
exports.PERMUTATIONA = function(number, number_chosen) {
number = utils.parseNumber(number);
number_chosen = utils.parseNumber(number_chosen);
if (utils.anyIsError(number, number_chosen)) {
return error.value;
}
return Math.pow(number, number_chosen);
};
exports.PHI = function(x) {
x = utils.parseNumber(x);
if (x instanceof Error) {
return error.value;
}
return Math.exp(-0.5 * x * x) / SQRT2PI;
};
exports.PROB = function(range, probability, lower, upper) {
if (lower === undefined) {
return 0;
}
upper = (upper === undefined) ? lower : upper;
range = utils.parseNumberArray(utils.flatten(range));
probability = utils.parseNumberArray(utils.flatten(probability));
lower = utils.parseNumber(lower);
upper = utils.parseNumber(upper);
if (utils.anyIsError(range, probability, lower, upper)) {
return error.value;
}
if (lower === upper) {
return (range.indexOf(lower) >= 0) ? probability[range.indexOf(lower)] : 0;
}
var sorted = range.sort(function(a, b) {
return a - b;
});
var n = sorted.length;
var result = 0;
for (var i = 0; i < n; i++) {
if (sorted[i] >= lower && sorted[i] <= upper) {
result += probability[range.indexOf(sorted[i])];
}
}
return result;
};
exports.QUARTILE = {};
exports.QUARTILE.EXC = function(range, quart) {
range = utils.parseNumberArray(utils.flatten(range));
quart = utils.parseNumber(quart);
if (utils.anyIsError(range, quart)) {
return error.value;
}
switch (quart) {
case 1:
return exports.PERCENTILE.EXC(range, 0.25);
case 2:
return exports.PERCENTILE.EXC(range, 0.5);
case 3:
return exports.PERCENTILE.EXC(range, 0.75);
default:
return error.num;
}
};
exports.QUARTILE.INC = function(range, quart) {
range = utils.parseNumberArray(utils.flatten(range));
quart = utils.parseNumber(quart);
if (utils.anyIsError(range, quart)) {
return error.value;
}
switch (quart) {
case 1:
return exports.PERCENTILE.INC(range, 0.25);
case 2:
return exports.PERCENTILE.INC(range, 0.5);
case 3:
return exports.PERCENTILE.INC(range, 0.75);
default:
return error.num;
}
};
exports.RANK = {};
exports.RANK.AVG = function(number, range, order) {
number = utils.parseNumber(number);
range = utils.parseNumberArray(utils.flatten(range));
if (utils.anyIsError(number, range)) {
return error.value;
}
range = utils.flatten(range);
order = order || false;
var sort = (order) ? function(a, b) {
return a - b;
} : function(a, b) {
return b - a;
};
range = range.sort(sort);
var length = range.length;
var count = 0;
for (var i = 0; i < length; i++) {
if (range[i] === number) {
count++;
}
}
return (count > 1) ? (2 * range.indexOf(number) + count + 1) / 2 : range.indexOf(number) + 1;
};
exports.RANK.EQ = function(number, range, order) {
number = utils.parseNumber(number);
range = utils.parseNumberArray(utils.flatten(range));
if (utils.anyIsError(number, range)) {
return error.value;
}
order = order || false;
var sort = (order) ? function(a, b) {
return a - b;
} : function(a, b) {
return b - a;
};
range = range.sort(sort);
return range.indexOf(number) + 1;
};
exports.RSQ = function(data_x, data_y) { // no need to flatten here, PEARSON will take care of that
data_x = utils.parseNumberArray(utils.flatten(data_x));
data_y = utils.parseNumberArray(utils.flatten(data_y));
if (utils.anyIsError(data_x, data_y)) {
return error.value;
}
return Math.pow(exports.PEARSON(data_x, data_y), 2);
};
exports.SMALL = function(range, k) {
range = utils.parseNumberArray(utils.flatten(range));
k = utils.parseNumber(k);
if (utils.anyIsError(range, k)) {
return range;
}
return range.sort(function(a, b) {
return a - b;
})[k - 1];
};
exports.STANDARDIZE = function(x, mean, sd) {
x = utils.parseNumber(x);
mean = utils.parseNumber(mean);
sd = utils.parseNumber(sd);
if (utils.anyIsError(x, mean, sd)) {
return error.value;
}
return (x - mean) / sd;
};
exports.STDEV = {};
exports.STDEV.P = function() {
var v = exports.VAR.P.apply(this, arguments);
return Math.sqrt(v);
};
exports.STDEV.S = function() {
var v = exports.VAR.S.apply(this, arguments);
return Math.sqrt(v);
};
exports.STDEVA = function() {
var v = exports.VARA.apply(this, arguments);
return Math.sqrt(v);
};
exports.STDEVPA = function() {
var v = exports.VARPA.apply(this, arguments);
return Math.sqrt(v);
};
exports.VAR = {};
exports.VAR.P = function() {
var range = utils.numbers(utils.flatten(arguments));
var n = range.length;
var sigma = 0;
var mean = exports.AVERAGE(range);
for (var i = 0; i < n; i++) {
sigma += Math.pow(range[i] - mean, 2);
}
return sigma / n;
};
exports.VAR.S = function() {
var range = utils.numbers(utils.flatten(arguments));
var n = range.length;
var sigma = 0;
var mean = exports.AVERAGE(range);
for (var i = 0; i < n; i++) {
sigma += Math.pow(range[i] - mean, 2);
}
return sigma / (n - 1);
};
exports.VARA = function() {
var range = utils.flatten(arguments);
var n = range.length;
var sigma = 0;
var count = 0;
var mean = exports.AVERAGEA(range);
for (var i = 0; i < n; i++) {
var el = range[i];
if (typeof el === 'number') {
sigma += Math.pow(el - mean, 2);
} else if (el === true) {
sigma += Math.pow(1 - mean, 2);
} else {
sigma += Math.pow(0 - mean, 2);
}
if (el !== null) {
count++;
}
}
return sigma / (count - 1);
};
exports.VARPA = function() {
var range = utils.flatten(arguments);
var n = range.length;
var sigma = 0;
var count = 0;
var mean = exports.AVERAGEA(range);
for (var i = 0; i < n; i++) {
var el = range[i];
if (typeof el === 'number') {
sigma += Math.pow(el - mean, 2);
} else if (el === true) {
sigma += Math.pow(1 - mean, 2);
} else {
sigma += Math.pow(0 - mean, 2);
}
if (el !== null) {
count++;
}
}
return sigma / count;
};
exports.WEIBULL = {};
exports.WEIBULL.DIST = function(x, alpha, beta, cumulative) {
x = utils.parseNumber(x);
alpha = utils.parseNumber(alpha);
beta = utils.parseNumber(beta);
if (utils.anyIsError(x, alpha, beta)) {
return error.value;
}
return (cumulative) ? 1 - Math.exp(-Math.pow(x / beta, alpha)) : Math.pow(x, alpha - 1) * Math.exp(-Math.pow(x / beta, alpha)) * alpha / Math.pow(beta, alpha);
};
exports.Z = {};
exports.Z.TEST = function(range, x, sd) {
range = utils.parseNumberArray(utils.flatten(range));
x = utils.parseNumber(x);
if (utils.anyIsError(range, x)) {
return error.value;
}
sd = sd || exports.STDEV.S(range);
var n = range.length;
return 1 - exports.NORM.S.DIST((exports.AVERAGE(range) - x) / (sd / Math.sqrt(n)), true);
};
return exports;
})();
met.utils = (function() {
var exports = {};
exports.PROGRESS = function(p, c) {
var color = c ? c : 'red';
var value = p ? p : '0';
return '<div style="width:' + value + '%;height:4px;background-color:' + color + ';margin-top:1px;"></div>';
};
exports.RATING = function(v) {
var html = '<div class="jrating">';
for (var i = 0; i < 5; i++) {
if (i < v) {
html += '<div class="jrating-selected"></div>';
} else {
html += '<div></div>';
}
}
html += '</div>';
return html;
}
return exports;
})();
for (var i = 0; i < Object.keys(met).length; i++) {
var methods = met[Object.keys(met)[i]];
var keys = Object.keys(methods);
for (var j = 0; j < keys.length; j++) {
if (! methods[keys[j]]) {
window[keys[j]] = function() {
return keys[j] + 'Not implemented';
}
} else if (typeof(methods[keys[j]]) == 'function' || typeof(methods[keys[j]]) == 'object') {
window[keys[j]] = methods[keys[j]];
window[keys[j]].toString = function() { return '#ERROR' };
if (typeof(methods[keys[j]]) == 'object') {
var tmp = Object.keys(methods[keys[j]]);
for (var z = 0; z < tmp.length; z++) {
window[keys[j]][tmp[z]].toString = function() { return '#ERROR' };
}
}
} else {
window[keys[j]] = function() {
return keys[j] + 'Not implemented';
}
}
}
}
/**
* Instance execution helpers
* /
var x = null;
var y = null;
var instance = null;
window['TABLE'] = function() {
return instance;
}
window['COLUMN'] = window['COL'] = function () {
return parseInt(x) + 1;
}
window['ROW'] = function() {
return parseInt(y) + 1;
}
window['CELL'] = function() {
return F.getColumnNameFromCoords(x, y);
}
window['VALUE'] = function(col, row, processed) {
return instance.getValueFromCoords(parseInt(col) - 1, parseInt(row) - 1, processed);
}
window['THISROWCELL'] = function(col) {
return instance.getValueFromCoords(parseInt(col) - 1, parseInt(y));
}
// Secure formula
var secureFormula = function(oldValue, runtime) {
var newValue = '';
var inside = 0;
var special = [ '=', '!', '>', '<'];
for (var i = 0; i < oldValue.length; i++) {
if (oldValue[i] == '"') {
if (inside == 0) {
inside = 1;
} else {
inside = 0;
}
}
if (inside == 1) {
newValue += oldValue[i];
} else {
newValue += oldValue[i].toUpperCase();
if (runtime == true) {
if (i > 0 && oldValue[i] == '=' && special.indexOf(oldValue[i-1]) == -1 && special.indexOf(oldValue[i+1]) == -1) {
newValue += '='
}
}
}
}
// Adapt to JS
newValue = newValue.replace(/\^/g, '**');
newValue = newValue.replace(/\<\>/g, '!=');
newValue = newValue.replace(/\&/g, '+');
newValue = newValue.replace(/\$/g, '');
return newValue;
}
// Convert range tokens
var tokensUpdate = function(tokens, e) {
for (var index = 0; index < tokens.length; index++) {
var f = F.getTokensFromRange(tokens[index])
e = e.replace(tokens[index], "[" + f.join(',') + "]");
}
return e;
}
var F = function(expression, variables, i, j, obj) {
// Global helpers
instance = obj;
x = i
y = j;
// String
var s = '';
var keys = Object.keys(variables);
if (keys.length) {
for (var i = 0; i < keys.length; i++) {
if (keys[i].indexOf('.') == -1 && keys[i].indexOf('!') == -1) {
s += 'var ' + keys[i] + ' = ' + variables[keys[i]] + ';\n';
} else {
s += keys[i] + ' = ' + variables[keys[i]] + ';\n';
}
}
}
// Remove $
expression = expression.replace(/\$/g, '');
// Replace ! per dot
expression = expression.replace(/\!/g, '.');
// Adapt to JS
expression = secureFormula(expression, true);
// Update range
var tokens = expression.match(/([A-Z]+[0-9]*\.)?(\$?[A-Z]+\$?[0-9]+):(\$?[A-Z]+\$?[0-9]+)?/g);
if (tokens && tokens.length) {
expression = tokensUpdate(tokens, expression);
}
// Calculate
return new Function(s + '; return ' + expression)();
}
/**
* Get letter based on a number
* @param {number} i
* @return {string}
* /
var getColumnName = function(i) {
var letter = '';
if (i > 701) {
letter += String.fromCharCode(64 + parseInt(i / 676));
letter += String.fromCharCode(64 + parseInt((i % 676) / 26));
} else if (i > 25) {
letter += String.fromCharCode(64 + parseInt(i / 26));
}
letter += String.fromCharCode(65 + (i % 26));
return letter;
}
/**
* Get column name from coords
* /
F.getColumnNameFromCoords = function(x, y) {
return getColumnName(parseInt(x)) + (parseInt(y) + 1);
}
F.getCoordsFromColumnName = function(columnName) {
// Get the letters
var t = /^[a-zA-Z]+/.exec(columnName);
if (t) {
// Base 26 calculation
var code = 0;
for (var i = 0; i < t[0].length; i++) {
code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, (t[0].length - 1 - i));
}
code--;
// Make sure jspreadsheet starts on zero
if (code < 0) {
code = 0;
}
// Number
var number = parseInt(/[0-9]+$/.exec(columnName)) || null;
if (number > 0) {
number--;
}
return [ code, number ];
}
}
F.getRangeFromTokens = function(tokens) {
tokens = tokens.filter(function(v) {
return v != '#REF!';
});
var d = '';
var t = '';
for (var i = 0; i < tokens.length; i++) {
if (tokens[i].indexOf('.') >= 0) {
d = '.';
} else if (tokens[i].indexOf('!') >= 0) {
d = '!';
}
if (d) {
t = tokens[i].split(d);
tokens[i] = t[1];
t = t[0] + d
}
}
tokens.sort(function(a, b) {
var t1 = Helpers.getCoordsFromColumnName(a);
var t2 = Helpers.getCoordsFromColumnName(b);
if (t1[1] > t2[1]) {
return 1;
} else if (t1[1] < t2[1]) {
return -1;
} else {
if (t1[0] > t2[0]) {
return 1;
} else if (t1[0] < t2[0]) {
return -1;
} else {
return 0;
}
}
});
if (! tokens.length) {
return '#REF!';
} else {
return t+(tokens[0] + ':' + tokens[tokens.length - 1]);
}
}
F.getTokensFromRange = function(range) {
if (range.indexOf('.') > 0) {
var t = range.split('.');
range = t[1];
t = t[0] + '.';
} else if (range.indexOf('!') > 0) {
var t = range.split('!');
range = t[1];
t = t[0] + '!';
} else {
var t = '';
}
var range = range.split(':');
var e1 = F.getCoordsFromColumnName(range[0]);
var e2 = F.getCoordsFromColumnName(range[1]);
if (e1[0] <= e2[0]) {
var x1 = e1[0];
var x2 = e2[0];
} else {
var x1 = e2[0];
var x2 = e1[0];
}
if (e1[1] === null && e2[1] == null) {
var y1 = null;
var y2 = null;
var k = Object.keys(vars);
for (var i = 0; i < k.length; i++) {
var tmp = F.getCoordsFromColumnName(k[i]);
if (tmp[0] === e1[0]) {
if (y1 === null || tmp[1] < y1) {
y1 = tmp[1]
}
}
if (tmp[0] === e2[0]) {
if (y2 === null || tmp[1] > y2) {
y2 = tmp[1]
}
}
}
} else {
if (e1[1] <= e2[1]) {
var y1 = e1[1];
var y2 = e2[1];
} else {
var y1 = e2[1];
var y2 = e1[1];
}
}
var f = [];
for (var j = y1; j <= y2; j++) {
var line = [];
for (var i = x1; i <= x2; i++) {
line.push(t + F.getColumnNameFromCoords(i, j));
}
f.push(line);
}
return f;
}
F.setFormula = function(o) {
var k = Object.keys(o);
for (var i = 0; i < k.length; i++) {
if (typeof(o[k[i]]) == 'function') {
window[k[i]] = o[k[i]];
}
}
}
return F;
})();
//}}}
!!!!!Init
***/
//{{{
(function() {
var css = store.getTiddlerText("jspreadsheet.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}")).replace(/\/\/\}\}\}[\s\S]*?\/\/\{\{\{/g, "");
css = ".viewer table.jss_worksheet { border-collapse: separate; }\n.viewer .jss_worksheet thead td { color: unset; }\n.viewer .jss_contextmenu>div:not(.contextmenu-line):hover a { background-color: unset; }"
+ css.replace(".jss_worksheet {", ".jss_worksheet, .viewer .jss_worksheet {\nmargin: 0;\n").replace("font-size:24px;", "");
setStylesheet(css, "jspreadsheet.js-stylesheet");
var index;
var code = store.getTiddlerText("jspreadsheet.js##Code").replace(/\* \//g, "*/");
code = code.replace(/if \(! jSuites[\s\S]*?\}/, "");
code = code.replace(/if \(! formula[\s\S]*?\}/, "");
code = store.getTiddlerText("jspreadsheet.js##Formula").replace(/\* \//g, "*/") + "\n" + code;
code = code.replace("document.removeEventListener", "root.removeEventListener");
code = code.replace("document.addEventListener", "root.addEventListener");
index = code.indexOf("delete(obj.options.mergeCells[cellName]);");
index = code.indexOf("}", index) + 1;
code = code.substring(0, index) + "\n_dispatch_js__WEBPACK_IMPORTED_MODULE_4__/* [\"default\"] */ .A.call(obj, 'onmerge', obj);" + code.substring(index);
eval(code);
})();
//}}}
/***
|Name|jstree.js|
|Source|https://github.com/vakata/jstree|
|Documentation|https://www.jstree.com|
|Version|3.3.8|
|License|[[MIT|http://www.opensource.org/licenses/mit-license.php]]|
|Description|jsTree is jquery plugin, that provides interactive trees|
|Requires|[[CollatorPlugin]]|
!!!!!CSS
//{{{
/* jsTree default theme * /
.jstree-node,
.jstree-children,
.jstree-container-ul {
display: block;
margin: 0;
padding: 0;
list-style-type: none;
list-style-image: none;
}
.jstree-node {
white-space: nowrap;
}
.jstree-anchor {
display: inline-block;
color: black;
white-space: nowrap;
padding: 0 4px 0 1px;
margin: 0;
vertical-align: top;
}
.jstree-anchor:focus {
outline: 0;
}
.jstree-anchor,
.jstree-anchor:link,
.jstree-anchor:visited,
.jstree-anchor:hover,
.jstree-anchor:active {
text-decoration: none;
color: inherit;
}
.jstree-icon {
display: inline-block;
text-decoration: none;
margin: 0;
padding: 0;
vertical-align: top;
text-align: center;
}
.jstree-icon:empty {
display: inline-block;
text-decoration: none;
margin: 0;
padding: 0;
vertical-align: top;
text-align: center;
}
.jstree-ocl {
cursor: pointer;
}
.jstree-leaf > .jstree-ocl {
cursor: default;
}
.jstree .jstree-open > .jstree-children {
display: block;
}
.jstree .jstree-closed > .jstree-children,
.jstree .jstree-leaf > .jstree-children {
display: none;
}
.jstree-anchor > .jstree-themeicon {
margin-right: 2px;
}
.jstree-no-icons .jstree-themeicon,
.jstree-anchor > .jstree-themeicon-hidden {
display: none;
}
.jstree-hidden,
.jstree-node.jstree-hidden {
display: none;
}
.jstree-rtl .jstree-anchor {
padding: 0 1px 0 4px;
}
.jstree-rtl .jstree-anchor > .jstree-themeicon {
margin-left: 2px;
margin-right: 0;
}
.jstree-rtl .jstree-node {
margin-left: 0;
}
.jstree-rtl .jstree-container-ul > .jstree-node {
margin-right: 0;
}
.jstree-wholerow-ul {
position: relative;
display: inline-block;
min-width: 100%;
}
.jstree-wholerow-ul .jstree-leaf > .jstree-ocl {
cursor: pointer;
}
.jstree-wholerow-ul .jstree-anchor,
.jstree-wholerow-ul .jstree-icon {
position: relative;
}
.jstree-wholerow-ul .jstree-wholerow {
width: 100%;
cursor: pointer;
position: absolute;
left: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jstree-contextmenu .jstree-anchor {
-webkit-user-select: none;
/* disable selection/Copy of UIWebView * /
-webkit-touch-callout: none;
/* disable the IOS popup when long-press on a link * /
}
.vakata-context {
display: none;
}
.vakata-context,
.vakata-context ul {
margin: 0;
padding: 2px;
position: absolute;
background: #f5f5f5;
border: 1px solid #979797;
box-shadow: 2px 2px 2px #999999;
}
.vakata-context ul {
list-style: none;
left: 100%;
margin-top: -2.7em;
margin-left: -4px;
}
.vakata-context .vakata-context-right ul {
left: auto;
right: 100%;
margin-left: auto;
margin-right: -4px;
}
.vakata-context li {
list-style: none;
}
.vakata-context li > a {
display: block;
padding: 0 2em 0 2em;
text-decoration: none;
width: auto;
color: black;
white-space: nowrap;
line-height: 2.4em;
text-shadow: 1px 1px 0 white;
border-radius: 1px;
}
.vakata-context li > a:hover {
position: relative;
background-color: #e8eff7;
box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context li > a.vakata-context-parent {
background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==");
background-position: right center;
background-repeat: no-repeat;
}
.vakata-context li > a:focus {
outline: 0;
}
.vakata-context .vakata-context-hover > a {
position: relative;
background-color: #e8eff7;
box-shadow: 0 0 2px #0a6aa1;
}
.vakata-context .vakata-context-separator > a,
.vakata-context .vakata-context-separator > a:hover {
background: white;
border: 0;
border-top: 1px solid #e2e3e3;
height: 1px;
min-height: 1px;
max-height: 1px;
padding: 0;
margin: 0 0 0 2.4em;
border-left: 1px solid #e0e0e0;
text-shadow: 0 0 0 transparent;
box-shadow: 0 0 0 transparent;
border-radius: 0;
}
.vakata-context .vakata-contextmenu-disabled a,
.vakata-context .vakata-contextmenu-disabled a:hover {
color: silver;
background-color: transparent;
border: 0;
box-shadow: 0 0 0;
}
.vakata-context .vakata-contextmenu-disabled > a > i {
filter: grayscale(100%);
}
.vakata-context li > a > i {
text-decoration: none;
display: inline-block;
width: 2.4em;
height: 2.4em;
background: transparent;
margin: 0 0 0 -2em;
vertical-align: top;
text-align: center;
line-height: 2.4em;
}
.vakata-context li > a > i:empty {
width: 2.4em;
line-height: 2.4em;
}
.vakata-context li > a .vakata-contextmenu-sep {
display: inline-block;
width: 1px;
height: 2.4em;
background: white;
margin: 0 0.5em 0 0;
border-left: 1px solid #e2e3e3;
}
.vakata-context .vakata-contextmenu-shortcut {
font-size: 0.8em;
color: silver;
opacity: 0.5;
display: none;
}
.vakata-context-rtl ul {
left: auto;
right: 100%;
margin-left: auto;
margin-right: -4px;
}
.vakata-context-rtl li > a.vakata-context-parent {
background-image: url("data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7");
background-position: left center;
background-repeat: no-repeat;
}
.vakata-context-rtl .vakata-context-separator > a {
margin: 0 2.4em 0 0;
border-left: 0;
border-right: 1px solid #e2e3e3;
}
.vakata-context-rtl .vakata-context-left ul {
right: auto;
left: 100%;
margin-left: -4px;
margin-right: auto;
}
.vakata-context-rtl li > a > i {
margin: 0 -2em 0 0;
}
.vakata-context-rtl li > a .vakata-contextmenu-sep {
margin: 0 0 0 0.5em;
border-left-color: white;
background: #e2e3e3;
}
#jstree-marker {
position: absolute;
top: 0;
left: 0;
margin: -5px 0 0 0;
padding: 0;
border-right: 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid;
width: 0;
height: 0;
font-size: 0;
line-height: 0;
}
#jstree-dnd {
line-height: 16px;
margin: 0;
padding: 4px;
}
#jstree-dnd .jstree-icon,
#jstree-dnd .jstree-copy {
display: inline-block;
text-decoration: none;
margin: 0 2px 0 0;
padding: 0;
width: 16px;
height: 16px;
}
#jstree-dnd .jstree-ok {
background: green;
}
#jstree-dnd .jstree-er {
background: red;
}
#jstree-dnd .jstree-copy {
margin: 0 2px 0 2px;
}
.jstree-default .jstree-node,
.jstree-default .jstree-icon {
background-repeat: no-repeat;
background-color: transparent;
}
.jstree-default .jstree-anchor,
.jstree-default .jstree-animated,
.jstree-default .jstree-wholerow {
transition: background-color 0.15s, box-shadow 0.15s;
}
.jstree-default .jstree-hovered {
background: #e7f4f9;
border-radius: 2px;
box-shadow: inset 0 0 1px #cccccc;
}
.jstree-default .jstree-context {
background: #e7f4f9;
border-radius: 2px;
box-shadow: inset 0 0 1px #cccccc;
}
.jstree-default .jstree-clicked {
background: #beebff;
border-radius: 2px;
box-shadow: inset 0 0 1px #999999;
}
.jstree-default .jstree-no-icons .jstree-anchor > .jstree-themeicon {
display: none;
}
.jstree-default .jstree-disabled {
background: transparent;
color: #666666;
}
.jstree-default .jstree-disabled.jstree-hovered {
background: transparent;
box-shadow: none;
}
.jstree-default .jstree-disabled.jstree-clicked {
background: #efefef;
}
.jstree-default .jstree-disabled > .jstree-icon {
opacity: 0.8;
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
/* Firefox 10+ * /
filter: gray;
/* IE6-9 * /
-webkit-filter: grayscale(100%);
/* Chrome 19+ & Safari 6+ * /
}
.jstree-default .jstree-search {
font-style: italic;
color: #8b0000;
font-weight: bold;
}
.jstree-default .jstree-no-checkboxes .jstree-checkbox {
display: none !important;
}
.jstree-default.jstree-checkbox-no-clicked .jstree-clicked {
background: transparent;
box-shadow: none;
}
.jstree-default.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered {
background: #e7f4f9;
}
.jstree-default.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked {
background: transparent;
}
.jstree-default.jstree-checkbox-no-clicked > .jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered {
background: #e7f4f9;
}
.jstree-default > .jstree-striped {
min-width: 100%;
display: inline-block;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==") left top repeat;
}
.jstree-default > .jstree-wholerow-ul .jstree-hovered,
.jstree-default > .jstree-wholerow-ul .jstree-clicked {
background: transparent;
box-shadow: none;
border-radius: 0;
}
.jstree-default .jstree-wholerow {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.jstree-default .jstree-wholerow-hovered {
background: #e7f4f9;
}
.jstree-default .jstree-wholerow-clicked {
background: #beebff;
background: -webkit-linear-gradient(top, #beebff 0%, #a8e4ff 100%);
background: linear-gradient(to bottom, #beebff 0%, #a8e4ff 100%);
}
.jstree-default .jstree-node {
min-height: 24px;
line-height: 24px;
margin-left: 24px;
min-width: 24px;
}
.jstree-default .jstree-anchor {
line-height: 24px;
height: 24px;
}
.jstree-default .jstree-icon {
width: 24px;
height: 24px;
line-height: 24px;
}
.jstree-default .jstree-icon:empty {
width: 24px;
height: 24px;
line-height: 24px;
}
.jstree-default.jstree-rtl .jstree-node {
margin-right: 24px;
}
.jstree-default .jstree-wholerow {
height: 24px;
}
.jstree-default .jstree-node,
.jstree-default .jstree-icon {
background-image: url("32px.png");
}
.jstree-default .jstree-node {
background-position: -292px -4px;
background-repeat: repeat-y;
}
.jstree-default .jstree-last {
background: transparent;
}
.jstree-default .jstree-open > .jstree-ocl {
background-position: -132px -4px;
}
.jstree-default .jstree-closed > .jstree-ocl {
background-position: -100px -4px;
}
.jstree-default .jstree-leaf > .jstree-ocl {
background-position: -68px -4px;
}
.jstree-default .jstree-themeicon {
background-position: -260px -4px;
}
.jstree-default > .jstree-no-dots .jstree-node,
.jstree-default > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -36px -4px;
}
.jstree-default > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: -4px -4px;
}
.jstree-default .jstree-disabled {
background: transparent;
}
.jstree-default .jstree-disabled.jstree-hovered {
background: transparent;
}
.jstree-default .jstree-disabled.jstree-clicked {
background: #efefef;
}
.jstree-default .jstree-checkbox {
background-position: -164px -4px;
}
.jstree-default .jstree-checkbox:hover {
background-position: -164px -36px;
}
.jstree-default.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default .jstree-checked > .jstree-checkbox {
background-position: -228px -4px;
}
.jstree-default.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default .jstree-checked > .jstree-checkbox:hover {
background-position: -228px -36px;
}
.jstree-default .jstree-anchor > .jstree-undetermined {
background-position: -196px -4px;
}
.jstree-default .jstree-anchor > .jstree-undetermined:hover {
background-position: -196px -36px;
}
.jstree-default .jstree-checkbox-disabled {
opacity: 0.8;
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
/* Firefox 10+ * /
filter: gray;
/* IE6-9 * /
-webkit-filter: grayscale(100%);
/* Chrome 19+ & Safari 6+ * /
}
.jstree-default > .jstree-striped {
background-size: auto 48px;
}
.jstree-default.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
background-position: 100% 1px;
background-repeat: repeat-y;
}
.jstree-default.jstree-rtl .jstree-last {
background: transparent;
}
.jstree-default.jstree-rtl .jstree-open > .jstree-ocl {
background-position: -132px -36px;
}
.jstree-default.jstree-rtl .jstree-closed > .jstree-ocl {
background-position: -100px -36px;
}
.jstree-default.jstree-rtl .jstree-leaf > .jstree-ocl {
background-position: -68px -36px;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -36px -36px;
}
.jstree-default.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: -4px -36px;
}
.jstree-default .jstree-themeicon-custom {
background-color: transparent;
background-image: none;
background-position: 0 0;
}
.jstree-default > .jstree-container-ul .jstree-loading > .jstree-ocl {
background: url("throbber.gif") center center no-repeat;
}
.jstree-default .jstree-file {
background: url("32px.png") -100px -68px no-repeat;
}
.jstree-default .jstree-folder {
background: url("32px.png") -260px -4px no-repeat;
}
.jstree-default > .jstree-container-ul > .jstree-node {
margin-left: 0;
margin-right: 0;
}
#jstree-dnd.jstree-default {
line-height: 24px;
padding: 0 4px;
}
#jstree-dnd.jstree-default .jstree-ok,
#jstree-dnd.jstree-default .jstree-er {
background-image: url("32px.png");
background-repeat: no-repeat;
background-color: transparent;
}
#jstree-dnd.jstree-default i {
background: transparent;
width: 24px;
height: 24px;
line-height: 24px;
}
#jstree-dnd.jstree-default .jstree-ok {
background-position: -4px -68px;
}
#jstree-dnd.jstree-default .jstree-er {
background-position: -36px -68px;
}
.jstree-default .jstree-ellipsis {
overflow: hidden;
}
.jstree-default .jstree-ellipsis .jstree-anchor {
width: calc(100% - 29px);
text-overflow: ellipsis;
overflow: hidden;
}
.jstree-default.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
}
.jstree-default.jstree-rtl .jstree-last {
background: transparent;
}
.jstree-default-small .jstree-node {
min-height: 18px;
line-height: 18px;
margin-left: 18px;
min-width: 18px;
}
.jstree-default-small .jstree-anchor {
line-height: 18px;
height: 18px;
}
.jstree-default-small .jstree-icon {
width: 18px;
height: 18px;
line-height: 18px;
}
.jstree-default-small .jstree-icon:empty {
width: 18px;
height: 18px;
line-height: 18px;
}
.jstree-default-small.jstree-rtl .jstree-node {
margin-right: 18px;
}
.jstree-default-small .jstree-wholerow {
height: 18px;
}
.jstree-default-small .jstree-node,
.jstree-default-small .jstree-icon {
background-image: url("32px.png");
}
.jstree-default-small .jstree-node {
background-position: -295px -7px;
background-repeat: repeat-y;
}
.jstree-default-small .jstree-last {
background: transparent;
}
.jstree-default-small .jstree-open > .jstree-ocl {
background-position: -135px -7px;
}
.jstree-default-small .jstree-closed > .jstree-ocl {
background-position: -103px -7px;
}
.jstree-default-small .jstree-leaf > .jstree-ocl {
background-position: -71px -7px;
}
.jstree-default-small .jstree-themeicon {
background-position: -263px -7px;
}
.jstree-default-small > .jstree-no-dots .jstree-node,
.jstree-default-small > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-small > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -39px -7px;
}
.jstree-default-small > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: -7px -7px;
}
.jstree-default-small .jstree-disabled {
background: transparent;
}
.jstree-default-small .jstree-disabled.jstree-hovered {
background: transparent;
}
.jstree-default-small .jstree-disabled.jstree-clicked {
background: #efefef;
}
.jstree-default-small .jstree-checkbox {
background-position: -167px -7px;
}
.jstree-default-small .jstree-checkbox:hover {
background-position: -167px -39px;
}
.jstree-default-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-small .jstree-checked > .jstree-checkbox {
background-position: -231px -7px;
}
.jstree-default-small.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-small .jstree-checked > .jstree-checkbox:hover {
background-position: -231px -39px;
}
.jstree-default-small .jstree-anchor > .jstree-undetermined {
background-position: -199px -7px;
}
.jstree-default-small .jstree-anchor > .jstree-undetermined:hover {
background-position: -199px -39px;
}
.jstree-default-small .jstree-checkbox-disabled {
opacity: 0.8;
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
/* Firefox 10+ * /
filter: gray;
/* IE6-9 * /
-webkit-filter: grayscale(100%);
/* Chrome 19+ & Safari 6+ * /
}
.jstree-default-small > .jstree-striped {
background-size: auto 36px;
}
.jstree-default-small.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
background-position: 100% 1px;
background-repeat: repeat-y;
}
.jstree-default-small.jstree-rtl .jstree-last {
background: transparent;
}
.jstree-default-small.jstree-rtl .jstree-open > .jstree-ocl {
background-position: -135px -39px;
}
.jstree-default-small.jstree-rtl .jstree-closed > .jstree-ocl {
background-position: -103px -39px;
}
.jstree-default-small.jstree-rtl .jstree-leaf > .jstree-ocl {
background-position: -71px -39px;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -39px -39px;
}
.jstree-default-small.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: -7px -39px;
}
.jstree-default-small .jstree-themeicon-custom {
background-color: transparent;
background-image: none;
background-position: 0 0;
}
.jstree-default-small > .jstree-container-ul .jstree-loading > .jstree-ocl {
background: url("throbber.gif") center center no-repeat;
}
.jstree-default-small .jstree-file {
background: url("32px.png") -103px -71px no-repeat;
}
.jstree-default-small .jstree-folder {
background: url("32px.png") -263px -7px no-repeat;
}
.jstree-default-small > .jstree-container-ul > .jstree-node {
margin-left: 0;
margin-right: 0;
}
#jstree-dnd.jstree-default-small {
line-height: 18px;
padding: 0 4px;
}
#jstree-dnd.jstree-default-small .jstree-ok,
#jstree-dnd.jstree-default-small .jstree-er {
background-image: url("32px.png");
background-repeat: no-repeat;
background-color: transparent;
}
#jstree-dnd.jstree-default-small i {
background: transparent;
width: 18px;
height: 18px;
line-height: 18px;
}
#jstree-dnd.jstree-default-small .jstree-ok {
background-position: -7px -71px;
}
#jstree-dnd.jstree-default-small .jstree-er {
background-position: -39px -71px;
}
.jstree-default-small .jstree-ellipsis {
overflow: hidden;
}
.jstree-default-small .jstree-ellipsis .jstree-anchor {
width: calc(100% - 23px);
text-overflow: ellipsis;
overflow: hidden;
}
.jstree-default-small.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==");
}
.jstree-default-small.jstree-rtl .jstree-last {
background: transparent;
}
.jstree-default-large .jstree-node {
min-height: 32px;
line-height: 32px;
margin-left: 32px;
min-width: 32px;
}
.jstree-default-large .jstree-anchor {
line-height: 32px;
height: 32px;
}
.jstree-default-large .jstree-icon {
width: 32px;
height: 32px;
line-height: 32px;
}
.jstree-default-large .jstree-icon:empty {
width: 32px;
height: 32px;
line-height: 32px;
}
.jstree-default-large.jstree-rtl .jstree-node {
margin-right: 32px;
}
.jstree-default-large .jstree-wholerow {
height: 32px;
}
.jstree-default-large .jstree-node,
.jstree-default-large .jstree-icon {
background-image: url("32px.png");
}
.jstree-default-large .jstree-node {
background-position: -288px 0px;
background-repeat: repeat-y;
}
.jstree-default-large .jstree-last {
background: transparent;
}
.jstree-default-large .jstree-open > .jstree-ocl {
background-position: -128px 0px;
}
.jstree-default-large .jstree-closed > .jstree-ocl {
background-position: -96px 0px;
}
.jstree-default-large .jstree-leaf > .jstree-ocl {
background-position: -64px 0px;
}
.jstree-default-large .jstree-themeicon {
background-position: -256px 0px;
}
.jstree-default-large > .jstree-no-dots .jstree-node,
.jstree-default-large > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-large > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -32px 0px;
}
.jstree-default-large > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: 0px 0px;
}
.jstree-default-large .jstree-disabled {
background: transparent;
}
.jstree-default-large .jstree-disabled.jstree-hovered {
background: transparent;
}
.jstree-default-large .jstree-disabled.jstree-clicked {
background: #efefef;
}
.jstree-default-large .jstree-checkbox {
background-position: -160px 0px;
}
.jstree-default-large .jstree-checkbox:hover {
background-position: -160px -32px;
}
.jstree-default-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-large .jstree-checked > .jstree-checkbox {
background-position: -224px 0px;
}
.jstree-default-large.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-large .jstree-checked > .jstree-checkbox:hover {
background-position: -224px -32px;
}
.jstree-default-large .jstree-anchor > .jstree-undetermined {
background-position: -192px 0px;
}
.jstree-default-large .jstree-anchor > .jstree-undetermined:hover {
background-position: -192px -32px;
}
.jstree-default-large .jstree-checkbox-disabled {
opacity: 0.8;
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'jstree-grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#jstree-grayscale");
/* Firefox 10+ * /
filter: gray;
/* IE6-9 * /
-webkit-filter: grayscale(100%);
/* Chrome 19+ & Safari 6+ * /
}
.jstree-default-large > .jstree-striped {
background-size: auto 64px;
}
.jstree-default-large.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==");
background-position: 100% 1px;
background-repeat: repeat-y;
}
.jstree-default-large.jstree-rtl .jstree-last {
background: transparent;
}
.jstree-default-large.jstree-rtl .jstree-open > .jstree-ocl {
background-position: -128px -32px;
}
.jstree-default-large.jstree-rtl .jstree-closed > .jstree-ocl {
background-position: -96px -32px;
}
.jstree-default-large.jstree-rtl .jstree-leaf > .jstree-ocl {
background-position: -64px -32px;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-node,
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-open > .jstree-ocl {
background-position: -32px -32px;
}
.jstree-default-large.jstree-rtl > .jstree-no-dots .jstree-closed > .jstree-ocl {
background-position: 0px -32px;
}
.jstree-default-large .jstree-themeicon-custom {
background-color: transparent;
background-image: none;
background-position: 0 0;
}
.jstree-default-large > .jstree-container-ul .jstree-loading > .jstree-ocl {
background: url("throbber.gif") center center no-repeat;
}
.jstree-default-large .jstree-file {
background: url("32px.png") -96px -64px no-repeat;
}
.jstree-default-large .jstree-folder {
background: url("32px.png") -256px 0px no-repeat;
}
.jstree-default-large > .jstree-container-ul > .jstree-node {
margin-left: 0;
margin-right: 0;
}
#jstree-dnd.jstree-default-large {
line-height: 32px;
padding: 0 4px;
}
#jstree-dnd.jstree-default-large .jstree-ok,
#jstree-dnd.jstree-default-large .jstree-er {
background-image: url("32px.png");
background-repeat: no-repeat;
background-color: transparent;
}
#jstree-dnd.jstree-default-large i {
background: transparent;
width: 32px;
height: 32px;
line-height: 32px;
}
#jstree-dnd.jstree-default-large .jstree-ok {
background-position: 0px -64px;
}
#jstree-dnd.jstree-default-large .jstree-er {
background-position: -32px -64px;
}
.jstree-default-large .jstree-ellipsis {
overflow: hidden;
}
.jstree-default-large .jstree-ellipsis .jstree-anchor {
width: calc(100% - 37px);
text-overflow: ellipsis;
overflow: hidden;
}
.jstree-default-large.jstree-rtl .jstree-node {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==");
}
.jstree-default-large.jstree-rtl .jstree-last {
background: transparent;
}
@media (max-width: 768px) {
#jstree-dnd.jstree-dnd-responsive {
line-height: 40px;
font-weight: bold;
font-size: 1.1em;
text-shadow: 1px 1px white;
}
#jstree-dnd.jstree-dnd-responsive > i {
background: transparent;
width: 40px;
height: 40px;
}
#jstree-dnd.jstree-dnd-responsive > .jstree-ok {
background-image: url("40px.png");
background-position: 0 -200px;
background-size: 120px 240px;
}
#jstree-dnd.jstree-dnd-responsive > .jstree-er {
background-image: url("40px.png");
background-position: -40px -200px;
background-size: 120px 240px;
}
#jstree-marker.jstree-dnd-responsive {
border-left-width: 10px;
border-top-width: 10px;
border-bottom-width: 10px;
margin-top: -10px;
}
}
@media (max-width: 768px) {
.jstree-default-responsive {
/*
.jstree-open > .jstree-ocl,
.jstree-closed > .jstree-ocl { border-radius:20px; background-color:white; }
* /
}
.jstree-default-responsive .jstree-icon {
background-image: url("40px.png");
}
.jstree-default-responsive .jstree-node,
.jstree-default-responsive .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-responsive .jstree-node {
min-height: 40px;
line-height: 40px;
margin-left: 40px;
min-width: 40px;
white-space: nowrap;
}
.jstree-default-responsive .jstree-anchor {
line-height: 40px;
height: 40px;
}
.jstree-default-responsive .jstree-icon,
.jstree-default-responsive .jstree-icon:empty {
width: 40px;
height: 40px;
line-height: 40px;
}
.jstree-default-responsive > .jstree-container-ul > .jstree-node {
margin-left: 0;
}
.jstree-default-responsive.jstree-rtl .jstree-node {
margin-left: 0;
margin-right: 40px;
background: transparent;
}
.jstree-default-responsive.jstree-rtl .jstree-container-ul > .jstree-node {
margin-right: 0;
}
.jstree-default-responsive .jstree-ocl,
.jstree-default-responsive .jstree-themeicon,
.jstree-default-responsive .jstree-checkbox {
background-size: 120px 240px;
}
.jstree-default-responsive .jstree-leaf > .jstree-ocl,
.jstree-default-responsive.jstree-rtl .jstree-leaf > .jstree-ocl {
background: transparent;
}
.jstree-default-responsive .jstree-open > .jstree-ocl {
background-position: 0 0 !important;
}
.jstree-default-responsive .jstree-closed > .jstree-ocl {
background-position: 0 -40px !important;
}
.jstree-default-responsive.jstree-rtl .jstree-closed > .jstree-ocl {
background-position: -40px 0 !important;
}
.jstree-default-responsive .jstree-themeicon {
background-position: -40px -40px;
}
.jstree-default-responsive .jstree-checkbox,
.jstree-default-responsive .jstree-checkbox:hover {
background-position: -40px -80px;
}
.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox,
.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked > .jstree-checkbox:hover,
.jstree-default-responsive .jstree-checked > .jstree-checkbox,
.jstree-default-responsive .jstree-checked > .jstree-checkbox:hover {
background-position: 0 -80px;
}
.jstree-default-responsive .jstree-anchor > .jstree-undetermined,
.jstree-default-responsive .jstree-anchor > .jstree-undetermined:hover {
background-position: 0 -120px;
}
.jstree-default-responsive .jstree-anchor {
font-weight: bold;
font-size: 1.1em;
text-shadow: 1px 1px white;
}
.jstree-default-responsive > .jstree-striped {
background: transparent;
}
.jstree-default-responsive .jstree-wholerow {
border-top: 1px solid rgba(255, 255, 255, 0.7);
border-bottom: 1px solid rgba(64, 64, 64, 0.2);
background: #ebebeb;
height: 40px;
}
.jstree-default-responsive .jstree-wholerow-hovered {
background: #e7f4f9;
}
.jstree-default-responsive .jstree-wholerow-clicked {
background: #beebff;
}
.jstree-default-responsive .jstree-children .jstree-last > .jstree-wholerow {
box-shadow: inset 0 -6px 3px -5px #666666;
}
.jstree-default-responsive .jstree-children .jstree-open > .jstree-wholerow {
box-shadow: inset 0 6px 3px -5px #666666;
border-top: 0;
}
.jstree-default-responsive .jstree-children .jstree-open + .jstree-open {
box-shadow: none;
}
.jstree-default-responsive .jstree-node,
.jstree-default-responsive .jstree-icon,
.jstree-default-responsive .jstree-node > .jstree-ocl,
.jstree-default-responsive .jstree-themeicon,
.jstree-default-responsive .jstree-checkbox {
background-image: url("40px.png");
background-size: 120px 240px;
}
.jstree-default-responsive .jstree-node {
background-position: -80px 0;
background-repeat: repeat-y;
}
.jstree-default-responsive .jstree-last {
background: transparent;
}
.jstree-default-responsive .jstree-leaf > .jstree-ocl {
background-position: -40px -120px;
}
.jstree-default-responsive .jstree-last > .jstree-ocl {
background-position: -40px -160px;
}
.jstree-default-responsive .jstree-themeicon-custom {
background-color: transparent;
background-image: none;
background-position: 0 0;
}
.jstree-default-responsive .jstree-file {
background: url("40px.png") 0 -160px no-repeat;
background-size: 120px 240px;
}
.jstree-default-responsive .jstree-folder {
background: url("40px.png") -40px -40px no-repeat;
background-size: 120px 240px;
}
.jstree-default-responsive > .jstree-container-ul > .jstree-node {
margin-left: 0;
margin-right: 0;
}
}
//}}}
!!!!!Code
***/
//{{{
/*globals jQuery, define, module, exports, require, window, document, postMessage */
(function (factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
}
else if(typeof module !== 'undefined' && module.exports) {
module.exports = factory(require('jquery'));
}
else {
factory(jQuery);
}
}(function ($, undefined) {
"use strict";
/*!
* jsTree 3.3.8
* http://jstree.com/
*
* Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
*
* Licensed same as jquery - under the terms of the MIT License
* http://www.opensource.org/licenses/mit-license.php
*/
/*!
* if using jslint please allow for the jQuery global and use following options:
* jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true
*/
/*jshint -W083 */
// prevent another load? maybe there is a better way?
if($.jstree) {
return;
}
/**
* ### jsTree core functionality
*/
// internal variables
var instance_counter = 0,
ccp_node = false,
ccp_mode = false,
ccp_inst = false,
themes_loaded = [],
src = $('script:last').attr('src'),
document = window.document; // local variable is always faster to access then a global
/**
* holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances.
* @name $.jstree
*/
$.jstree = {
/**
* specifies the jstree version in use
* @name $.jstree.version
*/
version : '3.3.8',
/**
* holds all the default options used when creating new instances
* @name $.jstree.defaults
*/
defaults : {
/**
* configure which plugins will be active on an instance. Should be an array of strings, where each element is a plugin name. The default is `[]`
* @name $.jstree.defaults.plugins
*/
plugins : []
},
/**
* stores all loaded jstree plugins (used internally)
* @name $.jstree.plugins
*/
plugins : {},
path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '',
idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g,
root : '#'
};
/**
* creates a jstree instance
* @name $.jstree.create(el [, options])
* @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector
* @param {Object} options options for this instance (extends `$.jstree.defaults`)
* @return {jsTree} the new instance
*/
$.jstree.create = function (el, options) {
var tmp = new $.jstree.core(++instance_counter),
opt = options;
options = $.extend(true, {}, $.jstree.defaults, options);
if(opt && opt.plugins) {
options.plugins = opt.plugins;
}
$.each(options.plugins, function (i, k) {
if(i !== 'core') {
tmp = tmp.plugin(k, options[k]);
}
});
$(el).data('jstree', tmp);
tmp.init(el, options);
return tmp;
};
/**
* remove all traces of jstree from the DOM and destroy all instances
* @name $.jstree.destroy()
*/
$.jstree.destroy = function () {
$('.jstree:jstree').jstree('destroy');
$(document).off('.jstree');
};
/**
* the jstree class constructor, used only internally
* @private
* @name $.jstree.core(id)
* @param {Number} id this instance's index
*/
$.jstree.core = function (id) {
this._id = id;
this._cnt = 0;
this._wrk = null;
this._data = {
core : {
themes : {
name : false,
dots : false,
icons : false,
ellipsis : false
},
selected : [],
last_error : {},
working : false,
worker_queue : [],
focused : null
}
};
};
/**
* get a reference to an existing instance
*
* __Examples__
*
* // provided a container with an ID of "tree", and a nested node with an ID of "branch"
* // all of there will return the same instance
* $.jstree.reference('tree');
* $.jstree.reference('#tree');
* $.jstree.reference($('#tree'));
* $.jstree.reference(document.getElementByID('tree'));
* $.jstree.reference('branch');
* $.jstree.reference('#branch');
* $.jstree.reference($('#branch'));
* $.jstree.reference(document.getElementByID('branch'));
*
* @name $.jstree.reference(needle)
* @param {DOMElement|jQuery|String} needle
* @return {jsTree|null} the instance or `null` if not found
*/
$.jstree.reference = function (needle) {
var tmp = null,
obj = null;
if(needle && needle.id && (!needle.tagName || !needle.nodeType)) { needle = needle.id; }
if(!obj || !obj.length) {
try { obj = $(needle); } catch (ignore) { }
}
if(!obj || !obj.length) {
try { obj = $('#' + needle.replace($.jstree.idregex,'\\$&')); } catch (ignore) { }
}
if(obj && obj.length && (obj = obj.closest('.jstree')).length && (obj = obj.data('jstree'))) {
tmp = obj;
}
else {
$('.jstree').each(function () {
var inst = $(this).data('jstree');
if(inst && inst._model.data[needle]) {
tmp = inst;
return false;
}
});
}
return tmp;
};
/**
* Create an instance, get an instance or invoke a command on a instance.
*
* If there is no instance associated with the current node a new one is created and `arg` is used to extend `$.jstree.defaults` for this new instance. There would be no return value (chaining is not broken).
*
* If there is an existing instance and `arg` is a string the command specified by `arg` is executed on the instance, with any additional arguments passed to the function. If the function returns a value it will be returned (chaining could break depending on function).
*
* If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`).
*
* In any other case - nothing is returned and chaining is not broken.
*
* __Examples__
*
* $('#tree1').jstree(); // creates an instance
* $('#tree2').jstree({ plugins : [] }); // create an instance with some options
* $('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments
* $('#tree2').jstree(); // get an existing instance (or create an instance)
* $('#tree2').jstree(true); // get an existing instance (will not create new instance)
* $('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method)
*
* @name $().jstree([arg])
* @param {String|Object} arg
* @return {Mixed}
*/
$.fn.jstree = function (arg) {
// check for string argument
var is_method = (typeof arg === 'string'),
args = Array.prototype.slice.call(arguments, 1),
result = null;
if(arg === true && !this.length) { return false; }
this.each(function () {
// get the instance (if there is one) and method (if it exists)
var instance = $.jstree.reference(this),
method = is_method && instance ? instance[arg] : null;
// if calling a method, and method is available - execute on the instance
result = is_method && method ?
method.apply(instance, args) :
null;
// if there is no instance and no method is being called - create one
if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) {
$.jstree.create(this, arg);
}
// if there is an instance and no method is called - return the instance
if( (instance && !is_method) || arg === true ) {
result = instance || false;
}
// if there was a method call which returned a result - break and return the value
if(result !== null && result !== undefined) {
return false;
}
});
// if there was a method call with a valid return value - return that, otherwise continue the chain
return result !== null && result !== undefined ?
result : this;
};
/**
* used to find elements containing an instance
*
* __Examples__
*
* $('div:jstree').each(function () {
* $(this).jstree('destroy');
* });
*
* @name $(':jstree')
* @return {jQuery}
*/
$.expr.pseudos.jstree = $.expr.createPseudo(function(search) {
return function(a) {
return $(a).hasClass('jstree') &&
$(a).data('jstree') !== undefined;
};
});
/**
* stores all defaults for the core
* @name $.jstree.defaults.core
*/
$.jstree.defaults.core = {
/**
* data configuration
*
* If left as `false` the HTML inside the jstree container element is used to populate the tree (that should be an unordered list with list items).
*
* You can also pass in a HTML string or a JSON array here.
*
* It is possible to pass in a standard jQuery-like AJAX config and jstree will automatically determine if the response is JSON or HTML and use that to populate the tree.
* In addition to the standard jQuery ajax options here you can suppy functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node is being loaded, the return value of those functions will be used.
*
* The last option is to specify a function, that function will receive the node being loaded as argument and a second param which is a function which should be called with the result.
*
* __Examples__
*
* // AJAX
* $('#tree').jstree({
* 'core' : {
* 'data' : {
* 'url' : '/get/children/',
* 'data' : function (node) {
* return { 'id' : node.id };
* }
* }
* });
*
* // direct data
* $('#tree').jstree({
* 'core' : {
* 'data' : [
* 'Simple root node',
* {
* 'id' : 'node_2',
* 'text' : 'Root node with options',
* 'state' : { 'opened' : true, 'selected' : true },
* 'children' : [ { 'text' : 'Child 1' }, 'Child 2']
* }
* ]
* }
* });
*
* // function
* $('#tree').jstree({
* 'core' : {
* 'data' : function (obj, callback) {
* callback.call(this, ['Root 1', 'Root 2']);
* }
* });
*
* @name $.jstree.defaults.core.data
*/
data : false,
/**
* configure the various strings used throughout the tree
*
* You can use an object where the key is the string you need to replace and the value is your replacement.
* Another option is to specify a function which will be called with an argument of the needed string and should return the replacement.
* If left as `false` no replacement is made.
*
* __Examples__
*
* $('#tree').jstree({
* 'core' : {
* 'strings' : {
* 'Loading ...' : 'Please wait ...'
* }
* }
* });
*
* @name $.jstree.defaults.core.strings
*/
strings : false,
/**
* determines what happens when a user tries to modify the structure of the tree
* If left as `false` all operations like create, rename, delete, move or copy are prevented.
* You can set this to `true` to allow all interactions or use a function to have better control.
*
* __Examples__
*
* $('#tree').jstree({
* 'core' : {
* 'check_callback' : function (operation, node, node_parent, node_position, more) {
* // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit'
* // in case of 'rename_node' node_position is filled with the new node name
* return operation === 'rename_node' ? true : false;
* }
* }
* });
*
* @name $.jstree.defaults.core.check_callback
*/
check_callback : false,
/**
* a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc)
* @name $.jstree.defaults.core.error
*/
error : $.noop,
/**
* the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`)
* @name $.jstree.defaults.core.animation
*/
animation : 200,
/**
* a boolean indicating if multiple nodes can be selected
* @name $.jstree.defaults.core.multiple
*/
multiple : true,
/**
* theme configuration object
* @name $.jstree.defaults.core.themes
*/
themes : {
/**
* the name of the theme to use (if left as `false` the default theme is used)
* @name $.jstree.defaults.core.themes.name
*/
name : false,
/**
* the URL of the theme's CSS file, leave this as `false` if you have manually included the theme CSS (recommended). You can set this to `true` too which will try to autoload the theme.
* @name $.jstree.defaults.core.themes.url
*/
url : false,
/**
* the location of all jstree themes - only used if `url` is set to `true`
* @name $.jstree.defaults.core.themes.dir
*/
dir : false,
/**
* a boolean indicating if connecting dots are shown
* @name $.jstree.defaults.core.themes.dots
*/
dots : true,
/**
* a boolean indicating if node icons are shown
* @name $.jstree.defaults.core.themes.icons
*/
icons : true,
/**
* a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container
* @name $.jstree.defaults.core.themes.ellipsis
*/
ellipsis : false,
/**
* a boolean indicating if the tree background is striped
* @name $.jstree.defaults.core.themes.stripes
*/
stripes : false,
/**
* a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants)
* @name $.jstree.defaults.core.themes.variant
*/
variant : false,
/**
* a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `false`.
* @name $.jstree.defaults.core.themes.responsive
*/
responsive : false
},
/**
* if left as `true` all parents of all selected nodes will be opened once the tree loads (so that all selected nodes are visible to the user)
* @name $.jstree.defaults.core.expand_selected_onload
*/
expand_selected_onload : true,
/**
* if left as `true` web workers will be used to parse incoming JSON data where possible, so that the UI will not be blocked by large requests. Workers are however about 30% slower. Defaults to `true`
* @name $.jstree.defaults.core.worker
*/
worker : true,
/**
* Force node text to plain text (and escape HTML). Defaults to `false`
* @name $.jstree.defaults.core.force_text
*/
force_text : false,
/**
* Should the node be toggled if the text is double clicked. Defaults to `true`
* @name $.jstree.defaults.core.dblclick_toggle
*/
dblclick_toggle : true,
/**
* Should the loaded nodes be part of the state. Defaults to `false`
* @name $.jstree.defaults.core.loaded_state
*/
loaded_state : false,
/**
* Should the last active node be focused when the tree container is blurred and the focused again. This helps working with screen readers. Defaults to `true`
* @name $.jstree.defaults.core.restore_focus
*/
restore_focus : true,
/**
* Default keyboard shortcuts (an object where each key is the button name or combo - like 'enter', 'ctrl-space', 'p', etc and the value is the function to execute in the instance's scope)
* @name $.jstree.defaults.core.keyboard
*/
keyboard : {
'ctrl-space': function (e) {
// aria defines space only with Ctrl
e.type = "click";
$(e.currentTarget).trigger(e);
},
'enter': function (e) {
// enter
e.type = "click";
$(e.currentTarget).trigger(e);
},
'left': function (e) {
// left
e.preventDefault();
if(this.is_open(e.currentTarget)) {
this.close_node(e.currentTarget);
}
else {
var o = this.get_parent(e.currentTarget);
if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); }
}
},
'up': function (e) {
// up
e.preventDefault();
var o = this.get_prev_dom(e.currentTarget);
if(o && o.length) { o.children('.jstree-anchor').focus(); }
},
'right': function (e) {
// right
e.preventDefault();
if(this.is_closed(e.currentTarget)) {
this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); });
}
else if (this.is_open(e.currentTarget)) {
var o = this.get_node(e.currentTarget, true).children('.jstree-children')[0];
if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); }
}
},
'down': function (e) {
// down
e.preventDefault();
var o = this.get_next_dom(e.currentTarget);
if(o && o.length) { o.children('.jstree-anchor').focus(); }
},
'*': function (e) {
// aria defines * on numpad as open_all - not very common
this.open_all();
},
'home': function (e) {
// home
e.preventDefault();
var o = this._firstChild(this.get_container_ul()[0]);
if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); }
},
'end': function (e) {
// end
e.preventDefault();
this.element.find('.jstree-anchor').filter(':visible').last().focus();
},
'f2': function (e) {
// f2 - safe to include - if check_callback is false it will fail
e.preventDefault();
this.edit(e.currentTarget);
}
}
};
$.jstree.core.prototype = {
/**
* used to decorate an instance with a plugin. Used internally.
* @private
* @name plugin(deco [, opts])
* @param {String} deco the plugin to decorate with
* @param {Object} opts options for the plugin
* @return {jsTree}
*/
plugin : function (deco, opts) {
var Child = $.jstree.plugins[deco];
if(Child) {
this._data[deco] = {};
Child.prototype = this;
return new Child(opts, this);
}
return this;
},
/**
* initialize the instance. Used internally.
* @private
* @name init(el, optons)
* @param {DOMElement|jQuery|String} el the element we are transforming
* @param {Object} options options for this instance
* @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree
*/
init : function (el, options) {
this._model = {
data : {},
changed : [],
force_full_redraw : false,
redraw_timeout : false,
default_state : {
loaded : true,
opened : false,
selected : false,
disabled : false
}
};
this._model.data[$.jstree.root] = {
id : $.jstree.root,
parent : null,
parents : [],
children : [],
children_d : [],
state : { loaded : false }
};
this.element = $(el).addClass('jstree jstree-' + this._id);
this.settings = options;
this._data.core.ready = false;
this._data.core.loaded = false;
this._data.core.rtl = (this.element.css("direction") === "rtl");
this.element[this._data.core.rtl ? 'addClass' : 'removeClass']("jstree-rtl");
this.element.attr('role','tree');
if(this.settings.core.multiple) {
this.element.attr('aria-multiselectable', true);
}
if(!this.element.attr('tabindex')) {
this.element.attr('tabindex','0');
}
this.bind();
/**
* triggered after all events are bound
* @event
* @name init.jstree
*/
this.trigger("init");
this._data.core.original_container_html = this.element.find(" > ul > li").clone(true);
this._data.core.original_container_html
.find("li").addBack()
.contents().filter(function() {
return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue));
})
.remove();
this.element.html("<"+"ul class='jstree-container-ul jstree-children' role='group'><"+"li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
this.element.attr('aria-activedescendant','j' + this._id + '_loading');
this._data.core.li_height = this.get_container_ul().children("li").first().outerHeight() || 24;
this._data.core.node = this._create_prototype_node();
/**
* triggered after the loading text is shown and before loading starts
* @event
* @name loading.jstree
*/
this.trigger("loading");
this.load_node($.jstree.root);
},
/**
* destroy an instance
* @name destroy()
* @param {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact
*/
destroy : function (keep_html) {
/**
* triggered before the tree is destroyed
* @event
* @name destroy.jstree
*/
this.trigger("destroy");
if(this._wrk) {
try {
window.URL.revokeObjectURL(this._wrk);
this._wrk = null;
}
catch (ignore) { }
}
if(!keep_html) { this.element.empty(); }
this.teardown();
},
/**
* Create a prototype node
* @name _create_prototype_node()
* @return {DOMElement}
*/
_create_prototype_node : function () {
var _node = document.createElement('LI'), _temp1, _temp2;
_node.setAttribute('role', 'treeitem');
_temp1 = document.createElement('I');
_temp1.className = 'jstree-icon jstree-ocl';
_temp1.setAttribute('role', 'presentation');
_node.appendChild(_temp1);
_temp1 = document.createElement('A');
_temp1.className = 'jstree-anchor';
_temp1.setAttribute('href','#');
_temp1.setAttribute('tabindex','-1');
_temp2 = document.createElement('I');
_temp2.className = 'jstree-icon jstree-themeicon';
_temp2.setAttribute('role', 'presentation');
_temp1.appendChild(_temp2);
_node.appendChild(_temp1);
_temp1 = _temp2 = null;
return _node;
},
_kbevent_to_func : function (e) {
var keys = {
8: "Backspace", 9: "Tab", 13: "Enter", 19: "Pause", 27: "Esc",
32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home",
37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "Print", 45: "Insert",
46: "Delete", 96: "Numpad0", 97: "Numpad1", 98: "Numpad2", 99 : "Numpad3",
100: "Numpad4", 101: "Numpad5", 102: "Numpad6", 103: "Numpad7",
104: "Numpad8", 105: "Numpad9", '-13': "NumpadEnter", 112: "F1",
113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7",
119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 144: "Numlock",
145: "Scrolllock", 16: 'Shift', 17: 'Ctrl', 18: 'Alt',
48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5',
54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 61: '=', 65: 'a',
66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h',
73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o',
80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v',
87: 'w', 88: 'x', 89: 'y', 90: 'z', 107: '+', 109: '-', 110: '.',
186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`',
219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*', 173: '-'
};
var parts = [];
if (e.ctrlKey) { parts.push('ctrl'); }
if (e.altKey) { parts.push('alt'); }
if (e.shiftKey) { parts.push('shift'); }
parts.push(keys[e.which] || e.which);
parts = parts.sort().join('-').toLowerCase();
var kb = this.settings.core.keyboard, i, tmp;
for (i in kb) {
if (kb.hasOwnProperty(i)) {
tmp = i;
if (tmp !== '-' && tmp !== '+') {
tmp = tmp.replace('--', '-MINUS').replace('+-', '-MINUS').replace('++', '-PLUS').replace('-+', '-PLUS');
tmp = tmp.split(/-|\+/).sort().join('-').replace('MINUS', '-').replace('PLUS', '+').toLowerCase();
}
if (tmp === parts) {
return kb[i];
}
}
}
return null;
},
/**
* part of the destroying of an instance. Used internally.
* @private
* @name teardown()
*/
teardown : function () {
this.unbind();
this.element
.removeClass('jstree')
.removeData('jstree')
.find("[class^='jstree']")
.addBack()
.attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); });
this.element = null;
},
/**
* bind all events. Used internally.
* @private
* @name bind()
*/
bind : function () {
var word = '',
tout = null,
was_click = 0;
this.element
.on("dblclick.jstree", function (e) {
if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
if(document.selection && document.selection.empty) {
document.selection.empty();
}
else {
if(window.getSelection) {
var sel = window.getSelection();
try {
sel.removeAllRanges();
sel.collapse();
} catch (ignore) { }
}
}
})
.on("mousedown.jstree", $.proxy(function (e) {
if(e.target === this.element[0]) {
e.preventDefault(); // prevent losing focus when clicking scroll arrows (FF, Chrome)
was_click = +(new Date()); // ie does not allow to prevent losing focus
}
}, this))
.on("mousedown.jstree", ".jstree-ocl", function (e) {
e.preventDefault(); // prevent any node inside from losing focus when clicking the open/close icon
})
.on("click.jstree", ".jstree-ocl", $.proxy(function (e) {
this.toggle_node(e.target);
}, this))
.on("dblclick.jstree", ".jstree-anchor", $.proxy(function (e) {
if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
if(this.settings.core.dblclick_toggle) {
this.toggle_node(e.target);
}
}, this))
.on("click.jstree", ".jstree-anchor", $.proxy(function (e) {
e.preventDefault();
if(e.currentTarget !== document.activeElement) { $(e.currentTarget).focus(); }
this.activate_node(e.currentTarget, e);
}, this))
.on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) {
if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
if(this._data.core.rtl) {
if(e.which === 37) { e.which = 39; }
else if(e.which === 39) { e.which = 37; }
}
var f = this._kbevent_to_func(e);
if (f) {
var r = f.call(this, e);
if (r === false || r === true) {
return r;
}
}
}, this))
.on("load_node.jstree", $.proxy(function (e, data) {
if(data.status) {
if(data.node.id === $.jstree.root && !this._data.core.loaded) {
this._data.core.loaded = true;
if(this._firstChild(this.get_container_ul()[0])) {
this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
}
/**
* triggered after the root node is loaded for the first time
* @event
* @name loaded.jstree
*/
this.trigger("loaded");
}
if(!this._data.core.ready) {
setTimeout($.proxy(function() {
if(this.element && !this.get_container_ul().find('.jstree-loading').length) {
this._data.core.ready = true;
if(this._data.core.selected.length) {
if(this.settings.core.expand_selected_onload) {
var tmp = [], i, j;
for(i = 0, j = this._data.core.selected.length; i < j; i++) {
tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents);
}
tmp = $.vakata.array_unique(tmp);
for(i = 0, j = tmp.length; i < j; i++) {
this.open_node(tmp[i], false, 0);
}
}
this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected });
}
/**
* triggered after all nodes are finished loading
* @event
* @name ready.jstree
*/
this.trigger("ready");
}
}, this), 0);
}
}
}, this))
// quick searching when the tree is focused
.on('keypress.jstree', $.proxy(function (e) {
if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
if(tout) { clearTimeout(tout); }
tout = setTimeout(function () {
word = '';
}, 500);
var chr = String.fromCharCode(e.which).toLowerCase(),
col = this.element.find('.jstree-anchor').filter(':visible'),
ind = col.index(document.activeElement) || 0,
end = false;
word += chr;
// match for whole word from current node down (including the current node)
if(word.length > 1) {
col.slice(ind).each($.proxy(function (i, v) {
if($(v).text().toLowerCase().indexOf(word) === 0) {
$(v).focus();
end = true;
return false;
}
}, this));
if(end) { return; }
// match for whole word from the beginning of the tree
col.slice(0, ind).each($.proxy(function (i, v) {
if($(v).text().toLowerCase().indexOf(word) === 0) {
$(v).focus();
end = true;
return false;
}
}, this));
if(end) { return; }
}
// list nodes that start with that letter (only if word consists of a single char)
if(new RegExp('^' + chr.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '+$').test(word)) {
// search for the next node starting with that letter
col.slice(ind + 1).each($.proxy(function (i, v) {
if($(v).text().toLowerCase().charAt(0) === chr) {
$(v).focus();
end = true;
return false;
}
}, this));
if(end) { return; }
// search from the beginning
col.slice(0, ind + 1).each($.proxy(function (i, v) {
if($(v).text().toLowerCase().charAt(0) === chr) {
$(v).focus();
end = true;
return false;
}
}, this));
if(end) { return; }
}
}, this))
// THEME RELATED
.on("init.jstree", $.proxy(function () {
var s = this.settings.core.themes;
this._data.core.themes.dots = s.dots;
this._data.core.themes.stripes = s.stripes;
this._data.core.themes.icons = s.icons;
this._data.core.themes.ellipsis = s.ellipsis;
this.set_theme(s.name || "default", s.url);
this.set_theme_variant(s.variant);
}, this))
.on("loading.jstree", $.proxy(function () {
this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ]();
this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ]();
this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ]();
this[ this._data.core.themes.ellipsis ? "show_ellipsis" : "hide_ellipsis" ]();
}, this))
.on('blur.jstree', '.jstree-anchor', $.proxy(function (e) {
this._data.core.focused = null;
$(e.currentTarget).filter('.jstree-hovered').trigger('mouseleave');
this.element.attr('tabindex', '0');
}, this))
.on('focus.jstree', '.jstree-anchor', $.proxy(function (e) {
var tmp = this.get_node(e.currentTarget);
if(tmp && tmp.id) {
this._data.core.focused = tmp.id;
}
this.element.find('.jstree-hovered').not(e.currentTarget).trigger('mouseleave');
$(e.currentTarget).trigger('mouseenter');
this.element.attr('tabindex', '-1');
}, this))
.on('focus.jstree', $.proxy(function () {
if(+(new Date()) - was_click > 500 && !this._data.core.focused && this.settings.core.restore_focus) {
was_click = 0;
var act = this.get_node(this.element.attr('aria-activedescendant'), true);
if(act) {
act.find('> .jstree-anchor').focus();
}
}
}, this))
.on('mouseenter.jstree', '.jstree-anchor', $.proxy(function (e) {
this.hover_node(e.currentTarget);
}, this))
.on('mouseleave.jstree', '.jstree-anchor', $.proxy(function (e) {
this.dehover_node(e.currentTarget);
}, this));
},
/**
* part of the destroying of an instance. Used internally.
* @private
* @name unbind()
*/
unbind : function () {
this.element.off('.jstree');
$(document).off('.jstree-' + this._id);
},
/**
* trigger an event. Used internally.
* @private
* @name trigger(ev [, data])
* @param {String} ev the name of the event to trigger
* @param {Object} data additional data to pass with the event
*/
trigger : function (ev, data) {
if(!data) {
data = {};
}
data.instance = this;
this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data);
},
/**
* returns the jQuery extended instance container
* @name get_container()
* @return {jQuery}
*/
get_container : function () {
return this.element;
},
/**
* returns the jQuery extended main UL node inside the instance container. Used internally.
* @private
* @name get_container_ul()
* @return {jQuery}
*/
get_container_ul : function () {
return this.element.children(".jstree-children").first();
},
/**
* gets string replacements (localization). Used internally.
* @private
* @name get_string(key)
* @param {String} key
* @return {String}
*/
get_string : function (key) {
var a = this.settings.core.strings;
if($.isFunction(a)) { return a.call(this, key); }
if(a && a[key]) { return a[key]; }
return key;
},
/**
* gets the first child of a DOM node. Used internally.
* @private
* @name _firstChild(dom)
* @param {DOMElement} dom
* @return {DOMElement}
*/
_firstChild : function (dom) {
dom = dom ? dom.firstChild : null;
while(dom !== null && dom.nodeType !== 1) {
dom = dom.nextSibling;
}
return dom;
},
/**
* gets the next sibling of a DOM node. Used internally.
* @private
* @name _nextSibling(dom)
* @param {DOMElement} dom
* @return {DOMElement}
*/
_nextSibling : function (dom) {
dom = dom ? dom.nextSibling : null;
while(dom !== null && dom.nodeType !== 1) {
dom = dom.nextSibling;
}
return dom;
},
/**
* gets the previous sibling of a DOM node. Used internally.
* @private
* @name _previousSibling(dom)
* @param {DOMElement} dom
* @return {DOMElement}
*/
_previousSibling : function (dom) {
dom = dom ? dom.previousSibling : null;
while(dom !== null && dom.nodeType !== 1) {
dom = dom.previousSibling;
}
return dom;
},
/**
* get the JSON representation of a node (or the actual jQuery extended DOM node) by using any input (child DOM element, ID string, selector, etc)
* @name get_node(obj [, as_dom])
* @param {mixed} obj
* @param {Boolean} as_dom
* @return {Object|jQuery}
*/
get_node : function (obj, as_dom) {
if(obj && obj.id) {
obj = obj.id;
}
if (obj instanceof $ && obj.length && obj[0].id) {
obj = obj[0].id;
}
var dom;
try {
if(this._model.data[obj]) {
obj = this._model.data[obj];
}
else if(typeof obj === "string" && this._model.data[obj.replace(/^#/, '')]) {
obj = this._model.data[obj.replace(/^#/, '')];
}
else if(typeof obj === "string" && (dom = $('#' + obj.replace($.jstree.idregex,'\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
obj = this._model.data[dom.closest('.jstree-node').attr('id')];
}
else if((dom = this.element.find(obj)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
obj = this._model.data[dom.closest('.jstree-node').attr('id')];
}
else if((dom = this.element.find(obj)).length && dom.hasClass('jstree')) {
obj = this._model.data[$.jstree.root];
}
else {
return false;
}
if(as_dom) {
obj = obj.id === $.jstree.root ? this.element : $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
}
return obj;
} catch (ex) { return false; }
},
/**
* get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array)
* @name get_path(obj [, glue, ids])
* @param {mixed} obj the node
* @param {String} glue if you want the path as a string - pass the glue here (for example '/'), if a falsy value is supplied here, an array is returned
* @param {Boolean} ids if set to true build the path using ID, otherwise node text is used
* @return {mixed}
*/
get_path : function (obj, glue, ids) {
obj = obj.parents ? obj : this.get_node(obj);
if(!obj || obj.id === $.jstree.root || !obj.parents) {
return false;
}
var i, j, p = [];
p.push(ids ? obj.id : obj.text);
for(i = 0, j = obj.parents.length; i < j; i++) {
p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i]));
}
p = p.reverse().slice(1);
return glue ? p.join(glue) : p;
},
/**
* get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
* @name get_next_dom(obj [, strict])
* @param {mixed} obj
* @param {Boolean} strict
* @return {jQuery}
*/
get_next_dom : function (obj, strict) {
var tmp;
obj = this.get_node(obj, true);
if(obj[0] === this.element[0]) {
tmp = this._firstChild(this.get_container_ul()[0]);
while (tmp && tmp.offsetHeight === 0) {
tmp = this._nextSibling(tmp);
}
return tmp ? $(tmp) : false;
}
if(!obj || !obj.length) {
return false;
}
if(strict) {
tmp = obj[0];
do {
tmp = this._nextSibling(tmp);
} while (tmp && tmp.offsetHeight === 0);
return tmp ? $(tmp) : false;
}
if(obj.hasClass("jstree-open")) {
tmp = this._firstChild(obj.children('.jstree-children')[0]);
while (tmp && tmp.offsetHeight === 0) {
tmp = this._nextSibling(tmp);
}
if(tmp !== null) {
return $(tmp);
}
}
tmp = obj[0];
do {
tmp = this._nextSibling(tmp);
} while (tmp && tmp.offsetHeight === 0);
if(tmp !== null) {
return $(tmp);
}
return obj.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first();
},
/**
* get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
* @name get_prev_dom(obj [, strict])
* @param {mixed} obj
* @param {Boolean} strict
* @return {jQuery}
*/
get_prev_dom : function (obj, strict) {
var tmp;
obj = this.get_node(obj, true);
if(obj[0] === this.element[0]) {
tmp = this.get_container_ul()[0].lastChild;
while (tmp && tmp.offsetHeight === 0) {
tmp = this._previousSibling(tmp);
}
return tmp ? $(tmp) : false;
}
if(!obj || !obj.length) {
return false;
}
if(strict) {
tmp = obj[0];
do {
tmp = this._previousSibling(tmp);
} while (tmp && tmp.offsetHeight === 0);
return tmp ? $(tmp) : false;
}
tmp = obj[0];
do {
tmp = this._previousSibling(tmp);
} while (tmp && tmp.offsetHeight === 0);
if(tmp !== null) {
obj = $(tmp);
while(obj.hasClass("jstree-open")) {
obj = obj.children(".jstree-children").first().children(".jstree-node:visible:last");
}
return obj;
}
tmp = obj[0].parentNode.parentNode;
return tmp && tmp.className && tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false;
},
/**
* get the parent ID of a node
* @name get_parent(obj)
* @param {mixed} obj
* @return {String}
*/
get_parent : function (obj) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
return obj.parent;
},
/**
* get a jQuery collection of all the children of a node (node must be rendered), returns false on error
* @name get_children_dom(obj)
* @param {mixed} obj
* @return {jQuery}
*/
get_children_dom : function (obj) {
obj = this.get_node(obj, true);
if(obj[0] === this.element[0]) {
return this.get_container_ul().children(".jstree-node");
}
if(!obj || !obj.length) {
return false;
}
return obj.children(".jstree-children").children(".jstree-node");
},
/**
* checks if a node has children
* @name is_parent(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_parent : function (obj) {
obj = this.get_node(obj);
return obj && (obj.state.loaded === false || obj.children.length > 0);
},
/**
* checks if a node is loaded (its children are available)
* @name is_loaded(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_loaded : function (obj) {
obj = this.get_node(obj);
return obj && obj.state.loaded;
},
/**
* check if a node is currently loading (fetching children)
* @name is_loading(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_loading : function (obj) {
obj = this.get_node(obj);
return obj && obj.state && obj.state.loading;
},
/**
* check if a node is opened
* @name is_open(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_open : function (obj) {
obj = this.get_node(obj);
return obj && obj.state.opened;
},
/**
* check if a node is in a closed state
* @name is_closed(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_closed : function (obj) {
obj = this.get_node(obj);
return obj && this.is_parent(obj) && !obj.state.opened;
},
/**
* check if a node has no children
* @name is_leaf(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_leaf : function (obj) {
return !this.is_parent(obj);
},
/**
* loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array.
* @name load_node(obj [, callback])
* @param {mixed} obj
* @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status
* @return {Boolean}
* @trigger load_node.jstree
*/
load_node : function (obj, callback) {
var k, l, i, j, c;
if($.isArray(obj)) {
this._load_nodes(obj.slice(), callback);
return true;
}
obj = this.get_node(obj);
if(!obj) {
if(callback) { callback.call(this, obj, false); }
return false;
}
// if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again?
if(obj.state.loaded) {
obj.state.loaded = false;
for(i = 0, j = obj.parents.length; i < j; i++) {
this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
return $.inArray(v, obj.children_d) === -1;
});
}
for(k = 0, l = obj.children_d.length; k < l; k++) {
if(this._model.data[obj.children_d[k]].state.selected) {
c = true;
}
delete this._model.data[obj.children_d[k]];
}
if (c) {
this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
return $.inArray(v, obj.children_d) === -1;
});
}
obj.children = [];
obj.children_d = [];
if(c) {
this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected });
}
}
obj.state.failed = false;
obj.state.loading = true;
this.get_node(obj, true).addClass("jstree-loading").attr('aria-busy',true);
this._load_node(obj, $.proxy(function (status) {
obj = this._model.data[obj.id];
obj.state.loading = false;
obj.state.loaded = status;
obj.state.failed = !obj.state.loaded;
var dom = this.get_node(obj, true), i = 0, j = 0, m = this._model.data, has_children = false;
for(i = 0, j = obj.children.length; i < j; i++) {
if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) {
has_children = true;
break;
}
}
if(obj.state.loaded && dom && dom.length) {
dom.removeClass('jstree-closed jstree-open jstree-leaf');
if (!has_children) {
dom.addClass('jstree-leaf');
}
else {
if (obj.id !== '#') {
dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed');
}
}
}
dom.removeClass("jstree-loading").attr('aria-busy',false);
/**
* triggered after a node is loaded
* @event
* @name load_node.jstree
* @param {Object} node the node that was loading
* @param {Boolean} status was the node loaded successfully
*/
this.trigger('load_node', { "node" : obj, "status" : status });
if(callback) {
callback.call(this, obj, status);
}
}, this));
return true;
},
/**
* load an array of nodes (will also load unavailable nodes as soon as they appear in the structure). Used internally.
* @private
* @name _load_nodes(nodes [, callback])
* @param {array} nodes
* @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes
*/
_load_nodes : function (nodes, callback, is_callback, force_reload) {
var r = true,
c = function () { this._load_nodes(nodes, callback, true); },
m = this._model.data, i, j, tmp = [];
for(i = 0, j = nodes.length; i < j; i++) {
if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) {
if(!this.is_loading(nodes[i])) {
this.load_node(nodes[i], c);
}
r = false;
}
}
if(r) {
for(i = 0, j = nodes.length; i < j; i++) {
if(m[nodes[i]] && m[nodes[i]].state.loaded) {
tmp.push(nodes[i]);
}
}
if(callback && !callback.done) {
callback.call(this, tmp);
callback.done = true;
}
}
},
/**
* loads all unloaded nodes
* @name load_all([obj, callback])
* @param {mixed} obj the node to load recursively, omit to load all nodes in the tree
* @param {function} callback a function to be executed once loading all the nodes is complete,
* @trigger load_all.jstree
*/
load_all : function (obj, callback) {
if(!obj) { obj = $.jstree.root; }
obj = this.get_node(obj);
if(!obj) { return false; }
var to_load = [],
m = this._model.data,
c = m[obj.id].children_d,
i, j;
if(obj.state && !obj.state.loaded) {
to_load.push(obj.id);
}
for(i = 0, j = c.length; i < j; i++) {
if(m[c[i]] && m[c[i]].state && !m[c[i]].state.loaded) {
to_load.push(c[i]);
}
}
if(to_load.length) {
this._load_nodes(to_load, function () {
this.load_all(obj, callback);
});
}
else {
/**
* triggered after a load_all call completes
* @event
* @name load_all.jstree
* @param {Object} node the recursively loaded node
*/
if(callback) { callback.call(this, obj); }
this.trigger('load_all', { "node" : obj });
}
},
/**
* handles the actual loading of a node. Used only internally.
* @private
* @name _load_node(obj [, callback])
* @param {mixed} obj
* @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status
* @return {Boolean}
*/
_load_node : function (obj, callback) {
var s = this.settings.core.data, t;
var notTextOrCommentNode = function notTextOrCommentNode () {
return this.nodeType !== 3 && this.nodeType !== 8;
};
// use original HTML
if(!s) {
if(obj.id === $.jstree.root) {
return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) {
callback.call(this, status);
});
}
else {
return callback.call(this, false);
}
// return callback.call(this, obj.id === $.jstree.root ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false);
}
if($.isFunction(s)) {
return s.call(this, obj, $.proxy(function (d) {
if(d === false) {
callback.call(this, false);
}
else {
this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) {
callback.call(this, status);
});
}
// return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d));
}, this));
}
if(typeof s === 'object') {
if(s.url) {
s = $.extend(true, {}, s);
if($.isFunction(s.url)) {
s.url = s.url.call(this, obj);
}
if($.isFunction(s.data)) {
s.data = s.data.call(this, obj);
}
return $.ajax(s)
.done($.proxy(function (d,t,x) {
var type = x.getResponseHeader('Content-Type');
if((type && type.indexOf('json') !== -1) || typeof d === "object") {
return this._append_json_data(obj, d, function (status) { callback.call(this, status); });
//return callback.call(this, this._append_json_data(obj, d));
}
if((type && type.indexOf('html') !== -1) || typeof d === "string") {
return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); });
// return callback.call(this, this._append_html_data(obj, $(d)));
}
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) };
this.settings.core.error.call(this, this._data.core.last_error);
return callback.call(this, false);
}, this))
.fail($.proxy(function (f) {
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) };
callback.call(this, false);
this.settings.core.error.call(this, this._data.core.last_error);
}, this));
}
if ($.isArray(s)) {
t = $.extend(true, [], s);
} else if ($.isPlainObject(s)) {
t = $.extend(true, {}, s);
} else {
t = s;
}
if(obj.id === $.jstree.root) {
return this._append_json_data(obj, t, function (status) {
callback.call(this, status);
});
}
else {
this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
this.settings.core.error.call(this, this._data.core.last_error);
return callback.call(this, false);
}
//return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) );
}
if(typeof s === 'string') {
if(obj.id === $.jstree.root) {
return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) {
callback.call(this, status);
});
}
else {
this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
this.settings.core.error.call(this, this._data.core.last_error);
return callback.call(this, false);
}
//return callback.call(this, (obj.id === $.jstree.root ? this._append_html_data(obj, $(s)) : false) );
}
return callback.call(this, false);
},
/**
* adds a node to the list of nodes to redraw. Used only internally.
* @private
* @name _node_changed(obj [, callback])
* @param {mixed} obj
*/
_node_changed : function (obj) {
obj = this.get_node(obj);
if (obj && $.inArray(obj.id, this._model.changed) === -1) {
this._model.changed.push(obj.id);
}
},
/**
* appends HTML content to the tree. Used internally.
* @private
* @name _append_html_data(obj, data)
* @param {mixed} obj the node to append to
* @param {String} data the HTML string to parse and append
* @trigger model.jstree, changed.jstree
*/
_append_html_data : function (dom, data, cb) {
dom = this.get_node(dom);
dom.children = [];
dom.children_d = [];
var dat = data.is('ul') ? data.children() : data,
par = dom.id,
chd = [],
dpc = [],
m = this._model.data,
p = m[par],
s = this._data.core.selected.length,
tmp, i, j;
dat.each($.proxy(function (i, v) {
tmp = this._parse_model_from_html($(v), par, p.parents.concat());
if(tmp) {
chd.push(tmp);
dpc.push(tmp);
if(m[tmp].children_d.length) {
dpc = dpc.concat(m[tmp].children_d);
}
}
}, this));
p.children = chd;
p.children_d = dpc;
for(i = 0, j = p.parents.length; i < j; i++) {
m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
}
/**
* triggered when new data is inserted to the tree model
* @event
* @name model.jstree
* @param {Array} nodes an array of node IDs
* @param {String} parent the parent ID of the nodes
*/
this.trigger('model', { "nodes" : dpc, 'parent' : par });
if(par !== $.jstree.root) {
this._node_changed(par);
this.redraw();
}
else {
this.get_container_ul().children('.jstree-initial-node').remove();
this.redraw(true);
}
if(this._data.core.selected.length !== s) {
this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
}
cb.call(this, true);
},
/**
* appends JSON content to the tree. Used internally.
* @private
* @name _append_json_data(obj, data)
* @param {mixed} obj the node to append to
* @param {String} data the JSON object to parse and append
* @param {Boolean} force_processing internal param - do not set
* @trigger model.jstree, changed.jstree
*/
_append_json_data : function (dom, data, cb, force_processing) {
if(this.element === null) { return; }
dom = this.get_node(dom);
dom.children = [];
dom.children_d = [];
// *%$@!!!
if(data.d) {
data = data.d;
if(typeof data === "string") {
data = JSON.parse(data);
}
}
if(!$.isArray(data)) { data = [data]; }
var w = null,
args = {
'df' : this._model.default_state,
'dat' : data,
'par' : dom.id,
'm' : this._model.data,
't_id' : this._id,
't_cnt' : this._cnt,
'sel' : this._data.core.selected
},
inst = this,
func = function (data, undefined) {
if(data.data) { data = data.data; }
var dat = data.dat,
par = data.par,
chd = [],
dpc = [],
add = [],
df = data.df,
t_id = data.t_id,
t_cnt = data.t_cnt,
m = data.m,
p = m[par],
sel = data.sel,
tmp, i, j, rslt,
parse_flat = function (d, p, ps) {
if(!ps) { ps = []; }
else { ps = ps.concat(); }
if(p) { ps.unshift(p); }
var tid = d.id.toString(),
i, j, c, e,
tmp = {
id : tid,
text : d.text || '',
icon : d.icon !== undefined ? d.icon : true,
parent : p,
parents : ps,
children : d.children || [],
children_d : d.children_d || [],
data : d.data,
state : { },
li_attr : { id : false },
a_attr : { href : '#' },
original : false
};
for(i in df) {
if(df.hasOwnProperty(i)) {
tmp.state[i] = df[i];
}
}
if(d && d.data && d.data.jstree && d.data.jstree.icon) {
tmp.icon = d.data.jstree.icon;
}
if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
tmp.icon = true;
}
if(d && d.data) {
tmp.data = d.data;
if(d.data.jstree) {
for(i in d.data.jstree) {
if(d.data.jstree.hasOwnProperty(i)) {
tmp.state[i] = d.data.jstree[i];
}
}
}
}
if(d && typeof d.state === 'object') {
for (i in d.state) {
if(d.state.hasOwnProperty(i)) {
tmp.state[i] = d.state[i];
}
}
}
if(d && typeof d.li_attr === 'object') {
for (i in d.li_attr) {
if(d.li_attr.hasOwnProperty(i)) {
tmp.li_attr[i] = d.li_attr[i];
}
}
}
if(!tmp.li_attr.id) {
tmp.li_attr.id = tid;
}
if(d && typeof d.a_attr === 'object') {
for (i in d.a_attr) {
if(d.a_attr.hasOwnProperty(i)) {
tmp.a_attr[i] = d.a_attr[i];
}
}
}
if(d && d.children && d.children === true) {
tmp.state.loaded = false;
tmp.children = [];
tmp.children_d = [];
}
m[tmp.id] = tmp;
for(i = 0, j = tmp.children.length; i < j; i++) {
c = parse_flat(m[tmp.children[i]], tmp.id, ps);
e = m[c];
tmp.children_d.push(c);
if(e.children_d.length) {
tmp.children_d = tmp.children_d.concat(e.children_d);
}
}
delete d.data;
delete d.children;
m[tmp.id].original = d;
if(tmp.state.selected) {
add.push(tmp.id);
}
return tmp.id;
},
parse_nest = function (d, p, ps) {
if(!ps) { ps = []; }
else { ps = ps.concat(); }
if(p) { ps.unshift(p); }
var tid = false, i, j, c, e, tmp;
do {
tid = 'j' + t_id + '_' + (++t_cnt);
} while(m[tid]);
tmp = {
id : false,
text : typeof d === 'string' ? d : '',
icon : typeof d === 'object' && d.icon !== undefined ? d.icon : true,
parent : p,
parents : ps,
children : [],
children_d : [],
data : null,
state : { },
li_attr : { id : false },
a_attr : { href : '#' },
original : false
};
for(i in df) {
if(df.hasOwnProperty(i)) {
tmp.state[i] = df[i];
}
}
if(d && d.id) { tmp.id = d.id.toString(); }
if(d && d.text) { tmp.text = d.text; }
if(d && d.data && d.data.jstree && d.data.jstree.icon) {
tmp.icon = d.data.jstree.icon;
}
if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
tmp.icon = true;
}
if(d && d.data) {
tmp.data = d.data;
if(d.data.jstree) {
for(i in d.data.jstree) {
if(d.data.jstree.hasOwnProperty(i)) {
tmp.state[i] = d.data.jstree[i];
}
}
}
}
if(d && typeof d.state === 'object') {
for (i in d.state) {
if(d.state.hasOwnProperty(i)) {
tmp.state[i] = d.state[i];
}
}
}
if(d && typeof d.li_attr === 'object') {
for (i in d.li_attr) {
if(d.li_attr.hasOwnProperty(i)) {
tmp.li_attr[i] = d.li_attr[i];
}
}
}
if(tmp.li_attr.id && !tmp.id) {
tmp.id = tmp.li_attr.id.toString();
}
if(!tmp.id) {
tmp.id = tid;
}
if(!tmp.li_attr.id) {
tmp.li_attr.id = tmp.id;
}
if(d && typeof d.a_attr === 'object') {
for (i in d.a_attr) {
if(d.a_attr.hasOwnProperty(i)) {
tmp.a_attr[i] = d.a_attr[i];
}
}
}
if(d && d.children && d.children.length) {
for(i = 0, j = d.children.length; i < j; i++) {
c = parse_nest(d.children[i], tmp.id, ps);
e = m[c];
tmp.children.push(c);
if(e.children_d.length) {
tmp.children_d = tmp.children_d.concat(e.children_d);
}
}
tmp.children_d = tmp.children_d.concat(tmp.children);
}
if(d && d.children && d.children === true) {
tmp.state.loaded = false;
tmp.children = [];
tmp.children_d = [];
}
delete d.data;
delete d.children;
tmp.original = d;
m[tmp.id] = tmp;
if(tmp.state.selected) {
add.push(tmp.id);
}
return tmp.id;
};
if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) {
// Flat JSON support (for easy import from DB):
// 1) convert to object (foreach)
for(i = 0, j = dat.length; i < j; i++) {
if(!dat[i].children) {
dat[i].children = [];
}
if(!dat[i].state) {
dat[i].state = {};
}
m[dat[i].id.toString()] = dat[i];
}
// 2) populate children (foreach)
for(i = 0, j = dat.length; i < j; i++) {
if (!m[dat[i].parent.toString()]) {
if (typeof inst !== "undefined") {
inst._data.core.last_error = { 'error' : 'parse', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Node with invalid parent', 'data' : JSON.stringify({ 'id' : dat[i].id.toString(), 'parent' : dat[i].parent.toString() }) };
inst.settings.core.error.call(inst, inst._data.core.last_error);
}
continue;
}
m[dat[i].parent.toString()].children.push(dat[i].id.toString());
// populate parent.children_d
p.children_d.push(dat[i].id.toString());
}
// 3) normalize && populate parents and children_d with recursion
for(i = 0, j = p.children.length; i < j; i++) {
tmp = parse_flat(m[p.children[i]], par, p.parents.concat());
dpc.push(tmp);
if(m[tmp].children_d.length) {
dpc = dpc.concat(m[tmp].children_d);
}
}
for(i = 0, j = p.parents.length; i < j; i++) {
m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
}
// ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true;
rslt = {
'cnt' : t_cnt,
'mod' : m,
'sel' : sel,
'par' : par,
'dpc' : dpc,
'add' : add
};
}
else {
for(i = 0, j = dat.length; i < j; i++) {
tmp = parse_nest(dat[i], par, p.parents.concat());
if(tmp) {
chd.push(tmp);
dpc.push(tmp);
if(m[tmp].children_d.length) {
dpc = dpc.concat(m[tmp].children_d);
}
}
}
p.children = chd;
p.children_d = dpc;
for(i = 0, j = p.parents.length; i < j; i++) {
m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
}
rslt = {
'cnt' : t_cnt,
'mod' : m,
'sel' : sel,
'par' : par,
'dpc' : dpc,
'add' : add
};
}
if(typeof window === 'undefined' || typeof window.document === 'undefined') {
postMessage(rslt);
}
else {
return rslt;
}
},
rslt = function (rslt, worker) {
if(this.element === null) { return; }
this._cnt = rslt.cnt;
var i, m = this._model.data;
for (i in m) {
if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) {
rslt.mod[i].state.loading = true;
}
}
this._model.data = rslt.mod; // breaks the reference in load_node - careful
if(worker) {
var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice();
m = this._model.data;
// if selection was changed while calculating in worker
if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) {
// deselect nodes that are no longer selected
for(i = 0, j = r.length; i < j; i++) {
if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) {
m[r[i]].state.selected = false;
}
}
// select nodes that were selected in the mean time
for(i = 0, j = s.length; i < j; i++) {
if($.inArray(s[i], r) === -1) {
m[s[i]].state.selected = true;
}
}
}
}
if(rslt.add.length) {
this._data.core.selected = this._data.core.selected.concat(rslt.add);
}
this.trigger('model', { "nodes" : rslt.dpc, 'parent' : rslt.par });
if(rslt.par !== $.jstree.root) {
this._node_changed(rslt.par);
this.redraw();
}
else {
// this.get_container_ul().children('.jstree-initial-node').remove();
this.redraw(true);
}
if(rslt.add.length) {
this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
}
cb.call(this, true);
};
if(this.settings.core.worker && window.Blob && window.URL && window.Worker) {
try {
if(this._wrk === null) {
this._wrk = window.URL.createObjectURL(
new window.Blob(
['self.onmessage = ' + func.toString()],
{type:"text/javascript"}
)
);
}
if(!this._data.core.working || force_processing) {
this._data.core.working = true;
w = new window.Worker(this._wrk);
w.onmessage = $.proxy(function (e) {
rslt.call(this, e.data, true);
try { w.terminate(); w = null; } catch(ignore) { }
if(this._data.core.worker_queue.length) {
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
}
else {
this._data.core.working = false;
}
}, this);
if(!args.par) {
if(this._data.core.worker_queue.length) {
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
}
else {
this._data.core.working = false;
}
}
else {
w.postMessage(args);
}
}
else {
this._data.core.worker_queue.push([dom, data, cb, true]);
}
}
catch(e) {
rslt.call(this, func(args), false);
if(this._data.core.worker_queue.length) {
this._append_json_data.apply(this, this._data.core.worker_queue.shift());
}
else {
this._data.core.working = false;
}
}
}
else {
rslt.call(this, func(args), false);
}
},
/**
* parses a node from a jQuery object and appends them to the in memory tree model. Used internally.
* @private
* @name _parse_model_from_html(d [, p, ps])
* @param {jQuery} d the jQuery object to parse
* @param {String} p the parent ID
* @param {Array} ps list of all parents
* @return {String} the ID of the object added to the model
*/
_parse_model_from_html : function (d, p, ps) {
if(!ps) { ps = []; }
else { ps = [].concat(ps); }
if(p) { ps.unshift(p); }
var c, e, m = this._model.data,
data = {
id : false,
text : false,
icon : true,
parent : p,
parents : ps,
children : [],
children_d : [],
data : null,
state : { },
li_attr : { id : false },
a_attr : { href : '#' },
original : false
}, i, tmp, tid;
for(i in this._model.default_state) {
if(this._model.default_state.hasOwnProperty(i)) {
data.state[i] = this._model.default_state[i];
}
}
tmp = $.vakata.attributes(d, true);
$.each(tmp, function (i, v) {
v = $.trim(v);
if(!v.length) { return true; }
data.li_attr[i] = v;
if(i === 'id') {
data.id = v.toString();
}
});
tmp = d.children('a').first();
if(tmp.length) {
tmp = $.vakata.attributes(tmp, true);
$.each(tmp, function (i, v) {
v = $.trim(v);
if(v.length) {
data.a_attr[i] = v;
}
});
}
tmp = d.children("a").first().length ? d.children("a").first().clone() : d.clone();
tmp.children("ins, i, ul").remove();
tmp = tmp.html();
tmp = $('<div />').html(tmp);
data.text = this.settings.core.force_text ? tmp.text() : tmp.html();
tmp = d.data();
data.data = tmp ? $.extend(true, {}, tmp) : null;
data.state.opened = d.hasClass('jstree-open');
data.state.selected = d.children('a').hasClass('jstree-clicked');
data.state.disabled = d.children('a').hasClass('jstree-disabled');
if(data.data && data.data.jstree) {
for(i in data.data.jstree) {
if(data.data.jstree.hasOwnProperty(i)) {
data.state[i] = data.data.jstree[i];
}
}
}
tmp = d.children("a").children(".jstree-themeicon");
if(tmp.length) {
data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel');
}
if(data.state.icon !== undefined) {
data.icon = data.state.icon;
}
if(data.icon === undefined || data.icon === null || data.icon === "") {
data.icon = true;
}
tmp = d.children("ul").children("li");
do {
tid = 'j' + this._id + '_' + (++this._cnt);
} while(m[tid]);
data.id = data.li_attr.id ? data.li_attr.id.toString() : tid;
if(tmp.length) {
tmp.each($.proxy(function (i, v) {
c = this._parse_model_from_html($(v), data.id, ps);
e = this._model.data[c];
data.children.push(c);
if(e.children_d.length) {
data.children_d = data.children_d.concat(e.children_d);
}
}, this));
data.children_d = data.children_d.concat(data.children);
}
else {
if(d.hasClass('jstree-closed')) {
data.state.loaded = false;
}
}
if(data.li_attr['class']) {
data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open','');
}
if(data.a_attr['class']) {
data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled','');
}
m[data.id] = data;
if(data.state.selected) {
this._data.core.selected.push(data.id);
}
return data.id;
},
/**
* parses a node from a JSON object (used when dealing with flat data, which has no nesting of children, but has id and parent properties) and appends it to the in memory tree model. Used internally.
* @private
* @name _parse_model_from_flat_json(d [, p, ps])
* @param {Object} d the JSON object to parse
* @param {String} p the parent ID
* @param {Array} ps list of all parents
* @return {String} the ID of the object added to the model
*/
_parse_model_from_flat_json : function (d, p, ps) {
if(!ps) { ps = []; }
else { ps = ps.concat(); }
if(p) { ps.unshift(p); }
var tid = d.id.toString(),
m = this._model.data,
df = this._model.default_state,
i, j, c, e,
tmp = {
id : tid,
text : d.text || '',
icon : d.icon !== undefined ? d.icon : true,
parent : p,
parents : ps,
children : d.children || [],
children_d : d.children_d || [],
data : d.data,
state : { },
li_attr : { id : false },
a_attr : { href : '#' },
original : false
};
for(i in df) {
if(df.hasOwnProperty(i)) {
tmp.state[i] = df[i];
}
}
if(d && d.data && d.data.jstree && d.data.jstree.icon) {
tmp.icon = d.data.jstree.icon;
}
if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
tmp.icon = true;
}
if(d && d.data) {
tmp.data = d.data;
if(d.data.jstree) {
for(i in d.data.jstree) {
if(d.data.jstree.hasOwnProperty(i)) {
tmp.state[i] = d.data.jstree[i];
}
}
}
}
if(d && typeof d.state === 'object') {
for (i in d.state) {
if(d.state.hasOwnProperty(i)) {
tmp.state[i] = d.state[i];
}
}
}
if(d && typeof d.li_attr === 'object') {
for (i in d.li_attr) {
if(d.li_attr.hasOwnProperty(i)) {
tmp.li_attr[i] = d.li_attr[i];
}
}
}
if(!tmp.li_attr.id) {
tmp.li_attr.id = tid;
}
if(d && typeof d.a_attr === 'object') {
for (i in d.a_attr) {
if(d.a_attr.hasOwnProperty(i)) {
tmp.a_attr[i] = d.a_attr[i];
}
}
}
if(d && d.children && d.children === true) {
tmp.state.loaded = false;
tmp.children = [];
tmp.children_d = [];
}
m[tmp.id] = tmp;
for(i = 0, j = tmp.children.length; i < j; i++) {
c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps);
e = m[c];
tmp.children_d.push(c);
if(e.children_d.length) {
tmp.children_d = tmp.children_d.concat(e.children_d);
}
}
delete d.data;
delete d.children;
m[tmp.id].original = d;
if(tmp.state.selected) {
this._data.core.selected.push(tmp.id);
}
return tmp.id;
},
/**
* parses a node from a JSON object and appends it to the in memory tree model. Used internally.
* @private
* @name _parse_model_from_json(d [, p, ps])
* @param {Object} d the JSON object to parse
* @param {String} p the parent ID
* @param {Array} ps list of all parents
* @return {String} the ID of the object added to the model
*/
_parse_model_from_json : function (d, p, ps) {
if(!ps) { ps = []; }
else { ps = ps.concat(); }
if(p) { ps.unshift(p); }
var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp;
do {
tid = 'j' + this._id + '_' + (++this._cnt);
} while(m[tid]);
tmp = {
id : false,
text : typeof d === 'string' ? d : '',
icon : typeof d === 'object' && d.icon !== undefined ? d.icon : true,
parent : p,
parents : ps,
children : [],
children_d : [],
data : null,
state : { },
li_attr : { id : false },
a_attr : { href : '#' },
original : false
};
for(i in df) {
if(df.hasOwnProperty(i)) {
tmp.state[i] = df[i];
}
}
if(d && d.id) { tmp.id = d.id.toString(); }
if(d && d.text) { tmp.text = d.text; }
if(d && d.data && d.data.jstree && d.data.jstree.icon) {
tmp.icon = d.data.jstree.icon;
}
if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
tmp.icon = true;
}
if(d && d.data) {
tmp.data = d.data;
if(d.data.jstree) {
for(i in d.data.jstree) {
if(d.data.jstree.hasOwnProperty(i)) {
tmp.state[i] = d.data.jstree[i];
}
}
}
}
if(d && typeof d.state === 'object') {
for (i in d.state) {
if(d.state.hasOwnProperty(i)) {
tmp.state[i] = d.state[i];
}
}
}
if(d && typeof d.li_attr === 'object') {
for (i in d.li_attr) {
if(d.li_attr.hasOwnProperty(i)) {
tmp.li_attr[i] = d.li_attr[i];
}
}
}
if(tmp.li_attr.id && !tmp.id) {
tmp.id = tmp.li_attr.id.toString();
}
if(!tmp.id) {
tmp.id = tid;
}
if(!tmp.li_attr.id) {
tmp.li_attr.id = tmp.id;
}
if(d && typeof d.a_attr === 'object') {
for (i in d.a_attr) {
if(d.a_attr.hasOwnProperty(i)) {
tmp.a_attr[i] = d.a_attr[i];
}
}
}
if(d && d.children && d.children.length) {
for(i = 0, j = d.children.length; i < j; i++) {
c = this._parse_model_from_json(d.children[i], tmp.id, ps);
e = m[c];
tmp.children.push(c);
if(e.children_d.length) {
tmp.children_d = tmp.children_d.concat(e.children_d);
}
}
tmp.children_d = tmp.children.concat(tmp.children_d);
}
if(d && d.children && d.children === true) {
tmp.state.loaded = false;
tmp.children = [];
tmp.children_d = [];
}
delete d.data;
delete d.children;
tmp.original = d;
m[tmp.id] = tmp;
if(tmp.state.selected) {
this._data.core.selected.push(tmp.id);
}
return tmp.id;
},
/**
* redraws all nodes that need to be redrawn. Used internally.
* @private
* @name _redraw()
* @trigger redraw.jstree
*/
_redraw : function () {
var nodes = this._model.force_full_redraw ? this._model.data[$.jstree.root].children.concat([]) : this._model.changed.concat([]),
f = document.createElement('UL'), tmp, i, j, fe = this._data.core.focused;
for(i = 0, j = nodes.length; i < j; i++) {
tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw);
if(tmp && this._model.force_full_redraw) {
f.appendChild(tmp);
}
}
if(this._model.force_full_redraw) {
f.className = this.get_container_ul()[0].className;
f.setAttribute('role','group');
this.element.empty().append(f);
//this.get_container_ul()[0].appendChild(f);
}
if(fe !== null && this.settings.core.restore_focus) {
tmp = this.get_node(fe, true);
if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) {
tmp.children('.jstree-anchor').focus();
}
else {
this._data.core.focused = null;
}
}
this._model.force_full_redraw = false;
this._model.changed = [];
/**
* triggered after nodes are redrawn
* @event
* @name redraw.jstree
* @param {array} nodes the redrawn nodes
*/
this.trigger('redraw', { "nodes" : nodes });
},
/**
* redraws all nodes that need to be redrawn or optionally - the whole tree
* @name redraw([full])
* @param {Boolean} full if set to `true` all nodes are redrawn.
*/
redraw : function (full) {
if(full) {
this._model.force_full_redraw = true;
}
//if(this._model.redraw_timeout) {
// clearTimeout(this._model.redraw_timeout);
//}
//this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0);
this._redraw();
},
/**
* redraws a single node's children. Used internally.
* @private
* @name draw_children(node)
* @param {mixed} node the node whose children will be redrawn
*/
draw_children : function (node) {
var obj = this.get_node(node),
i = false,
j = false,
k = false,
d = document;
if(!obj) { return false; }
if(obj.id === $.jstree.root) { return this.redraw(true); }
node = this.get_node(node, true);
if(!node || !node.length) { return false; } // TODO: quick toggle
node.children('.jstree-children').remove();
node = node[0];
if(obj.children.length && obj.state.loaded) {
k = d.createElement('UL');
k.setAttribute('role', 'group');
k.className = 'jstree-children';
for(i = 0, j = obj.children.length; i < j; i++) {
k.appendChild(this.redraw_node(obj.children[i], true, true));
}
node.appendChild(k);
}
},
/**
* redraws a single node. Used internally.
* @private
* @name redraw_node(node, deep, is_callback, force_render)
* @param {mixed} node the node to redraw
* @param {Boolean} deep should child nodes be redrawn too
* @param {Boolean} is_callback is this a recursion call
* @param {Boolean} force_render should children of closed parents be drawn anyway
*/
redraw_node : function (node, deep, is_callback, force_render) {
var obj = this.get_node(node),
par = false,
ind = false,
old = false,
i = false,
j = false,
k = false,
c = '',
d = document,
m = this._model.data,
f = false,
s = false,
tmp = null,
t = 0,
l = 0,
has_children = false,
last_sibling = false;
if(!obj) { return false; }
if(obj.id === $.jstree.root) { return this.redraw(true); }
deep = deep || obj.children.length === 0;
node = !document.querySelector ? document.getElementById(obj.id) : this.element[0].querySelector('#' + ("0123456789".indexOf(obj.id[0]) !== -1 ? '\\3' + obj.id[0] + ' ' + obj.id.substr(1).replace($.jstree.idregex,'\\$&') : obj.id.replace($.jstree.idregex,'\\$&')) ); //, this.element);
if(!node) {
deep = true;
//node = d.createElement('LI');
if(!is_callback) {
par = obj.parent !== $.jstree.root ? $('#' + obj.parent.replace($.jstree.idregex,'\\$&'), this.element)[0] : null;
if(par !== null && (!par || !m[obj.parent].state.opened)) {
return false;
}
ind = $.inArray(obj.id, par === null ? m[$.jstree.root].children : m[obj.parent].children);
}
}
else {
node = $(node);
if(!is_callback) {
par = node.parent().parent()[0];
if(par === this.element[0]) {
par = null;
}
ind = node.index();
}
// m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage
if(!deep && obj.children.length && !node.children('.jstree-children').length) {
deep = true;
}
if(!deep) {
old = node.children('.jstree-children')[0];
}
f = node.children('.jstree-anchor')[0] === document.activeElement;
node.remove();
//node = d.createElement('LI');
//node = node[0];
}
node = this._data.core.node.cloneNode(true);
// node is DOM, deep is boolean
c = 'jstree-node ';
for(i in obj.li_attr) {
if(obj.li_attr.hasOwnProperty(i)) {
if(i === 'id') { continue; }
if(i !== 'class') {
node.setAttribute(i, obj.li_attr[i]);
}
else {
c += obj.li_attr[i];
}
}
}
if(!obj.a_attr.id) {
obj.a_attr.id = obj.id + '_anchor';
}
node.setAttribute('aria-selected', !!obj.state.selected);
node.setAttribute('aria-level', obj.parents.length);
node.setAttribute('aria-labelledby', obj.a_attr.id);
if(obj.state.disabled) {
node.setAttribute('aria-disabled', true);
}
for(i = 0, j = obj.children.length; i < j; i++) {
if(!m[obj.children[i]].state.hidden) {
has_children = true;
break;
}
}
if(obj.parent !== null && m[obj.parent] && !obj.state.hidden) {
i = $.inArray(obj.id, m[obj.parent].children);
last_sibling = obj.id;
if(i !== -1) {
i++;
for(j = m[obj.parent].children.length; i < j; i++) {
if(!m[m[obj.parent].children[i]].state.hidden) {
last_sibling = m[obj.parent].children[i];
}
if(last_sibling !== obj.id) {
break;
}
}
}
}
if(obj.state.hidden) {
c += ' jstree-hidden';
}
if (obj.state.loading) {
c += ' jstree-loading';
}
if(obj.state.loaded && !has_children) {
c += ' jstree-leaf';
}
else {
c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed';
node.setAttribute('aria-expanded', (obj.state.opened && obj.state.loaded) );
}
if(last_sibling === obj.id) {
c += ' jstree-last';
}
node.id = obj.id;
node.className = c;
c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : '');
for(j in obj.a_attr) {
if(obj.a_attr.hasOwnProperty(j)) {
if(j === 'href' && obj.a_attr[j] === '#') { continue; }
if(j !== 'class') {
node.childNodes[1].setAttribute(j, obj.a_attr[j]);
}
else {
c += ' ' + obj.a_attr[j];
}
}
}
if(c.length) {
node.childNodes[1].className = 'jstree-anchor ' + c;
}
if((obj.icon && obj.icon !== true) || obj.icon === false) {
if(obj.icon === false) {
node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden';
}
else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) {
node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom';
}
else {
node.childNodes[1].childNodes[0].style.backgroundImage = 'url("'+obj.icon+'")';
node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center';
node.childNodes[1].childNodes[0].style.backgroundSize = 'auto';
node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom';
}
}
if(this.settings.core.force_text) {
node.childNodes[1].appendChild(d.createTextNode(obj.text));
}
else {
node.childNodes[1].innerHTML += obj.text;
}
if(deep && obj.children.length && (obj.state.opened || force_render) && obj.state.loaded) {
k = d.createElement('UL');
k.setAttribute('role', 'group');
k.className = 'jstree-children';
for(i = 0, j = obj.children.length; i < j; i++) {
k.appendChild(this.redraw_node(obj.children[i], deep, true));
}
node.appendChild(k);
}
if(old) {
node.appendChild(old);
}
if(!is_callback) {
// append back using par / ind
if(!par) {
par = this.element[0];
}
for(i = 0, j = par.childNodes.length; i < j; i++) {
if(par.childNodes[i] && par.childNodes[i].className && par.childNodes[i].className.indexOf('jstree-children') !== -1) {
tmp = par.childNodes[i];
break;
}
}
if(!tmp) {
tmp = d.createElement('UL');
tmp.setAttribute('role', 'group');
tmp.className = 'jstree-children';
par.appendChild(tmp);
}
par = tmp;
if(ind < par.childNodes.length) {
par.insertBefore(node, par.childNodes[ind]);
}
else {
par.appendChild(node);
}
if(f) {
t = this.element[0].scrollTop;
l = this.element[0].scrollLeft;
node.childNodes[1].focus();
this.element[0].scrollTop = t;
this.element[0].scrollLeft = l;
}
}
if(obj.state.opened && !obj.state.loaded) {
obj.state.opened = false;
setTimeout($.proxy(function () {
this.open_node(obj.id, false, 0);
}, this), 0);
}
return node;
},
/**
* opens a node, revealing its children. If the node is not loaded it will be loaded and opened once ready.
* @name open_node(obj [, callback, animation])
* @param {mixed} obj the node to open
* @param {Function} callback a function to execute once the node is opened
* @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation.
* @trigger open_node.jstree, after_open.jstree, before_open.jstree
*/
open_node : function (obj, callback, animation) {
var t1, t2, d, t;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.open_node(obj[t1], callback, animation);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
animation = animation === undefined ? this.settings.core.animation : animation;
if(!this.is_closed(obj)) {
if(callback) {
callback.call(this, obj, false);
}
return false;
}
if(!this.is_loaded(obj)) {
if(this.is_loading(obj)) {
return setTimeout($.proxy(function () {
this.open_node(obj, callback, animation);
}, this), 500);
}
this.load_node(obj, function (o, ok) {
return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false);
});
}
else {
d = this.get_node(obj, true);
t = this;
if(d.length) {
if(animation && d.children(".jstree-children").length) {
d.children(".jstree-children").stop(true, true);
}
if(obj.children.length && !this._firstChild(d.children('.jstree-children')[0])) {
this.draw_children(obj);
//d = this.get_node(obj, true);
}
if(!animation) {
this.trigger('before_open', { "node" : obj });
d[0].className = d[0].className.replace('jstree-closed', 'jstree-open');
d[0].setAttribute("aria-expanded", true);
}
else {
this.trigger('before_open', { "node" : obj });
d
.children(".jstree-children").css("display","none").end()
.removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true)
.children(".jstree-children").stop(true, true)
.slideDown(animation, function () {
this.style.display = "";
if (t.element) {
t.trigger("after_open", { "node" : obj });
}
});
}
}
obj.state.opened = true;
if(callback) {
callback.call(this, obj, true);
}
if(!d.length) {
/**
* triggered when a node is about to be opened (if the node is supposed to be in the DOM, it will be, but it won't be visible yet)
* @event
* @name before_open.jstree
* @param {Object} node the opened node
*/
this.trigger('before_open', { "node" : obj });
}
/**
* triggered when a node is opened (if there is an animation it will not be completed yet)
* @event
* @name open_node.jstree
* @param {Object} node the opened node
*/
this.trigger('open_node', { "node" : obj });
if(!animation || !d.length) {
/**
* triggered when a node is opened and the animation is complete
* @event
* @name after_open.jstree
* @param {Object} node the opened node
*/
this.trigger("after_open", { "node" : obj });
}
return true;
}
},
/**
* opens every parent of a node (node should be loaded)
* @name _open_to(obj)
* @param {mixed} obj the node to reveal
* @private
*/
_open_to : function (obj) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
var i, j, p = obj.parents;
for(i = 0, j = p.length; i < j; i+=1) {
if(i !== $.jstree.root) {
this.open_node(p[i], false, 0);
}
}
return $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
},
/**
* closes a node, hiding its children
* @name close_node(obj [, animation])
* @param {mixed} obj the node to close
* @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation.
* @trigger close_node.jstree, after_close.jstree
*/
close_node : function (obj, animation) {
var t1, t2, t, d;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.close_node(obj[t1], animation);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
if(this.is_closed(obj)) {
return false;
}
animation = animation === undefined ? this.settings.core.animation : animation;
t = this;
d = this.get_node(obj, true);
obj.state.opened = false;
/**
* triggered when a node is closed (if there is an animation it will not be complete yet)
* @event
* @name close_node.jstree
* @param {Object} node the closed node
*/
this.trigger('close_node',{ "node" : obj });
if(!d.length) {
/**
* triggered when a node is closed and the animation is complete
* @event
* @name after_close.jstree
* @param {Object} node the closed node
*/
this.trigger("after_close", { "node" : obj });
}
else {
if(!animation) {
d[0].className = d[0].className.replace('jstree-open', 'jstree-closed');
d.attr("aria-expanded", false).children('.jstree-children').remove();
this.trigger("after_close", { "node" : obj });
}
else {
d
.children(".jstree-children").attr("style","display:block !important").end()
.removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false)
.children(".jstree-children").stop(true, true).slideUp(animation, function () {
this.style.display = "";
d.children('.jstree-children').remove();
if (t.element) {
t.trigger("after_close", { "node" : obj });
}
});
}
}
},
/**
* toggles a node - closing it if it is open, opening it if it is closed
* @name toggle_node(obj)
* @param {mixed} obj the node to toggle
*/
toggle_node : function (obj) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.toggle_node(obj[t1]);
}
return true;
}
if(this.is_closed(obj)) {
return this.open_node(obj);
}
if(this.is_open(obj)) {
return this.close_node(obj);
}
},
/**
* opens all nodes within a node (or the tree), revealing their children. If the node is not loaded it will be loaded and opened once ready.
* @name open_all([obj, animation, original_obj])
* @param {mixed} obj the node to open recursively, omit to open all nodes in the tree
* @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation
* @param {jQuery} reference to the node that started the process (internal use)
* @trigger open_all.jstree
*/
open_all : function (obj, animation, original_obj) {
if(!obj) { obj = $.jstree.root; }
obj = this.get_node(obj);
if(!obj) { return false; }
var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), i, j, _this;
if(!dom.length) {
for(i = 0, j = obj.children_d.length; i < j; i++) {
if(this.is_closed(this._model.data[obj.children_d[i]])) {
this._model.data[obj.children_d[i]].state.opened = true;
}
}
return this.trigger('open_all', { "node" : obj });
}
original_obj = original_obj || dom;
_this = this;
dom = this.is_closed(obj) ? dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed');
dom.each(function () {
_this.open_node(
this,
function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } },
animation || 0
);
});
if(original_obj.find('.jstree-closed').length === 0) {
/**
* triggered when an `open_all` call completes
* @event
* @name open_all.jstree
* @param {Object} node the opened node
*/
this.trigger('open_all', { "node" : this.get_node(original_obj) });
}
},
/**
* closes all nodes within a node (or the tree), revealing their children
* @name close_all([obj, animation])
* @param {mixed} obj the node to close recursively, omit to close all nodes in the tree
* @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation
* @trigger close_all.jstree
*/
close_all : function (obj, animation) {
if(!obj) { obj = $.jstree.root; }
obj = this.get_node(obj);
if(!obj) { return false; }
var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true),
_this = this, i, j;
if(dom.length) {
dom = this.is_open(obj) ? dom.find('.jstree-open').addBack() : dom.find('.jstree-open');
$(dom.get().reverse()).each(function () { _this.close_node(this, animation || 0); });
}
for(i = 0, j = obj.children_d.length; i < j; i++) {
this._model.data[obj.children_d[i]].state.opened = false;
}
/**
* triggered when an `close_all` call completes
* @event
* @name close_all.jstree
* @param {Object} node the closed node
*/
this.trigger('close_all', { "node" : obj });
},
/**
* checks if a node is disabled (not selectable)
* @name is_disabled(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_disabled : function (obj) {
obj = this.get_node(obj);
return obj && obj.state && obj.state.disabled;
},
/**
* enables a node - so that it can be selected
* @name enable_node(obj)
* @param {mixed} obj the node to enable
* @trigger enable_node.jstree
*/
enable_node : function (obj) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.enable_node(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
obj.state.disabled = false;
this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled').attr('aria-disabled', false);
/**
* triggered when an node is enabled
* @event
* @name enable_node.jstree
* @param {Object} node the enabled node
*/
this.trigger('enable_node', { 'node' : obj });
},
/**
* disables a node - so that it can not be selected
* @name disable_node(obj)
* @param {mixed} obj the node to disable
* @trigger disable_node.jstree
*/
disable_node : function (obj) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.disable_node(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
obj.state.disabled = true;
this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled').attr('aria-disabled', true);
/**
* triggered when an node is disabled
* @event
* @name disable_node.jstree
* @param {Object} node the disabled node
*/
this.trigger('disable_node', { 'node' : obj });
},
/**
* determines if a node is hidden
* @name is_hidden(obj)
* @param {mixed} obj the node
*/
is_hidden : function (obj) {
obj = this.get_node(obj);
return obj.state.hidden === true;
},
/**
* hides a node - it is still in the structure but will not be visible
* @name hide_node(obj)
* @param {mixed} obj the node to hide
* @param {Boolean} skip_redraw internal parameter controlling if redraw is called
* @trigger hide_node.jstree
*/
hide_node : function (obj, skip_redraw) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.hide_node(obj[t1], true);
}
if (!skip_redraw) {
this.redraw();
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
if(!obj.state.hidden) {
obj.state.hidden = true;
this._node_changed(obj.parent);
if(!skip_redraw) {
this.redraw();
}
/**
* triggered when an node is hidden
* @event
* @name hide_node.jstree
* @param {Object} node the hidden node
*/
this.trigger('hide_node', { 'node' : obj });
}
},
/**
* shows a node
* @name show_node(obj)
* @param {mixed} obj the node to show
* @param {Boolean} skip_redraw internal parameter controlling if redraw is called
* @trigger show_node.jstree
*/
show_node : function (obj, skip_redraw) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.show_node(obj[t1], true);
}
if (!skip_redraw) {
this.redraw();
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
if(obj.state.hidden) {
obj.state.hidden = false;
this._node_changed(obj.parent);
if(!skip_redraw) {
this.redraw();
}
/**
* triggered when an node is shown
* @event
* @name show_node.jstree
* @param {Object} node the shown node
*/
this.trigger('show_node', { 'node' : obj });
}
},
/**
* hides all nodes
* @name hide_all()
* @trigger hide_all.jstree
*/
hide_all : function (skip_redraw) {
var i, m = this._model.data, ids = [];
for(i in m) {
if(m.hasOwnProperty(i) && i !== $.jstree.root && !m[i].state.hidden) {
m[i].state.hidden = true;
ids.push(i);
}
}
this._model.force_full_redraw = true;
if(!skip_redraw) {
this.redraw();
}
/**
* triggered when all nodes are hidden
* @event
* @name hide_all.jstree
* @param {Array} nodes the IDs of all hidden nodes
*/
this.trigger('hide_all', { 'nodes' : ids });
return ids;
},
/**
* shows all nodes
* @name show_all()
* @trigger show_all.jstree
*/
show_all : function (skip_redraw) {
var i, m = this._model.data, ids = [];
for(i in m) {
if(m.hasOwnProperty(i) && i !== $.jstree.root && m[i].state.hidden) {
m[i].state.hidden = false;
ids.push(i);
}
}
this._model.force_full_redraw = true;
if(!skip_redraw) {
this.redraw();
}
/**
* triggered when all nodes are shown
* @event
* @name show_all.jstree
* @param {Array} nodes the IDs of all shown nodes
*/
this.trigger('show_all', { 'nodes' : ids });
return ids;
},
/**
* called when a node is selected by the user. Used internally.
* @private
* @name activate_node(obj, e)
* @param {mixed} obj the node
* @param {Object} e the related event
* @trigger activate_node.jstree, changed.jstree
*/
activate_node : function (obj, e) {
if(this.is_disabled(obj)) {
return false;
}
if(!e || typeof e !== 'object') {
e = {};
}
// ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node
this._data.core.last_clicked = this._data.core.last_clicked && this._data.core.last_clicked.id !== undefined ? this.get_node(this._data.core.last_clicked.id) : null;
if(this._data.core.last_clicked && !this._data.core.last_clicked.state.selected) { this._data.core.last_clicked = null; }
if(!this._data.core.last_clicked && this._data.core.selected.length) { this._data.core.last_clicked = this.get_node(this._data.core.selected[this._data.core.selected.length - 1]); }
if(!this.settings.core.multiple || (!e.metaKey && !e.ctrlKey && !e.shiftKey) || (e.shiftKey && (!this._data.core.last_clicked || !this.get_parent(obj) || this.get_parent(obj) !== this._data.core.last_clicked.parent ) )) {
if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) {
this.deselect_node(obj, false, e);
}
else {
this.deselect_all(true);
this.select_node(obj, false, false, e);
this._data.core.last_clicked = this.get_node(obj);
}
}
else {
if(e.shiftKey) {
var o = this.get_node(obj).id,
l = this._data.core.last_clicked.id,
p = this.get_node(this._data.core.last_clicked.parent).children,
c = false,
i, j;
for(i = 0, j = p.length; i < j; i += 1) {
// separate IFs work whem o and l are the same
if(p[i] === o) {
c = !c;
}
if(p[i] === l) {
c = !c;
}
if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) {
if (!this.is_hidden(p[i])) {
this.select_node(p[i], true, false, e);
}
}
else {
this.deselect_node(p[i], true, e);
}
}
this.trigger('changed', { 'action' : 'select_node', 'node' : this.get_node(obj), 'selected' : this._data.core.selected, 'event' : e });
}
else {
if(!this.is_selected(obj)) {
this.select_node(obj, false, false, e);
}
else {
this.deselect_node(obj, false, e);
}
}
}
/**
* triggered when an node is clicked or intercated with by the user
* @event
* @name activate_node.jstree
* @param {Object} node
* @param {Object} event the ooriginal event (if any) which triggered the call (may be an empty object)
*/
this.trigger('activate_node', { 'node' : this.get_node(obj), 'event' : e });
},
/**
* applies the hover state on a node, called when a node is hovered by the user. Used internally.
* @private
* @name hover_node(obj)
* @param {mixed} obj
* @trigger hover_node.jstree
*/
hover_node : function (obj) {
obj = this.get_node(obj, true);
if(!obj || !obj.length || obj.children('.jstree-hovered').length) {
return false;
}
var o = this.element.find('.jstree-hovered'), t = this.element;
if(o && o.length) { this.dehover_node(o); }
obj.children('.jstree-anchor').addClass('jstree-hovered');
/**
* triggered when an node is hovered
* @event
* @name hover_node.jstree
* @param {Object} node
*/
this.trigger('hover_node', { 'node' : this.get_node(obj) });
setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); }, 0);
},
/**
* removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally.
* @private
* @name dehover_node(obj)
* @param {mixed} obj
* @trigger dehover_node.jstree
*/
dehover_node : function (obj) {
obj = this.get_node(obj, true);
if(!obj || !obj.length || !obj.children('.jstree-hovered').length) {
return false;
}
obj.children('.jstree-anchor').removeClass('jstree-hovered');
/**
* triggered when an node is no longer hovered
* @event
* @name dehover_node.jstree
* @param {Object} node
*/
this.trigger('dehover_node', { 'node' : this.get_node(obj) });
},
/**
* select a node
* @name select_node(obj [, supress_event, prevent_open])
* @param {mixed} obj an array can be used to select multiple nodes
* @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
* @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened
* @trigger select_node.jstree, changed.jstree
*/
select_node : function (obj, supress_event, prevent_open, e) {
var dom, t1, t2, th;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.select_node(obj[t1], supress_event, prevent_open, e);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(!obj.state.selected) {
obj.state.selected = true;
this._data.core.selected.push(obj.id);
if(!prevent_open) {
dom = this._open_to(obj);
}
if(dom && dom.length) {
dom.attr('aria-selected', true).children('.jstree-anchor').addClass('jstree-clicked');
}
/**
* triggered when an node is selected
* @event
* @name select_node.jstree
* @param {Object} node
* @param {Array} selected the current selection
* @param {Object} event the event (if any) that triggered this select_node
*/
this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
if(!supress_event) {
/**
* triggered when selection changes
* @event
* @name changed.jstree
* @param {Object} node
* @param {Object} action the action that caused the selection to change
* @param {Array} selected the current selection
* @param {Object} event the event (if any) that triggered this changed event
*/
this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
}
}
},
/**
* deselect a node
* @name deselect_node(obj [, supress_event])
* @param {mixed} obj an array can be used to deselect multiple nodes
* @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
* @trigger deselect_node.jstree, changed.jstree
*/
deselect_node : function (obj, supress_event, e) {
var t1, t2, dom;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.deselect_node(obj[t1], supress_event, e);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(obj.state.selected) {
obj.state.selected = false;
this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id);
if(dom.length) {
dom.attr('aria-selected', false).children('.jstree-anchor').removeClass('jstree-clicked');
}
/**
* triggered when an node is deselected
* @event
* @name deselect_node.jstree
* @param {Object} node
* @param {Array} selected the current selection
* @param {Object} event the event (if any) that triggered this deselect_node
*/
this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
if(!supress_event) {
this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
}
}
},
/**
* select all nodes in the tree
* @name select_all([supress_event])
* @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
* @trigger select_all.jstree, changed.jstree
*/
select_all : function (supress_event) {
var tmp = this._data.core.selected.concat([]), i, j;
this._data.core.selected = this._model.data[$.jstree.root].children_d.concat();
for(i = 0, j = this._data.core.selected.length; i < j; i++) {
if(this._model.data[this._data.core.selected[i]]) {
this._model.data[this._data.core.selected[i]].state.selected = true;
}
}
this.redraw(true);
/**
* triggered when all nodes are selected
* @event
* @name select_all.jstree
* @param {Array} selected the current selection
*/
this.trigger('select_all', { 'selected' : this._data.core.selected });
if(!supress_event) {
this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
}
},
/**
* deselect all selected nodes
* @name deselect_all([supress_event])
* @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
* @trigger deselect_all.jstree, changed.jstree
*/
deselect_all : function (supress_event) {
var tmp = this._data.core.selected.concat([]), i, j;
for(i = 0, j = this._data.core.selected.length; i < j; i++) {
if(this._model.data[this._data.core.selected[i]]) {
this._model.data[this._data.core.selected[i]].state.selected = false;
}
}
this._data.core.selected = [];
this.element.find('.jstree-clicked').removeClass('jstree-clicked').parent().attr('aria-selected', false);
/**
* triggered when all nodes are deselected
* @event
* @name deselect_all.jstree
* @param {Object} node the previous selection
* @param {Array} selected the current selection
*/
this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp });
if(!supress_event) {
this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
}
},
/**
* checks if a node is selected
* @name is_selected(obj)
* @param {mixed} obj
* @return {Boolean}
*/
is_selected : function (obj) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
return obj.state.selected;
},
/**
* get an array of all selected nodes
* @name get_selected([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
*/
get_selected : function (full) {
return full ? $.map(this._data.core.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.core.selected.slice();
},
/**
* get an array of all top level selected nodes (ignoring children of selected nodes)
* @name get_top_selected([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
*/
get_top_selected : function (full) {
var tmp = this.get_selected(true),
obj = {}, i, j, k, l;
for(i = 0, j = tmp.length; i < j; i++) {
obj[tmp[i].id] = tmp[i];
}
for(i = 0, j = tmp.length; i < j; i++) {
for(k = 0, l = tmp[i].children_d.length; k < l; k++) {
if(obj[tmp[i].children_d[k]]) {
delete obj[tmp[i].children_d[k]];
}
}
}
tmp = [];
for(i in obj) {
if(obj.hasOwnProperty(i)) {
tmp.push(i);
}
}
return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp;
},
/**
* get an array of all bottom level selected nodes (ignoring selected parents)
* @name get_bottom_selected([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
*/
get_bottom_selected : function (full) {
var tmp = this.get_selected(true),
obj = [], i, j;
for(i = 0, j = tmp.length; i < j; i++) {
if(!tmp[i].children.length) {
obj.push(tmp[i].id);
}
}
return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj;
},
/**
* gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally.
* @name get_state()
* @private
* @return {Object}
*/
get_state : function () {
var state = {
'core' : {
'open' : [],
'loaded' : [],
'scroll' : {
'left' : this.element.scrollLeft(),
'top' : this.element.scrollTop()
},
/*!
'themes' : {
'name' : this.get_theme(),
'icons' : this._data.core.themes.icons,
'dots' : this._data.core.themes.dots
},
*/
'selected' : []
}
}, i;
for(i in this._model.data) {
if(this._model.data.hasOwnProperty(i)) {
if(i !== $.jstree.root) {
if(this._model.data[i].state.loaded && this.settings.core.loaded_state) {
state.core.loaded.push(i);
}
if(this._model.data[i].state.opened) {
state.core.open.push(i);
}
if(this._model.data[i].state.selected) {
state.core.selected.push(i);
}
}
}
}
return state;
},
/**
* sets the state of the tree. Used internally.
* @name set_state(state [, callback])
* @private
* @param {Object} state the state to restore. Keep in mind this object is passed by reference and jstree will modify it.
* @param {Function} callback an optional function to execute once the state is restored.
* @trigger set_state.jstree
*/
set_state : function (state, callback) {
if(state) {
if(state.core && state.core.selected && state.core.initial_selection === undefined) {
state.core.initial_selection = this._data.core.selected.concat([]).sort().join(',');
}
if(state.core) {
var res, n, t, _this, i;
if(state.core.loaded) {
if(!this.settings.core.loaded_state || !$.isArray(state.core.loaded) || !state.core.loaded.length) {
delete state.core.loaded;
this.set_state(state, callback);
}
else {
this._load_nodes(state.core.loaded, function (nodes) {
delete state.core.loaded;
this.set_state(state, callback);
});
}
return false;
}
if(state.core.open) {
if(!$.isArray(state.core.open) || !state.core.open.length) {
delete state.core.open;
this.set_state(state, callback);
}
else {
this._load_nodes(state.core.open, function (nodes) {
this.open_node(nodes, false, 0);
delete state.core.open;
this.set_state(state, callback);
});
}
return false;
}
if(state.core.scroll) {
if(state.core.scroll && state.core.scroll.left !== undefined) {
this.element.scrollLeft(state.core.scroll.left);
}
if(state.core.scroll && state.core.scroll.top !== undefined) {
this.element.scrollTop(state.core.scroll.top);
}
delete state.core.scroll;
this.set_state(state, callback);
return false;
}
if(state.core.selected) {
_this = this;
if (state.core.initial_selection === undefined ||
state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',')
) {
this.deselect_all();
$.each(state.core.selected, function (i, v) {
_this.select_node(v, false, true);
});
}
delete state.core.initial_selection;
delete state.core.selected;
this.set_state(state, callback);
return false;
}
for(i in state) {
if(state.hasOwnProperty(i) && i !== "core" && $.inArray(i, this.settings.plugins) === -1) {
delete state[i];
}
}
if($.isEmptyObject(state.core)) {
delete state.core;
this.set_state(state, callback);
return false;
}
}
if($.isEmptyObject(state)) {
state = null;
if(callback) { callback.call(this); }
/**
* triggered when a `set_state` call completes
* @event
* @name set_state.jstree
*/
this.trigger('set_state');
return false;
}
return true;
}
return false;
},
/**
* refreshes the tree - all nodes are reloaded with calls to `load_node`.
* @name refresh()
* @param {Boolean} skip_loading an option to skip showing the loading indicator
* @param {Mixed} forget_state if set to `true` state will not be reapplied, if set to a function (receiving the current state as argument) the result of that function will be used as state
* @trigger refresh.jstree
*/
refresh : function (skip_loading, forget_state) {
this._data.core.state = forget_state === true ? {} : this.get_state();
if(forget_state && $.isFunction(forget_state)) { this._data.core.state = forget_state.call(this, this._data.core.state); }
this._cnt = 0;
this._model.data = {};
this._model.data[$.jstree.root] = {
id : $.jstree.root,
parent : null,
parents : [],
children : [],
children_d : [],
state : { loaded : false }
};
this._data.core.selected = [];
this._data.core.last_clicked = null;
this._data.core.focused = null;
var c = this.get_container_ul()[0].className;
if(!skip_loading) {
this.element.html("<"+"ul class='"+c+"' role='group'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
this.element.attr('aria-activedescendant','j'+this._id+'_loading');
}
this.load_node($.jstree.root, function (o, s) {
if(s) {
this.get_container_ul()[0].className = c;
if(this._firstChild(this.get_container_ul()[0])) {
this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
}
this.set_state($.extend(true, {}, this._data.core.state), function () {
/**
* triggered when a `refresh` call completes
* @event
* @name refresh.jstree
*/
this.trigger('refresh');
});
}
this._data.core.state = null;
});
},
/**
* refreshes a node in the tree (reload its children) all opened nodes inside that node are reloaded with calls to `load_node`.
* @name refresh_node(obj)
* @param {mixed} obj the node
* @trigger refresh_node.jstree
*/
refresh_node : function (obj) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
var opened = [], to_load = [], s = this._data.core.selected.concat([]);
to_load.push(obj.id);
if(obj.state.opened === true) { opened.push(obj.id); }
this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); });
this._load_nodes(to_load, $.proxy(function (nodes) {
this.open_node(opened, false, 0);
this.select_node(s);
/**
* triggered when a node is refreshed
* @event
* @name refresh_node.jstree
* @param {Object} node - the refreshed node
* @param {Array} nodes - an array of the IDs of the nodes that were reloaded
*/
this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes });
}, this), false, true);
},
/**
* set (change) the ID of a node
* @name set_id(obj, id)
* @param {mixed} obj the node
* @param {String} id the new ID
* @return {Boolean}
* @trigger set_id.jstree
*/
set_id : function (obj, id) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
var i, j, m = this._model.data, old = obj.id;
id = id.toString();
// update parents (replace current ID with new one in children and children_d)
m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id;
for(i = 0, j = obj.parents.length; i < j; i++) {
m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id;
}
// update children (replace current ID with new one in parent and parents)
for(i = 0, j = obj.children.length; i < j; i++) {
m[obj.children[i]].parent = id;
}
for(i = 0, j = obj.children_d.length; i < j; i++) {
m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id;
}
i = $.inArray(obj.id, this._data.core.selected);
if(i !== -1) { this._data.core.selected[i] = id; }
// update model and obj itself (obj.id, this._model.data[KEY])
i = this.get_node(obj.id, true);
if(i) {
i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor');
if(this.element.attr('aria-activedescendant') === obj.id) {
this.element.attr('aria-activedescendant', id);
}
}
delete m[obj.id];
obj.id = id;
obj.li_attr.id = id;
m[id] = obj;
/**
* triggered when a node id value is changed
* @event
* @name set_id.jstree
* @param {Object} node
* @param {String} old the old id
*/
this.trigger('set_id',{ "node" : obj, "new" : obj.id, "old" : old });
return true;
},
/**
* get the text value of a node
* @name get_text(obj)
* @param {mixed} obj the node
* @return {String}
*/
get_text : function (obj) {
obj = this.get_node(obj);
return (!obj || obj.id === $.jstree.root) ? false : obj.text;
},
/**
* set the text value of a node. Used internally, please use `rename_node(obj, val)`.
* @private
* @name set_text(obj, val)
* @param {mixed} obj the node, you can pass an array to set the text on multiple nodes
* @param {String} val the new text value
* @return {Boolean}
* @trigger set_text.jstree
*/
set_text : function (obj, val) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.set_text(obj[t1], val);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
obj.text = val;
if(this.get_node(obj, true).length) {
this.redraw_node(obj.id);
}
/**
* triggered when a node text value is changed
* @event
* @name set_text.jstree
* @param {Object} obj
* @param {String} text the new value
*/
this.trigger('set_text',{ "obj" : obj, "text" : val });
return true;
},
/**
* gets a JSON representation of a node (or the whole tree)
* @name get_json([obj, options])
* @param {mixed} obj
* @param {Object} options
* @param {Boolean} options.no_state do not return state information
* @param {Boolean} options.no_id do not return ID
* @param {Boolean} options.no_children do not include children
* @param {Boolean} options.no_data do not include node data
* @param {Boolean} options.no_li_attr do not include LI attributes
* @param {Boolean} options.no_a_attr do not include A attributes
* @param {Boolean} options.flat return flat JSON instead of nested
* @return {Object}
*/
get_json : function (obj, options, flat) {
obj = this.get_node(obj || $.jstree.root);
if(!obj) { return false; }
if(options && options.flat && !flat) { flat = []; }
var tmp = {
'id' : obj.id,
'text' : obj.text,
'icon' : this.get_icon(obj),
'li_attr' : $.extend(true, {}, obj.li_attr),
'a_attr' : $.extend(true, {}, obj.a_attr),
'state' : {},
'data' : options && options.no_data ? false : $.extend(true, $.isArray(obj.data)?[]:{}, obj.data)
//( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),
}, i, j;
if(options && options.flat) {
tmp.parent = obj.parent;
}
else {
tmp.children = [];
}
if(!options || !options.no_state) {
for(i in obj.state) {
if(obj.state.hasOwnProperty(i)) {
tmp.state[i] = obj.state[i];
}
}
} else {
delete tmp.state;
}
if(options && options.no_li_attr) {
delete tmp.li_attr;
}
if(options && options.no_a_attr) {
delete tmp.a_attr;
}
if(options && options.no_id) {
delete tmp.id;
if(tmp.li_attr && tmp.li_attr.id) {
delete tmp.li_attr.id;
}
if(tmp.a_attr && tmp.a_attr.id) {
delete tmp.a_attr.id;
}
}
if(options && options.flat && obj.id !== $.jstree.root) {
flat.push(tmp);
}
if(!options || !options.no_children) {
for(i = 0, j = obj.children.length; i < j; i++) {
if(options && options.flat) {
this.get_json(obj.children[i], options, flat);
}
else {
tmp.children.push(this.get_json(obj.children[i], options));
}
}
}
return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp);
},
/**
* create a new node (do not confuse with load_node)
* @name create_node([par, node, pos, callback, is_loaded])
* @param {mixed} par the parent node (to create a root node use either "#" (string) or `null`)
* @param {mixed} node the data for the new node (a valid JSON object, or a simple string with the name)
* @param {mixed} pos the index at which to insert the node, "first" and "last" are also supported, default is "last"
* @param {Function} callback a function to be called once the node is created
* @param {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded
* @return {String} the ID of the newly create node
* @trigger model.jstree, create_node.jstree
*/
create_node : function (par, node, pos, callback, is_loaded) {
if(par === null) { par = $.jstree.root; }
par = this.get_node(par);
if(!par) { return false; }
pos = pos === undefined ? "last" : pos;
if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); });
}
if(!node) { node = { "text" : this.get_string('New node') }; }
if(typeof node === "string") {
node = { "text" : node };
} else {
node = $.extend(true, {}, node);
}
if(node.text === undefined) { node.text = this.get_string('New node'); }
var tmp, dpc, i, j;
if(par.id === $.jstree.root) {
if(pos === "before") { pos = "first"; }
if(pos === "after") { pos = "last"; }
}
switch(pos) {
case "before":
tmp = this.get_node(par.parent);
pos = $.inArray(par.id, tmp.children);
par = tmp;
break;
case "after" :
tmp = this.get_node(par.parent);
pos = $.inArray(par.id, tmp.children) + 1;
par = tmp;
break;
case "inside":
case "first":
pos = 0;
break;
case "last":
pos = par.children.length;
break;
default:
if(!pos) { pos = 0; }
break;
}
if(pos > par.children.length) { pos = par.children.length; }
if(!node.id) { node.id = true; }
if(!this.check("create_node", node, par, pos)) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
if(node.id === true) { delete node.id; }
node = this._parse_model_from_json(node, par.id, par.parents.concat());
if(!node) { return false; }
tmp = this.get_node(node);
dpc = [];
dpc.push(node);
dpc = dpc.concat(tmp.children_d);
this.trigger('model', { "nodes" : dpc, "parent" : par.id });
par.children_d = par.children_d.concat(dpc);
for(i = 0, j = par.parents.length; i < j; i++) {
this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc);
}
node = tmp;
tmp = [];
for(i = 0, j = par.children.length; i < j; i++) {
tmp[i >= pos ? i+1 : i] = par.children[i];
}
tmp[pos] = node.id;
par.children = tmp;
this.redraw_node(par, true);
/**
* triggered when a node is created
* @event
* @name create_node.jstree
* @param {Object} node
* @param {String} parent the parent's ID
* @param {Number} position the position of the new node among the parent's children
*/
this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos });
if(callback) { callback.call(this, this.get_node(node)); }
return node.id;
},
/**
* set the text value of a node
* @name rename_node(obj, val)
* @param {mixed} obj the node, you can pass an array to rename multiple nodes to the same name
* @param {String} val the new text value
* @return {Boolean}
* @trigger rename_node.jstree
*/
rename_node : function (obj, val) {
var t1, t2, old;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.rename_node(obj[t1], val);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
old = obj.text;
if(!this.check("rename_node", obj, this.get_parent(obj), val)) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments))
/**
* triggered when a node is renamed
* @event
* @name rename_node.jstree
* @param {Object} node
* @param {String} text the new value
* @param {String} old the old value
*/
this.trigger('rename_node', { "node" : obj, "text" : val, "old" : old });
return true;
},
/**
* remove a node
* @name delete_node(obj)
* @param {mixed} obj the node, you can pass an array to delete multiple nodes
* @return {Boolean}
* @trigger delete_node.jstree, changed.jstree
*/
delete_node : function (obj) {
var t1, t2, par, pos, tmp, i, j, k, l, c, top, lft;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.delete_node(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
par = this.get_node(obj.parent);
pos = $.inArray(obj.id, par.children);
c = false;
if(!this.check("delete_node", obj, par, pos)) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
if(pos !== -1) {
par.children = $.vakata.array_remove(par.children, pos);
}
tmp = obj.children_d.concat([]);
tmp.push(obj.id);
for(i = 0, j = obj.parents.length; i < j; i++) {
this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
return $.inArray(v, tmp) === -1;
});
}
for(k = 0, l = tmp.length; k < l; k++) {
if(this._model.data[tmp[k]].state.selected) {
c = true;
break;
}
}
if (c) {
this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
return $.inArray(v, tmp) === -1;
});
}
/**
* triggered when a node is deleted
* @event
* @name delete_node.jstree
* @param {Object} node
* @param {String} parent the parent's ID
*/
this.trigger('delete_node', { "node" : obj, "parent" : par.id });
if(c) {
this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id });
}
for(k = 0, l = tmp.length; k < l; k++) {
delete this._model.data[tmp[k]];
}
if($.inArray(this._data.core.focused, tmp) !== -1) {
this._data.core.focused = null;
top = this.element[0].scrollTop;
lft = this.element[0].scrollLeft;
if(par.id === $.jstree.root) {
if (this._model.data[$.jstree.root].children[0]) {
this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus();
}
}
else {
this.get_node(par, true).children('.jstree-anchor').focus();
}
this.element[0].scrollTop = top;
this.element[0].scrollLeft = lft;
}
this.redraw_node(par, true);
return true;
},
/**
* check if an operation is premitted on the tree. Used internally.
* @private
* @name check(chk, obj, par, pos)
* @param {String} chk the operation to check, can be "create_node", "rename_node", "delete_node", "copy_node" or "move_node"
* @param {mixed} obj the node
* @param {mixed} par the parent
* @param {mixed} pos the position to insert at, or if "rename_node" - the new name
* @param {mixed} more some various additional information, for example if a "move_node" operations is triggered by DND this will be the hovered node
* @return {Boolean}
*/
check : function (chk, obj, par, pos, more) {
obj = obj && obj.id ? obj : this.get_node(obj);
par = par && par.id ? par : this.get_node(par);
var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj,
chc = this.settings.core.check_callback;
if(chk === "move_node" || chk === "copy_node") {
if((!more || !more.is_multi) && (chk === "move_node" && $.inArray(obj.id, par.children) === pos)) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_08', 'reason' : 'Moving node to its current position', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
if((!more || !more.is_multi) && (obj.id === par.id || (chk === "move_node" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
}
if(tmp && tmp.data) { tmp = tmp.data; }
if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) {
if(tmp.functions[chk] === false) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_02', 'reason' : 'Node data prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
}
return tmp.functions[chk];
}
if(chc === false || ($.isFunction(chc) && chc.call(this, chk, obj, par, pos, more) === false) || (chc && chc[chk] === false)) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_03', 'reason' : 'User config for core.check_callback prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
return true;
},
/**
* get the last error
* @name last_error()
* @return {Object}
*/
last_error : function () {
return this._data.core.last_error;
},
/**
* move a node to a new parent
* @name move_node(obj, par [, pos, callback, is_loaded])
* @param {mixed} obj the node to move, pass an array to move multiple nodes
* @param {mixed} par the new parent
* @param {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
* @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
* @param {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
* @param {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
* @param {Boolean} instance internal parameter indicating if the node comes from another instance
* @trigger move_node.jstree
*/
move_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
var t1, t2, old_par, old_pos, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p;
par = this.get_node(par);
pos = pos === undefined ? 0 : pos;
if(!par) { return false; }
if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true, false, origin); });
}
if($.isArray(obj)) {
if(obj.length === 1) {
obj = obj[0];
}
else {
//obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
if((tmp = this.move_node(obj[t1], par, pos, callback, is_loaded, false, origin))) {
par = tmp;
pos = "after";
}
}
this.redraw();
return true;
}
}
obj = obj && obj.id ? obj : this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
old_par = (obj.parent || $.jstree.root).toString();
new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);
old_pos = old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1;
if(old_ins && old_ins._id) {
obj = old_ins._model.data[obj.id];
}
if(is_multi) {
if((tmp = this.copy_node(obj, par, pos, callback, is_loaded, false, origin))) {
if(old_ins) { old_ins.delete_node(obj); }
return tmp;
}
return false;
}
//var m = this._model.data;
if(par.id === $.jstree.root) {
if(pos === "before") { pos = "first"; }
if(pos === "after") { pos = "last"; }
}
switch(pos) {
case "before":
pos = $.inArray(par.id, new_par.children);
break;
case "after" :
pos = $.inArray(par.id, new_par.children) + 1;
break;
case "inside":
case "first":
pos = 0;
break;
case "last":
pos = new_par.children.length;
break;
default:
if(!pos) { pos = 0; }
break;
}
if(pos > new_par.children.length) { pos = new_par.children.length; }
if(!this.check("move_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
if(obj.parent === new_par.id) {
dpc = new_par.children.concat();
tmp = $.inArray(obj.id, dpc);
if(tmp !== -1) {
dpc = $.vakata.array_remove(dpc, tmp);
if(pos > tmp) { pos--; }
}
tmp = [];
for(i = 0, j = dpc.length; i < j; i++) {
tmp[i >= pos ? i+1 : i] = dpc[i];
}
tmp[pos] = obj.id;
new_par.children = tmp;
this._node_changed(new_par.id);
this.redraw(new_par.id === $.jstree.root);
}
else {
// clean old parent and up
tmp = obj.children_d.concat();
tmp.push(obj.id);
for(i = 0, j = obj.parents.length; i < j; i++) {
dpc = [];
p = old_ins._model.data[obj.parents[i]].children_d;
for(k = 0, l = p.length; k < l; k++) {
if($.inArray(p[k], tmp) === -1) {
dpc.push(p[k]);
}
}
old_ins._model.data[obj.parents[i]].children_d = dpc;
}
old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id);
// insert into new parent and up
for(i = 0, j = new_par.parents.length; i < j; i++) {
this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp);
}
dpc = [];
for(i = 0, j = new_par.children.length; i < j; i++) {
dpc[i >= pos ? i+1 : i] = new_par.children[i];
}
dpc[pos] = obj.id;
new_par.children = dpc;
new_par.children_d.push(obj.id);
new_par.children_d = new_par.children_d.concat(obj.children_d);
// update object
obj.parent = new_par.id;
tmp = new_par.parents.concat();
tmp.unshift(new_par.id);
p = obj.parents.length;
obj.parents = tmp;
// update object children
tmp = tmp.concat();
for(i = 0, j = obj.children_d.length; i < j; i++) {
this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1);
Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp);
}
if(old_par === $.jstree.root || new_par.id === $.jstree.root) {
this._model.force_full_redraw = true;
}
if(!this._model.force_full_redraw) {
this._node_changed(old_par);
this._node_changed(new_par.id);
}
if(!skip_redraw) {
this.redraw();
}
}
if(callback) { callback.call(this, obj, new_par, pos); }
/**
* triggered when a node is moved
* @event
* @name move_node.jstree
* @param {Object} node
* @param {String} parent the parent's ID
* @param {Number} position the position of the node among the parent's children
* @param {String} old_parent the old parent of the node
* @param {Number} old_position the old position of the node
* @param {Boolean} is_multi do the node and new parent belong to different instances
* @param {jsTree} old_instance the instance the node came from
* @param {jsTree} new_instance the instance of the new parent
*/
this.trigger('move_node', { "node" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_pos, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
return obj.id;
},
/**
* copy a node to a new parent
* @name copy_node(obj, par [, pos, callback, is_loaded])
* @param {mixed} obj the node to copy, pass an array to copy multiple nodes
* @param {mixed} par the new parent
* @param {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
* @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
* @param {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
* @param {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
* @param {Boolean} instance internal parameter indicating if the node comes from another instance
* @trigger model.jstree copy_node.jstree
*/
copy_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi;
par = this.get_node(par);
pos = pos === undefined ? 0 : pos;
if(!par) { return false; }
if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true, false, origin); });
}
if($.isArray(obj)) {
if(obj.length === 1) {
obj = obj[0];
}
else {
//obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
if((tmp = this.copy_node(obj[t1], par, pos, callback, is_loaded, true, origin))) {
par = tmp;
pos = "after";
}
}
this.redraw();
return true;
}
}
obj = obj && obj.id ? obj : this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
old_par = (obj.parent || $.jstree.root).toString();
new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);
if(old_ins && old_ins._id) {
obj = old_ins._model.data[obj.id];
}
if(par.id === $.jstree.root) {
if(pos === "before") { pos = "first"; }
if(pos === "after") { pos = "last"; }
}
switch(pos) {
case "before":
pos = $.inArray(par.id, new_par.children);
break;
case "after" :
pos = $.inArray(par.id, new_par.children) + 1;
break;
case "inside":
case "first":
pos = 0;
break;
case "last":
pos = new_par.children.length;
break;
default:
if(!pos) { pos = 0; }
break;
}
if(pos > new_par.children.length) { pos = new_par.children.length; }
if(!this.check("copy_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj;
if(!node) { return false; }
if(node.id === true) { delete node.id; }
node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat());
if(!node) { return false; }
tmp = this.get_node(node);
if(obj && obj.state && obj.state.loaded === false) { tmp.state.loaded = false; }
dpc = [];
dpc.push(node);
dpc = dpc.concat(tmp.children_d);
this.trigger('model', { "nodes" : dpc, "parent" : new_par.id });
// insert into new parent and up
for(i = 0, j = new_par.parents.length; i < j; i++) {
this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc);
}
dpc = [];
for(i = 0, j = new_par.children.length; i < j; i++) {
dpc[i >= pos ? i+1 : i] = new_par.children[i];
}
dpc[pos] = tmp.id;
new_par.children = dpc;
new_par.children_d.push(tmp.id);
new_par.children_d = new_par.children_d.concat(tmp.children_d);
if(new_par.id === $.jstree.root) {
this._model.force_full_redraw = true;
}
if(!this._model.force_full_redraw) {
this._node_changed(new_par.id);
}
if(!skip_redraw) {
this.redraw(new_par.id === $.jstree.root);
}
if(callback) { callback.call(this, tmp, new_par, pos); }
/**
* triggered when a node is copied
* @event
* @name copy_node.jstree
* @param {Object} node the copied node
* @param {Object} original the original node
* @param {String} parent the parent's ID
* @param {Number} position the position of the node among the parent's children
* @param {String} old_parent the old parent of the node
* @param {Number} old_position the position of the original node
* @param {Boolean} is_multi do the node and new parent belong to different instances
* @param {jsTree} old_instance the instance the node came from
* @param {jsTree} new_instance the instance of the new parent
*/
this.trigger('copy_node', { "node" : tmp, "original" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1,'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
return tmp.id;
},
/**
* cut a node (a later call to `paste(obj)` would move the node)
* @name cut(obj)
* @param {mixed} obj multiple objects can be passed using an array
* @trigger cut.jstree
*/
cut : function (obj) {
if(!obj) { obj = this._data.core.selected.concat(); }
if(!$.isArray(obj)) { obj = [obj]; }
if(!obj.length) { return false; }
var tmp = [], o, t1, t2;
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
o = this.get_node(obj[t1]);
if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
}
if(!tmp.length) { return false; }
ccp_node = tmp;
ccp_inst = this;
ccp_mode = 'move_node';
/**
* triggered when nodes are added to the buffer for moving
* @event
* @name cut.jstree
* @param {Array} node
*/
this.trigger('cut', { "node" : obj });
},
/**
* copy a node (a later call to `paste(obj)` would copy the node)
* @name copy(obj)
* @param {mixed} obj multiple objects can be passed using an array
* @trigger copy.jstree
*/
copy : function (obj) {
if(!obj) { obj = this._data.core.selected.concat(); }
if(!$.isArray(obj)) { obj = [obj]; }
if(!obj.length) { return false; }
var tmp = [], o, t1, t2;
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
o = this.get_node(obj[t1]);
if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
}
if(!tmp.length) { return false; }
ccp_node = tmp;
ccp_inst = this;
ccp_mode = 'copy_node';
/**
* triggered when nodes are added to the buffer for copying
* @event
* @name copy.jstree
* @param {Array} node
*/
this.trigger('copy', { "node" : obj });
},
/**
* get the current buffer (any nodes that are waiting for a paste operation)
* @name get_buffer()
* @return {Object} an object consisting of `mode` ("copy_node" or "move_node"), `node` (an array of objects) and `inst` (the instance)
*/
get_buffer : function () {
return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst };
},
/**
* check if there is something in the buffer to paste
* @name can_paste()
* @return {Boolean}
*/
can_paste : function () {
return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node];
},
/**
* copy or move the previously cut or copied nodes to a new parent
* @name paste(obj [, pos])
* @param {mixed} obj the new parent
* @param {mixed} pos the position to insert at (besides integer, "first" and "last" are supported), defaults to integer `0`
* @trigger paste.jstree
*/
paste : function (obj, pos) {
obj = this.get_node(obj);
if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }
if(this[ccp_mode](ccp_node, obj, pos, false, false, false, ccp_inst)) {
/**
* triggered when paste is invoked
* @event
* @name paste.jstree
* @param {String} parent the ID of the receiving node
* @param {Array} node the nodes in the buffer
* @param {String} mode the performed operation - "copy_node" or "move_node"
*/
this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode });
}
ccp_node = false;
ccp_mode = false;
ccp_inst = false;
},
/**
* clear the buffer of previously copied or cut nodes
* @name clear_buffer()
* @trigger clear_buffer.jstree
*/
clear_buffer : function () {
ccp_node = false;
ccp_mode = false;
ccp_inst = false;
/**
* triggered when the copy / cut buffer is cleared
* @event
* @name clear_buffer.jstree
*/
this.trigger('clear_buffer');
},
/**
* put a node in edit mode (input field to rename the node)
* @name edit(obj [, default_text, callback])
* @param {mixed} obj
* @param {String} default_text the text to populate the input with (if omitted or set to a non-string value the node's text value is used)
* @param {Function} callback a function to be called once the text box is blurred, it is called in the instance's scope and receives the node, a status parameter (true if the rename is successful, false otherwise) and a boolean indicating if the user cancelled the edit. You can access the node's title using .text
*/
edit : function (obj, default_text, callback) {
var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false;
obj = this.get_node(obj);
if(!obj) { return false; }
if(!this.check("edit", obj, this.get_parent(obj))) {
this.settings.core.error.call(this, this._data.core.last_error);
return false;
}
tmp = obj;
default_text = typeof default_text === 'string' ? default_text : obj.text;
this.set_text(obj, "");
obj = this._open_to(obj);
tmp.text = default_text;
rtl = this._data.core.rtl;
w = this.element.width();
this._data.core.focused = tmp.id;
a = obj.children('.jstree-anchor').focus();
s = $('<span>');
/*!
oi = obj.children("i:visible"),
ai = a.children("i:visible"),
w1 = oi.width() * oi.length,
w2 = ai.width() * ai.length,
*/
t = default_text;
h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo(document.body);
h2 = $("<"+"input />", {
"value" : t,
"class" : "jstree-rename-input",
// "size" : t.length,
"css" : {
"padding" : "0",
"border" : "1px solid silver",
"box-sizing" : "border-box",
"display" : "inline-block",
"height" : (this._data.core.li_height) + "px",
"lineHeight" : (this._data.core.li_height) + "px",
"width" : "150px" // will be set a bit further down
},
"blur" : $.proxy(function (e) {
e.stopImmediatePropagation();
e.preventDefault();
var i = s.children(".jstree-rename-input"),
v = i.val(),
f = this.settings.core.force_text,
nv;
if(v === "") { v = t; }
h1.remove();
s.replaceWith(a);
s.remove();
t = f ? t : $('<div></div>').append($.parseHTML(t)).html();
obj = this.get_node(obj);
this.set_text(obj, t);
nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html());
if(!nv) {
this.set_text(obj, t); // move this up? and fix #483
}
this._data.core.focused = tmp.id;
setTimeout($.proxy(function () {
var node = this.get_node(tmp.id, true);
if(node.length) {
this._data.core.focused = tmp.id;
node.children('.jstree-anchor').focus();
}
}, this), 0);
if(callback) {
callback.call(this, tmp, nv, cancel);
}
h2 = null;
}, this),
"keydown" : function (e) {
var key = e.which;
if(key === 27) {
cancel = true;
this.value = t;
}
if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) {
e.stopImmediatePropagation();
}
if(key === 27 || key === 13) {
e.preventDefault();
this.blur();
}
},
"click" : function (e) { e.stopImmediatePropagation(); },
"mousedown" : function (e) { e.stopImmediatePropagation(); },
"keyup" : function (e) {
h2.width(Math.min(h1.text("pW" + this.value).width(),w));
},
"keypress" : function(e) {
if(e.which === 13) { return false; }
}
});
fn = {
fontFamily : a.css('fontFamily') || '',
fontSize : a.css('fontSize') || '',
fontWeight : a.css('fontWeight') || '',
fontStyle : a.css('fontStyle') || '',
fontStretch : a.css('fontStretch') || '',
fontVariant : a.css('fontVariant') || '',
letterSpacing : a.css('letterSpacing') || '',
wordSpacing : a.css('wordSpacing') || ''
};
s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2);
a.replaceWith(s);
h1.css(fn);
h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select();
$(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) {
if (h2 && e.target !== h2) {
$(h2).blur();
}
});
},
/**
* changes the theme
* @name set_theme(theme_name [, theme_url])
* @param {String} theme_name the name of the new theme to apply
* @param {mixed} theme_url the location of the CSS file for this theme. Omit or set to `false` if you manually included the file. Set to `true` to autoload from the `core.themes.dir` directory.
* @trigger set_theme.jstree
*/
set_theme : function (theme_name, theme_url) {
if(!theme_name) { return false; }
if(theme_url === true) {
var dir = this.settings.core.themes.dir;
if(!dir) { dir = $.jstree.path + '/themes'; }
theme_url = dir + '/' + theme_name + '/style.css';
}
if(theme_url && $.inArray(theme_url, themes_loaded) === -1) {
$('head').append('<'+'link rel="stylesheet" href="' + theme_url + '" type="text/css" />');
themes_loaded.push(theme_url);
}
if(this._data.core.themes.name) {
this.element.removeClass('jstree-' + this._data.core.themes.name);
}
this._data.core.themes.name = theme_name;
this.element.addClass('jstree-' + theme_name);
this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive');
/**
* triggered when a theme is set
* @event
* @name set_theme.jstree
* @param {String} theme the new theme
*/
this.trigger('set_theme', { 'theme' : theme_name });
},
/**
* gets the name of the currently applied theme name
* @name get_theme()
* @return {String}
*/
get_theme : function () { return this._data.core.themes.name; },
/**
* changes the theme variant (if the theme has variants)
* @name set_theme_variant(variant_name)
* @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed)
*/
set_theme_variant : function (variant_name) {
if(this._data.core.themes.variant) {
this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
}
this._data.core.themes.variant = variant_name;
if(variant_name) {
this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
}
},
/**
* gets the name of the currently applied theme variant
* @name get_theme()
* @return {String}
*/
get_theme_variant : function () { return this._data.core.themes.variant; },
/**
* shows a striped background on the container (if the theme supports it)
* @name show_stripes()
*/
show_stripes : function () {
this._data.core.themes.stripes = true;
this.get_container_ul().addClass("jstree-striped");
/**
* triggered when stripes are shown
* @event
* @name show_stripes.jstree
*/
this.trigger('show_stripes');
},
/**
* hides the striped background on the container
* @name hide_stripes()
*/
hide_stripes : function () {
this._data.core.themes.stripes = false;
this.get_container_ul().removeClass("jstree-striped");
/**
* triggered when stripes are hidden
* @event
* @name hide_stripes.jstree
*/
this.trigger('hide_stripes');
},
/**
* toggles the striped background on the container
* @name toggle_stripes()
*/
toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } },
/**
* shows the connecting dots (if the theme supports it)
* @name show_dots()
*/
show_dots : function () {
this._data.core.themes.dots = true;
this.get_container_ul().removeClass("jstree-no-dots");
/**
* triggered when dots are shown
* @event
* @name show_dots.jstree
*/
this.trigger('show_dots');
},
/**
* hides the connecting dots
* @name hide_dots()
*/
hide_dots : function () {
this._data.core.themes.dots = false;
this.get_container_ul().addClass("jstree-no-dots");
/**
* triggered when dots are hidden
* @event
* @name hide_dots.jstree
*/
this.trigger('hide_dots');
},
/**
* toggles the connecting dots
* @name toggle_dots()
*/
toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } },
/**
* show the node icons
* @name show_icons()
*/
show_icons : function () {
this._data.core.themes.icons = true;
this.get_container_ul().removeClass("jstree-no-icons");
/**
* triggered when icons are shown
* @event
* @name show_icons.jstree
*/
this.trigger('show_icons');
},
/**
* hide the node icons
* @name hide_icons()
*/
hide_icons : function () {
this._data.core.themes.icons = false;
this.get_container_ul().addClass("jstree-no-icons");
/**
* triggered when icons are hidden
* @event
* @name hide_icons.jstree
*/
this.trigger('hide_icons');
},
/**
* toggle the node icons
* @name toggle_icons()
*/
toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } },
/**
* show the node ellipsis
* @name show_icons()
*/
show_ellipsis : function () {
this._data.core.themes.ellipsis = true;
this.get_container_ul().addClass("jstree-ellipsis");
/**
* triggered when ellisis is shown
* @event
* @name show_ellipsis.jstree
*/
this.trigger('show_ellipsis');
},
/**
* hide the node ellipsis
* @name hide_ellipsis()
*/
hide_ellipsis : function () {
this._data.core.themes.ellipsis = false;
this.get_container_ul().removeClass("jstree-ellipsis");
/**
* triggered when ellisis is hidden
* @event
* @name hide_ellipsis.jstree
*/
this.trigger('hide_ellipsis');
},
/**
* toggle the node ellipsis
* @name toggle_icons()
*/
toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } },
/**
* set the node icon for a node
* @name set_icon(obj, icon)
* @param {mixed} obj
* @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class
*/
set_icon : function (obj, icon) {
var t1, t2, dom, old;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.set_icon(obj[t1], icon);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
old = obj.icon;
obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon;
dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon");
if(icon === false) {
dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
this.hide_icon(obj);
}
else if(icon === true || icon === null || icon === undefined || icon === '') {
dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
if(old === false) { this.show_icon(obj); }
}
else if(icon.indexOf("/") === -1 && icon.indexOf(".") === -1) {
dom.removeClass(old).css("background","");
dom.addClass(icon + ' jstree-themeicon-custom').attr("rel",icon);
if(old === false) { this.show_icon(obj); }
}
else {
dom.removeClass(old).css("background","");
dom.addClass('jstree-themeicon-custom').css("background", "url('" + icon + "') center center no-repeat").attr("rel",icon);
if(old === false) { this.show_icon(obj); }
}
return true;
},
/**
* get the node icon for a node
* @name get_icon(obj)
* @param {mixed} obj
* @return {String}
*/
get_icon : function (obj) {
obj = this.get_node(obj);
return (!obj || obj.id === $.jstree.root) ? false : obj.icon;
},
/**
* hide the icon on an individual node
* @name hide_icon(obj)
* @param {mixed} obj
*/
hide_icon : function (obj) {
var t1, t2;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.hide_icon(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj === $.jstree.root) { return false; }
obj.icon = false;
this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon").addClass('jstree-themeicon-hidden');
return true;
},
/**
* show the icon on an individual node
* @name show_icon(obj)
* @param {mixed} obj
*/
show_icon : function (obj) {
var t1, t2, dom;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.show_icon(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj === $.jstree.root) { return false; }
dom = this.get_node(obj, true);
obj.icon = dom.length ? dom.children(".jstree-anchor").children(".jstree-themeicon").attr('rel') : true;
if(!obj.icon) { obj.icon = true; }
dom.children(".jstree-anchor").children(".jstree-themeicon").removeClass('jstree-themeicon-hidden');
return true;
}
};
// helpers
$.vakata = {};
// collect attributes
$.vakata.attributes = function(node, with_values) {
node = $(node)[0];
var attr = with_values ? {} : [];
if(node && node.attributes) {
$.each(node.attributes, function (i, v) {
if($.inArray(v.name.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; }
if(v.value !== null && $.trim(v.value) !== '') {
if(with_values) { attr[v.name] = v.value; }
else { attr.push(v.name); }
}
});
}
return attr;
};
$.vakata.array_unique = function(array) {
var a = [], i, j, l, o = {};
for(i = 0, l = array.length; i < l; i++) {
if(o[array[i]] === undefined) {
a.push(array[i]);
o[array[i]] = true;
}
}
return a;
};
// remove item from array
$.vakata.array_remove = function(array, from) {
array.splice(from, 1);
return array;
//var rest = array.slice((to || from) + 1 || array.length);
//array.length = from < 0 ? array.length + from : from;
//array.push.apply(array, rest);
//return array;
};
// remove item from array
$.vakata.array_remove_item = function(array, item) {
var tmp = $.inArray(item, array);
return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array;
};
$.vakata.array_filter = function(c,a,b,d,e) {
if (c.filter) {
return c.filter(a, b);
}
d=[];
for (e in c) {
if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) {
d.push(c[e]);
}
}
return d;
};
/**
* ### Changed plugin
*
* This plugin adds more information to the `changed.jstree` event. The new data is contained in the `changed` event data property, and contains a lists of `selected` and `deselected` nodes.
*/
$.jstree.plugins.changed = function (options, parent) {
var last = [];
this.trigger = function (ev, data) {
var i, j;
if(!data) {
data = {};
}
if(ev.replace('.jstree','') === 'changed') {
data.changed = { selected : [], deselected : [] };
var tmp = {};
for(i = 0, j = last.length; i < j; i++) {
tmp[last[i]] = 1;
}
for(i = 0, j = data.selected.length; i < j; i++) {
if(!tmp[data.selected[i]]) {
data.changed.selected.push(data.selected[i]);
}
else {
tmp[data.selected[i]] = 2;
}
}
for(i = 0, j = last.length; i < j; i++) {
if(tmp[last[i]] === 1) {
data.changed.deselected.push(last[i]);
}
}
last = data.selected.slice();
}
/**
* triggered when selection changes (the "changed" plugin enhances the original event with more data)
* @event
* @name changed.jstree
* @param {Object} node
* @param {Object} action the action that caused the selection to change
* @param {Array} selected the current selection
* @param {Object} changed an object containing two properties `selected` and `deselected` - both arrays of node IDs, which were selected or deselected since the last changed event
* @param {Object} event the event (if any) that triggered this changed event
* @plugin changed
*/
parent.trigger.call(this, ev, data);
};
this.refresh = function (skip_loading, forget_state) {
last = [];
return parent.refresh.apply(this, arguments);
};
};
/**
* ### Checkbox plugin
*
* This plugin renders checkbox icons in front of each node, making multiple selection much easier.
* It also supports tri-state behavior, meaning that if a node has a few of its children checked it will be rendered as undetermined, and state will be propagated up.
*/
var _i = document.createElement('I');
_i.className = 'jstree-icon jstree-checkbox';
_i.setAttribute('role', 'presentation');
/**
* stores all defaults for the checkbox plugin
* @name $.jstree.defaults.checkbox
* @plugin checkbox
*/
$.jstree.defaults.checkbox = {
/**
* a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`.
* @name $.jstree.defaults.checkbox.visible
* @plugin checkbox
*/
visible : true,
/**
* a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`.
* @name $.jstree.defaults.checkbox.three_state
* @plugin checkbox
*/
three_state : true,
/**
* a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`.
* @name $.jstree.defaults.checkbox.whole_node
* @plugin checkbox
*/
whole_node : true,
/**
* a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`.
* @name $.jstree.defaults.checkbox.keep_selected_style
* @plugin checkbox
*/
keep_selected_style : true,
/**
* This setting controls how cascading and undetermined nodes are applied.
* If 'up' is in the string - cascading up is enabled, if 'down' is in the string - cascading down is enabled, if 'undetermined' is in the string - undetermined nodes will be used.
* If `three_state` is set to `true` this setting is automatically set to 'up+down+undetermined'. Defaults to ''.
* @name $.jstree.defaults.checkbox.cascade
* @plugin checkbox
*/
cascade : '',
/**
* This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin. Defaults to `true`, only set to `false` if you know exactly what you are doing.
* @name $.jstree.defaults.checkbox.tie_selection
* @plugin checkbox
*/
tie_selection : true,
/**
* This setting controls if cascading down affects disabled checkboxes
* @name $.jstree.defaults.checkbox.cascade_to_disabled
* @plugin checkbox
*/
cascade_to_disabled : true,
/**
* This setting controls if cascading down affects hidden checkboxes
* @name $.jstree.defaults.checkbox.cascade_to_hidden
* @plugin checkbox
*/
cascade_to_hidden : true
};
$.jstree.plugins.checkbox = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
this._data.checkbox.uto = false;
this._data.checkbox.selected = [];
if(this.settings.checkbox.three_state) {
this.settings.checkbox.cascade = 'up+down+undetermined';
}
this.element
.on("init.jstree", $.proxy(function () {
this._data.checkbox.visible = this.settings.checkbox.visible;
if(!this.settings.checkbox.keep_selected_style) {
this.element.addClass('jstree-checkbox-no-clicked');
}
if(this.settings.checkbox.tie_selection) {
this.element.addClass('jstree-checkbox-selection');
}
}, this))
.on("loading.jstree", $.proxy(function () {
this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ]();
}, this));
if(this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {
this.element
.on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', $.proxy(function () {
// only if undetermined is in setting
if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }
this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50);
}, this));
}
if(!this.settings.checkbox.tie_selection) {
this.element
.on('model.jstree', $.proxy(function (e, data) {
var m = this._model.data,
p = m[data.parent],
dpc = data.nodes,
i, j;
for(i = 0, j = dpc.length; i < j; i++) {
m[dpc[i]].state.checked = m[dpc[i]].state.checked || (m[dpc[i]].original && m[dpc[i]].original.state && m[dpc[i]].original.state.checked);
if(m[dpc[i]].state.checked) {
this._data.checkbox.selected.push(dpc[i]);
}
}
}, this));
}
if(this.settings.checkbox.cascade.indexOf('up') !== -1 || this.settings.checkbox.cascade.indexOf('down') !== -1) {
this.element
.on('model.jstree', $.proxy(function (e, data) {
var m = this._model.data,
p = m[data.parent],
dpc = data.nodes,
chd = [],
c, i, j, k, l, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection;
if(s.indexOf('down') !== -1) {
// apply down
if(p.state[ t ? 'selected' : 'checked' ]) {
for(i = 0, j = dpc.length; i < j; i++) {
m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true;
}
this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc);
}
else {
for(i = 0, j = dpc.length; i < j; i++) {
if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) {
for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) {
m[m[dpc[i]].children_d[k]].state[ t ? 'selected' : 'checked' ] = true;
}
this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(m[dpc[i]].children_d);
}
}
}
}
if(s.indexOf('up') !== -1) {
// apply up
for(i = 0, j = p.children_d.length; i < j; i++) {
if(!m[p.children_d[i]].children.length) {
chd.push(m[p.children_d[i]].parent);
}
}
chd = $.vakata.array_unique(chd);
for(k = 0, l = chd.length; k < l; k++) {
p = m[chd[k]];
while(p && p.id !== $.jstree.root) {
c = 0;
for(i = 0, j = p.children.length; i < j; i++) {
c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
}
if(c === j) {
p.state[ t ? 'selected' : 'checked' ] = true;
this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
tmp = this.get_node(p, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', true).children('.jstree-anchor').addClass( t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
break;
}
p = this.get_node(p.parent);
}
}
}
this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected);
}, this))
.on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) {
var self = this,
obj = data.node,
m = this._model.data,
par = this.get_node(obj.parent),
i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected;
for (i = 0, j = cur.length; i < j; i++) {
sel[cur[i]] = true;
}
// apply down
if(s.indexOf('down') !== -1) {
//this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d));
var selectedIds = this._cascade_new_checked_state(obj.id, true);
var temp = obj.children_d.concat(obj.id);
for (i = 0, j = temp.length; i < j; i++) {
if (selectedIds.indexOf(temp[i]) > -1) {
sel[temp[i]] = true;
}
else {
delete sel[temp[i]];
}
}
}
// apply up
if(s.indexOf('up') !== -1) {
while(par && par.id !== $.jstree.root) {
c = 0;
for(i = 0, j = par.children.length; i < j; i++) {
c += m[par.children[i]].state[ t ? 'selected' : 'checked' ];
}
if(c === j) {
par.state[ t ? 'selected' : 'checked' ] = true;
sel[par.id] = true;
//this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id);
tmp = this.get_node(par, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
break;
}
par = this.get_node(par.parent);
}
}
cur = [];
for (i in sel) {
if (sel.hasOwnProperty(i)) {
cur.push(i);
}
}
this._data[ t ? 'core' : 'checkbox' ].selected = cur;
}, this))
.on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', $.proxy(function (e, data) {
var obj = this.get_node($.jstree.root),
m = this._model.data,
i, j, tmp;
for(i = 0, j = obj.children_d.length; i < j; i++) {
tmp = m[obj.children_d[i]];
if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
tmp.original.state.undetermined = false;
}
}
}, this))
.on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) {
var self = this,
obj = data.node,
dom = this.get_node(obj, true),
i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection,
cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {},
stillSelectedIds = [],
allIds = obj.children_d.concat(obj.id);
// apply down
if(s.indexOf('down') !== -1) {
var selectedIds = this._cascade_new_checked_state(obj.id, false);
cur = $.vakata.array_filter(cur, function(id) {
return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1;
});
}
// only apply up if cascade up is enabled and if this node is not selected
// (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected).
if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) {
for(i = 0, j = obj.parents.length; i < j; i++) {
tmp = this._model.data[obj.parents[i]];
tmp.state[ t ? 'selected' : 'checked' ] = false;
if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) {
tmp.original.state.undetermined = false;
}
tmp = this.get_node(obj.parents[i], true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
cur = $.vakata.array_filter(cur, function(id) {
return obj.parents.indexOf(id) === -1;
});
}
this._data[ t ? 'core' : 'checkbox' ].selected = cur;
}, this));
}
if(this.settings.checkbox.cascade.indexOf('up') !== -1) {
this.element
.on('delete_node.jstree', $.proxy(function (e, data) {
// apply up (whole handler)
var p = this.get_node(data.parent),
m = this._model.data,
i, j, c, tmp, t = this.settings.checkbox.tie_selection;
while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {
c = 0;
for(i = 0, j = p.children.length; i < j; i++) {
c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
}
if(j > 0 && c === j) {
p.state[ t ? 'selected' : 'checked' ] = true;
this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
tmp = this.get_node(p, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
break;
}
p = this.get_node(p.parent);
}
}, this))
.on('move_node.jstree', $.proxy(function (e, data) {
// apply up (whole handler)
var is_multi = data.is_multi,
old_par = data.old_parent,
new_par = this.get_node(data.parent),
m = this._model.data,
p, c, i, j, tmp, t = this.settings.checkbox.tie_selection;
if(!is_multi) {
p = this.get_node(old_par);
while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) {
c = 0;
for(i = 0, j = p.children.length; i < j; i++) {
c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
}
if(j > 0 && c === j) {
p.state[ t ? 'selected' : 'checked' ] = true;
this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
tmp = this.get_node(p, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
break;
}
p = this.get_node(p.parent);
}
}
p = new_par;
while(p && p.id !== $.jstree.root) {
c = 0;
for(i = 0, j = p.children.length; i < j; i++) {
c += m[p.children[i]].state[ t ? 'selected' : 'checked' ];
}
if(c === j) {
if(!p.state[ t ? 'selected' : 'checked' ]) {
p.state[ t ? 'selected' : 'checked' ] = true;
this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id);
tmp = this.get_node(p, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
}
else {
if(p.state[ t ? 'selected' : 'checked' ]) {
p.state[ t ? 'selected' : 'checked' ] = false;
this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_remove_item(this._data[ t ? 'core' : 'checkbox' ].selected, p.id);
tmp = this.get_node(p, true);
if(tmp && tmp.length) {
tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
break;
}
}
p = this.get_node(p.parent);
}
}, this));
}
};
/**
* get an array of all nodes whose state is "undetermined"
* @name get_undetermined([full])
* @param {boolean} full: if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
* @plugin checkbox
*/
this.get_undetermined = function (full) {
if (this.settings.checkbox.cascade.indexOf('undetermined') === -1) {
return [];
}
var i, j, k, l, o = {}, m = this._model.data, t = this.settings.checkbox.tie_selection, s = this._data[ t ? 'core' : 'checkbox' ].selected, p = [], tt = this, r = [];
for(i = 0, j = s.length; i < j; i++) {
if(m[s[i]] && m[s[i]].parents) {
for(k = 0, l = m[s[i]].parents.length; k < l; k++) {
if(o[m[s[i]].parents[k]] !== undefined) {
break;
}
if(m[s[i]].parents[k] !== $.jstree.root) {
o[m[s[i]].parents[k]] = true;
p.push(m[s[i]].parents[k]);
}
}
}
}
// attempt for server side undetermined state
this.element.find('.jstree-closed').not(':has(.jstree-children)')
.each(function () {
var tmp = tt.get_node(this), tmp2;
if(!tmp) { return; }
if(!tmp.state.loaded) {
if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) {
if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) {
o[tmp.id] = true;
p.push(tmp.id);
}
for(k = 0, l = tmp.parents.length; k < l; k++) {
if(o[tmp.parents[k]] === undefined && tmp.parents[k] !== $.jstree.root) {
o[tmp.parents[k]] = true;
p.push(tmp.parents[k]);
}
}
}
}
else {
for(i = 0, j = tmp.children_d.length; i < j; i++) {
tmp2 = m[tmp.children_d[i]];
if(!tmp2.state.loaded && tmp2.original && tmp2.original.state && tmp2.original.state.undetermined && tmp2.original.state.undetermined === true) {
if(o[tmp2.id] === undefined && tmp2.id !== $.jstree.root) {
o[tmp2.id] = true;
p.push(tmp2.id);
}
for(k = 0, l = tmp2.parents.length; k < l; k++) {
if(o[tmp2.parents[k]] === undefined && tmp2.parents[k] !== $.jstree.root) {
o[tmp2.parents[k]] = true;
p.push(tmp2.parents[k]);
}
}
}
}
}
});
for (i = 0, j = p.length; i < j; i++) {
if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) {
r.push(full ? m[p[i]] : p[i]);
}
}
return r;
};
/**
* set the undetermined state where and if necessary. Used internally.
* @private
* @name _undetermined()
* @plugin checkbox
*/
this._undetermined = function () {
if(this.element === null) { return; }
var p = this.get_undetermined(false), i, j, s;
this.element.find('.jstree-undetermined').removeClass('jstree-undetermined');
for (i = 0, j = p.length; i < j; i++) {
s = this.get_node(p[i], true);
if(s && s.length) {
s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined');
}
}
};
this.redraw_node = function(obj, deep, is_callback, force_render) {
obj = parent.redraw_node.apply(this, arguments);
if(obj) {
var i, j, tmp = null, icon = null;
for(i = 0, j = obj.childNodes.length; i < j; i++) {
if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
tmp = obj.childNodes[i];
break;
}
}
if(tmp) {
if(!this.settings.checkbox.tie_selection && this._model.data[obj.id].state.checked) { tmp.className += ' jstree-checked'; }
icon = _i.cloneNode(false);
if(this._model.data[obj.id].state.checkbox_disabled) { icon.className += ' jstree-checkbox-disabled'; }
tmp.insertBefore(icon, tmp.childNodes[0]);
}
}
if(!is_callback && this.settings.checkbox.cascade.indexOf('undetermined') !== -1) {
if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); }
this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50);
}
return obj;
};
/**
* show the node checkbox icons
* @name show_checkboxes()
* @plugin checkbox
*/
this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.get_container_ul().removeClass("jstree-no-checkboxes"); };
/**
* hide the node checkbox icons
* @name hide_checkboxes()
* @plugin checkbox
*/
this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.get_container_ul().addClass("jstree-no-checkboxes"); };
/**
* toggle the node icons
* @name toggle_checkboxes()
* @plugin checkbox
*/
this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } };
/**
* checks if a node is in an undetermined state
* @name is_undetermined(obj)
* @param {mixed} obj
* @return {Boolean}
*/
this.is_undetermined = function (obj) {
obj = this.get_node(obj);
var s = this.settings.checkbox.cascade, i, j, t = this.settings.checkbox.tie_selection, d = this._data[ t ? 'core' : 'checkbox' ].selected, m = this._model.data;
if(!obj || obj.state[ t ? 'selected' : 'checked' ] === true || s.indexOf('undetermined') === -1 || (s.indexOf('down') === -1 && s.indexOf('up') === -1)) {
return false;
}
if(!obj.state.loaded && obj.original.state.undetermined === true) {
return true;
}
for(i = 0, j = obj.children_d.length; i < j; i++) {
if($.inArray(obj.children_d[i], d) !== -1 || (!m[obj.children_d[i]].state.loaded && m[obj.children_d[i]].original.state.undetermined)) {
return true;
}
}
return false;
};
/**
* disable a node's checkbox
* @name disable_checkbox(obj)
* @param {mixed} obj an array can be used too
* @trigger disable_checkbox.jstree
* @plugin checkbox
*/
this.disable_checkbox = function (obj) {
var t1, t2, dom;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.disable_checkbox(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(!obj.state.checkbox_disabled) {
obj.state.checkbox_disabled = true;
if(dom && dom.length) {
dom.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-checkbox-disabled');
}
/**
* triggered when an node's checkbox is disabled
* @event
* @name disable_checkbox.jstree
* @param {Object} node
* @plugin checkbox
*/
this.trigger('disable_checkbox', { 'node' : obj });
}
};
/**
* enable a node's checkbox
* @name enable_checkbox(obj)
* @param {mixed} obj an array can be used too
* @trigger enable_checkbox.jstree
* @plugin checkbox
*/
this.enable_checkbox = function (obj) {
var t1, t2, dom;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.enable_checkbox(obj[t1]);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(obj.state.checkbox_disabled) {
obj.state.checkbox_disabled = false;
if(dom && dom.length) {
dom.children('.jstree-anchor').children('.jstree-checkbox').removeClass('jstree-checkbox-disabled');
}
/**
* triggered when an node's checkbox is enabled
* @event
* @name enable_checkbox.jstree
* @param {Object} node
* @plugin checkbox
*/
this.trigger('enable_checkbox', { 'node' : obj });
}
};
this.activate_node = function (obj, e) {
if($(e.target).hasClass('jstree-checkbox-disabled')) {
return false;
}
if(this.settings.checkbox.tie_selection && (this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox'))) {
e.ctrlKey = true;
}
if(this.settings.checkbox.tie_selection || (!this.settings.checkbox.whole_node && !$(e.target).hasClass('jstree-checkbox'))) {
return parent.activate_node.call(this, obj, e);
}
if(this.is_disabled(obj)) {
return false;
}
if(this.is_checked(obj)) {
this.uncheck_node(obj, e);
}
else {
this.check_node(obj, e);
}
this.trigger('activate_node', { 'node' : this.get_node(obj) });
};
/**
* Cascades checked state to a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants).
* However if these unaffected nodes are already selected their ids will be included in the returned array.
* @private
* @param {string} id the node ID
* @param {bool} checkedState should the nodes be checked or not
* @returns {Array} Array of all node id's (in this tree branch) that are checked.
*/
this._cascade_new_checked_state = function (id, checkedState) {
var self = this;
var t = this.settings.checkbox.tie_selection;
var node = this._model.data[id];
var selectedNodeIds = [];
var selectedChildrenIds = [], i, j, selectedChildIds;
if (
(this.settings.checkbox.cascade_to_disabled || !node.state.disabled) &&
(this.settings.checkbox.cascade_to_hidden || !node.state.hidden)
) {
//First try and check/uncheck the children
if (node.children) {
for (i = 0, j = node.children.length; i < j; i++) {
var childId = node.children[i];
selectedChildIds = self._cascade_new_checked_state(childId, checkedState);
selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
if (selectedChildIds.indexOf(childId) > -1) {
selectedChildrenIds.push(childId);
}
}
}
var dom = self.get_node(node, true);
//A node's state is undetermined if some but not all of it's children are checked/selected .
var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length;
if(node.original && node.original.state && node.original.state.undetermined) {
node.original.state.undetermined = undetermined;
}
//If a node is undetermined then remove selected class
if (undetermined) {
node.state[ t ? 'selected' : 'checked' ] = false;
dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
}
//Otherwise, if the checkedState === true (i.e. the node is being checked now) and all of the node's children are checked (if it has any children),
//check the node and style it correctly.
else if (checkedState && selectedChildrenIds.length === node.children.length) {
node.state[ t ? 'selected' : 'checked' ] = checkedState;
selectedNodeIds.push(node.id);
dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked');
}
else {
node.state[ t ? 'selected' : 'checked' ] = false;
dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked');
}
}
else {
selectedChildIds = this.get_checked_descendants(id);
if (node.state[ t ? 'selected' : 'checked' ]) {
selectedChildIds.push(node.id);
}
selectedNodeIds = selectedNodeIds.concat(selectedChildIds);
}
return selectedNodeIds;
};
/**
* Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id)
* @name get_checked_descendants(obj)
* @param {string} id the node ID
* @return {Array} array of IDs
* @plugin checkbox
*/
this.get_checked_descendants = function (id) {
var self = this;
var t = self.settings.checkbox.tie_selection;
var node = self._model.data[id];
return $.vakata.array_filter(node.children_d, function(_id) {
return self._model.data[_id].state[ t ? 'selected' : 'checked' ];
});
};
/**
* check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally)
* @name check_node(obj)
* @param {mixed} obj an array can be used to check multiple nodes
* @trigger check_node.jstree
* @plugin checkbox
*/
this.check_node = function (obj, e) {
if(this.settings.checkbox.tie_selection) { return this.select_node(obj, false, true, e); }
var dom, t1, t2, th;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.check_node(obj[t1], e);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(!obj.state.checked) {
obj.state.checked = true;
this._data.checkbox.selected.push(obj.id);
if(dom && dom.length) {
dom.children('.jstree-anchor').addClass('jstree-checked');
}
/**
* triggered when an node is checked (only if tie_selection in checkbox settings is false)
* @event
* @name check_node.jstree
* @param {Object} node
* @param {Array} selected the current selection
* @param {Object} event the event (if any) that triggered this check_node
* @plugin checkbox
*/
this.trigger('check_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });
}
};
/**
* uncheck a node (only if tie_selection in checkbox settings is false, otherwise deselect_node will be called internally)
* @name uncheck_node(obj)
* @param {mixed} obj an array can be used to uncheck multiple nodes
* @trigger uncheck_node.jstree
* @plugin checkbox
*/
this.uncheck_node = function (obj, e) {
if(this.settings.checkbox.tie_selection) { return this.deselect_node(obj, false, e); }
var t1, t2, dom;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.uncheck_node(obj[t1], e);
}
return true;
}
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) {
return false;
}
dom = this.get_node(obj, true);
if(obj.state.checked) {
obj.state.checked = false;
this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, obj.id);
if(dom.length) {
dom.children('.jstree-anchor').removeClass('jstree-checked');
}
/**
* triggered when an node is unchecked (only if tie_selection in checkbox settings is false)
* @event
* @name uncheck_node.jstree
* @param {Object} node
* @param {Array} selected the current selection
* @param {Object} event the event (if any) that triggered this uncheck_node
* @plugin checkbox
*/
this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e });
}
};
/**
* checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally)
* @name check_all()
* @trigger check_all.jstree, changed.jstree
* @plugin checkbox
*/
this.check_all = function () {
if(this.settings.checkbox.tie_selection) { return this.select_all(); }
var tmp = this._data.checkbox.selected.concat([]), i, j;
this._data.checkbox.selected = this._model.data[$.jstree.root].children_d.concat();
for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {
if(this._model.data[this._data.checkbox.selected[i]]) {
this._model.data[this._data.checkbox.selected[i]].state.checked = true;
}
}
this.redraw(true);
/**
* triggered when all nodes are checked (only if tie_selection in checkbox settings is false)
* @event
* @name check_all.jstree
* @param {Array} selected the current selection
* @plugin checkbox
*/
this.trigger('check_all', { 'selected' : this._data.checkbox.selected });
};
/**
* uncheck all checked nodes (only if tie_selection in checkbox settings is false, otherwise deselect_all will be called internally)
* @name uncheck_all()
* @trigger uncheck_all.jstree
* @plugin checkbox
*/
this.uncheck_all = function () {
if(this.settings.checkbox.tie_selection) { return this.deselect_all(); }
var tmp = this._data.checkbox.selected.concat([]), i, j;
for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) {
if(this._model.data[this._data.checkbox.selected[i]]) {
this._model.data[this._data.checkbox.selected[i]].state.checked = false;
}
}
this._data.checkbox.selected = [];
this.element.find('.jstree-checked').removeClass('jstree-checked');
/**
* triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false)
* @event
* @name uncheck_all.jstree
* @param {Object} node the previous selection
* @param {Array} selected the current selection
* @plugin checkbox
*/
this.trigger('uncheck_all', { 'selected' : this._data.checkbox.selected, 'node' : tmp });
};
/**
* checks if a node is checked (if tie_selection is on in the settings this function will return the same as is_selected)
* @name is_checked(obj)
* @param {mixed} obj
* @return {Boolean}
* @plugin checkbox
*/
this.is_checked = function (obj) {
if(this.settings.checkbox.tie_selection) { return this.is_selected(obj); }
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
return obj.state.checked;
};
/**
* get an array of all checked nodes (if tie_selection is on in the settings this function will return the same as get_selected)
* @name get_checked([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
* @plugin checkbox
*/
this.get_checked = function (full) {
if(this.settings.checkbox.tie_selection) { return this.get_selected(full); }
return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected.slice();
};
/**
* get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected)
* @name get_top_checked([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
* @plugin checkbox
*/
this.get_top_checked = function (full) {
if(this.settings.checkbox.tie_selection) { return this.get_top_selected(full); }
var tmp = this.get_checked(true),
obj = {}, i, j, k, l;
for(i = 0, j = tmp.length; i < j; i++) {
obj[tmp[i].id] = tmp[i];
}
for(i = 0, j = tmp.length; i < j; i++) {
for(k = 0, l = tmp[i].children_d.length; k < l; k++) {
if(obj[tmp[i].children_d[k]]) {
delete obj[tmp[i].children_d[k]];
}
}
}
tmp = [];
for(i in obj) {
if(obj.hasOwnProperty(i)) {
tmp.push(i);
}
}
return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp;
};
/**
* get an array of all bottom level checked nodes (ignoring selected parents) (if tie_selection is on in the settings this function will return the same as get_bottom_selected)
* @name get_bottom_checked([full])
* @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
* @return {Array}
* @plugin checkbox
*/
this.get_bottom_checked = function (full) {
if(this.settings.checkbox.tie_selection) { return this.get_bottom_selected(full); }
var tmp = this.get_checked(true),
obj = [], i, j;
for(i = 0, j = tmp.length; i < j; i++) {
if(!tmp[i].children.length) {
obj.push(tmp[i].id);
}
}
return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj;
};
this.load_node = function (obj, callback) {
var k, l, i, j, c, tmp;
if(!$.isArray(obj) && !this.settings.checkbox.tie_selection) {
tmp = this.get_node(obj);
if(tmp && tmp.state.loaded) {
for(k = 0, l = tmp.children_d.length; k < l; k++) {
if(this._model.data[tmp.children_d[k]].state.checked) {
c = true;
this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, tmp.children_d[k]);
}
}
}
}
return parent.load_node.apply(this, arguments);
};
this.get_state = function () {
var state = parent.get_state.apply(this, arguments);
if(this.settings.checkbox.tie_selection) { return state; }
state.checkbox = this._data.checkbox.selected.slice();
return state;
};
this.set_state = function (state, callback) {
var res = parent.set_state.apply(this, arguments);
if(res && state.checkbox) {
if(!this.settings.checkbox.tie_selection) {
this.uncheck_all();
var _this = this;
$.each(state.checkbox, function (i, v) {
_this.check_node(v);
});
}
delete state.checkbox;
this.set_state(state, callback);
return false;
}
return res;
};
this.refresh = function (skip_loading, forget_state) {
if(this.settings.checkbox.tie_selection) {
this._data.checkbox.selected = [];
}
return parent.refresh.apply(this, arguments);
};
};
// include the checkbox plugin by default
// $.jstree.defaults.plugins.push("checkbox");
/**
* ### Conditionalselect plugin
*
* This plugin allows defining a callback to allow or deny node selection by user input (activate node method).
*/
/**
* a callback (function) which is invoked in the instance's scope and receives two arguments - the node and the event that triggered the `activate_node` call. Returning false prevents working with the node, returning true allows invoking activate_node. Defaults to returning `true`.
* @name $.jstree.defaults.checkbox.visible
* @plugin checkbox
*/
$.jstree.defaults.conditionalselect = function () { return true; };
$.jstree.plugins.conditionalselect = function (options, parent) {
// own function
this.activate_node = function (obj, e) {
if(this.settings.conditionalselect.call(this, this.get_node(obj), e)) {
return parent.activate_node.call(this, obj, e);
}
};
};
/**
* ### Contextmenu plugin
*
* Shows a context menu when a node is right-clicked.
*/
/**
* stores all defaults for the contextmenu plugin
* @name $.jstree.defaults.contextmenu
* @plugin contextmenu
*/
$.jstree.defaults.contextmenu = {
/**
* a boolean indicating if the node should be selected when the context menu is invoked on it. Defaults to `true`.
* @name $.jstree.defaults.contextmenu.select_node
* @plugin contextmenu
*/
select_node : true,
/**
* a boolean indicating if the menu should be shown aligned with the node. Defaults to `true`, otherwise the mouse coordinates are used.
* @name $.jstree.defaults.contextmenu.show_at_node
* @plugin contextmenu
*/
show_at_node : true,
/**
* an object of actions, or a function that accepts a node and a callback function and calls the callback function with an object of actions available for that node (you can also return the items too).
*
* Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required). Once a menu item is activated the `action` function will be invoked with an object containing the following keys: item - the contextmenu item definition as seen below, reference - the DOM node that was used (the tree node), element - the contextmenu DOM element, position - an object with x/y properties indicating the position of the menu.
*
* * `separator_before` - a boolean indicating if there should be a separator before this item
* * `separator_after` - a boolean indicating if there should be a separator after this item
* * `_disabled` - a boolean indicating if this action should be disabled
* * `label` - a string - the name of the action (could be a function returning a string)
* * `title` - a string - an optional tooltip for the item
* * `action` - a function to be executed if this item is chosen, the function will receive
* * `icon` - a string, can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class
* * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2)
* * `shortcut_label` - shortcut label (like for example `F2` for rename)
* * `submenu` - an object with the same structure as $.jstree.defaults.contextmenu.items which can be used to create a submenu - each key will be rendered as a separate option in a submenu that will appear once the current item is hovered
*
* @name $.jstree.defaults.contextmenu.items
* @plugin contextmenu
*/
items : function (o, cb) { // Could be an object directly
return {
"create" : {
"separator_before" : false,
"separator_after" : true,
"_disabled" : false, //(this.check("create_node", data.reference, {}, "last")),
"label" : "Create",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.create_node(obj, {}, "last", function (new_node) {
try {
inst.edit(new_node);
} catch (ex) {
setTimeout(function () { inst.edit(new_node); },0);
}
});
}
},
"rename" : {
"separator_before" : false,
"separator_after" : false,
"_disabled" : false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Rename",
/*!
"shortcut" : 113,
"shortcut_label" : 'F2',
"icon" : "glyphicon glyphicon-leaf",
*/
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.edit(obj);
}
},
"remove" : {
"separator_before" : false,
"icon" : false,
"separator_after" : false,
"_disabled" : false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Delete",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
if(inst.is_selected(obj)) {
inst.delete_node(inst.get_selected());
}
else {
inst.delete_node(obj);
}
}
},
"ccp" : {
"separator_before" : true,
"icon" : false,
"separator_after" : false,
"label" : "Edit",
"action" : false,
"submenu" : {
"cut" : {
"separator_before" : false,
"separator_after" : false,
"label" : "Cut",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
if(inst.is_selected(obj)) {
inst.cut(inst.get_top_selected());
}
else {
inst.cut(obj);
}
}
},
"copy" : {
"separator_before" : false,
"icon" : false,
"separator_after" : false,
"label" : "Copy",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
if(inst.is_selected(obj)) {
inst.copy(inst.get_top_selected());
}
else {
inst.copy(obj);
}
}
},
"paste" : {
"separator_before" : false,
"icon" : false,
"_disabled" : function (data) {
return !$.jstree.reference(data.reference).can_paste();
},
"separator_after" : false,
"label" : "Paste",
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
obj = inst.get_node(data.reference);
inst.paste(obj);
}
}
}
}
};
}
};
$.jstree.plugins.contextmenu = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
var last_ts = 0, cto = null, ex, ey;
this.element
.on("init.jstree loading.jstree ready.jstree", $.proxy(function () {
this.get_container_ul().addClass('jstree-contextmenu');
}, this))
.on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e, data) {
if (e.target.tagName.toLowerCase() === 'input') {
return;
}
e.preventDefault();
last_ts = e.ctrlKey ? +new Date() : 0;
if(data || cto) {
last_ts = (+new Date()) + 10000;
}
if(cto) {
clearTimeout(cto);
}
if(!this.is_loading(e.currentTarget)) {
this.show_contextmenu(e.currentTarget, e.pageX, e.pageY, e);
}
}, this))
.on("click.jstree", ".jstree-anchor", $.proxy(function (e) {
if(this._data.contextmenu.visible && (!last_ts || (+new Date()) - last_ts > 250)) { // work around safari & macOS ctrl+click
$.vakata.context.hide();
}
last_ts = 0;
}, this))
.on("touchstart.jstree", ".jstree-anchor", function (e) {
if(!e.originalEvent || !e.originalEvent.changedTouches || !e.originalEvent.changedTouches[0]) {
return;
}
ex = e.originalEvent.changedTouches[0].clientX;
ey = e.originalEvent.changedTouches[0].clientY;
cto = setTimeout(function () {
$(e.currentTarget).trigger('contextmenu', true);
}, 750);
})
.on('touchmove.vakata.jstree', function (e) {
if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 10 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 10)) {
clearTimeout(cto);
$.vakata.context.hide();
}
})
.on('touchend.vakata.jstree', function (e) {
if(cto) {
clearTimeout(cto);
}
});
/*!
if(!('oncontextmenu' in document.body) && ('ontouchstart' in document.body)) {
var el = null, tm = null;
this.element
.on("touchstart", ".jstree-anchor", function (e) {
el = e.currentTarget;
tm = +new Date();
$(document).one("touchend", function (e) {
e.target = document.elementFromPoint(e.originalEvent.targetTouches[0].pageX - window.pageXOffset, e.originalEvent.targetTouches[0].pageY - window.pageYOffset);
e.currentTarget = e.target;
tm = ((+(new Date())) - tm);
if(e.target === el && tm > 600 && tm < 1000) {
e.preventDefault();
$(el).trigger('contextmenu', e);
}
el = null;
tm = null;
});
});
}
*/
$(document).on("context_hide.vakata.jstree", $.proxy(function (e, data) {
this._data.contextmenu.visible = false;
$(data.reference).removeClass('jstree-context');
}, this));
};
this.teardown = function () {
if(this._data.contextmenu.visible) {
$.vakata.context.hide();
}
parent.teardown.call(this);
};
/**
* prepare and show the context menu for a node
* @name show_contextmenu(obj [, x, y])
* @param {mixed} obj the node
* @param {Number} x the x-coordinate relative to the document to show the menu at
* @param {Number} y the y-coordinate relative to the document to show the menu at
* @param {Object} e the event if available that triggered the contextmenu
* @plugin contextmenu
* @trigger show_contextmenu.jstree
*/
this.show_contextmenu = function (obj, x, y, e) {
obj = this.get_node(obj);
if(!obj || obj.id === $.jstree.root) { return false; }
var s = this.settings.contextmenu,
d = this.get_node(obj, true),
a = d.children(".jstree-anchor"),
o = false,
i = false;
if(s.show_at_node || x === undefined || y === undefined) {
o = a.offset();
x = o.left;
y = o.top + this._data.core.li_height;
}
if(this.settings.contextmenu.select_node && !this.is_selected(obj)) {
this.activate_node(obj, e);
}
i = s.items;
if($.isFunction(i)) {
i = i.call(this, obj, $.proxy(function (i) {
this._show_contextmenu(obj, x, y, i);
}, this));
}
if($.isPlainObject(i)) {
this._show_contextmenu(obj, x, y, i);
}
};
/**
* show the prepared context menu for a node
* @name _show_contextmenu(obj, x, y, i)
* @param {mixed} obj the node
* @param {Number} x the x-coordinate relative to the document to show the menu at
* @param {Number} y the y-coordinate relative to the document to show the menu at
* @param {Number} i the object of items to show
* @plugin contextmenu
* @trigger show_contextmenu.jstree
* @private
*/
this._show_contextmenu = function (obj, x, y, i) {
var d = this.get_node(obj, true),
a = d.children(".jstree-anchor");
$(document).one("context_show.vakata.jstree", $.proxy(function (e, data) {
var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu';
$(data.element).addClass(cls);
a.addClass('jstree-context');
}, this));
this._data.contextmenu.visible = true;
$.vakata.context.show(a, { 'x' : x, 'y' : y }, i);
/**
* triggered when the contextmenu is shown for a node
* @event
* @name show_contextmenu.jstree
* @param {Object} node the node
* @param {Number} x the x-coordinate of the menu relative to the document
* @param {Number} y the y-coordinate of the menu relative to the document
* @plugin contextmenu
*/
this.trigger('show_contextmenu', { "node" : obj, "x" : x, "y" : y });
};
};
// contextmenu helper
(function ($) {
var right_to_left = false,
vakata_context = {
element : false,
reference : false,
position_x : 0,
position_y : 0,
items : [],
html : "",
is_visible : false
};
$.vakata.context = {
settings : {
hide_onmouseleave : 0,
icons : true
},
_trigger : function (event_name) {
$(document).triggerHandler("context_" + event_name + ".vakata", {
"reference" : vakata_context.reference,
"element" : vakata_context.element,
"position" : {
"x" : vakata_context.position_x,
"y" : vakata_context.position_y
}
});
},
_execute : function (i) {
i = vakata_context.items[i];
return i && (!i._disabled || ($.isFunction(i._disabled) && !i._disabled({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }))) && i.action ? i.action.call(null, {
"item" : i,
"reference" : vakata_context.reference,
"element" : vakata_context.element,
"position" : {
"x" : vakata_context.position_x,
"y" : vakata_context.position_y
}
}) : false;
},
_parse : function (o, is_callback) {
if(!o) { return false; }
if(!is_callback) {
vakata_context.html = "";
vakata_context.items = [];
}
var str = "",
sep = false,
tmp;
if(is_callback) { str += "<"+"ul>"; }
$.each(o, function (i, val) {
if(!val) { return true; }
vakata_context.items.push(val);
if(!sep && val.separator_before) {
str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>";
}
sep = false;
str += "<"+"li class='" + (val._class || "") + (val._disabled === true || ($.isFunction(val._disabled) && val._disabled({ "item" : val, "reference" : vakata_context.reference, "element" : vakata_context.element })) ? " vakata-contextmenu-disabled " : "") + "' "+(val.shortcut?" data-shortcut='"+val.shortcut+"' ":'')+">";
str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "' " + (val.title ? "title='" + val.title + "'" : "") + ">";
if($.vakata.context.settings.icons) {
str += "<"+"i ";
if(val.icon) {
if(val.icon.indexOf("/") !== -1 || val.icon.indexOf(".") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; }
else { str += " class='" + val.icon + "' "; }
}
str += "><"+"/i><"+"span class='vakata-contextmenu-sep'> <"+"/span>";
}
str += ($.isFunction(val.label) ? val.label({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }) : val.label) + (val.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+val.shortcut+'">'+ (val.shortcut_label || '') +'</span>':'') + "<"+"/a>";
if(val.submenu) {
tmp = $.vakata.context._parse(val.submenu, true);
if(tmp) { str += tmp; }
}
str += "<"+"/li>";
if(val.separator_after) {
str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>";
sep = true;
}
});
str = str.replace(/<li class\='vakata-context-separator'\><\/li\>$/,"");
if(is_callback) { str += "</ul>"; }
/**
* triggered on the document when the contextmenu is parsed (HTML is built)
* @event
* @plugin contextmenu
* @name context_parse.vakata
* @param {jQuery} reference the element that was right clicked
* @param {jQuery} element the DOM element of the menu itself
* @param {Object} position the x & y coordinates of the menu
*/
if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); }
return str.length > 10 ? str : false;
},
_show_submenu : function (o) {
o = $(o);
if(!o.length || !o.children("ul").length) { return; }
var e = o.children("ul"),
xl = o.offset().left,
x = xl + o.outerWidth(),
y = o.offset().top,
w = e.width(),
h = e.height(),
dw = $(window).width() + $(window).scrollLeft(),
dh = $(window).height() + $(window).scrollTop();
// може да се спести е една проверка - дали няма някой от класовете вече нагоре
if(right_to_left) {
o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left");
}
else {
o[x + w > dw && xl > dw - x ? "addClass" : "removeClass"]("vakata-context-right");
}
if(y + h + 10 > dh) {
e.css("bottom","-1px");
}
//if does not fit - stick it to the side
if (o.hasClass('vakata-context-right')) {
if (xl < w) {
e.css("margin-right", xl - w);
}
} else {
if (dw - x < w) {
e.css("margin-left", dw - x - w);
}
}
e.show();
},
show : function (reference, position, data) {
var o, e, x, y, w, h, dw, dh, cond = true;
if(vakata_context.element && vakata_context.element.length) {
vakata_context.element.width('');
}
switch(cond) {
case (!position && !reference):
return false;
case (!!position && !!reference):
vakata_context.reference = reference;
vakata_context.position_x = position.x;
vakata_context.position_y = position.y;
break;
case (!position && !!reference):
vakata_context.reference = reference;
o = reference.offset();
vakata_context.position_x = o.left + reference.outerHeight();
vakata_context.position_y = o.top;
break;
case (!!position && !reference):
vakata_context.position_x = position.x;
vakata_context.position_y = position.y;
break;
}
if(!!reference && !data && $(reference).data('vakata_contextmenu')) {
data = $(reference).data('vakata_contextmenu');
}
if($.vakata.context._parse(data)) {
vakata_context.element.html(vakata_context.html);
}
if(vakata_context.items.length) {
vakata_context.element.appendTo(document.body);
e = vakata_context.element;
x = vakata_context.position_x;
y = vakata_context.position_y;
w = e.width();
h = e.height();
dw = $(window).width() + $(window).scrollLeft();
dh = $(window).height() + $(window).scrollTop();
if(right_to_left) {
x -= (e.outerWidth() - $(reference).outerWidth());
if(x < $(window).scrollLeft() + 20) {
x = $(window).scrollLeft() + 20;
}
}
if(x + w + 20 > dw) {
x = dw - (w + 20);
}
if(y + h + 20 > dh) {
y = dh - (h + 20);
}
vakata_context.element
.css({ "left" : x, "top" : y })
.show()
.find('a').first().focus().parent().addClass("vakata-context-hover");
vakata_context.is_visible = true;
/**
* triggered on the document when the contextmenu is shown
* @event
* @plugin contextmenu
* @name context_show.vakata
* @param {jQuery} reference the element that was right clicked
* @param {jQuery} element the DOM element of the menu itself
* @param {Object} position the x & y coordinates of the menu
*/
$.vakata.context._trigger("show");
}
},
hide : function () {
if(vakata_context.is_visible) {
vakata_context.element.hide().find("ul").hide().end().find(':focus').blur().end().detach();
vakata_context.is_visible = false;
/**
* triggered on the document when the contextmenu is hidden
* @event
* @plugin contextmenu
* @name context_hide.vakata
* @param {jQuery} reference the element that was right clicked
* @param {jQuery} element the DOM element of the menu itself
* @param {Object} position the x & y coordinates of the menu
*/
$.vakata.context._trigger("hide");
}
}
};
$(function () {
right_to_left = $(document.body).css("direction") === "rtl";
var to = false;
vakata_context.element = $("<ul class='vakata-context'></ul>");
vakata_context.element
.on("mouseenter", "li", function (e) {
e.stopImmediatePropagation();
if($.contains(this, e.relatedTarget)) {
// премахнато заради delegate mouseleave по-долу
// $(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
return;
}
if(to) { clearTimeout(to); }
vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end();
$(this)
.siblings().find("ul").hide().end().end()
.parentsUntil(".vakata-context", "li").addBack().addClass("vakata-context-hover");
$.vakata.context._show_submenu(this);
})
// тестово - дали не натоварва?
.on("mouseleave", "li", function (e) {
if($.contains(this, e.relatedTarget)) { return; }
$(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover");
})
.on("mouseleave", function (e) {
$(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
if($.vakata.context.settings.hide_onmouseleave) {
to = setTimeout(
(function (t) {
return function () { $.vakata.context.hide(); };
}(this)), $.vakata.context.settings.hide_onmouseleave);
}
})
.on("click", "a", function (e) {
e.preventDefault();
//})
//.on("mouseup", "a", function (e) {
if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) {
$.vakata.context.hide();
}
})
.on('keydown', 'a', function (e) {
var o = null;
switch(e.which) {
case 13:
case 32:
e.type = "click";
e.preventDefault();
$(e.currentTarget).trigger(e);
break;
case 37:
if(vakata_context.is_visible) {
vakata_context.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children('a').focus();
e.stopImmediatePropagation();
e.preventDefault();
}
break;
case 38:
if(vakata_context.is_visible) {
o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first();
if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last(); }
o.addClass("vakata-context-hover").children('a').focus();
e.stopImmediatePropagation();
e.preventDefault();
}
break;
case 39:
if(vakata_context.is_visible) {
vakata_context.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children('a').focus();
e.stopImmediatePropagation();
e.preventDefault();
}
break;
case 40:
if(vakata_context.is_visible) {
o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first();
if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first(); }
o.addClass("vakata-context-hover").children('a').focus();
e.stopImmediatePropagation();
e.preventDefault();
}
break;
case 27:
$.vakata.context.hide();
e.preventDefault();
break;
default:
//console.log(e.which);
break;
}
})
.on('keydown', function (e) {
e.preventDefault();
var a = vakata_context.element.find('.vakata-contextmenu-shortcut-' + e.which).parent();
if(a.parent().not('.vakata-context-disabled')) {
a.click();
}
});
$(document)
.on("mousedown.vakata.jstree", function (e) {
if(vakata_context.is_visible && vakata_context.element[0] !== e.target && !$.contains(vakata_context.element[0], e.target)) {
$.vakata.context.hide();
}
})
.on("context_show.vakata.jstree", function (e, data) {
vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent");
if(right_to_left) {
vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl");
}
// also apply a RTL class?
vakata_context.element.find("ul").hide().end();
});
});
}($));
// $.jstree.defaults.plugins.push("contextmenu");
/**
* ### Drag'n'drop plugin
*
* Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations.
*/
/**
* stores all defaults for the drag'n'drop plugin
* @name $.jstree.defaults.dnd
* @plugin dnd
*/
$.jstree.defaults.dnd = {
/**
* a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`.
* @name $.jstree.defaults.dnd.copy
* @plugin dnd
*/
copy : true,
/**
* a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`.
* @name $.jstree.defaults.dnd.open_timeout
* @plugin dnd
*/
open_timeout : 500,
/**
* a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the nodes about to be dragged as an argument (array) and the event that started the drag - return `false` to prevent dragging
* @name $.jstree.defaults.dnd.is_draggable
* @plugin dnd
*/
is_draggable : true,
/**
* a boolean indicating if checks should constantly be made while the user is dragging the node (as opposed to checking only on drop), default is `true`
* @name $.jstree.defaults.dnd.check_while_dragging
* @plugin dnd
*/
check_while_dragging : true,
/**
* a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false`
* @name $.jstree.defaults.dnd.always_copy
* @plugin dnd
*/
always_copy : false,
/**
* when dropping a node "inside", this setting indicates the position the node should go to - it can be an integer or a string: "first" (same as 0) or "last", default is `0`
* @name $.jstree.defaults.dnd.inside_pos
* @plugin dnd
*/
inside_pos : 0,
/**
* when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node, default is `true`, which means all selected nodes are dragged when the drag is started on a selected node
* @name $.jstree.defaults.dnd.drag_selection
* @plugin dnd
*/
drag_selection : true,
/**
* controls whether dnd works on touch devices. If left as boolean true dnd will work the same as in desktop browsers, which in some cases may impair scrolling. If set to boolean false dnd will not work on touch devices. There is a special third option - string "selected" which means only selected nodes can be dragged on touch devices.
* @name $.jstree.defaults.dnd.touch
* @plugin dnd
*/
touch : true,
/**
* controls whether items can be dropped anywhere on the node, not just on the anchor, by default only the node anchor is a valid drop target. Works best with the wholerow plugin. If enabled on mobile depending on the interface it might be hard for the user to cancel the drop, since the whole tree container will be a valid drop target.
* @name $.jstree.defaults.dnd.large_drop_target
* @plugin dnd
*/
large_drop_target : false,
/**
* controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected".
* @name $.jstree.defaults.dnd.large_drag_target
* @plugin dnd
*/
large_drag_target : false,
/**
* controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls.
* @reference http://caniuse.com/#feat=dragndrop
* @name $.jstree.defaults.dnd.use_html5
* @plugin dnd
*/
use_html5: false
};
var drg, elm;
// TODO: now check works by checking for each node individually, how about max_children, unique, etc?
$.jstree.plugins.dnd = function (options, parent) {
this.init = function (el, options) {
parent.init.call(this, el, options);
this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span'));
};
this.bind = function () {
parent.bind.call(this);
this.element
.on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) {
return true;
}
if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) {
return true;
}
var obj = this.get_node(e.target),
mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1,
txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget));
if(this.settings.core.force_text) {
txt = $.vakata.html.escape(txt);
}
if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart" || e.type === "dragstart") &&
(this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e)))
) {
drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] };
elm = e.currentTarget;
if (this.settings.dnd.use_html5) {
$.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg });
} else {
this.element.trigger('mousedown.jstree');
return $.vakata.dnd.start(e, drg, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>');
}
}
}, this));
if (this.settings.dnd.use_html5) {
this.element
.on('dragover.jstree', function (e) {
e.preventDefault();
$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
return false;
})
//.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) {
// e.preventDefault();
// $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
// return false;
// }, this))
.on('drop.jstree', $.proxy(function (e) {
e.preventDefault();
$.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
return false;
}, this));
}
};
this.redraw_node = function(obj, deep, callback, force_render) {
obj = parent.redraw_node.apply(this, arguments);
if (obj && this.settings.dnd.use_html5) {
if (this.settings.dnd.large_drag_target) {
obj.setAttribute('draggable', true);
} else {
var i, j, tmp = null;
for(i = 0, j = obj.childNodes.length; i < j; i++) {
if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
tmp = obj.childNodes[i];
break;
}
}
if(tmp) {
tmp.setAttribute('draggable', true);
}
}
}
return obj;
};
};
$(function() {
// bind only once for all instances
var lastmv = false,
laster = false,
lastev = false,
opento = false,
marker = $('<div id="jstree-marker"> </div>').hide(); //.appendTo('body');
$(document)
.on('dragover.vakata.jstree', function (e) {
if (elm) {
$.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg });
}
})
.on('drop.vakata.jstree', function (e) {
if (elm) {
$.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg });
elm = null;
drg = null;
}
})
.on('dnd_start.vakata.jstree', function (e, data) {
lastmv = false;
lastev = false;
if(!data || !data.data || !data.data.jstree) { return; }
marker.appendTo(document.body); //.show();
})
.on('dnd_move.vakata.jstree', function (e, data) {
var isDifferentNode = data.event.target !== lastev.target;
if(opento) {
if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
clearTimeout(opento);
}
}
if(!data || !data.data || !data.data.jstree) { return; }
// if we are hovering the marker image do nothing (can happen on "inside" drags)
if(data.event.target.id && data.event.target.id === 'jstree-marker') {
return;
}
lastev = data.event;
var ins = $.jstree.reference(data.event.target),
ref = false,
off = false,
rel = false,
tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn;
// if we are over an instance
if(ins && ins._data && ins._data.dnd) {
marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ));
is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)));
data.helper
.children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ))
.find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ]();
// if are hovering the container itself add a new root node
//console.log(data.event);
if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) {
ok = true;
for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? "copy_node" : "move_node"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) });
if(!ok) { break; }
}
if(ok) {
lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' };
marker.hide();
data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
}
return;
}
}
else {
// if we are hovering a tree node
ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor');
if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) {
off = ref.offset();
rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top;
h = ref.outerHeight();
if(rel < h / 3) {
o = ['b', 'i', 'a'];
}
else if(rel > h - h / 3) {
o = ['a', 'i', 'b'];
}
else {
o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a'];
}
$.each(o, function (j, v) {
switch(v) {
case 'b':
l = off.left - 6;
t = off.top;
p = ins.get_parent(ref);
i = ref.parent().index();
break;
case 'i':
ip = ins.settings.dnd.inside_pos;
tm = ins.get_node(ref.parent());
l = off.left - 2;
t = off.top + h / 2 + 1;
p = tm.id;
i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length));
break;
case 'a':
l = off.left - 6;
t = off.top + h;
p = ins.get_parent(ref);
i = ref.parent().index() + 1;
break;
}
ok = true;
for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) {
op = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? "copy_node" : "move_node";
ps = i;
if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) {
pr = ins.get_node(p);
if(ps > $.inArray(data.data.nodes[t1], pr.children)) {
ps -= 1;
}
}
ok = ok && ( (ins && ins.settings && ins.settings.dnd && ins.settings.dnd.check_while_dragging === false) || ins.check(op, (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), p, ps, { 'dnd' : true, 'ref' : ins.get_node(ref.parent()), 'pos' : v, 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) );
if(!ok) {
if(ins && ins.last_error) { laster = ins.last_error(); }
break;
}
}
if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) {
if (!data.event || data.event.type !== 'dragover' || isDifferentNode) {
if (opento) { clearTimeout(opento); }
opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout);
}
}
if(ok) {
pn = ins.get_node(p, true);
if (!pn.hasClass('.jstree-dnd-parent')) {
$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
pn.addClass('jstree-dnd-parent');
}
lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i };
marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show();
data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok');
if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move';
}
laster = {};
o = true;
return false;
}
});
if(o === true) { return; }
}
}
}
$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
lastmv = false;
data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er');
if (data.event.originalEvent && data.event.originalEvent.dataTransfer) {
//data.event.originalEvent.dataTransfer.dropEffect = 'none';
}
marker.hide();
})
.on('dnd_scroll.vakata.jstree', function (e, data) {
if(!data || !data.data || !data.data.jstree) { return; }
marker.hide();
lastmv = false;
lastev = false;
data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er');
})
.on('dnd_stop.vakata.jstree', function (e, data) {
$('.jstree-dnd-parent').removeClass('jstree-dnd-parent');
if(opento) { clearTimeout(opento); }
if(!data || !data.data || !data.data.jstree) { return; }
marker.hide().detach();
var i, j, nodes = [];
if(lastmv) {
for(i = 0, j = data.data.nodes.length; i < j; i++) {
nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i];
}
lastmv.ins[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos, false, false, false, data.data.origin);
}
else {
i = $(data.event.target).closest('.jstree');
if(i.length && laster && laster.error && laster.error === 'check') {
i = i.jstree(true);
if(i) {
i.settings.core.error.call(this, laster);
}
}
}
lastev = false;
lastmv = false;
})
.on('keyup.jstree keydown.jstree', function (e, data) {
data = $.vakata.dnd._get();
if(data && data.data && data.data.jstree) {
if (e.type === "keyup" && e.which === 27) {
if (opento) { clearTimeout(opento); }
lastmv = false;
laster = false;
lastev = false;
opento = false;
marker.hide().detach();
$.vakata.dnd._clean();
} else {
data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ]();
if(lastev) {
lastev.metaKey = e.metaKey;
lastev.ctrlKey = e.ctrlKey;
$.vakata.dnd._trigger('move', lastev);
}
}
}
});
});
// helpers
(function ($) {
$.vakata.html = {
div : $('<div />'),
escape : function (str) {
return $.vakata.html.div.text(str).html();
},
strip : function (str) {
return $.vakata.html.div.empty().append($.parseHTML(str)).text();
}
};
// private variable
var vakata_dnd = {
element : false,
target : false,
is_down : false,
is_drag : false,
helper : false,
helper_w: 0,
data : false,
init_x : 0,
init_y : 0,
scroll_l: 0,
scroll_t: 0,
scroll_e: false,
scroll_i: false,
is_touch: false
};
$.vakata.dnd = {
settings : {
scroll_speed : 10,
scroll_proximity : 20,
helper_left : 5,
helper_top : 10,
threshold : 5,
threshold_touch : 10
},
_trigger : function (event_name, e, data) {
if (data === undefined) {
data = $.vakata.dnd._get();
}
data.event = e;
$(document).triggerHandler("dnd_" + event_name + ".vakata", data);
},
_get : function () {
return {
"data" : vakata_dnd.data,
"element" : vakata_dnd.element,
"helper" : vakata_dnd.helper
};
},
_clean : function () {
if(vakata_dnd.helper) { vakata_dnd.helper.remove(); }
if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
vakata_dnd = {
element : false,
target : false,
is_down : false,
is_drag : false,
helper : false,
helper_w: 0,
data : false,
init_x : 0,
init_y : 0,
scroll_l: 0,
scroll_t: 0,
scroll_e: false,
scroll_i: false,
is_touch: false
};
$(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
$(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
},
_scroll : function (init_only) {
if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) {
if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; }
return false;
}
if(!vakata_dnd.scroll_i) {
vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100);
return false;
}
if(init_only === true) { return false; }
var i = vakata_dnd.scroll_e.scrollTop(),
j = vakata_dnd.scroll_e.scrollLeft();
vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed);
vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed);
if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) {
/**
* triggered on the document when a drag causes an element to scroll
* @event
* @plugin dnd
* @name dnd_scroll.vakata
* @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
* @param {DOM} element the DOM element being dragged
* @param {jQuery} helper the helper shown next to the mouse
* @param {jQuery} event the element that is scrolling
*/
$.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e);
}
},
start : function (e, data, html) {
if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
e.pageX = e.originalEvent.changedTouches[0].pageX;
e.pageY = e.originalEvent.changedTouches[0].pageY;
e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
}
if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); }
try {
e.currentTarget.unselectable = "on";
e.currentTarget.onselectstart = function() { return false; };
if(e.currentTarget.style) {
e.currentTarget.style.touchAction = "none";
e.currentTarget.style.msTouchAction = "none";
e.currentTarget.style.MozUserSelect = "none";
}
} catch(ignore) { }
vakata_dnd.init_x = e.pageX;
vakata_dnd.init_y = e.pageY;
vakata_dnd.data = data;
vakata_dnd.is_down = true;
vakata_dnd.element = e.currentTarget;
vakata_dnd.target = e.target;
vakata_dnd.is_touch = e.type === "touchstart";
if(html !== false) {
vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({
"display" : "block",
"margin" : "0",
"padding" : "0",
"position" : "absolute",
"top" : "-2000px",
"lineHeight" : "16px",
"zIndex" : "10000"
});
}
$(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag);
$(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop);
return false;
},
drag : function (e) {
if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
e.pageX = e.originalEvent.changedTouches[0].pageX;
e.pageY = e.originalEvent.changedTouches[0].pageY;
e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
}
if(!vakata_dnd.is_down) { return; }
if(!vakata_dnd.is_drag) {
if(
Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) ||
Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold)
) {
if(vakata_dnd.helper) {
vakata_dnd.helper.appendTo(document.body);
vakata_dnd.helper_w = vakata_dnd.helper.outerWidth();
}
vakata_dnd.is_drag = true;
$(vakata_dnd.target).one('click.vakata', false);
/**
* triggered on the document when a drag starts
* @event
* @plugin dnd
* @name dnd_start.vakata
* @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
* @param {DOM} element the DOM element being dragged
* @param {jQuery} helper the helper shown next to the mouse
* @param {Object} event the event that caused the start (probably mousemove)
*/
$.vakata.dnd._trigger("start", e);
}
else { return; }
}
var d = false, w = false,
dh = false, wh = false,
dw = false, ww = false,
dt = false, dl = false,
ht = false, hl = false;
vakata_dnd.scroll_t = 0;
vakata_dnd.scroll_l = 0;
vakata_dnd.scroll_e = false;
$($(e.target).parentsUntil("body").addBack().get().reverse())
.filter(function () {
return (/^auto|scroll$/).test($(this).css("overflow")) &&
(this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth);
})
.each(function () {
var t = $(this), o = t.offset();
if(this.scrollHeight > this.offsetHeight) {
if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
}
if(this.scrollWidth > this.offsetWidth) {
if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
}
if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
vakata_dnd.scroll_e = $(this);
return false;
}
});
if(!vakata_dnd.scroll_e) {
d = $(document); w = $(window);
dh = d.height(); wh = w.height();
dw = d.width(); ww = w.width();
dt = d.scrollTop(); dl = d.scrollLeft();
if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; }
if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; }
if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; }
if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; }
if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) {
vakata_dnd.scroll_e = d;
}
}
if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); }
if(vakata_dnd.helper) {
ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10);
hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10);
if(dh && ht + 25 > dh) { ht = dh - 50; }
if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); }
vakata_dnd.helper.css({
left : hl + "px",
top : ht + "px"
});
}
/**
* triggered on the document when a drag is in progress
* @event
* @plugin dnd
* @name dnd_move.vakata
* @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
* @param {DOM} element the DOM element being dragged
* @param {jQuery} helper the helper shown next to the mouse
* @param {Object} event the event that caused this to trigger (most likely mousemove)
*/
$.vakata.dnd._trigger("move", e);
return false;
},
stop : function (e) {
if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) {
e.pageX = e.originalEvent.changedTouches[0].pageX;
e.pageY = e.originalEvent.changedTouches[0].pageY;
e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset);
}
if(vakata_dnd.is_drag) {
/**
* triggered on the document when a drag stops (the dragged element is dropped)
* @event
* @plugin dnd
* @name dnd_stop.vakata
* @param {Mixed} data any data supplied with the call to $.vakata.dnd.start
* @param {DOM} element the DOM element being dragged
* @param {jQuery} helper the helper shown next to the mouse
* @param {Object} event the event that caused the stop
*/
if (e.target !== vakata_dnd.target) {
$(vakata_dnd.target).off('click.vakata');
}
$.vakata.dnd._trigger("stop", e);
}
else {
if(e.type === "touchend" && e.target === vakata_dnd.target) {
var to = setTimeout(function () { $(e.target).click(); }, 100);
$(e.target).one('click', function() { if(to) { clearTimeout(to); } });
}
}
$.vakata.dnd._clean();
return false;
}
};
}($));
// include the dnd plugin by default
// $.jstree.defaults.plugins.push("dnd");
/**
* ### Massload plugin
*
* Adds massload functionality to jsTree, so that multiple nodes can be loaded in a single request (only useful with lazy loading).
*/
/**
* massload configuration
*
* It is possible to set this to a standard jQuery-like AJAX config.
* In addition to the standard jQuery ajax options here you can supply functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node IDs need to be loaded, the return value of those functions will be used.
*
* You can also set this to a function, that function will receive the node IDs being loaded as argument and a second param which is a function (callback) which should be called with the result.
*
* Both the AJAX and the function approach rely on the same return value - an object where the keys are the node IDs, and the value is the children of that node as an array.
*
* {
* "id1" : [{ "text" : "Child of ID1", "id" : "c1" }, { "text" : "Another child of ID1", "id" : "c2" }],
* "id2" : [{ "text" : "Child of ID2", "id" : "c3" }]
* }
*
* @name $.jstree.defaults.massload
* @plugin massload
*/
$.jstree.defaults.massload = null;
$.jstree.plugins.massload = function (options, parent) {
this.init = function (el, options) {
this._data.massload = {};
parent.init.call(this, el, options);
};
this._load_nodes = function (nodes, callback, is_callback, force_reload) {
var s = this.settings.massload,
nodesString = JSON.stringify(nodes),
toLoad = [],
m = this._model.data,
i, j, dom;
if (!is_callback) {
for(i = 0, j = nodes.length; i < j; i++) {
if(!m[nodes[i]] || ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || force_reload) ) {
toLoad.push(nodes[i]);
dom = this.get_node(nodes[i], true);
if (dom && dom.length) {
dom.addClass("jstree-loading").attr('aria-busy',true);
}
}
}
this._data.massload = {};
if (toLoad.length) {
if($.isFunction(s)) {
return s.call(this, toLoad, $.proxy(function (data) {
var i, j;
if(data) {
for(i in data) {
if(data.hasOwnProperty(i)) {
this._data.massload[i] = data[i];
}
}
}
for(i = 0, j = nodes.length; i < j; i++) {
dom = this.get_node(nodes[i], true);
if (dom && dom.length) {
dom.removeClass("jstree-loading").attr('aria-busy',false);
}
}
parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
}, this));
}
if(typeof s === 'object' && s && s.url) {
s = $.extend(true, {}, s);
if($.isFunction(s.url)) {
s.url = s.url.call(this, toLoad);
}
if($.isFunction(s.data)) {
s.data = s.data.call(this, toLoad);
}
return $.ajax(s)
.done($.proxy(function (data,t,x) {
var i, j;
if(data) {
for(i in data) {
if(data.hasOwnProperty(i)) {
this._data.massload[i] = data[i];
}
}
}
for(i = 0, j = nodes.length; i < j; i++) {
dom = this.get_node(nodes[i], true);
if (dom && dom.length) {
dom.removeClass("jstree-loading").attr('aria-busy',false);
}
}
parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
}, this))
.fail($.proxy(function (f) {
parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
}, this));
}
}
}
return parent._load_nodes.call(this, nodes, callback, is_callback, force_reload);
};
this._load_node = function (obj, callback) {
var data = this._data.massload[obj.id],
rslt = null, dom;
if(data) {
rslt = this[typeof data === 'string' ? '_append_html_data' : '_append_json_data'](
obj,
typeof data === 'string' ? $($.parseHTML(data)).filter(function () { return this.nodeType !== 3; }) : data,
function (status) { callback.call(this, status); }
);
dom = this.get_node(obj.id, true);
if (dom && dom.length) {
dom.removeClass("jstree-loading").attr('aria-busy',false);
}
delete this._data.massload[obj.id];
return rslt;
}
return parent._load_node.call(this, obj, callback);
};
};
/**
* ### Search plugin
*
* Adds search functionality to jsTree.
*/
/**
* stores all defaults for the search plugin
* @name $.jstree.defaults.search
* @plugin search
*/
$.jstree.defaults.search = {
/**
* a jQuery-like AJAX config, which jstree uses if a server should be queried for results.
*
* A `str` (which is the search string) parameter will be added with the request, an optional `inside` parameter will be added if the search is limited to a node id. The expected result is a JSON array with nodes that need to be opened so that matching nodes will be revealed.
* Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to
* @name $.jstree.defaults.search.ajax
* @plugin search
*/
ajax : false,
/**
* Indicates if the search should be fuzzy or not (should `chnd3` match `child node 3`). Default is `false`.
* @name $.jstree.defaults.search.fuzzy
* @plugin search
*/
fuzzy : false,
/**
* Indicates if the search should be case sensitive. Default is `false`.
* @name $.jstree.defaults.search.case_sensitive
* @plugin search
*/
case_sensitive : false,
/**
* Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers).
* This setting can be changed at runtime when calling the search method. Default is `false`.
* @name $.jstree.defaults.search.show_only_matches
* @plugin search
*/
show_only_matches : false,
/**
* Indicates if the children of matched element are shown (when show_only_matches is true)
* This setting can be changed at runtime when calling the search method. Default is `false`.
* @name $.jstree.defaults.search.show_only_matches_children
* @plugin search
*/
show_only_matches_children : false,
/**
* Indicates if all nodes opened to reveal the search result, should be closed when the search is cleared or a new search is performed. Default is `true`.
* @name $.jstree.defaults.search.close_opened_onclear
* @plugin search
*/
close_opened_onclear : true,
/**
* Indicates if only leaf nodes should be included in search results. Default is `false`.
* @name $.jstree.defaults.search.search_leaves_only
* @plugin search
*/
search_leaves_only : false,
/**
* If set to a function it wil be called in the instance's scope with two arguments - search string and node (where node will be every node in the structure, so use with caution).
* If the function returns a truthy value the node will be considered a match (it might not be displayed if search_only_leaves is set to true and the node is not a leaf). Default is `false`.
* @name $.jstree.defaults.search.search_callback
* @plugin search
*/
search_callback : false
};
$.jstree.plugins.search = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
this._data.search.str = "";
this._data.search.dom = $();
this._data.search.res = [];
this._data.search.opn = [];
this._data.search.som = false;
this._data.search.smc = false;
this._data.search.hdn = [];
this.element
.on("search.jstree", $.proxy(function (e, data) {
if(this._data.search.som && data.res.length) {
var m = this._model.data, i, j, p = [], k, l;
for(i = 0, j = data.res.length; i < j; i++) {
if(m[data.res[i]] && !m[data.res[i]].state.hidden) {
p.push(data.res[i]);
p = p.concat(m[data.res[i]].parents);
if(this._data.search.smc) {
for (k = 0, l = m[data.res[i]].children_d.length; k < l; k++) {
if (m[m[data.res[i]].children_d[k]] && !m[m[data.res[i]].children_d[k]].state.hidden) {
p.push(m[data.res[i]].children_d[k]);
}
}
}
}
}
p = $.vakata.array_remove_item($.vakata.array_unique(p), $.jstree.root);
this._data.search.hdn = this.hide_all(true);
this.show_node(p, true);
this.redraw(true);
}
}, this))
.on("clear_search.jstree", $.proxy(function (e, data) {
if(this._data.search.som && data.res.length) {
this.show_node(this._data.search.hdn, true);
this.redraw(true);
}
}, this));
};
/**
* used to search the tree nodes for a given string
* @name search(str [, skip_async])
* @param {String} str the search string
* @param {Boolean} skip_async if set to true server will not be queried even if configured
* @param {Boolean} show_only_matches if set to true only matching nodes will be shown (keep in mind this can be very slow on large trees or old browsers)
* @param {mixed} inside an optional node to whose children to limit the search
* @param {Boolean} append if set to true the results of this search are appended to the previous search
* @plugin search
* @trigger search.jstree
*/
this.search = function (str, skip_async, show_only_matches, inside, append, show_only_matches_children) {
if(str === false || $.trim(str.toString()) === "") {
return this.clear_search();
}
inside = this.get_node(inside);
inside = inside && inside.id ? inside.id : null;
str = str.toString();
var s = this.settings.search,
a = s.ajax ? s.ajax : false,
m = this._model.data,
f = null,
r = [],
p = [], i, j;
if(this._data.search.res.length && !append) {
this.clear_search();
}
if(show_only_matches === undefined) {
show_only_matches = s.show_only_matches;
}
if(show_only_matches_children === undefined) {
show_only_matches_children = s.show_only_matches_children;
}
if(!skip_async && a !== false) {
if($.isFunction(a)) {
return a.call(this, str, $.proxy(function (d) {
if(d && d.d) { d = d.d; }
this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () {
this.search(str, true, show_only_matches, inside, append, show_only_matches_children);
});
}, this), inside);
}
else {
a = $.extend({}, a);
if(!a.data) { a.data = {}; }
a.data.str = str;
if(inside) {
a.data.inside = inside;
}
if (this._data.search.lastRequest) {
this._data.search.lastRequest.abort();
}
this._data.search.lastRequest = $.ajax(a)
.fail($.proxy(function () {
this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) };
this.settings.core.error.call(this, this._data.core.last_error);
}, this))
.done($.proxy(function (d) {
if(d && d.d) { d = d.d; }
this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () {
this.search(str, true, show_only_matches, inside, append, show_only_matches_children);
});
}, this));
return this._data.search.lastRequest;
}
}
if(!append) {
this._data.search.str = str;
this._data.search.dom = $();
this._data.search.res = [];
this._data.search.opn = [];
this._data.search.som = show_only_matches;
this._data.search.smc = show_only_matches_children;
}
f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy });
$.each(m[inside ? inside : $.jstree.root].children_d, function (ii, i) {
var v = m[i];
if(v.text && !v.state.hidden && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) {
r.push(i);
p = p.concat(v.parents);
}
});
if(r.length) {
p = $.vakata.array_unique(p);
for(i = 0, j = p.length; i < j; i++) {
if(p[i] !== $.jstree.root && m[p[i]] && this.open_node(p[i], null, 0) === true) {
this._data.search.opn.push(p[i]);
}
}
if(!append) {
this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #')));
this._data.search.res = r;
}
else {
this._data.search.dom = this._data.search.dom.add($(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #'))));
this._data.search.res = $.vakata.array_unique(this._data.search.res.concat(r));
}
this._data.search.dom.children(".jstree-anchor").addClass('jstree-search');
}
/**
* triggered after search is complete
* @event
* @name search.jstree
* @param {jQuery} nodes a jQuery collection of matching nodes
* @param {String} str the search string
* @param {Array} res a collection of objects represeing the matching nodes
* @plugin search
*/
this.trigger('search', { nodes : this._data.search.dom, str : str, res : this._data.search.res, show_only_matches : show_only_matches });
};
/**
* used to clear the last search (removes classes and shows all nodes if filtering is on)
* @name clear_search()
* @plugin search
* @trigger clear_search.jstree
*/
this.clear_search = function () {
if(this.settings.search.close_opened_onclear) {
this.close_node(this._data.search.opn, 0);
}
/**
* triggered after search is complete
* @event
* @name clear_search.jstree
* @param {jQuery} nodes a jQuery collection of matching nodes (the result from the last search)
* @param {String} str the search string (the last search string)
* @param {Array} res a collection of objects represeing the matching nodes (the result from the last search)
* @plugin search
*/
this.trigger('clear_search', { 'nodes' : this._data.search.dom, str : this._data.search.str, res : this._data.search.res });
if(this._data.search.res.length) {
this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(this._data.search.res, function (v) {
return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&');
}).join(', #')));
this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search");
}
this._data.search.str = "";
this._data.search.res = [];
this._data.search.opn = [];
this._data.search.dom = $();
};
this.redraw_node = function(obj, deep, callback, force_render) {
obj = parent.redraw_node.apply(this, arguments);
if(obj) {
if($.inArray(obj.id, this._data.search.res) !== -1) {
var i, j, tmp = null;
for(i = 0, j = obj.childNodes.length; i < j; i++) {
if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) {
tmp = obj.childNodes[i];
break;
}
}
if(tmp) {
tmp.className += ' jstree-search';
}
}
}
return obj;
};
};
// helpers
(function ($) {
// from http://kiro.me/projects/fuse.html
$.vakata.search = function(pattern, txt, options) {
options = options || {};
options = $.extend({}, $.vakata.search.defaults, options);
if(options.fuzzy !== false) {
options.fuzzy = true;
}
pattern = options.caseSensitive ? pattern : pattern.toLowerCase();
var MATCH_LOCATION = options.location,
MATCH_DISTANCE = options.distance,
MATCH_THRESHOLD = options.threshold,
patternLen = pattern.length,
matchmask, pattern_alphabet, match_bitapScore, search;
if(patternLen > 32) {
options.fuzzy = false;
}
if(options.fuzzy) {
matchmask = 1 << (patternLen - 1);
pattern_alphabet = (function () {
var mask = {},
i = 0;
for (i = 0; i < patternLen; i++) {
mask[pattern.charAt(i)] = 0;
}
for (i = 0; i < patternLen; i++) {
mask[pattern.charAt(i)] |= 1 << (patternLen - i - 1);
}
return mask;
}());
match_bitapScore = function (e, x) {
var accuracy = e / patternLen,
proximity = Math.abs(MATCH_LOCATION - x);
if(!MATCH_DISTANCE) {
return proximity ? 1.0 : accuracy;
}
return accuracy + (proximity / MATCH_DISTANCE);
};
}
search = function (text) {
text = options.caseSensitive ? text : text.toLowerCase();
if(pattern === text || text.indexOf(pattern) !== -1) {
return {
isMatch: true,
score: 0
};
}
if(!options.fuzzy) {
return {
isMatch: false,
score: 1
};
}
var i, j,
textLen = text.length,
scoreThreshold = MATCH_THRESHOLD,
bestLoc = text.indexOf(pattern, MATCH_LOCATION),
binMin, binMid,
binMax = patternLen + textLen,
lastRd, start, finish, rd, charMatch,
score = 1,
locations = [];
if (bestLoc !== -1) {
scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);
bestLoc = text.lastIndexOf(pattern, MATCH_LOCATION + patternLen);
if (bestLoc !== -1) {
scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold);
}
}
bestLoc = -1;
for (i = 0; i < patternLen; i++) {
binMin = 0;
binMid = binMax;
while (binMin < binMid) {
if (match_bitapScore(i, MATCH_LOCATION + binMid) <= scoreThreshold) {
binMin = binMid;
} else {
binMax = binMid;
}
binMid = Math.floor((binMax - binMin) / 2 + binMin);
}
binMax = binMid;
start = Math.max(1, MATCH_LOCATION - binMid + 1);
finish = Math.min(MATCH_LOCATION + binMid, textLen) + patternLen;
rd = new Array(finish + 2);
rd[finish + 1] = (1 << i) - 1;
for (j = finish; j >= start; j--) {
charMatch = pattern_alphabet[text.charAt(j - 1)];
if (i === 0) {
rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
} else {
rd[j] = ((rd[j + 1] << 1) | 1) & charMatch | (((lastRd[j + 1] | lastRd[j]) << 1) | 1) | lastRd[j + 1];
}
if (rd[j] & matchmask) {
score = match_bitapScore(i, j - 1);
if (score <= scoreThreshold) {
scoreThreshold = score;
bestLoc = j - 1;
locations.push(bestLoc);
if (bestLoc > MATCH_LOCATION) {
start = Math.max(1, 2 * MATCH_LOCATION - bestLoc);
} else {
break;
}
}
}
}
if (match_bitapScore(i + 1, MATCH_LOCATION) > scoreThreshold) {
break;
}
lastRd = rd;
}
return {
isMatch: bestLoc >= 0,
score: score
};
};
return txt === true ? { 'search' : search } : search(txt);
};
$.vakata.search.defaults = {
location : 0,
distance : 100,
threshold : 0.6,
fuzzy : false,
caseSensitive : false
};
}($));
// include the search plugin by default
// $.jstree.defaults.plugins.push("search");
/**
* ### Sort plugin
*
* Automatically sorts all siblings in the tree according to a sorting function.
*/
/**
* the settings function used to sort the nodes.
* It is executed in the tree's context, accepts two nodes as arguments and should return `1` or `-1`.
* @name $.jstree.defaults.sort
* @plugin sort
*/
$.jstree.defaults.sort = function (a, b) {
//return this.get_type(a) === this.get_type(b) ? (this.get_text(a) > this.get_text(b) ? 1 : -1) : this.get_type(a) >= this.get_type(b);
return this.get_text(a) > this.get_text(b) ? 1 : -1;
};
$.jstree.plugins.sort = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
this.element
.on("model.jstree", $.proxy(function (e, data) {
this.sort(data.parent, true);
}, this))
.on("rename_node.jstree create_node.jstree", $.proxy(function (e, data) {
this.sort(data.parent || data.node.parent, false);
this.redraw_node(data.parent || data.node.parent, true);
}, this))
.on("move_node.jstree copy_node.jstree", $.proxy(function (e, data) {
this.sort(data.parent, false);
this.redraw_node(data.parent, true);
}, this));
};
/**
* used to sort a node's children
* @private
* @name sort(obj [, deep])
* @param {mixed} obj the node
* @param {Boolean} deep if set to `true` nodes are sorted recursively.
* @plugin sort
* @trigger search.jstree
*/
this.sort = function (obj, deep) {
var i, j;
obj = this.get_node(obj);
if(obj && obj.children && obj.children.length) {
obj.children.sort($.proxy(this.settings.sort, this));
if(deep) {
for(i = 0, j = obj.children_d.length; i < j; i++) {
this.sort(obj.children_d[i], false);
}
}
}
};
};
// include the sort plugin by default
// $.jstree.defaults.plugins.push("sort");
/**
* ### State plugin
*
* Saves the state of the tree (selected nodes, opened nodes) on the user's computer using available options (localStorage, cookies, etc)
*/
var to = false;
/**
* stores all defaults for the state plugin
* @name $.jstree.defaults.state
* @plugin state
*/
$.jstree.defaults.state = {
/**
* A string for the key to use when saving the current tree (change if using multiple trees in your project). Defaults to `jstree`.
* @name $.jstree.defaults.state.key
* @plugin state
*/
key : 'jstree',
/**
* A space separated list of events that trigger a state save. Defaults to `changed.jstree open_node.jstree close_node.jstree`.
* @name $.jstree.defaults.state.events
* @plugin state
*/
events : 'changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree',
/**
* Time in milliseconds after which the state will expire. Defaults to 'false' meaning - no expire.
* @name $.jstree.defaults.state.ttl
* @plugin state
*/
ttl : false,
/**
* A function that will be executed prior to restoring state with one argument - the state object. Can be used to clear unwanted parts of the state.
* @name $.jstree.defaults.state.filter
* @plugin state
*/
filter : false,
/**
* Should loaded nodes be restored (setting this to true means that it is possible that the whole tree will be loaded for some users - use with caution). Defaults to `false`
* @name $.jstree.defaults.state.preserve_loaded
* @plugin state
*/
preserve_loaded : false
};
$.jstree.plugins.state = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
var bind = $.proxy(function () {
this.element.on(this.settings.state.events, $.proxy(function () {
if(to) { clearTimeout(to); }
to = setTimeout($.proxy(function () { this.save_state(); }, this), 100);
}, this));
/**
* triggered when the state plugin is finished restoring the state (and immediately after ready if there is no state to restore).
* @event
* @name state_ready.jstree
* @plugin state
*/
this.trigger('state_ready');
}, this);
this.element
.on("ready.jstree", $.proxy(function (e, data) {
this.element.one("restore_state.jstree", bind);
if(!this.restore_state()) { bind(); }
}, this));
};
/**
* save the state
* @name save_state()
* @plugin state
*/
this.save_state = function () {
var tm = this.get_state();
if (!this.settings.state.preserve_loaded) {
delete tm.core.loaded;
}
var st = { 'state' : tm, 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) };
$.vakata.storage.set(this.settings.state.key, JSON.stringify(st));
};
/**
* restore the state from the user's computer
* @name restore_state()
* @plugin state
*/
this.restore_state = function () {
var k = $.vakata.storage.get(this.settings.state.key);
if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } }
if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; }
if(!!k && k.state) { k = k.state; }
if(!!k && $.isFunction(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); }
if(!!k) {
if (!this.settings.state.preserve_loaded) {
delete k.core.loaded;
}
this.element.one("set_state.jstree", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); });
this.set_state(k);
return true;
}
return false;
};
/**
* clear the state on the user's computer
* @name clear_state()
* @plugin state
*/
this.clear_state = function () {
return $.vakata.storage.del(this.settings.state.key);
};
};
(function ($, undefined) {
$.vakata.storage = {
// simply specifying the functions in FF throws an error
set : function (key, val) { return window.localStorage.setItem(key, val); },
get : function (key) { return window.localStorage.getItem(key); },
del : function (key) { return window.localStorage.removeItem(key); }
};
}($));
// include the state plugin by default
// $.jstree.defaults.plugins.push("state");
/**
* ### Types plugin
*
* Makes it possible to add predefined types for groups of nodes, which make it possible to easily control nesting rules and icon for each group.
*/
/**
* An object storing all types as key value pairs, where the key is the type name and the value is an object that could contain following keys (all optional).
*
* * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited.
* * `max_depth` the maximum number of nesting this node type can have. A value of `1` would mean that the node can have children, but no grandchildren. Do not specify or set to `-1` for unlimited.
* * `valid_children` an array of node type strings, that nodes of this type can have as children. Do not specify or set to `-1` for no limits.
* * `icon` a string - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class. Omit to use the default icon from your theme.
* * `li_attr` an object of values which will be used to add HTML attributes on the resulting LI DOM node (merged with the node's own data)
* * `a_attr` an object of values which will be used to add HTML attributes on the resulting A DOM node (merged with the node's own data)
*
* There are two predefined types:
*
* * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes.
* * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified.
*
* @name $.jstree.defaults.types
* @plugin types
*/
$.jstree.defaults.types = {
'default' : {}
};
$.jstree.defaults.types[$.jstree.root] = {};
$.jstree.plugins.types = function (options, parent) {
this.init = function (el, options) {
var i, j;
if(options && options.types && options.types['default']) {
for(i in options.types) {
if(i !== "default" && i !== $.jstree.root && options.types.hasOwnProperty(i)) {
for(j in options.types['default']) {
if(options.types['default'].hasOwnProperty(j) && options.types[i][j] === undefined) {
options.types[i][j] = options.types['default'][j];
}
}
}
}
}
parent.init.call(this, el, options);
this._model.data[$.jstree.root].type = $.jstree.root;
};
this.refresh = function (skip_loading, forget_state) {
parent.refresh.call(this, skip_loading, forget_state);
this._model.data[$.jstree.root].type = $.jstree.root;
};
this.bind = function () {
this.element
.on('model.jstree', $.proxy(function (e, data) {
var m = this._model.data,
dpc = data.nodes,
t = this.settings.types,
i, j, c = 'default', k;
for(i = 0, j = dpc.length; i < j; i++) {
c = 'default';
if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) {
c = m[dpc[i]].original.type;
}
if(m[dpc[i]].data && m[dpc[i]].data.jstree && m[dpc[i]].data.jstree.type && t[m[dpc[i]].data.jstree.type]) {
c = m[dpc[i]].data.jstree.type;
}
m[dpc[i]].type = c;
if(m[dpc[i]].icon === true && t[c].icon !== undefined) {
m[dpc[i]].icon = t[c].icon;
}
if(t[c].li_attr !== undefined && typeof t[c].li_attr === 'object') {
for (k in t[c].li_attr) {
if (t[c].li_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (m[dpc[i]].li_attr[k] === undefined) {
m[dpc[i]].li_attr[k] = t[c].li_attr[k];
}
else if (k === 'class') {
m[dpc[i]].li_attr['class'] = t[c].li_attr['class'] + ' ' + m[dpc[i]].li_attr['class'];
}
}
}
}
if(t[c].a_attr !== undefined && typeof t[c].a_attr === 'object') {
for (k in t[c].a_attr) {
if (t[c].a_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (m[dpc[i]].a_attr[k] === undefined) {
m[dpc[i]].a_attr[k] = t[c].a_attr[k];
}
else if (k === 'href' && m[dpc[i]].a_attr[k] === '#') {
m[dpc[i]].a_attr['href'] = t[c].a_attr['href'];
}
else if (k === 'class') {
m[dpc[i]].a_attr['class'] = t[c].a_attr['class'] + ' ' + m[dpc[i]].a_attr['class'];
}
}
}
}
}
m[$.jstree.root].type = $.jstree.root;
}, this));
parent.bind.call(this);
};
this.get_json = function (obj, options, flat) {
var i, j,
m = this._model.data,
opt = options ? $.extend(true, {}, options, {no_id:false}) : {},
tmp = parent.get_json.call(this, obj, opt, flat);
if(tmp === false) { return false; }
if($.isArray(tmp)) {
for(i = 0, j = tmp.length; i < j; i++) {
tmp[i].type = tmp[i].id && m[tmp[i].id] && m[tmp[i].id].type ? m[tmp[i].id].type : "default";
if(options && options.no_id) {
delete tmp[i].id;
if(tmp[i].li_attr && tmp[i].li_attr.id) {
delete tmp[i].li_attr.id;
}
if(tmp[i].a_attr && tmp[i].a_attr.id) {
delete tmp[i].a_attr.id;
}
}
}
}
else {
tmp.type = tmp.id && m[tmp.id] && m[tmp.id].type ? m[tmp.id].type : "default";
if(options && options.no_id) {
tmp = this._delete_ids(tmp);
}
}
return tmp;
};
this._delete_ids = function (tmp) {
if($.isArray(tmp)) {
for(var i = 0, j = tmp.length; i < j; i++) {
tmp[i] = this._delete_ids(tmp[i]);
}
return tmp;
}
delete tmp.id;
if(tmp.li_attr && tmp.li_attr.id) {
delete tmp.li_attr.id;
}
if(tmp.a_attr && tmp.a_attr.id) {
delete tmp.a_attr.id;
}
if(tmp.children && $.isArray(tmp.children)) {
tmp.children = this._delete_ids(tmp.children);
}
return tmp;
};
this.check = function (chk, obj, par, pos, more) {
if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }
obj = obj && obj.id ? obj : this.get_node(obj);
par = par && par.id ? par : this.get_node(par);
var m = obj && obj.id ? (more && more.origin ? more.origin : $.jstree.reference(obj.id)) : null, tmp, d, i, j;
m = m && m._model && m._model.data ? m._model.data : null;
switch(chk) {
case "create_node":
case "move_node":
case "copy_node":
if(chk !== 'move_node' || $.inArray(obj.id, par.children) === -1) {
tmp = this.get_rules(par);
if(tmp.max_children !== undefined && tmp.max_children !== -1 && tmp.max_children === par.children.length) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_01', 'reason' : 'max_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
if(tmp.valid_children !== undefined && tmp.valid_children !== -1 && $.inArray((obj.type || 'default'), tmp.valid_children) === -1) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_02', 'reason' : 'valid_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
if(m && obj.children_d && obj.parents) {
d = 0;
for(i = 0, j = obj.children_d.length; i < j; i++) {
d = Math.max(d, m[obj.children_d[i]].parents.length);
}
d = d - obj.parents.length + 1;
}
if(d <= 0 || d === undefined) { d = 1; }
do {
if(tmp.max_depth !== undefined && tmp.max_depth !== -1 && tmp.max_depth < d) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_03', 'reason' : 'max_depth prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
return false;
}
par = this.get_node(par.parent);
tmp = this.get_rules(par);
d++;
} while(par);
}
break;
}
return true;
};
/**
* used to retrieve the type settings object for a node
* @name get_rules(obj)
* @param {mixed} obj the node to find the rules for
* @return {Object}
* @plugin types
*/
this.get_rules = function (obj) {
obj = this.get_node(obj);
if(!obj) { return false; }
var tmp = this.get_type(obj, true);
if(tmp.max_depth === undefined) { tmp.max_depth = -1; }
if(tmp.max_children === undefined) { tmp.max_children = -1; }
if(tmp.valid_children === undefined) { tmp.valid_children = -1; }
return tmp;
};
/**
* used to retrieve the type string or settings object for a node
* @name get_type(obj [, rules])
* @param {mixed} obj the node to find the rules for
* @param {Boolean} rules if set to `true` instead of a string the settings object will be returned
* @return {String|Object}
* @plugin types
*/
this.get_type = function (obj, rules) {
obj = this.get_node(obj);
return (!obj) ? false : ( rules ? $.extend({ 'type' : obj.type }, this.settings.types[obj.type]) : obj.type);
};
/**
* used to change a node's type
* @name set_type(obj, type)
* @param {mixed} obj the node to change
* @param {String} type the new type
* @plugin types
*/
this.set_type = function (obj, type) {
var m = this._model.data, t, t1, t2, old_type, old_icon, k, d, a;
if($.isArray(obj)) {
obj = obj.slice();
for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
this.set_type(obj[t1], type);
}
return true;
}
t = this.settings.types;
obj = this.get_node(obj);
if(!t[type] || !obj) { return false; }
d = this.get_node(obj, true);
if (d && d.length) {
a = d.children('.jstree-anchor');
}
old_type = obj.type;
old_icon = this.get_icon(obj);
obj.type = type;
if(old_icon === true || !t[old_type] || (t[old_type].icon !== undefined && old_icon === t[old_type].icon)) {
this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true);
}
// remove old type props
if(t[old_type] && t[old_type].li_attr !== undefined && typeof t[old_type].li_attr === 'object') {
for (k in t[old_type].li_attr) {
if (t[old_type].li_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (k === 'class') {
m[obj.id].li_attr['class'] = (m[obj.id].li_attr['class'] || '').replace(t[old_type].li_attr[k], '');
if (d) { d.removeClass(t[old_type].li_attr[k]); }
}
else if (m[obj.id].li_attr[k] === t[old_type].li_attr[k]) {
m[obj.id].li_attr[k] = null;
if (d) { d.removeAttr(k); }
}
}
}
}
if(t[old_type] && t[old_type].a_attr !== undefined && typeof t[old_type].a_attr === 'object') {
for (k in t[old_type].a_attr) {
if (t[old_type].a_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (k === 'class') {
m[obj.id].a_attr['class'] = (m[obj.id].a_attr['class'] || '').replace(t[old_type].a_attr[k], '');
if (a) { a.removeClass(t[old_type].a_attr[k]); }
}
else if (m[obj.id].a_attr[k] === t[old_type].a_attr[k]) {
if (k === 'href') {
m[obj.id].a_attr[k] = '#';
if (a) { a.attr('href', '#'); }
}
else {
delete m[obj.id].a_attr[k];
if (a) { a.removeAttr(k); }
}
}
}
}
}
// add new props
if(t[type].li_attr !== undefined && typeof t[type].li_attr === 'object') {
for (k in t[type].li_attr) {
if (t[type].li_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (m[obj.id].li_attr[k] === undefined) {
m[obj.id].li_attr[k] = t[type].li_attr[k];
if (d) {
if (k === 'class') {
d.addClass(t[type].li_attr[k]);
}
else {
d.attr(k, t[type].li_attr[k]);
}
}
}
else if (k === 'class') {
m[obj.id].li_attr['class'] = t[type].li_attr[k] + ' ' + m[obj.id].li_attr['class'];
if (d) { d.addClass(t[type].li_attr[k]); }
}
}
}
}
if(t[type].a_attr !== undefined && typeof t[type].a_attr === 'object') {
for (k in t[type].a_attr) {
if (t[type].a_attr.hasOwnProperty(k)) {
if (k === 'id') {
continue;
}
else if (m[obj.id].a_attr[k] === undefined) {
m[obj.id].a_attr[k] = t[type].a_attr[k];
if (a) {
if (k === 'class') {
a.addClass(t[type].a_attr[k]);
}
else {
a.attr(k, t[type].a_attr[k]);
}
}
}
else if (k === 'href' && m[obj.id].a_attr[k] === '#') {
m[obj.id].a_attr['href'] = t[type].a_attr['href'];
if (a) { a.attr('href', t[type].a_attr['href']); }
}
else if (k === 'class') {
m[obj.id].a_attr['class'] = t[type].a_attr['class'] + ' ' + m[obj.id].a_attr['class'];
if (a) { a.addClass(t[type].a_attr[k]); }
}
}
}
}
return true;
};
};
// include the types plugin by default
// $.jstree.defaults.plugins.push("types");
/**
* ### Unique plugin
*
* Enforces that no nodes with the same name can coexist as siblings.
*/
/**
* stores all defaults for the unique plugin
* @name $.jstree.defaults.unique
* @plugin unique
*/
$.jstree.defaults.unique = {
/**
* Indicates if the comparison should be case sensitive. Default is `false`.
* @name $.jstree.defaults.unique.case_sensitive
* @plugin unique
*/
case_sensitive : false,
/**
* Indicates if white space should be trimmed before the comparison. Default is `false`.
* @name $.jstree.defaults.unique.trim_whitespace
* @plugin unique
*/
trim_whitespace : false,
/**
* A callback executed in the instance's scope when a new node is created and the name is already taken, the two arguments are the conflicting name and the counter. The default will produce results like `New node (2)`.
* @name $.jstree.defaults.unique.duplicate
* @plugin unique
*/
duplicate : function (name, counter) {
return name + ' (' + counter + ')';
}
};
$.jstree.plugins.unique = function (options, parent) {
this.check = function (chk, obj, par, pos, more) {
if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; }
obj = obj && obj.id ? obj : this.get_node(obj);
par = par && par.id ? par : this.get_node(par);
if(!par || !par.children) { return true; }
var n = chk === "rename_node" ? pos : obj.text,
c = [],
s = this.settings.unique.case_sensitive,
w = this.settings.unique.trim_whitespace,
m = this._model.data, i, j, t;
for(i = 0, j = par.children.length; i < j; i++) {
t = m[par.children[i]].text;
if (!s) {
t = t.toLowerCase();
}
if (w) {
t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
c.push(t);
}
if(!s) { n = n.toLowerCase(); }
if (w) { n = n.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }
switch(chk) {
case "delete_node":
return true;
case "rename_node":
t = obj.text || '';
if (!s) {
t = t.toLowerCase();
}
if (w) {
t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
i = ($.inArray(n, c) === -1 || (obj.text && t === n));
if(!i) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_01', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
}
return i;
case "create_node":
i = ($.inArray(n, c) === -1);
if(!i) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_04', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
}
return i;
case "copy_node":
i = ($.inArray(n, c) === -1);
if(!i) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_02', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
}
return i;
case "move_node":
i = ( (obj.parent === par.id && (!more || !more.is_multi)) || $.inArray(n, c) === -1);
if(!i) {
this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_03', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
}
return i;
}
return true;
};
this.create_node = function (par, node, pos, callback, is_loaded) {
if(!node || node.text === undefined) {
if(par === null) {
par = $.jstree.root;
}
par = this.get_node(par);
if(!par) {
return parent.create_node.call(this, par, node, pos, callback, is_loaded);
}
pos = pos === undefined ? "last" : pos;
if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
return parent.create_node.call(this, par, node, pos, callback, is_loaded);
}
if(!node) { node = {}; }
var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, w = this.settings.unique.trim_whitespace, cb = this.settings.unique.duplicate, t;
n = tmp = this.get_string('New node');
dpc = [];
for(i = 0, j = par.children.length; i < j; i++) {
t = m[par.children[i]].text;
if (!s) {
t = t.toLowerCase();
}
if (w) {
t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
dpc.push(t);
}
i = 1;
t = n;
if (!s) {
t = t.toLowerCase();
}
if (w) {
t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
while($.inArray(t, dpc) !== -1) {
n = cb.call(this, tmp, (++i)).toString();
t = n;
if (!s) {
t = t.toLowerCase();
}
if (w) {
t = t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
}
node.text = n;
}
return parent.create_node.call(this, par, node, pos, callback, is_loaded);
};
};
// include the unique plugin by default
// $.jstree.defaults.plugins.push("unique");
/**
* ### Wholerow plugin
*
* Makes each node appear block level. Making selection easier. May cause slow down for large trees in old browsers.
*/
var div = document.createElement('DIV');
div.setAttribute('unselectable','on');
div.setAttribute('role','presentation');
div.className = 'jstree-wholerow';
div.innerHTML = ' ';
$.jstree.plugins.wholerow = function (options, parent) {
this.bind = function () {
parent.bind.call(this);
this.element
.on('ready.jstree set_state.jstree', $.proxy(function () {
this.hide_dots();
}, this))
.on("init.jstree loading.jstree ready.jstree", $.proxy(function () {
//div.style.height = this._data.core.li_height + 'px';
this.get_container_ul().addClass('jstree-wholerow-ul');
}, this))
.on("deselect_all.jstree", $.proxy(function (e, data) {
this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');
}, this))
.on("changed.jstree", $.proxy(function (e, data) {
this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked');
var tmp = false, i, j;
for(i = 0, j = data.selected.length; i < j; i++) {
tmp = this.get_node(data.selected[i], true);
if(tmp && tmp.length) {
tmp.children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
}
}
}, this))
.on("open_node.jstree", $.proxy(function (e, data) {
this.get_node(data.node, true).find('.jstree-clicked').parent().children('.jstree-wholerow').addClass('jstree-wholerow-clicked');
}, this))
.on("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) {
if(e.type === "hover_node" && this.is_disabled(data.node)) { return; }
this.get_node(data.node, true).children('.jstree-wholerow')[e.type === "hover_node"?"addClass":"removeClass"]('jstree-wholerow-hovered');
}, this))
.on("contextmenu.jstree", ".jstree-wholerow", $.proxy(function (e) {
if (this._data.contextmenu) {
e.preventDefault();
var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, pageX : e.pageX, pageY : e.pageY });
$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp);
}
}, this))
/*!
.on("mousedown.jstree touchstart.jstree", ".jstree-wholerow", function (e) {
if(e.target === e.currentTarget) {
var a = $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor");
e.target = a[0];
a.trigger(e);
}
})
*/
.on("click.jstree", ".jstree-wholerow", function (e) {
e.stopImmediatePropagation();
var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
})
.on("dblclick.jstree", ".jstree-wholerow", function (e) {
e.stopImmediatePropagation();
var tmp = $.Event('dblclick', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
})
.on("click.jstree", ".jstree-leaf > .jstree-ocl", $.proxy(function (e) {
e.stopImmediatePropagation();
var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey });
$(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus();
}, this))
.on("mouseover.jstree", ".jstree-wholerow, .jstree-icon", $.proxy(function (e) {
e.stopImmediatePropagation();
if(!this.is_disabled(e.currentTarget)) {
this.hover_node(e.currentTarget);
}
return false;
}, this))
.on("mouseleave.jstree", ".jstree-node", $.proxy(function (e) {
this.dehover_node(e.currentTarget);
}, this));
};
this.teardown = function () {
if(this.settings.wholerow) {
this.element.find(".jstree-wholerow").remove();
}
parent.teardown.call(this);
};
this.redraw_node = function(obj, deep, callback, force_render) {
obj = parent.redraw_node.apply(this, arguments);
if(obj) {
var tmp = div.cloneNode(true);
//tmp.style.height = this._data.core.li_height + 'px';
if($.inArray(obj.id, this._data.core.selected) !== -1) { tmp.className += ' jstree-wholerow-clicked'; }
if(this._data.core.focused && this._data.core.focused === obj.id) { tmp.className += ' jstree-wholerow-hovered'; }
obj.insertBefore(tmp, obj.childNodes[0]);
}
return obj;
};
};
// include the wholerow plugin by default
// $.jstree.defaults.plugins.push("wholerow");
if(window.customElements && Object && Object.create) {
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function () {
var c = { core : {}, plugins : [] }, i;
for(i in $.jstree.plugins) {
if($.jstree.plugins.hasOwnProperty(i) && this.attributes[i]) {
c.plugins.push(i);
if(this.getAttribute(i) && JSON.parse(this.getAttribute(i))) {
c[i] = JSON.parse(this.getAttribute(i));
}
}
}
for(i in $.jstree.defaults.core) {
if($.jstree.defaults.core.hasOwnProperty(i) && this.attributes[i]) {
c.core[i] = JSON.parse(this.getAttribute(i)) || this.getAttribute(i);
}
}
$(this).jstree(c);
};
// proto.attributeChangedCallback = function (name, previous, value) { };
try {
window.customElements.define("vakata-jstree", function() {}, { prototype: proto });
} catch (ignore) { }
}
}));
//}}}
//{{{
(function() {
var css = store.getTiddlerText("jstree.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}"));
css = css.replace(/32px\.png/g, "data:image/png;base64,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");
css = css.replace(/40px\.png/g, "data:image/png;base64,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");
css = css.replace(/throbber\.gif/g, "data:image/gif;base64,R0lGODlhEAAQAPMPAJycnBQUFODg4L6+vnp6egAAAFhYWDY2NoqKivDw8CQkJGhoaEZGRqysrAQEBP///yH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQFCgAPACwAAAAAEAAQAEAEcPDJl+gydUrEOtBTIopMgSSFYxhC1kqC8AjH0AACIlOaUCgJR6G1CyUah6IkE0IEGMqEYKCcEWSAz2PAnAgUrcKQ15MhTMJYd1ZyUDXSDGelBY0qogFh/ZoQFAAJCDoxDRRvIQYED1kzgSAgAwOQDxEAIfkEBQoADwAsAAAAAA8AEAAABF3wyfnIkolOJpUUQaNJR6I4glIMowQoAVBwkxEnwWEVRZZJhsJBMBkQW7TWo7EgEFjKB8CwWECjH8DoN2kwDFvBD8FoJAANgfoo1iwEA/NVQ9DGJXMMIHN/HJVqIxEAIfkEBQoADwAsAAAAABAADwAABFrwyfkAolguyVRKRyY5RtIlhphURdM1kzBIxNGADIIchYAUPU0gFQMAC7PJYLFSFi6SgQ4gyHyuDYJWxH3AupQFotkFEAaJAfqqkiAE8ISAjAHM4A8594vPUyIAIfkEBQoADwAsAAAAABAAEAAABFzwySkBvZKkZ5g0A/YcBOct2CYFQxeiiSAhi8wQAOAlVqASpIfqMVCIHiWKrJbYABoN0SIQcIByANllUegeKKGLYCxQCRCW42SQfYzVQkli0BweB7Im3C23w/UYEQAh+QQFCgAIACwAAAAAEAAOAAAERRDJiQihWBpZCs/c1iEBKBFeB5CYEQxIcVheFU9GsWH1Su2Yw0SAAPiCnULJcsmUPkPMQJY5TqYdIkJrSma4lAFQAgZFAAAh+QQFCgAOACwAAAAAEAAQAAAEa9DJ6cag2KUmEZGEkCUEhyAOooiZsAinEKDU0IgIYBFHpTCSxAAxSEwSAoPjwJIIOJQEA2DUCK5VCsBgYCCu2AxAoQgsohjBAYBBNh1PCTAoMCaqgULSEchmGgVKDlBoEwoFFyNpBWwZjn4RACH5BAUKAA8ALAAAAAAQABAAAARb8Mn5hKBYDgkAz9zVeAADSkgyPkyDWcnTDBawSMwtCXS2BAZM4oJZuCaJJKixWASTykyDcTgQThNDI4rdPBYxEGGheCRuh1OggHhceaDEmkiMOwoXxzFuOIQpEQAh+QQFCgAOACwBAAEADwAPAAAEVtBJIceQTepa71jb5l3L5SSnJlBOQziCgUiJgIbE8W73hpih4IBAzAQ1H4PsSCsFa6yawrHbFAoIBGM6Y/gKBIUC4JiFBFdKIHE4NgqXwNqhAKo0hmMEACH5BAUKAA8ALAAAAAAQABAAAARd8Mn5UqJY3ieEHBsmXB0HhFTidQniYZtlDY2EABkMLHiOAS8fRYAoCicCgrL2UQwwriGjUOhNGAGExlE4eBSAxoLxACgWEsDTdjjUesGJItF+GBw+x/cgIec2SjkRACH5BAUKAA4ALAAAAQAQAA8AAARa0MnpUpKCamnl2Fr3bZkmlMAITg0yXFRJqc4S3AvY7MNR/AwQAOBKnAABmQSwcrwmicWBKVHUEgpGw4EwOA0ESUMGMDAGgS1Mc3CYHYT2ZmB1MII15QaBAEUAACH5BAUKAA8ALAAAAQAQAA8AAARY8MlJX6p4JlEFx8JFFQVSicnwDWTxTYJaEUXTiZKiZ8/gCwZHIEDI+BqfRIOBsVUMkpcEsXA+Do8iw+ADLCQEgGQgETcICwGWF2CiHwAmSL6AJoq8shgTAQAh+QQFCgAPACwAAAEADwAPAAAEWvDJEYS8+I6ispdG0XxS9whF4QnJaQLjlbAY4AxZKyEHwyCemUBAUBiBuaGlNTB4cDLCI+BpIKBO4IIwbABOsNMkMEBIF5aHTn1YPMyPBmF9cb+ljy8pHsNEAAA7");
setStylesheet(css, "jstree.js-stylesheet");
})();
//}}}
//{{{
(function() {
var Search = jQuery.vakata.search;
jQuery.vakata.search = function (pattern, txt, options) {
options = options || {};
var isNormalized = options.fuzzy === "normalized";
if (options.fuzzy) {
pattern = config.macros.collator.removeDiacritics(pattern);
if (jQuery.type(txt) === "string") {
pattern = config.macros.collator.removeDiacritics(pattern);
}
}
if (isNormalized) {
options = jQuery.extend({}, options);
options.fuzzy = false;
}
var result = Search.call(this, pattern, txt, options);
if (txt === true) {
return {
search: function (text) {
if (options.fuzzy || isNormalized) {
text = config.macros.collator.removeDiacritics(text);
}
return result.search(text);
}
};
}
return result;
};
jQuery.vakata.search.prototype = Object.create(Search.prototype);
jQuery.vakata.search.prototype.constructor = jQuery.vakata.search;
jQuery.vakata.search.defaults = Search.defaults;
})();
//}}}
/***
|Name|jsuites.js|
|Source|https://github.com/jsuites/jsuites|
|Documentation|https://jsuites.net/docs|
|Version|commit 5efc4ebb728ea5527b3f0e55be5f46610a95f6a4 2024-12-16 https://raw.githubusercontent.com/jsuites/jsuites/refs/heads/master/dist/jsuites.css <br>commit 8726442feaf09faed971d9620e581fc7fde72848 2025-04-03 https://raw.githubusercontent.com/jsuites/jsuites/refs/heads/master/dist/jsuites.js|
|License|[[MIT license|https://opensource.org/license/mit/]]|
|Description|jSuites is a collection of lightweight common required javascript web components|
!!!!!CSS
//{{{
:root {
--jbutton-color: #298BA8;
--jactive-color: #007aff;
--jsafe-area-top: env(safe-area-inset-top);
--jsafe-area-bottom: env(safe-area-inset-bottom);
}
[data-visible="false"], .row[data-visible="false"] {
display: none;
}
div[data-before]:before {
content: attr(data-before);
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jreadonly {
pointer-events: none;
}
.jdragging {
opacity:0.2;
filter: alpha(opacity=20);
}
.jupload.input {
position: relative;
box-sizing: border-box;
background-size: initial;
height: 33px;
min-height: initial;
padding: 6px;
padding-right: 30px;
}
.jupload.input:before {
content: "save";
font-size: 18px;
font-family: "Material Icons";
color: #000;
position: absolute;
right: 5px;
}
.jupload:empty:before {
z-index: 0;
}
.jupload img {
width: 100%;
}
.jupload.input img {
width: initial;
max-width: 100%;
height: 100%;
}
.jupload[data-multiple] {
padding: 10px;
}
.jupload[data-multiple] img {
height: 70px;
width: 100px;
object-fit: cover;
margin-right: 5px;
margin-bottom: 5px;
}
.jupload {
position: relative;
border: 1px dotted #eee;
cursor: pointer;
box-sizing: border-box;
width: 100%;
max-width: 100%;
max-height: 100%;
min-height: 180px;
}
.jupload:not(.input):empty:before {
content: "\e2c3";
font-family: "Material Icons";
font-size: 90px;
color: #eee;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
}
.jupload-item {
padding-right: 22px;
border-radius: 1px;
display: inline-block;
position: relative;
}
.jphoto {
position: relative;
border: 1px dotted #eee;
cursor: pointer;
box-sizing: border-box;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.jphoto:empty:before {
content: "\e2c3";
font-family: "Material Icons";
font-size: 90px;
color: #eee;
width: 100%;
height: 100%;
text-align: center;
}
.jremove {
opacity: 0.2;
filter: alpha(opacity=20);
}
.round img {
border-radius: 1000px;
}
.jtooltip {
position: fixed;
top: 10px;
left: 10px;
z-index: 5;
font-family: initial;
font-size: 12px;
color: #000;
background-color: #fff;
border: 1px solid black;
padding: 8px;
margin: 10px;
display: block;
animation: jfadeIn 0.5s;
pointer-events: none;
}
.jtooltip:empty {
display: none;
}
@keyframes jfadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.jpanel {
position: absolute;
box-sizing: border-box;
z-index: 10;
}
.jpanel:focus {
outline: 2px solid black;
outline-offset: -1px;
}
.jpanel-action {
position: absolute;
width: 8px;
height: 8px;
background-color: white;
border:1px solid black;
box-sizing: border-box;
pointer-events: auto;
z-index: 1000;
}
.jpanel-action[data-position="n-resize"] {
top: -4px;
left: calc(50% - 4px);
}
.jpanel-action[data-position="ne-resize"] {
top: -4px;
left: calc(100% - 4px);
}
.jpanel-action[data-position="e-resize"] {
top: calc(50% - 4px);
left: calc(100% - 4px);
}
.jpanel-action[data-position="se-resize"] {
top: calc(100% - 4px);
left: calc(100% - 4px);
}
.jpanel-action[data-position="s-resize"] {
top: calc(100% - 4px);
left: calc(50% - 4px);
}
.jpanel-action[data-position="sw-resize"] {
top: calc(100% - 4px);
left: -4px;
}
.jpanel-action[data-position="w-resize"] {
top: calc(50% - 4px);
left: -4px;
}
.jpanel-action[data-position="nw-resize"] {
top: -4px;
left: -4px;
}
.jpanel-action[data-position="nw-resize"] {
top: -4px;
left: -4px;
}
.jpanel-action[data-action="rotate"] {
top: -50px;
left: calc(50% - 12px);
width: 24px;
height: 24px;
border: 0;
cursor: move;
background-color: transparent;
}
.jpanel-action[data-action="rotate"]:before {
content: 'refresh';
font-family: 'Material Icons';
font-size: 24px;
}
.jpanel-action[data-action="rotate"]:after {
position: absolute;
content: '';
bottom: -22px;
left: calc(50%);
width: 1px;
height: 28px;
background-color: black;
}
/** Loading * /
.jloading {
position:fixed;
z-index:10001;
width:100%;
left:0;
right:0;
top:0;
bottom:0;
background-color: rgba(0,0,0,0.7);
}
.jloading::after {
content:'';
display:block;
margin:0 auto;
margin-top:50vh;
width:40px;
height:40px;
border-style:solid;
border-color:white;
border-top-color:transparent;
border-width:4px;
border-radius:50%;
-webkit-animation: jspin .8s linear infinite;
animation: jspin .8s linear infinite;
}
.jloading.jspin {
background-color:transparent;
}
.jloading.jspin::after {
margin:0 auto;
margin-top:80px;
border-color:#aaa;
border-top-color:transparent;
}
/** Animations ** /
.jfade-in {
animation: jfade-in 2s forwards;
}
.jfade-out {
animation: jfade-out 1s forwards;
}
.jslide-left-in {
position: relative;
animation: jslide-left-in 0.4s forwards;
}
.jslide-left-out {
position: relative;
animation: jslide-left-out 0.4s forwards;
}
.jslide-right-in {
position: relative;
animation: jslide-right-in 0.4s forwards;
}
.jslide-right-out {
position: relative;
animation: jslide-right-out 0.4s forwards;
}
.jslide-top-in {
position: relative;
animation: jslide-top-in 0.4s forwards;
}
.jslide-top-out {
position: relative;
animation: jslide-top-out 0.2s forwards;
}
.jslide-bottom-in {
position: relative;
animation: jslide-bottom-in 0.4s forwards;
}
.jslide-bottom-out {
position: relative;
animation: jslide-bottom-out 0.1s forwards;
}
.jslide-left-in > div {
-webkit-transform: translateZ(0px);
-webkit-transform: translate3d(0,0,0);
}
.jslide-left-out > div {
-webkit-transform: translateZ(0px);
-webkit-transform: translate3d(0,0,0);
}
.jslide-right-in > div {
-webkit-transform: translateZ(0px);
-webkit-transform: translate3d(0,0,0);
}
.jslide-right-out > div {
-webkit-transform: translateZ(0px);
-webkit-transform: translate3d(0,0,0);
}
.jspin {
animation: jspin 2s infinite linear;
}
/** Fadein and Fadeout ** /
@keyframes jfade-in {
0% { opacity: 0; }
100% { opacity: 100; }
}
@-webkit-keyframes jfade-in {
0% { opacity: 0; }
100% { opacity: 100; }
}
@keyframes jfade-out {
0% { opacity: 100; }
100% { opacity: 0; }
}
@-webkit-keyframes jfade-out {
0% { opacity: 100; }
100% { opacity: 0; }
}
/** Keyframes Left to Right ** /
@keyframes jslide-left-in {
0% { left: -100%; }
100% { left: 0%; }
}
@-webkit-keyframes jslide-left-in {
0% { left: -100%; }
100% { left: 0%; }
}
@keyframes jslide-left-out {
0% { left: 0%; }
100% { left: -100%; }
}
@-webkit-keyframes jslide-left-out {
0% { left: 0%; }
100% { left: -100%; }
}
/** Keyframes Right to Left ** /
@keyframes jslide-right-in {
0% { left: 100%; }
100% { left: 0%; }
}
@-webkit-keyframes jslide-right-in
{
0% { left: 100%; }
100% { left: 0%; }
}
@keyframes jslide-right-out {
0% { left: 0%; }
100% { left: 100%; }
}
@-webkit-keyframes jslide-right-out {
0% { left: 0%; }
100% { left: 100%; }
}
/** Keyframes Top to Bottom ** /
@keyframes jslide-top-in {
0% { transform: translateY(-100%); }
100% { transform: translateY(0%); }
}
@-webkit-keyframes jslide-top-in {
0% { transform: translateY(-100%); }
100% { -webkit-transform: translateY(0%); }
}
@keyframes jslide-top-out {
0% { transform: translateY(0%); }
100% { transform: translateY(-100%); }
}
@-webkit-keyframes jslide-top-out {
0% { -webkit-transform: translateY(0%); }
100% { -webkit-transform: translateY(-100%); }
}
/** Keyframes Bottom to Top ** /
@keyframes jslide-bottom-in {
0% { transform: translateY(100%); }
100% { transform: translateY(0%); }
}
@-webkit-keyframes jslide-bottom-in {
0% { transform: translateY(100%); }
100% { -webkit-transform: translateY(0%); }
}
@keyframes jslide-bottom-out {
0% { transform: translateY(0%); }
100% { transform: translateY(100%); }
}
@-webkit-keyframes jslide-bottom-out {
0% { -webkit-transform: translateY(0%); }
100% { -webkit-transform: translateY(100%); }
}
@-webkit-keyframes jspin {
from {
-webkit-transform:rotate(0deg);
}
to {
-webkit-transform:rotate(359deg);
}
}
@keyframes jspin {
from {
transform:rotate(0deg);
}
to {
transform:rotate(359deg);
}
}
.jcalendar {
position:absolute;
z-index:9000;
display:none;
box-sizing:border-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-tap-highlight-color: transparent;
min-width:280px;
}
.jcalendar.jcalendar-focus {
display:block;
}
.jcalendar .jcalendar-backdrop {
position:fixed;
top:0px;
left:0px;
z-index:9000;
min-width:100%;
min-height:100%;
background-color:rgba(0,0,0,0.5);
border:0px;
padding:0px;
display:none;
}
.jcalendar .jcalendar-container {
position:relative;
box-sizing:border-box;
}
.jcalendar .jcalendar-content {
position:absolute;
z-index:9001;
-webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
-moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
background-color:#fff;
}
.jcalendar-header {
text-align:center;
}
.jcalendar-header span {
margin-right:4px;
font-size:1.1em;
font-weight:bold;
}
.jcalendar-prev {
cursor:pointer;
background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z%27 fill=%27%23000%27 /%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3C/svg%3E");
background-position:center;
background-repeat:no-repeat;
}
.jcalendar-next {
cursor:pointer;
background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z%27 fill=%27%23000%27 /%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3C/svg%3E");
background-position:center;
background-repeat:no-repeat;
}
.jcalendar-weekday {
font-weight: 600;
background-color: #fcfcfc;
padding: 14px;
}
.jcalendar-table {
padding: 10px;
}
.jcalendar-table > table {
width:100%;
background-color:#fff;
}
.jcalendar-table > table > thead {
cursor: pointer;
}
.jcalendar-table thead td {
padding: 10px;
height: 40px;
}
.jcalendar-table > table > tbody > tr {
height: 34px;
}
.jcalendar-table > table > tbody td {
box-sizing:border-box;
cursor:pointer;
padding:9px;
font-size:0.9em;
}
.jcalendar-table tfoot td {
padding:10px;
}
.jcalendar-months td, .jcalendar-years td {
height:24px;
}
.jcalendar-input {
padding-right:18px;
background-image:url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2716%27 height=%2716%27 viewBox=%270 0 24 24%27 fill=%27gray%27%3E%3Cpath d=%27M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z%27/%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0z%27/%3E%3C/svg%3E");
background-position:top 50% right 5px;
background-repeat:no-repeat;
box-sizing: border-box;
}
.jcalendar-done {
-webkit-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
-moz-box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
box-shadow: 1px 1px 5px 0px rgba(0,0,0,0.39);
background-color:#fff;
}
.jcalendar-update {
border:1px solid #ccc;
background-color:#fff;
border-radius:4px;
padding:5px;
width:100%;
}
.jcalendar-container select {
width:55px;
display:inline-block;
border:0px;
padding:4px;
text-align:center;
font-size:1.1em;
user-select:none;
margin-right:10px;
}
.jcalendar-container select:first-child {
margin-right:2px;
}
.jcalendar-selected {
background-color:#eee;
}
.jcalendar-reset, .jcalendar-confirm {
text-transform:uppercase;
cursor:pointer;
color: var(--jactive-color);
}
.jcalendar-controls {
padding:15px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
vertical-align:middle;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row wrap;
justify-content: space-between;
align-items:center;
}
.jcalendar-controls div {
font-weight:bold;
}
.jcalendar-fullsize {
position:fixed;
width:100%;
top:0px;
left:0px;
}
.jcalendar-fullsize .jcalendar-content
{
position:fixed;
width:100%;
left:0px;
bottom:0px;
}
.jcalendar-focus.jcalendar-fullsize .jcalendar-backdrop {
display:block;
}
.jcalendar-sunday {
color: red;
}
.jcalendar-disabled {
color: #ccc;
}
.jcalendar-time {
display:flex;
}
.jcalendar_warning {
color: red;
}
.jcalendar-hide-controls .jcalendar-controls {
display: none;
}
.jcolor {
display: none;
outline: none;
position: absolute;
}
.jcolor-input {
padding-right: 24px !important;
background: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27black%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z%27/%3E%3C/svg%3E") top 50% right 4px no-repeat, content-box;
box-sizing: border-box;
}
.jcolor-content {
position: absolute;
z-index: 9000;
user-select: none;
-webkit-font-smoothing: antialiased;
font-size: .875rem;
letter-spacing: .2px;
-webkit-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
background-color:#fff;
box-sizing: border-box;
min-width: 260px;
}
.jmodal .jcolor-content {
position: fixed;
}
.jcolor-controls {
display: flex;
padding: 10px;
border-bottom: 1px solid #eee;
margin-bottom: 5px;
}
.jcolor-controls div {
flex: 1;
font-size: 1em;
color: var(--jactive-color);
text-transform: uppercase;
font-weight: bold;
box-sizing: border-box;
}
.jcolor-content table {
border-collapse: collapse;
box-sizing: border-box;
}
.jcolor-focus {
display:block;
}
.jcolor table {
width:100%;
height:100%;
min-height: 160px;
}
.jcolor td {
padding: 7px;
}
.jcolor-selected {
background-repeat:no-repeat;
background-size: 16px;
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z%27 fill=%27white%27/%3E%3C/svg%3E");
}
.jcolor-fullscreen {
position: fixed;
left: 0px;
bottom: 0px;
width:100%;
max-height: 290px;
border-radius: 0px;
box-sizing: border-box;
}
.jcolor-fullscreen .jcolor-controls {
padding: 15px;
-webkit-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);
-moz-box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);
box-shadow: 1px 0px 1px 0px rgba(0,0,0,0.39);
}
.jcolor-reset {
text-align: left;
}
.jcolor-close {
text-align: right;
}
.jcolor-backdrop {
position: fixed;
top: 0px;
left: 0px;
min-width: 100%;
min-height: 100%;
background-color: rgba(0,0,0,0.5);
border: 0px;
padding: 0px;
z-index: 8000;
display: none;
-webkit-touch-callout: none; /* iOS Safari * /
-webkit-user-select: none; /* Safari * /
-khtml-user-select: none; /* Konqueror HTML * /
-moz-user-select: none; /* Firefox * /
-ms-user-select: none; /* Internet Explorer/Edge * /
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera * /
}
.jcolor-content .jtabs-content {
padding: 7px;
}
.jcolor-grid tr:first-child > td:first-child {
border-top-left-radius: 3px;
}
.jcolor-grid tr:first-child > td:last-child {
border-top-right-radius: 3px;
}
.jcolor-grid tr:last-child > td:first-child {
border-bottom-left-radius: 3px;
}
.jcolor-grid tr:last-child > td:last-child {
border-bottom-right-radius: 3px;
}
.jcolor-hsl {
box-sizing: border-box;
}
.jcolor-hsl > div {
height: 100%;
position: relative;
}
.jcolor-hsl canvas {
display: block;
border-radius: 4px;
-webkit-user-drag: none;
}
.jcolor-point {
height: 5px;
width: 5px;
background-color: #000;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
}
.jcolor-sliders {
padding: 10px 20px 10px 10px;
}
.jcolor-sliders input {
-webkit-appearance: none;
height: 12px;
width: 80%;
background: #d3d3d3;
opacity: 1;
border-radius: 30px;
outline: none;
}
.jcolor-sliders-input-subcontainer {
display: flex;
justify-content: space-between;
align-items: center;
}
.jcolor-sliders-input-container {
margin-top: 4px;
line-height: 0.8em;
text-align: left;
}
.jcolor-sliders-input-container > label {
font-size: 10px;
text-transform: uppercase;
color: #bbbbbd;
}
.jcolor-sliders-input-subcontainer > input {
border: 0px;
padding: 1px;
}
.jcolor-sliders-input-container input::-webkit-slider-thumb {
-webkit-appearance: none;
height: 12px;
width: 12px;
border-radius: 50%;
background: #000;
border: 2px solid #fff;
cursor: pointer;
}
.jcolor-sliders-input-container input::-moz-range-thumb {
-webkit-appearance: none;
height: 12px;
width: 12px;
border-radius: 50%;
background: #000;
border: 2px solid #fff;
cursor: pointer;
}
.jcolor-sliders-final-color {
padding: 6px;
user-select: all;
margin-top: 10px;
text-align: center;
}
.jcolor-sliders-final-color > div:nth-child(2) {
width: 71px;
text-transform: uppercase;
}
.jcolor .jtabs .jtabs-headers-container .jtabs-controls {
display: none !important;
}
.jcolor .jtabs .jtabs-headers-container {
display: flex !important;
justify-content: center;
padding: 4px;
}
.jcolor .jtabs-headers > div:not(.jtabs-border) {
padding: 2px !important;
padding-left: 15px !important;
padding-right: 15px !important;
font-size: 0.8em;
}
.jcontextmenu {
position:fixed;
z-index:10000;
background:#fff;
color: #555;
font-size: 11px;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);
-moz-box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);
box-shadow: 2px 2px 2px 0px rgba(143, 144, 145, 1);
border: 1px solid #C6C6C6;
padding: 0px;
padding-top:4px;
padding-bottom:4px;
margin:0px;
outline:none;
display:none;
}
.jcontextmenu.jcontextmenu-focus {
display:inline-block;
}
.jcontextmenu > div {
box-sizing: border-box;
display: flex;
padding: 8px 8px 8px 32px;
width: 250px;
position: relative;
cursor: default;
font-size: 11px;
font-family:sans-serif;
text-align: left;
-webkit-box-align: center;
align-items: center;
}
.jcontextmenu > div::before {
content: attr(data-icon);
font-family: 'Material Icons' !important;
font-size: 15px;
position: absolute;
left: 9px;
line-height: 24px;
}
.jcontextmenu.symbols > div::before {
font-family: 'Material Symbols Outlined' !important;
}
.jcontextmenu > div.header {
display: none;
}
.jcontextmenu > div a {
color: #555;
text-decoration: none;
flex: 1;
cursor: pointer;
}
.jcontextmenu > div span {
margin-right: 10px;
font-size: 0.9em;
}
.jcontextmenu .jcontextmenu-disabled a {
color: #ccc;
}
.jcontextmenu .jcontextmenu-disabled::before {
color: #ccc;
}
.jcontextmenu > div:hover {
background: #ebebeb;
}
.jcontextmenu hr {
border: 1px solid #e9e9e9;
border-bottom: 0;
margin-top:5px;
margin-bottom:5px;
}
.jcontextmenu > hr:hover {
background: transparent;
}
.jcontextmenu .jcontextmenu {
top: 4px;
left: 99%;
opacity: 0;
position: absolute;
}
.jcontextmenu > div:hover > .jcontextmenu {
display: block;
opacity: 1;
-webkit-transform: translate(0, 0) scale(1);
transform: translate(0, 0) scale(1);
pointer-events: auto;
}
@media only screen and (max-width: 420px) {
.jcontextmenu {
top: initial !important;
left: 0px !important;
bottom: 0px !important;
width: 100vw;
height: 260px;
overflow: scroll;
animation: jslide-bottom-in 0.4s forwards;
padding-top: 0px;
}
.jcontextmenu div {
width: 100%;
text-align: center;
border-bottom: 1px solid #ccc;
padding: 15px;
}
.jcontextmenu > div.header {
background-color: lightgray;
padding: 5px;
top: 0px;
position: sticky;
z-index: 2;
}
.jcontextmenu > div.header > a.title {
text-align: left;
}
.jcontextmenu > div.header > a.close {
text-align: right;
}
.jcontextmenu a {
font-size: 1.4em;
text-transform: uppercase;
}
.jcontextmenu span {
display: none;
}
.jcontextmenu span {
display: none;
}
.jcontextmenu hr {
display: none;
}
}
.jdropdown {
cursor:pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
box-sizing: border-box;
background:#fff;
-webkit-tap-highlight-color: transparent;
display: inline-block;
}
.jdropdown-backdrop {
position:fixed;
top:0px;
left:0px;
min-width:100%;
min-height:100%;
background-color:rgba(0,0,0,0.5);
border:0px;
padding:0px;
z-index:8000;
display:none;
}
.jdropdown[disabled] {
opacity: 0.5;
pointer-events: none;
}
.jdropdown-focus {
position:relative;
}
.jdropdown-focus .jdropdown-container {
transform: translate3d(0,0,0);
}
.jdropdown-default.jdropdown-focus .jdropdown-header {
outline:auto 5px -webkit-focus-ring-color;
}
.jdropdown-default.jdropdown-focus .jdropdown-header.jdropdown-add {
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2724px%27 height=%2724px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z%27/%3E%3C/svg%3E");
}
.jdropdown-container-header {
padding:0px;
margin:0px;
position:relative;
box-sizing: border-box;
}
.jdropdown-header {
width:100%;
appearance: none;
background-repeat: no-repeat;
background-position:top 50% right 5px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3Cpath d=%27M7 10l5 5 5-5H7z%27 fill=%27gray%27/%3E%3C/svg%3E");
text-overflow: ellipsis;
cursor:pointer;
box-sizing: border-box;
-webkit-appearance: none;
-moz-appearance: none;
padding-right:30px !important;
}
.jdropdown-insert-button {
font-size: 1.4em;
text-transform: uppercase;
position:absolute;
right: 30px;
top: 4px;
display:none;
}
.jdropdown-container {
min-width: inherit;
transform: translate3d(-10000px,0,0);
position:absolute;
z-index:9001;
}
.jdropdown-close {
display:none;
font-size:1em;
color: var(--jactive-color);
text-transform:uppercase;
text-align:right;
padding:12px;
font-weight:bold;
}
.jdropdown-content {
min-width:inherit;
margin:0px;
box-sizing:border-box;
}
.jdropdown-content:empty {
}
.jdropdown-item {
white-space: nowrap;
text-align: left;
text-overflow: ellipsis;
overflow-x: hidden;
color: #000;
display: flex;
align-items: center;
}
.jdropdown-description {
text-overflow: ellipsis;
overflow: hidden;
line-height: 1.5em;
}
.jdropdown-image {
margin-right:10px;
width: 32px;
height: 32px;
border-radius:20px;
}
.jdropdown-image-small {
width:24px;
height:24px;
}
.jdropdown-icon {
margin-right:10px;
font-size: 30px;
margin-left: -5px;
}
.jdropdown-icon-small {
font-size: 24px;
margin-left: 0px;
}
.jdropdown-title {
font-size: 0.7em;
text-overflow: ellipsis;
overflow-x: hidden;
display: block;
}
/** Default visual ** /
.jdropdown-default .jdropdown-header {
border:1px solid #ccc;
padding:5px;
padding-left:10px;
padding-right:16px;
}
.jdropdown-default .jdropdown-container {
background-color:#fff;
}
.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-header {
padding-right:50px;
}
.jdropdown-default.jdropdown-focus.jdropdown-insert .jdropdown-insert-button {
display:block;
}
.jdropdown-default .jdropdown-content
{
min-width:inherit;
border:1px solid #8fb1e3;
margin:0px;
background-color:#fff;
box-sizing:border-box;
min-height:10px;
max-height:215px;
overflow-y:auto;
}
.jdropdown-default .jdropdown-item
{
padding:4px;
padding-left:8px;
padding-right:40px;
}
.jdropdown-default .jdropdown-item:hover
{
background-color:#1f93ff;
color:#fff;
}
.jdropdown-default .jdropdown-cursor
{
background-color:#eee;
}
.jdropdown-default .jdropdown-selected
{
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo=);
background-repeat:no-repeat;
background-position:top 50% right 5px;
background-color:#1f93ff;
color:#fff;
}
.jdropdown-default .jdropdown-group {
margin-top:5px;
}
.jdropdown-default .jdropdown-group .jdropdown-item {
padding-left:16px;
}
.jdropdown-default .jdropdown-group-name {
padding-left: 8px;
font-weight: bold;
text-align: left;
}
.jdropdown-default .jdropdown-reset_ {
content:'x';
position:absolute;
top:0;
right:0;
margin:5px;
margin-right:10px;
font-size:12px;
width:12px;
cursor:pointer;
text-shadow: 0px 0px 5px #fff;
display:none;
line-height: 1.8em;
}
.jdropdown-default.jdropdown-focus .jdropdown-reset_ {
display:block;
}
/** Default render for mobile ** /
.jdropdown-picker.jdropdown-focus .jdropdown-backdrop {
display:block;
}
.jdropdown-picker .jdropdown-header {
outline: none;
}
.jdropdown-picker .jdropdown-container
{
position:fixed;
bottom:0px;
left:0px;
border-bottom:1px solid #e6e6e8;
width:100%;
background-color:#fff;
box-sizing: border-box;
}
.jdropdown-picker .jdropdown-close
{
-webkit-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);
-moz-box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);
box-shadow: 0px -1px 5px 0px rgba(0,0,0,0.39);
background-color:#fff;
display:block;
}
.jdropdown-picker .jdropdown-content
{
overflow-y:scroll;
height:280px;
background-color:#fafafa;
border-top:1px solid #e6e6e8;
}
.jdropdown-picker .jdropdown-group-name
{
font-size: 1em;
text-transform: uppercase;
padding-top:10px;
padding-bottom:10px;
display: block;
border-bottom: 1px solid #e6e6e8;
padding-left:20px;
padding-right:20px;
text-align:center;
font-weight:bold;
}
.jdropdown-picker .jdropdown-item
{
font-size: 1em;
text-transform: uppercase;
padding-top:10px;
padding-bottom:10px;
border-bottom: 1px solid #e6e6e8;
padding-left:20px;
padding-right:20px;
}
.jdropdown-picker .jdropdown-selected
{
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIiAvPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiIGZpbGw9IndoaXRlIiAvPjwvc3ZnPgo=);
background-repeat:no-repeat;
background-position:top 50% right 15px;
background-color:#1f93ff;
color:#fff;
}
.jdropdown-picker .jdropdown-cursor
{
background-color:#1f93ff;
color:#fff;
}
/** Default render for mobile searchbar ** /
.jdropdown-searchbar.jdropdown-focus
{
position:fixed;
top:0px !important;
left:0px !important;
width:100% !important;
height:100% !important;
background-color:#fafafa;
padding:0px;
z-index:9001;
overflow-y:scroll;
will-change: scroll-position;
-webkit-overflow-scrolling: touch;
}
.jdropdown-searchbar.jdropdown-focus .jdropdown-container-header
{
position: fixed;
top: 0px;
left: 0px;
z-index: 9002;
padding: 6px;
background-color:#fff;
box-shadow: 0 1px 2px rgba(0,0,0,.1);
width: 100%;
height: 40px;
}
.jdropdown-searchbar.jdropdown-focus .jdropdown-header
{
border: 0px !important;
background-position-x: 0% !important;
background-position-y: 40% !important;
background-repeat: no-repeat;
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHoiIGZpbGw9IiNlNmU2ZTgiLz48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PC9zdmc+);
padding-left: 30px !important;
padding-right: 60px !important;
}
.jdropdown-searchbar.jdropdown-focus .jdropdown-close
{
display:block;
}
.jdropdown-searchbar .jdropdown-header {
outline: none;
}
.jdropdown-searchbar .jdropdown-container
{
margin-top: 40px;
width:100%;
}
.jdropdown-searchbar .jdropdown-close
{
position:fixed;
top:0px;
right:0px;
}
.jdropdown-searchbar .jdropdown-content
{
margin-top:10px;
}
.jdropdown-searchbar .jdropdown-group
{
margin-top:10px;
margin-bottom:15px;
background-color:#fff;
}
.jdropdown-searchbar .jdropdown-group-name
{
border-top: 1px solid #e6e6e8;
border-bottom: 1px solid #e6e6e8;
padding:10px;
padding-left:12px;
font-weight:bold;
}
.jdropdown-searchbar .jdropdown-group-arrow
{
float:right;
width:24px;
height:24px;
background-repeat:no-repeat;
}
.jdropdown-searchbar .jdropdown-group-arrow-down
{
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNNy40MSA4LjU5TDEyIDEzLjE3bDQuNTktNC41OEwxOCAxMGwtNiA2LTYtNiAxLjQxLTEuNDF6Ii8+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+PC9zdmc+);
}
.jdropdown-searchbar .jdropdown-group-arrow-up
{
background-image: url(data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTcuNDEgMTUuNDFMMTIgMTAuODNsNC41OSA0LjU4TDE4IDE0bC02LTYtNiA2eiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48L3N2Zz4=);
}
.jdropdown-searchbar .jdropdown-item
{
padding-top:10px;
padding-bottom:10px;
border-bottom: 1px solid #e6e6e8;
padding-left:15px;
padding-right:40px;
background-color:#fff;
font-size:0.9em;
}
.jdropdown-searchbar .jdropdown-description {
text-overflow: ellipsis;
overflow: hidden;
max-width: calc(100% - 20px);
}
.jdropdown-searchbar .jdropdown-content > .jdropdown-item:first-child
{
border-top: 1px solid #e6e6e8;
}
.jdropdown-searchbar .jdropdown-selected
{
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg==);
background-repeat:no-repeat;
background-position:top 50% right 15px;
}
/** List render ** /
.jdropdown-list
{
}
.jdropdown-list .jdropdown-container
{
display:block;
}
.jdropdown-list .jdropdown-header
{
display:none;
}
.jdropdown-list .jdropdown-group
{
background-color:#fff;
}
.jdropdown-list .jdropdown-group-name
{
border-bottom: 1px solid #e6e6e8;
padding-top:10px;
padding-bottom:10px;
font-weight:bold;
}
.jdropdown-list .jdropdown-item
{
padding-top:10px;
padding-bottom:10px;
border-bottom: 1px solid #e6e6e8;
padding-left:10px;
padding-right:40px;
background-color:#fff;
}
.jdropdown-list .jdropdown-selected
{
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTkgMTYuMTdMNC44MyAxMmwtMS40MiAxLjQxTDkgMTkgMjEgN2wtMS40MS0xLjQxeiIgZmlsbD0iIzAwN2FmZiIvPjwvc3ZnPg==);
background-repeat:no-repeat;
background-position:top 50% right 10px;
}
@media only screen and (max-width : 800px)
{
.jdropdown-list {
width:100% !important;
border:0px;
padding:0px;
}
.jdropdown-list .jdropdown-container {
min-width:100%;
}
.jdropdown-searchbar.jdropdown-focus .jdropdown-description {
text-transform: uppercase;
}
}
.app .jdropdown-item {
text-transform:uppercase;
}
.jdropdown-create-container {
margin: 10px;
border: 1px solid #ccc;
border-radius: 2px;
padding: 6px;
}
.jdropdown-color {
background-color: #fff;
border: 1px solid transparent;
border-radius: 12px;
width: 12px;
height: 12px;
margin-right: 6px;
}
.jdropdown-item[data-disabled] {
opacity: 0.5;
pointer-events: none;
}
.jeditor-container {
border:1px solid #ccc;
box-sizing: border-box;
}
.jeditor-container.with-margin {
background-color: #f2f2f2;
max-width: 1200px;
}
.jeditor-dragging {
border:1px dashed #000;
}
.jeditor {
outline:none;
word-break: break-word;
}
.jeditor-container.with-margin .jeditor {
background-color: #fff;
margin: 80px;
min-height: 800px;
padding: 80px;
max-width: 800px;
}
.jeditor[data-placeholder]:empty:before {
content: attr(data-placeholder);
color: lightgray;
}
/** Snippet ** /
.jsnippet {
margin-top:15px;
cursor:pointer;
border: 1px solid #ccc;
position:relative;
}
.jsnippet:focus {
outline: none;
}
.jsnippet img {
width:100%;
}
.jsnippet .jsnippet-title {
padding:15px;
font-size:1.4em;
}
.jsnippet .jsnippet-description {
padding-left:15px;
padding-right:15px;
font-size:1em;
}
.jsnippet .jsnippet-host {
padding:15px;
text-transform:uppercase;
font-size:0.8em;
color:#777;
text-align:right;
}
.jsnippet .jsnippet-url {
display:none;
}
.jeditor .jsnippet:after {
content: 'close';
font-family: 'Material icons';
font-size: 24px;
width: 24px;
height: 24px;
line-height: 24px;
cursor: pointer;
text-shadow: 0px 0px 2px #fff;
position: absolute;
top: 12px;
right: 12px;
}
.jsnippet * {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
}
.jeditor img {
border:2px solid transparent;
box-sizing: border-box;
}
.jeditor img.resizing {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
}
.jeditor img:focus {
border: 2px solid #0096FD;
outline: #0096FD;
}
.jeditor .pdf {
background-image: url("data:image/svg+xml,%3Csvg version=%271.1%27 id=%27Layer_1%27 xmlns=%27http://www.w3.org/2000/svg%27 xmlns:xlink=%27http://www.w3.org/1999/xlink%27 x=%270px%27 y=%270px%27 viewBox=%270 0 512 512%27 style=%27enable-background:new 0 0 512 512;%27 xml:space=%27preserve%27%3E%3Cpath style=%27fill:%23C30B15;%27 d=%27M511.344,274.266C511.77,268.231,512,262.143,512,256C512,114.615,397.385,0,256,0S0,114.615,0,256 c0,117.769,79.53,216.949,187.809,246.801L511.344,274.266z%27/%3E%3Cpath style=%27fill:%2385080E;%27 d=%27M511.344,274.266L314.991,77.913L119.096,434.087l68.714,68.714C209.522,508.787,232.385,512,256,512 C391.243,512,501.976,407.125,511.344,274.266z%27/%3E%3Cpolygon style=%27fill:%23FFFFFF;%27 points=%27278.328,333.913 255.711,77.913 119.096,77.913 119.096,311.652 %27/%3E%3Cpolygon style=%27fill:%23E8E6E6;%27 points=%27392.904,311.652 392.904,155.826 337.252,133.565 314.991,77.913 255.711,77.913 256.067,333.913 %27/%3E%3Cpolygon style=%27fill:%23FFFFFF;%27 points=%27314.991,155.826 314.991,77.913 392.904,155.826 %27/%3E%3Crect x=%27119.096%27 y=%27311.652%27 style=%27fill:%23FC0F1A;%27 width=%27273.809%27 height=%27122.435%27/%3E%3Cg%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M204.871,346.387c13.547,0,21.341,6.659,21.341,18.465c0,12.412-7.795,19.601-21.341,19.601h-9.611 v14.909h-13.471v-52.975L204.871,346.387L204.871,346.387z M195.26,373.858h8.93c5.904,0,9.308-2.952,9.308-8.552 c0-5.525-3.406-8.324-9.308-8.324h-8.93V373.858z%27/%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M257.928,346.387c16.649,0,28.152,10.746,28.152,26.487c0,15.666-11.655,26.488-28.683,26.488 h-22.25v-52.975H257.928z M248.619,388.615h9.611c8.249,0,14.151-6.357,14.151-15.665c0-9.384-6.205-15.817-14.757-15.817h-9.006 V388.615z%27/%3E%3Cpath style=%27fill:%23FFFFFF;%27 d=%27M308.563,356.982v12.26h23.763v10.596h-23.763v19.525h-13.471v-52.975h39.277v10.595h-25.806 V356.982z%27/%3E%3C/g%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-size: cover;
width:60px;
height:60px;
}
.jeditor-toolbar {
width: fit-content;
max-width: 100%;
box-sizing: border-box;
margin: 10px;
}
.toolbar-on-top .jeditor-toolbar {
width: initial;
margin: 0px;
box-shadow: 1px 1px 2px rgb(0 0 0 / 10%);
display: block;
}
.toolbar-on-top .jeditor {
padding: 15px;
}
.toolbar-on-top .jtoolbar .material-icons {
font-size: 24px;
transform: initial;
margin: 4px;
}
.toolbar-on-top .jtoolbar .jpicker-header {
font-size: 1em;
margin-top: 4px;
margin-bottom: 4px;
}
.jeditor table {
border-collapse: collapse;
}
.jeditor table td {
border: 1px solid #bbb;
height: 2em;
}
.jeditor table td:focus {
border: 1px solid blue;
}
.jeditor .line-break {
border-top: 1px dashed #ccc;
display: flex;
justify-content: center;
pointer-events: none;
}
.jeditor .line-break:before {
content: 'New page';
background-color: #fff;
color: #ccc;
margin: -1em;
padding: 6px;
position: absolute;
}
.jfloating {
position:fixed;
bottom:0px;
right:0px;
margin-right:5px;
-webkit-box-shadow: 0 2px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 2px 10px rgba(0,0,0,.2);
border:1px solid #ccc;
background-color:#fff;
box-sizing: border-box;
padding-top:50px !important;
z-index:9002;
border-radius: 8px;
}
.jfloating.jfloating-big {
width: 510px !important;
height: 472px !important;
}
.jfloating.jfloating-small {
width: 300px !important;
height: 320px !important;
}
.jfloating.jfloating-large {
width: 600px !important;
height: 600px !important;
}
.jfloating:before {
position:absolute;
top:0;
left:0;
width:100%;
content:attr(title);
padding:15px;
box-sizing: border-box;
font-size:1.2em;
box-shadow: 1px 1px 3px rgba(0,0,0,.2);
background-color: #fff;
border-radius: 8px 8px 0px 0px;
background-color: #404040;
font-size: .93rem;
font-weight: 600;
color: white;
letter-spacing: .5px;
}
.jfloating:after {
content:'';
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27%23FFF%27 d=%27M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
position:absolute;
top:0;
right:0;
margin:14px;
font-size:24px;
width:24px;
height:24px;
cursor:pointer;
text-shadow: 0px 0px 5px #fff;
}
.jfloating_content {
padding:20px;
overflow-y:auto;
max-height:100%;
box-sizing: border-box;
height: -webkit-fill-available;
}
.jfloating.jfloating-minimized {
height: 50px !important;
}
.jfloating.jfloating-minimized .jfloating_content {
display: none;
}
.jmodal {
position:fixed;
top:50%;
left:50%;
width:60%;
height:60%;
-webkit-box-shadow: 0 2px 12px rgba(0,0,0,.2);
-moz-box-shadow: 0 2px 12px rgba(0,0,0,.2);
border:1px solid #ccc;
background-color:#fff;
transform: translate(-50%, -50%);
box-sizing: border-box;
z-index:9002;
border-radius: 4px;
display: flex;
flex-direction: column;
}
.jmodal_title {
padding: 20px;
height: 70px;
box-sizing: border-box;
font-size: 1.4em;
background-color: #fff;
border-radius: 8px 8px 0px 0px;
pointer-events: none;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
align-items: center;
border-bottom: 1px solid #eee;
}
.jmodal_title > div {
font-size: 1.4em;
}
.jmodal_title[data-icon]:before {
content: attr(data-icon);
font-family: 'Material Icons' !important;
width: 24px;
height: 24px;
font-size: 24px;
margin-right: 10px;
line-height: 24px;
}
.jmodal_content {
padding: 20px;
overflow-y: auto;
height: 100%;
box-sizing: border-box;
scrollbar-width: thin;
scrollbar-color: #333 transparent;
}
.jmodal_title:empty {
display: none;
}
.jmodal_title:empty + .jmodal_content {
height: 100%;
}
.jmodal_content::-webkit-scrollbar {
height: 12px;
}
.jmodal_content::-webkit-scrollbar {
width: 12px;
}
.jmodal_content::-webkit-scrollbar-track {
border: 1px solid #fff;
background: #eee;
}
.jmodal_content::-webkit-scrollbar-thumb {
border: 1px solid #fff;
background: #888;
}
.jmodal:after {
content: '';
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
position: absolute;
top: 0;
right: 0;
margin: 25px;
font-size: 24px;
width: 24px;
height: 24px;
cursor: pointer;
text-shadow: 0px 0px 5px #fff;
}
.jmodal_fullscreen {
width: 100% !important;
height: 100% !important;
top: 0px;
left: 0px;
transform: none;
border: 0px;
border-radius: 0px;
}
.jmodal_backdrop {
position: fixed;
top: 0px;
left: 0px;
min-width: 100%;
min-height: 100%;
background-color: rgba(0,0,0,0.2);
border: 0px;
padding: 0px;
z-index: 8000;
display: none;
-webkit-touch-callout: none; /* iOS Safari * /
-webkit-user-select: none; /* Safari * /
-khtml-user-select: none; /* Konqueror HTML * /
-moz-user-select: none; /* Firefox * /
-ms-user-select: none; /* Internet Explorer/Edge * /
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera * /
}
.jmodal_content .jcalendar .jcalendar-content,
.jmodal_content .jdropdown-container {
position: fixed;
}
.jnotification {
position: fixed;
z-index: 10000;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
bottom: 0px;
}
.jnotification-container {
-webkit-box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);
box-shadow: 0px 2px 15px -5px rgba(0, 0, 0, 0.7);
padding: 12px;
border-radius: 8px;
background-color: #000;
background: rgba(92,92,92,1);
background: linear-gradient(0deg, rgba(92,92,92,1) 0%, rgba(77,77,77,1) 100%);
color: #fff;
width: 320px;
margin: 30px;
padding: 20px;
}
.jnotification-close {
content: '';
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27white%27%3E%3Cpath d=%27M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
font-size: 20px;
width: 20px;
height: 20px;
cursor: pointer;
}
.jnotification-title {
font-weight: bold;
}
.jnotification-header {
display: flex;
padding-bottom: 5px;
}
.jnotification-header:empty {
display: none;
}
.jnotification-image {
margin-right: 5px;
}
.jnotification-image:empty {
display: none;
}
.jnotification-image img {
width: 24px;
}
.jnotification-name {
text-transform: uppercase;
font-size: 0.9em;
flex: 1;
letter-spacing: 0.1em;
}
.jnotification-error .jnotification-container {
background: rgb(182,38,6);
background: linear-gradient(0deg, rgba(170,41,13,1) 0%, rgba(149,11,11,1) 100%);
}
@media (max-width: 800px) {
.jnotification {
top: calc(0px + var(--jsafe-area-top));
width: 100%;
}
.jnotification-container {
background: rgba(255,255,255,0.95);
border: 1px solid #eee;
color: #444;
margin: 0px;
width: initial;
}
.jnotification-error .jnotification-container {
background: rgba(255,255,255,0.95);
color: #790909;
}
.jnotification-close {
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2720%27 height=%2720%27 viewBox=%270 0 24 24%27 fill=%27black%27%3E%3Cpath d=%27M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
}
}
.jnotification-header {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.jpicker {
cursor: pointer;
white-space: nowrap;
display: inline-flex;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none;
position: relative;
min-height: 26px;
}
.jpicker-header {
background-repeat: no-repeat;
background-position: top 50% right 5px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath fill=%27none%27 d=%27M0 0h24v24H0V0z%27/%3E%3Cpath d=%27M7 10l5 5 5-5H7z%27 fill=%27gray%27/%3E%3C/svg%3E");
text-overflow: ellipsis;
cursor: pointer;
box-sizing: border-box;
text-align: left;
outline: none;
line-height: 24px;
padding: 2px 35px 2px 12px;
border-radius: 4px;
}
.jpicker-header:hover {
background-color: #f2f2f2;
}
.jpicker-content {
position: absolute;
top: 0;
display: none;
box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2);
border-radius: 4px;
background-color: #fff;
padding: 4px;
z-index: 50;
text-align: left;
max-height: 250px;
scrollbar-width: thin;
scrollbar-color: #333 transparent;
}
.jpicker-content::-webkit-scrollbar {
width: 8px;
}
.jpicker-content::-webkit-scrollbar-track {
background: #eee;
}
.jpicker-content::-webkit-scrollbar-thumb {
background: #888;
}
.jpicker-content > div {
padding: 6px;
padding-left: 15px;
padding-right: 15px;
}
.jpicker-focus > .jpicker-content {
display: block;
}
.jpicker-content > div:hover {
background-color:#efefef;
}
.jpicker-content > div:empty {
opacity: 0;
}
.jpicker-header > i, .jpicker-header > div {
display: block;
}
.jpicker-focus > .jpicker-content.jpicker-columns {
display: flex !important ;
justify-content: center;
flex-wrap: wrap;
}
.jpicker-focus .jpicker-content.jpicker-grid {
display: inline-grid;
}
.jprogressbar
{
cursor:pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
box-sizing: border-box;
background:#fff;
-webkit-tap-highlight-color: transparent;
display: inline-block;
box-sizing: border-box;
cursor:pointer;
border:1px solid #ccc;
position:relative;
}
.jprogressbar::before {
content:attr(data-value);
position:absolute;
margin:5px;
margin-left:10px;
}
.jprogressbar-header::placeholder
{
color:#000;
}
.jprogressbar:focus {
outline: auto 5px -webkit-focus-ring-color;
}
.jprogressbar > div {
background-color: #eee;
background-color: red;
box-sizing: border-box;
height:31px;
}
.jrating {
display:flex;
}
.jrating > div {
width:24px;
height:24px;
line-height:24px;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27%3E%3Cpath d=%27M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z%27 fill=%27gray%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
}
.jrating .jrating-over {
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27black%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
opacity: 0.7;
}
.jrating .jrating-selected {
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27red%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
}
.jsearch {
position: relative;
display: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jsearch_container {
position: absolute;
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.302), 0 2px 6px 2px rgba(60,64,67,0.149);
border: none;
-webkit-border-radius: 4px;
border-radius: 4px;
width: 280px;
padding: 8px 0;
z-index: 1;
-webkit-box-shadow: 0 2px 4px rgba(0,0,0,0.2);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
-webkit-transition: opacity .218s;
transition: opacity .218s;
background: #fff;
border: 1px solid rgba(0,0,0,.2);
cursor: pointer;
margin: 0;
min-width: 300px;
outline: none;
width: auto;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jsearch_container:empty:after {
content: attr(data-placeholder);
}
.jsearch_container > div {
color: #333;
cursor: pointer;
display: -webkit-box;
display: -webkit-flex;
display: flex;
padding: 5px 10px;
user-select: none;
-webkit-align-items: center;
align-items: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jsearch_container > div:hover {
background-color: #e8eaed;
}
.jsearch_container > div > img {
width: 32px;
height: 32px;
user-select: none;
border-radius: 16px;
margin-right: 2px;
}
.jsearch_container > div > div {
overflow: hidden;
text-overflow: ellipsis;
margin-left: 2px;
max-width: 300px;
white-space: nowrap;
user-select: none;
}
.jsearch_container .selected {
background-color: #e8eaed;
}
.jslider {
outline: none;
}
.jslider-focus {
width: 100% !important;
height: 100% !important;
}
.jslider-focus img {
display: none;
}
.jslider img {
width: 100px;
}
.jslider-left::before {
position: fixed;
left: 15px;
top: 50%;
content:'arrow_back_ios';
color: #fff;
width: 30px;
height: 30px;
font-family: 'Material Icons';
font-size: 30px;
/* before it was 0px 0px 0px #000 * /
text-shadow: 0px 0px 6px rgb(56,56,56);
text-align: center;
cursor: pointer;
}
.jslider-right::after {
position: fixed;
right: 15px;
top: 50%;
content: 'arrow_forward_ios';
color: #fff;
width: 30px;
height: 30px;
font-family: 'Material Icons';
font-size: 30px;
/* before it was 0px 0px 0px #000 * /
text-shadow: 0px 0px 6px rgb(56,56,56);
text-align: center;
cursor: pointer;
}
.jslider-close {
width:24px;
height:24px;
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2724%27 height=%2724%27 viewBox=%270 0 24 24%27 fill=%27white%27%3E%3Cpath d=%27M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
position:fixed;
top:15px;
right:15px;
cursor:pointer;
z-index:3000;
display: block !important;
}
.jslider-counter {
height:24px;
background-color: transparent;
position:fixed;
left: 50%;
transform: translateX(-50%);
bottom: 15px;
cursor:pointer;
z-index:3000;
display: flex;
display: -webkit-flex;
-webkit-justify-content: center;
-webkit-align-items: center;
-webkit-flex-direction: row;
justify-content: center;
align-items: center;
flex-direction: row;
}
.jslider-caption {
position: fixed;
max-width: 90vw;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
top:15px;
left: 15px;
z-index:3000;
color: #FFF;
font-size: 1rem;
display: block !important;
}
.jslider-counter div {
width: 10px;
height: 10px;
background: #fff;
border-radius: 50%;
margin: 0px 5px;
display: block !important;
}
.jslider-counter .jslider-counter-focus {
background-color: cornflowerblue;
pointer-events: none;
}
.jslider-focus {
position:fixed;
left:0;
top:0;
width: 100%;
min-height:100%;
max-height:100%;
z-index:2000;
margin:0px;
box-sizing:border-box;
background-color:rgba(0,0,0,0.8);
-webkit-transition-duration: .05s;
transition-duration: .05s;
display: flex;
-ms-flex-align: center;
-webkit-align-items: center;
-webkit-box-align: center;
align-items: center;
}
.jslider-focus img {
width: 50vw;
height: auto;
box-sizing: border-box;
margin:0 auto;
vertical-align:middle;
display:none;
}
.jslider-focus img.jslider-vertical {
width: auto;
/* before it was 50vh * /
height: 80vh;
}
@media only screen and (max-width: 576px) {
.jslider-focus img.jslider-vertical {
width: 99vw !important;
height: auto !important;
}
.jslider-focus img {
width: 100vw !important;
height: auto !important;
}
}
.jslider-grid {
display: -ms-grid;
display: grid;
grid-gap: 1px;
position: relative;
}
.jslider-grid[data-number='2'] {
-ms-grid-columns: 1fr 50%;
grid-template-columns: 1fr 50%;
}
.jslider-grid[data-number='3'] {
-ms-grid-columns: 1fr 33%;
grid-template-columns: 1fr 33%;
}
.jslider-grid[data-number='4'] {
-ms-grid-columns: 1fr 25%;
grid-template-columns: 1fr 25%;
}
.jslider-grid img {
display: none;
width: 100%;
height: 100%;
object-fit: cover;
}
.jslider-grid[data-total]:after {
content: attr(data-total) "+";
font-size: 1.5em;
position:absolute;
color: #fff;
right: 15px;
bottom: 6px;
}
.jslider-grid img:first-child {
-ms-grid-column: 1;
-ms-grid-row: 1;
grid-column: 1;
grid-row: 1;
display: block;
}
.jslider-grid[data-number='2'] img:nth-child(2) {
-ms-grid-column: 2;
-ms-grid-row: 1;
grid-column: 2;
grid-row: 1;
display: block;
}
.jslider-grid[data-number='3'] img:first-child {
-ms-grid-column: 1 / 2;
-ms-grid-row: 1 / 4;
grid-column: 1 / 2;
grid-row: 1 / 4;
}
.jslider-grid[data-number='3'] img:nth-child(2) {
-ms-grid-column: 2;
-ms-grid-row: 1;
grid-column: 2;
grid-row: 1;
display: block;
}
.jslider-grid[data-number='3'] img:nth-child(3) {
-ms-grid-column: 2;
-ms-grid-row: 2;
grid-column: 2;
grid-row: 2;
display: block;
}
.jslider-grid[data-number='4'] img:first-child {
-ms-grid-column: 1 / 2;
-ms-grid-row: 1 / 4;
grid-column: 1 / 2;
grid-row: 1 / 4;
}
.jslider-grid[data-number='4'] img:nth-child(2) {
-ms-grid-column: 2;
-ms-grid-row: 1;
grid-column: 2;
grid-row: 1;
display: block;
}
.jslider-grid[data-number='4'] img:nth-child(3) {
-ms-grid-column: 2;
-ms-grid-row: 2;
grid-column: 2;
grid-row: 2;
display: block;
}
.jslider-grid[data-number='4'] img:nth-child(4) {
-ms-grid-column: 2;
-ms-grid-row: 3;
grid-column: 2;
grid-row: 3;
display: block;
}
.jtabs {
max-width: 100vw;
position: relative;
}
.jtabs .jtabs-headers-container {
display: flex;
align-items: center;
}
.jtabs .jtabs-headers {
display: flex;
align-items: center;
overflow: hidden;
position: relative;
}
.jtabs .jtabs-headers > div:not(.jtabs-border) {
padding: 8px;
padding-left: 20px;
padding-right: 20px;
margin-left: 1px;
margin-right: 1px;
background-color: #f1f1f1;
cursor: pointer;
white-space: nowrap;
text-align: center;
}
.jtabs .jtabs-headers > div.jtabs-selected {
background-color: #e8e8e8;
color: #000;
}
.jtabs .jtabs-headers > div > div {
color: #555;
width: 100%;
overflow: hidden;
}
.jtabs .jtabs-headers i {
display: block;
margin: auto;
}
.jtabs .jtabs-content {
box-sizing: border-box;
}
.jtabs .jtabs-content > div {
display: none;
box-sizing: border-box;
}
.jtabs .jtabs-content > div.jtabs-selected {
display: block;
}
.jtabs .jtabs-border {
position: absolute;
height: 2px;
background-color: #888;
transform-origin: left;
transition: all .2s cubic-bezier(0.4,0,0.2,1);
transition-property: color,left,transform;
display: none;
pointer-events: none;
}
.jtabs-animation .jtabs-border {
display: initial;
}
.jtabs .jtabs-controls {
margin: 3px;
margin-left: 10px;
display: flex;
min-width: 82px;
}
.jtabs .jtabs-controls > div {
cursor: pointer;
background-position: center;
background-repeat: no-repeat;
width: 24px;
height: 24px;
line-height: 24px;
}
.jtabs .jtabs-prev {
margin-left: 10px;
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z%27/%3E%3C/svg%3E");
}
.jtabs .jtabs-prev.disabled {
margin-left: 10px;
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27lightgray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z%27/%3E%3C/svg%3E");
}
.jtabs .jtabs-next {
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27gray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z%27/%3E%3C/svg%3E");
}
.jtabs .jtabs-next.disabled {
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27lightgray%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z%27/%3E%3C/svg%3E");
}
.jtabs .jtabs-add {
background-image: url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 height=%2724%27 viewBox=%270 0 24 24%27 width=%2724%27%3E%3Cpath d=%27M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z%27 fill=%27%23bbbbbb%27/%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3C/svg%3E");
}
/** Modern skin ** /
.jtabs.jtabs-modern .jtabs-headers > div:not(.jtabs-border) {
padding: 4px;
padding-left: 10px;
padding-right: 10px;
background-color: #fff;
}
.jtabs.jtabs-modern .jtabs-headers > .jtabs-selected {
color: #000;
}
.jtabs.jtabs-modern .jtabs-headers > .jtabs-selected .material-icons {
color: #000;
}
.jtabs.jtabs-modern .jtabs-headers {
background: #EEEEEF !important;
padding: 2px;
border-radius: 4px;
}
.jtabs.jtabs-modern .jtabs-headers .jtabs-border {
border-color: #EEEEEF !important;
}
.jtabs.jtabs-modern .jtabs-border {
background-color: rgba(194, 197, 188, 0.884);
}
.jtags {
display: flex;
flex-wrap: wrap;
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
-ms-flex-pack: flex-start;
-webkit-justify-content: space-between;
justify-content: flex-start;
padding: 1px;
border: 1px solid #ccc;
position: relative;
}
.jtags.jtags-empty:not(.jtags-focus)::before {
position: absolute;
margin: 3px;
color: #ccc;
content: attr(data-placeholder);
top: 0;
margin-left: 6px;
}
.jtags > div {
padding: 3px 22px 3px 10px;
font-size: 0.9em;
position: relative;
border-radius: 1px;
margin: 2px;
display: block;
outline: none;
}
.jtags > div:empty:before {
content: " ";
white-space: pre;
}
.jtags > div::after {
content: 'x';
position: absolute;
right: 4px;
width: 12px;
height: 12px;
cursor: pointer;
font-size: 0.9em;
display: none;
}
.jtags_label {
background-color: #e4e4e4 !important;
}
.jtags_label::after {
display: inline-block !important;
}
.jtags_error::after {
color: #fff !important;
}
.jtags_error {
background-color: #d93025 !important;
color: #fff;
}
.jtoolbar-container {
border-radius: 2px;
box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2);
display: inline-flex !important;
}
.jtoolbar {
cursor: pointer;
white-space: nowrap;
display: flex;
padding:4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
font-size: 13px;
}
.jtoolbar-disabled {
pointer-events: none;
opacity: 0.4;
}
.jtoolbar-mobile {
display: flex;
position:fixed;
bottom: 0;
margin: 0;
left: 0;
width: 100%;
background: #f7f7f8;
z-index: 1;
box-sizing: border-box;
box-shadow: 0 -1px 2px rgba(0,0,0,.1);
border-radius: 0px;
}
.jtoolbar > div {
display: inline-flex;
align-items: center;
box-sizing: border-box;
vertical-align:middle;
justify-content: space-evenly;
}
.jtoolbar-mobile > div {
display: flex;
width: 100%;
}
.jtoolbar .jtoolbar-item {
text-align: center;
margin: auto;
padding: 2px;
padding-left:4px;
padding-right:4px;
}
.jtoolbar-mobile .jtoolbar-item {
position: relative;
flex:1;
}
.jtoolbar .jtoolbar-divisor {
width: 2px;
height: 18px;
padding: 0px;
margin-left: 4px;
margin-right: 4px;
background-color: #ddd;
}
.jtoolbar .jtoolbar-label {
padding-left: 8px;
padding-right: 8px;
}
.jtoolbar-mobile a
{
text-decoration:none;
display:inline-block;
}
.jtoolbar-mobile i {
display: inline-flex !important;
color:#929292;
}
.jtoolbar-mobile span {
font-size:0.7em;
display:block;
color:#929292;
}
.jtoolbar-mobile .jtoolbar-selected a, .jtoolbar-mobile .jtoolbar-selected i, .jtoolbar-mobile .jtoolbar-selected span {
color:var(--jactive-color) !important;
background-color:transparent;
}
.jtoolbar-item {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.jtoolbar-item i {
display: block;
color:#333;
}
.jtoolbar-item:hover {
background-color:#f2f2f2;
}
.jtoolbar .jpicker {
padding-left:0px;
padding-right:0px;
}
.jtoolbar .jpicker-header {
height: 24px;
line-height: 24px;
padding: 0px;
padding-right: 20px;
padding-left: 8px;
background-position: top 50% right 0px;
display: flex;
align-items: center;
font-size: 0.9em;
}
.jtoolbar .jpicker-content > div {
padding: 6px;
}
.jtoolbar-active {
background-color:#eee;
}
.jtoolbar .fa {
width: 18px;
height: 18px;
display: block;
line-height: 18px;
font-size: 14px;
}
.jtoolbar .material-icons {
font-size: 18px;
width: 24px;
height: 24px;
display: block;
line-height: 24px;
transform: rotate(0.03deg);
text-align: center;
}
.jtoolbar .jtoolbar-arrow {
background-repeat: no-repeat;
background-position: center;
background-image: url("data:image/svg+xml,%0A%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27black%27 width=%2718px%27 height=%2718px%27%3E%3Cpath d=%27M0 0h24v24H0z%27 fill=%27none%27/%3E%3Cpath d=%27M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z%27/%3E%3C/svg%3E");
width: 24px;
height: 16px;
margin-left: 4px;
border-left: 1px solid #f2f2f2;
}
.jtoolbar-floating {
position: absolute;
display: none;
box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.2);
border-radius: 4px;
background-color: #fff;
padding: 4px;
z-index: 50;
text-align: left;
margin-right: 20px;
}
.jtoolbar-floating .jtoolbar-divisor {
display: none;
}
.jtoolbar-arrow-selected .jtoolbar-floating {
display: flex;
flex-wrap: wrap;
}
//}}}
!!!!!Code
***/
//{{{
;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
global.jSuites = factory();
}(this, (function () {
var jSuites;
/******/ (function() { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 195:
/***/ (function(module) {
/**
* (c) jSuites Javascript Plugins and Web Components (v4)
*
* Website: https://jsuites.net
* Description: Create amazing web based applications.
* Plugin: Organogram
*
* MIT License
*/
;(function (global, factory) {
true ? module.exports = factory() :
0;
}(this, (function () {
return (function(str) {
function int64(msint_32, lsint_32) {
this.highOrder = msint_32;
this.lowOrder = lsint_32;
}
var H = [new int64(0x6a09e667, 0xf3bcc908), new int64(0xbb67ae85, 0x84caa73b),
new int64(0x3c6ef372, 0xfe94f82b), new int64(0xa54ff53a, 0x5f1d36f1),
new int64(0x510e527f, 0xade682d1), new int64(0x9b05688c, 0x2b3e6c1f),
new int64(0x1f83d9ab, 0xfb41bd6b), new int64(0x5be0cd19, 0x137e2179)];
var K = [new int64(0x428a2f98, 0xd728ae22), new int64(0x71374491, 0x23ef65cd),
new int64(0xb5c0fbcf, 0xec4d3b2f), new int64(0xe9b5dba5, 0x8189dbbc),
new int64(0x3956c25b, 0xf348b538), new int64(0x59f111f1, 0xb605d019),
new int64(0x923f82a4, 0xaf194f9b), new int64(0xab1c5ed5, 0xda6d8118),
new int64(0xd807aa98, 0xa3030242), new int64(0x12835b01, 0x45706fbe),
new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, 0xd5ffb4e2),
new int64(0x72be5d74, 0xf27b896f), new int64(0x80deb1fe, 0x3b1696b1),
new int64(0x9bdc06a7, 0x25c71235), new int64(0xc19bf174, 0xcf692694),
new int64(0xe49b69c1, 0x9ef14ad2), new int64(0xefbe4786, 0x384f25e3),
new int64(0x0fc19dc6, 0x8b8cd5b5), new int64(0x240ca1cc, 0x77ac9c65),
new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
new int64(0x5cb0a9dc, 0xbd41fbd4), new int64(0x76f988da, 0x831153b5),
new int64(0x983e5152, 0xee66dfab), new int64(0xa831c66d, 0x2db43210),
new int64(0xb00327c8, 0x98fb213f), new int64(0xbf597fc7, 0xbeef0ee4),
new int64(0xc6e00bf3, 0x3da88fc2), new int64(0xd5a79147, 0x930aa725),
new int64(0x06ca6351, 0xe003826f), new int64(0x14292967, 0x0a0e6e70),
new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, 0x9d95b3df),
new int64(0x650a7354, 0x8baf63de), new int64(0x766a0abb, 0x3c77b2a8),
new int64(0x81c2c92e, 0x47edaee6), new int64(0x92722c85, 0x1482353b),
new int64(0xa2bfe8a1, 0x4cf10364), new int64(0xa81a664b, 0xbc423001),
new int64(0xc24b8b70, 0xd0f89791), new int64(0xc76c51a3, 0x0654be30),
new int64(0xd192e819, 0xd6ef5218), new int64(0xd6990624, 0x5565a910),
new int64(0xf40e3585, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
new int64(0x19a4c116, 0xb8d2d0c8), new int64(0x1e376c08, 0x5141ab53),
new int64(0x2748774c, 0xdf8eeb99), new int64(0x34b0bcb5, 0xe19b48a8),
new int64(0x391c0cb3, 0xc5c95a63), new int64(0x4ed8aa4a, 0xe3418acb),
new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, 0xd6b2b8a3),
new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
new int64(0x84c87814, 0xa1f0ab72), new int64(0x8cc70208, 0x1a6439ec),
new int64(0x90befffa, 0x23631e28), new int64(0xa4506ceb, 0xde82bde9),
new int64(0xbef9a3f7, 0xb2c67915), new int64(0xc67178f2, 0xe372532b),
new int64(0xca273ece, 0xea26619c), new int64(0xd186b8c7, 0x21c0c207),
new int64(0xeada7dd6, 0xcde0eb1e), new int64(0xf57d4f7f, 0xee6ed178),
new int64(0x06f067aa, 0x72176fba), new int64(0x0a637dc5, 0xa2c898a6),
new int64(0x113f9804, 0xbef90dae), new int64(0x1b710b35, 0x131c471b),
new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, 0x9c100d4c),
new int64(0x4cc5d4be, 0xcb3e42b6), new int64(0x597f299c, 0xfc657e2a),
new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)];
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
var charsize = 8;
function utf8_encode(str) {
return unescape(encodeURIComponent(str));
}
function str2binb(str) {
var bin = [];
var mask = (1 << charsize) - 1;
var len = str.length * charsize;
for (var i = 0; i < len; i += charsize) {
bin[i >> 5] |= (str.charCodeAt(i / charsize) & mask) << (32 - charsize - (i % 32));
}
return bin;
}
function binb2hex(binarray) {
var hex_tab = "0123456789abcdef";
var str = "";
var length = binarray.length * 4;
var srcByte;
for (var i = 0; i < length; i += 1) {
srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8);
str += hex_tab.charAt((srcByte >> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF);
}
return str;
}
function safe_add_2(x, y) {
var lsw, msw, lowOrder, highOrder;
lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF);
msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16);
msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function safe_add_4(a, b, c, d) {
var lsw, msw, lowOrder, highOrder;
lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF);
msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16);
msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function safe_add_5(a, b, c, d, e) {
var lsw, msw, lowOrder, highOrder;
lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF);
msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16);
lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16);
msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16);
highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF);
return new int64(highOrder, lowOrder);
}
function maj(x, y, z) {
return new int64(
(x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder),
(x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder)
);
}
function ch(x, y, z) {
return new int64(
(x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder),
(x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder)
);
}
function rotr(x, n) {
if (n <= 32) {
return new int64(
(x.highOrder >>> n) | (x.lowOrder << (32 - n)),
(x.lowOrder >>> n) | (x.highOrder << (32 - n))
);
} else {
return new int64(
(x.lowOrder >>> n) | (x.highOrder << (32 - n)),
(x.highOrder >>> n) | (x.lowOrder << (32 - n))
);
}
}
function sigma0(x) {
var rotr28 = rotr(x, 28);
var rotr34 = rotr(x, 34);
var rotr39 = rotr(x, 39);
return new int64(
rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder,
rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder
);
}
function sigma1(x) {
var rotr14 = rotr(x, 14);
var rotr18 = rotr(x, 18);
var rotr41 = rotr(x, 41);
return new int64(
rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder,
rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder
);
}
function gamma0(x) {
var rotr1 = rotr(x, 1), rotr8 = rotr(x, 8), shr7 = shr(x, 7);
return new int64(
rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder,
rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder
);
}
function gamma1(x) {
var rotr19 = rotr(x, 19);
var rotr61 = rotr(x, 61);
var shr6 = shr(x, 6);
return new int64(
rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder,
rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder
);
}
function shr(x, n) {
if (n <= 32) {
return new int64(
x.highOrder >>> n,
x.lowOrder >>> n | (x.highOrder << (32 - n))
);
} else {
return new int64(
0,
x.highOrder << (32 - n)
);
}
}
var str = utf8_encode(str);
var strlen = str.length*charsize;
str = str2binb(str);
str[strlen >> 5] |= 0x80 << (24 - strlen % 32);
str[(((strlen + 128) >> 10) << 5) + 31] = strlen;
for (var i = 0; i < str.length; i += 32) {
a = H[0];
b = H[1];
c = H[2];
d = H[3];
e = H[4];
f = H[5];
g = H[6];
h = H[7];
for (var j = 0; j < 80; j++) {
if (j < 16) {
W[j] = new int64(str[j*2 + i], str[j*2 + i + 1]);
} else {
W[j] = safe_add_4(gamma1(W[j - 2]), W[j - 7], gamma0(W[j - 15]), W[j - 16]);
}
T1 = safe_add_5(h, sigma1(e), ch(e, f, g), K[j], W[j]);
T2 = safe_add_2(sigma0(a), maj(a, b, c));
h = g;
g = f;
f = e;
e = safe_add_2(d, T1);
d = c;
c = b;
b = a;
a = safe_add_2(T1, T2);
}
H[0] = safe_add_2(a, H[0]);
H[1] = safe_add_2(b, H[1]);
H[2] = safe_add_2(c, H[2]);
H[3] = safe_add_2(d, H[3]);
H[4] = safe_add_2(e, H[4]);
H[5] = safe_add_2(f, H[5]);
H[6] = safe_add_2(g, H[6]);
H[7] = safe_add_2(h, H[7]);
}
var binarray = [];
for (var i = 0; i < H.length; i++) {
binarray.push(H[i].highOrder);
binarray.push(H[i].lowOrder);
}
return binb2hex(binarray);
});
})));
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ !function() {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function() { return module['default']; } :
/******/ function() { return module; };
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ !function() {
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
/******/ }();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
!function() {
"use strict";
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": function() { return /* binding */ jsuites; }
});
;// CONCATENATED MODULE: ./src/utils/dictionary.js
// Update dictionary
var setDictionary = function(d) {
if (! document.dictionary) {
document.dictionary = {}
}
// Replace the key into the dictionary and append the new ones
var t = null;
var i = null;
var k = Object.keys(d);
for (i = 0; i < k.length; i++) {
document.dictionary[k[i]] = d[k[i]];
}
}
// Translate
var translate = function(t) {
if (typeof(document) !== "undefined" && document.dictionary) {
return document.dictionary[t] || t;
} else {
return t;
}
}
/* harmony default export */ var dictionary = ({ setDictionary, translate });
;// CONCATENATED MODULE: ./src/utils/tracking.js
const Tracking = function(component, state) {
if (state === true) {
window['jSuitesStateControl'] = window['jSuitesStateControl'].filter(function(v) {
return v !== null;
});
// Start after all events
setTimeout(function() {
window['jSuitesStateControl'].push(component);
}, 0);
} else {
var index = window['jSuitesStateControl'].indexOf(component);
if (index >= 0) {
window['jSuitesStateControl'].splice(index, 1);
}
}
}
/* harmony default export */ var tracking = (Tracking);
;// CONCATENATED MODULE: ./src/utils/helpers.js
var Helpers = {};
// Two digits
Helpers.two = function(value) {
value = '' + value;
if (value.length == 1) {
value = '0' + value;
}
return value;
}
Helpers.focus = function(el) {
if (el.innerText.length) {
var range = document.createRange();
var sel = window.getSelection();
var node = el.childNodes[el.childNodes.length-1];
range.setStart(node, node.length)
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
el.scrollLeft = el.scrollWidth;
}
}
Helpers.isNumeric = (function (num) {
if (typeof(num) === 'string') {
num = num.trim();
}
return !isNaN(num) && num !== null && num !== '';
});
Helpers.guid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
Helpers.getNode = function() {
var node = document.getSelection().anchorNode;
if (node) {
return (node.nodeType == 3 ? node.parentNode : node);
} else {
return null;
}
}
/**
* Generate hash from a string
*/
Helpers.hash = function(str) {
var hash = 0, i, chr;
if (str.length === 0) {
return hash;
} else {
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
if (chr > 32) {
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
}
}
return hash;
}
/**
* Generate a random color
*/
Helpers.randomColor = function(h) {
var lum = -0.25;
var hex = String('#' + Math.random().toString(16).slice(2, 8).toUpperCase()).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
var rgb = [], c, i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i * 2, 2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb.push(("00" + c).substr(c.length));
}
// Return hex
if (h == true) {
return '#' + Helpers.two(rgb[0].toString(16)) + Helpers.two(rgb[1].toString(16)) + Helpers.two(rgb[2].toString(16));
}
return rgb;
}
Helpers.getWindowWidth = function() {
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth;
return x;
}
Helpers.getWindowHeight = function() {
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
y = w.innerHeight|| e.clientHeight|| g.clientHeight;
return y;
}
Helpers.getPosition = function(e) {
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].pageX;
var y = e.changedTouches[0].pageY;
} else {
var x = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
var y = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
}
return [ x, y ];
}
Helpers.click = function(el) {
if (el.click) {
el.click();
} else {
var evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
el.dispatchEvent(evt);
}
}
Helpers.findElement = function(element, condition) {
var foundElement = false;
function path (element) {
if (element && ! foundElement) {
if (typeof(condition) == 'function') {
foundElement = condition(element)
} else if (typeof(condition) == 'string') {
if (element.classList && element.classList.contains(condition)) {
foundElement = element;
}
}
}
if (element.parentNode && ! foundElement) {
path(element.parentNode);
}
}
path(element);
return foundElement;
}
/* harmony default export */ var helpers = (Helpers);
;// CONCATENATED MODULE: ./src/utils/path.js
function Path(str, val, remove) {
str = str.split('.');
if (str.length) {
let o = this;
let p = null;
while (str.length > 1) {
// Get the property
p = str.shift();
// Check if the property exists
if (o.hasOwnProperty(p)) {
o = o[p];
} else {
// Property does not exists
if (typeof(val) === 'undefined') {
return undefined;
} else {
// Create the property
o[p] = {};
// Next property
o = o[p];
}
}
}
// Get the property
p = str.shift();
// Set or get the value
if (typeof(val) !== 'undefined') {
if (remove === true) {
delete o[p];
} else {
o[p] = val;
}
// Success
return true;
} else {
// Return the value
if (o) {
return o[p];
}
}
}
// Something went wrong
return false;
}
;// CONCATENATED MODULE: ./src/utils/sorting.js
function Sorting(el, options) {
var obj = {};
obj.options = {};
var defaults = {
pointer: null,
direction: null,
ondragstart: null,
ondragend: null,
ondrop: null,
}
var dragElement = null;
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
el.classList.add('jsorting');
el.addEventListener('dragstart', function(e) {
let target = e.target;
if (target.nodeType === 3) {
if (target.parentNode.getAttribute('draggable') === 'true') {
target = target.parentNode;
} else {
e.preventDefault();
e.stopPropagation();
return;
}
}
if (target.getAttribute('draggable') === 'true') {
let position = Array.prototype.indexOf.call(target.parentNode.children, target);
dragElement = {
element: target,
o: position,
d: position
}
target.style.opacity = '0.25';
if (typeof (obj.options.ondragstart) == 'function') {
obj.options.ondragstart(el, target, e);
}
e.dataTransfer.setDragImage(target,0,0);
}
});
el.addEventListener('dragover', function(e) {
e.preventDefault();
if (dragElement) {
if (getElement(e.target)) {
if (e.target.getAttribute('draggable') == 'true' && dragElement.element != e.target) {
if (!obj.options.direction) {
var condition = e.target.clientHeight / 2 > e.offsetY;
} else {
var condition = e.target.clientWidth / 2 > e.offsetX;
}
if (condition) {
e.target.parentNode.insertBefore(dragElement.element, e.target);
} else {
e.target.parentNode.insertBefore(dragElement.element, e.target.nextSibling);
}
dragElement.d = Array.prototype.indexOf.call(e.target.parentNode.children, dragElement.element);
}
}
}
});
el.addEventListener('dragleave', function(e) {
e.preventDefault();
});
el.addEventListener('dragend', function(e) {
e.preventDefault();
if (dragElement) {
if (typeof(obj.options.ondragend) == 'function') {
obj.options.ondragend(el, dragElement.element, e);
}
// Cancelled put element to the original position
if (dragElement.o < dragElement.d) {
e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o]);
} else {
e.target.parentNode.insertBefore(dragElement.element, e.target.parentNode.children[dragElement.o].nextSibling);
}
dragElement.element.style.opacity = '';
dragElement = null;
}
});
el.addEventListener('drop', function(e) {
e.preventDefault();
if (dragElement) {
if (dragElement.o !== dragElement.d) {
if (typeof (obj.options.ondrop) == 'function') {
obj.options.ondrop(el, dragElement.o, dragElement.d, dragElement.element, e.target, e);
}
}
dragElement.element.style.opacity = '';
dragElement = null;
}
});
var getElement = function(element) {
var sorting = false;
function path (element) {
if (element.className) {
if (element.classList.contains('jsorting')) {
sorting = true;
}
}
if (! sorting) {
path(element.parentNode);
}
}
path(element);
return sorting;
}
for (var i = 0; i < el.children.length; i++) {
if (! el.children[i].hasAttribute('draggable')) {
el.children[i].setAttribute('draggable', 'true');
}
}
el.val = function() {
var id = null;
var data = [];
for (var i = 0; i < el.children.length; i++) {
if (id = el.children[i].getAttribute('data-id')) {
data.push(id);
}
}
return data;
}
return el;
}
;// CONCATENATED MODULE: ./src/utils/lazyloading.js
function LazyLoading(el, options) {
var obj = {}
// Mandatory options
if (! options.loadUp || typeof(options.loadUp) != 'function') {
options.loadUp = function() {
return false;
}
}
if (! options.loadDown || typeof(options.loadDown) != 'function') {
options.loadDown = function() {
return false;
}
}
// Timer ms
if (! options.timer) {
options.timer = 100;
}
// Timer
var timeControlLoading = null;
// Controls
var scrollControls = function(e) {
if (timeControlLoading == null) {
var event = false;
var scrollTop = el.scrollTop;
if (el.scrollTop + (el.clientHeight * 2) >= el.scrollHeight) {
if (options.loadDown()) {
if (scrollTop == el.scrollTop) {
el.scrollTop = el.scrollTop - (el.clientHeight);
}
event = true;
}
} else if (el.scrollTop <= el.clientHeight) {
if (options.loadUp()) {
if (scrollTop == el.scrollTop) {
el.scrollTop = el.scrollTop + (el.clientHeight);
}
event = true;
}
}
timeControlLoading = setTimeout(function() {
timeControlLoading = null;
}, options.timer);
if (event) {
if (typeof(options.onupdate) == 'function') {
options.onupdate();
}
}
}
}
// Onscroll
el.onscroll = function(e) {
scrollControls(e);
}
el.onwheel = function(e) {
scrollControls(e);
}
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/ajax.js
function Ajax() {
var Component = (function(options, complete) {
if (Array.isArray(options)) {
// Create multiple request controller
var multiple = {
instance: [],
complete: complete,
}
if (options.length > 0) {
for (var i = 0; i < options.length; i++) {
options[i].multiple = multiple;
multiple.instance.push(Component(options[i]));
}
}
return multiple;
}
if (! options.data) {
options.data = {};
}
if (options.type) {
options.method = options.type;
}
// Default method
if (! options.method) {
options.method = 'GET';
}
// Default type
if (! options.dataType) {
options.dataType = 'json';
}
if (options.data) {
// Parse object to variables format
var parseData = function (value, key) {
var vars = [];
if (value) {
var keys = Object.keys(value);
if (keys.length) {
for (var i = 0; i < keys.length; i++) {
if (key) {
var k = key + '[' + keys[i] + ']';
} else {
var k = keys[i];
}
if (value[k] instanceof FileList) {
vars[k] = value[keys[i]];
} else if (value[keys[i]] === null || value[keys[i]] === undefined) {
vars[k] = '';
} else if (typeof(value[keys[i]]) == 'object') {
var r = parseData(value[keys[i]], k);
var o = Object.keys(r);
for (var j = 0; j < o.length; j++) {
vars[o[j]] = r[o[j]];
}
} else {
vars[k] = value[keys[i]];
}
}
}
}
return vars;
}
var d = parseData(options.data);
var k = Object.keys(d);
// Data form
if (options.method == 'GET') {
if (k.length) {
var data = [];
for (var i = 0; i < k.length; i++) {
data.push(k[i] + '=' + encodeURIComponent(d[k[i]]));
}
if (options.url.indexOf('?') < 0) {
options.url += '?';
}
options.url += data.join('&');
}
} else {
var data = new FormData();
for (var i = 0; i < k.length; i++) {
if (d[k[i]] instanceof FileList) {
if (d[k[i]].length) {
for (var j = 0; j < d[k[i]].length; j++) {
data.append(k[i], d[k[i]][j], d[k[i]][j].name);
}
}
} else {
data.append(k[i], d[k[i]]);
}
}
}
}
var httpRequest = new XMLHttpRequest();
httpRequest.open(options.method, options.url, true);
if (options.requestedWith) {
httpRequest.setRequestHeader('X-Requested-With', options.requestedWith);
} else {
if (options.requestedWith !== false) {
httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
}
}
// Content type
if (options.contentType) {
httpRequest.setRequestHeader('Content-Type', options.contentType);
}
// Headers
if (options.method === 'POST') {
httpRequest.setRequestHeader('Accept', 'application/json');
} else {
if (options.dataType === 'blob') {
httpRequest.responseType = "blob";
} else {
if (! options.contentType) {
if (options.dataType === 'json') {
httpRequest.setRequestHeader('Content-Type', 'text/json');
} else if (options.dataType === 'html') {
httpRequest.setRequestHeader('Content-Type', 'text/html');
}
}
}
}
// No cache
if (options.cache !== true) {
httpRequest.setRequestHeader('pragma', 'no-cache');
httpRequest.setRequestHeader('cache-control', 'no-cache');
}
// Authentication
if (options.withCredentials === true) {
httpRequest.withCredentials = true
}
// Before send
if (typeof(options.beforeSend) == 'function') {
options.beforeSend(httpRequest);
}
// Before send
if (typeof(Component.beforeSend) == 'function') {
Component.beforeSend(httpRequest);
}
if (document.ajax && typeof(document.ajax.beforeSend) == 'function') {
document.ajax.beforeSend(httpRequest);
}
httpRequest.onerror = function() {
if (options.error && typeof(options.error) == 'function') {
options.error({
message: 'Network error: Unable to reach the server.',
status: 0
});
}
}
httpRequest.ontimeout = function() {
if (options.error && typeof(options.error) == 'function') {
options.error({
message: 'Request timed out after ' + httpRequest.timeout + 'ms.',
status: 0
});
}
}
httpRequest.onload = function() {
if (httpRequest.status >= 200 && httpRequest.status < 300) {
if (options.dataType === 'json') {
try {
var result = JSON.parse(httpRequest.responseText);
if (options.success && typeof(options.success) == 'function') {
options.success(result);
}
} catch(err) {
if (options.error && typeof(options.error) == 'function') {
options.error(err, result);
}
}
} else {
if (options.dataType === 'blob') {
var result = httpRequest.response;
} else {
var result = httpRequest.responseText;
}
if (options.success && typeof(options.success) == 'function') {
options.success(result);
}
}
} else {
if (options.error && typeof(options.error) == 'function') {
options.error(httpRequest.responseText, httpRequest.status);
}
}
// Global queue
if (Component.queue && Component.queue.length > 0) {
Component.send(Component.queue.shift());
}
// Global complete method
if (Component.requests && Component.requests.length) {
// Get index of this request in the container
var index = Component.requests.indexOf(httpRequest);
// Remove from the ajax requests container
Component.requests.splice(index, 1);
// Deprecated: Last one?
if (! Component.requests.length) {
// Object event
if (options.complete && typeof(options.complete) == 'function') {
options.complete(result);
}
}
// Group requests
if (options.group) {
if (Component.oncomplete && typeof(Component.oncomplete[options.group]) == 'function') {
if (! Component.pending(options.group)) {
Component.oncomplete[options.group]();
Component.oncomplete[options.group] = null;
}
}
}
// Multiple requests controller
if (options.multiple && options.multiple.instance) {
// Get index of this request in the container
var index = options.multiple.instance.indexOf(httpRequest);
// Remove from the ajax requests container
options.multiple.instance.splice(index, 1);
// If this is the last one call method complete
if (! options.multiple.instance.length) {
if (options.multiple.complete && typeof(options.multiple.complete) == 'function') {
options.multiple.complete(result);
}
}
}
}
}
// Keep the options
httpRequest.options = options;
// Data
httpRequest.data = data;
// Queue
if (options.queue === true && Component.requests.length > 0) {
Component.queue.push(httpRequest);
} else {
Component.send(httpRequest)
}
return httpRequest;
});
Component.send = function(httpRequest) {
if (httpRequest.data) {
if (Array.isArray(httpRequest.data)) {
httpRequest.send(httpRequest.data.join('&'));
} else {
httpRequest.send(httpRequest.data);
}
} else {
httpRequest.send();
}
Component.requests.push(httpRequest);
}
Component.exists = function(url, __callback) {
var http = new XMLHttpRequest();
http.open('HEAD', url, false);
http.send();
if (http.status) {
__callback(http.status);
}
}
Component.pending = function(group) {
var n = 0;
var o = Component.requests;
if (o && o.length) {
for (var i = 0; i < o.length; i++) {
if (! group || group == o[i].options.group) {
n++
}
}
}
return n;
}
Component.oncomplete = {};
Component.requests = [];
Component.queue = [];
return Component
}
/* harmony default export */ var ajax = (Ajax());
;// CONCATENATED MODULE: ./src/plugins/animation.js
function Animation() {
const Component = {
loading: {}
}
Component.loading.show = function(timeout) {
if (! Component.loading.element) {
Component.loading.element = document.createElement('div');
Component.loading.element.className = 'jloading';
}
document.body.appendChild(Component.loading.element);
// Max timeout in seconds
if (timeout > 0) {
setTimeout(function() {
Component.loading.hide();
}, timeout * 1000)
}
}
Component.loading.hide = function() {
if (Component.loading.element && Component.loading.element.parentNode) {
document.body.removeChild(Component.loading.element);
}
}
Component.slideLeft = function (element, direction, done) {
if (direction == true) {
element.classList.add('jslide-left-in');
setTimeout(function () {
element.classList.remove('jslide-left-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-left-out');
setTimeout(function () {
element.classList.remove('jslide-left-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideRight = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-right-in');
setTimeout(function () {
element.classList.remove('jslide-right-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-right-out');
setTimeout(function () {
element.classList.remove('jslide-right-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideTop = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-top-in');
setTimeout(function () {
element.classList.remove('jslide-top-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-top-out');
setTimeout(function () {
element.classList.remove('jslide-top-out');
if (typeof (done) == 'function') {
done();
}
}, 400);
}
}
Component.slideBottom = function (element, direction, done) {
if (direction === true) {
element.classList.add('jslide-bottom-in');
setTimeout(function () {
element.classList.remove('jslide-bottom-in');
if (typeof (done) == 'function') {
done();
}
}, 400);
} else {
element.classList.add('jslide-bottom-out');
setTimeout(function () {
element.classList.remove('jslide-bottom-out');
if (typeof (done) == 'function') {
done();
}
}, 100);
}
}
Component.fadeIn = function (element, done) {
element.style.display = '';
element.classList.add('jfade-in');
setTimeout(function () {
element.classList.remove('jfade-in');
if (typeof (done) == 'function') {
done();
}
}, 2000);
}
Component.fadeOut = function (element, done) {
element.classList.add('jfade-out');
setTimeout(function () {
element.style.display = 'none';
element.classList.remove('jfade-out');
if (typeof (done) == 'function') {
done();
}
}, 1000);
}
return Component;
}
/* harmony default export */ var animation = (Animation());
;// CONCATENATED MODULE: ./src/utils/helpers.date.js
function HelpersDate() {
var Component = {};
Component.now = function (date, dateOnly) {
var y = null;
var m = null;
var d = null;
var h = null;
var i = null;
var s = null;
if (Array.isArray(date)) {
y = date[0];
m = date[1];
d = date[2];
h = date[3];
i = date[4];
s = date[5];
} else {
if (! date) {
date = new Date();
}
y = date.getFullYear();
m = date.getMonth() + 1;
d = date.getDate();
h = date.getHours();
i = date.getMinutes();
s = date.getSeconds();
}
if (dateOnly == true) {
return helpers.two(y) + '-' + helpers.two(m) + '-' + helpers.two(d);
} else {
return helpers.two(y) + '-' + helpers.two(m) + '-' + helpers.two(d) + ' ' + helpers.two(h) + ':' + helpers.two(i) + ':' + helpers.two(s);
}
}
Component.toArray = function (value) {
var date = value.split(((value.indexOf('T') !== -1) ? 'T' : ' '));
var time = date[1];
var date = date[0].split('-');
var y = parseInt(date[0]);
var m = parseInt(date[1]);
var d = parseInt(date[2]);
var h = 0;
var i = 0;
if (time) {
time = time.split(':');
h = parseInt(time[0]);
i = parseInt(time[1]);
}
return [y, m, d, h, i, 0];
}
var excelInitialTime = Date.UTC(1900, 0, 0);
var excelLeapYearBug = Date.UTC(1900, 1, 29);
var millisecondsPerDay = 86400000;
/**
* Date to number
*/
Component.dateToNum = function (jsDate) {
if (typeof (jsDate) === 'string') {
jsDate = new Date(jsDate + ' GMT+0');
}
var jsDateInMilliseconds = jsDate.getTime();
if (jsDateInMilliseconds >= excelLeapYearBug) {
jsDateInMilliseconds += millisecondsPerDay;
}
jsDateInMilliseconds -= excelInitialTime;
return jsDateInMilliseconds / millisecondsPerDay;
}
/**
* Number to date
*
* IMPORTANT: Excel incorrectly considers 1900 to be a leap year
*/
Component.numToDate = function (excelSerialNumber) {
var jsDateInMilliseconds = excelInitialTime + excelSerialNumber * millisecondsPerDay;
if (jsDateInMilliseconds >= excelLeapYearBug) {
jsDateInMilliseconds -= millisecondsPerDay;
}
const d = new Date(jsDateInMilliseconds);
var date = [
d.getUTCFullYear(),
d.getUTCMonth() + 1,
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds(),
];
return Component.now(date);
}
let weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
Object.defineProperty(Component, 'weekdays', {
get: function () {
return weekdays.map(function(v) {
return dictionary.translate(v);
});
},
});
Object.defineProperty(Component, 'weekdaysShort', {
get: function () {
return weekdays.map(function(v) {
return dictionary.translate(v).substring(0,3);
});
},
});
Object.defineProperty(Component, 'months', {
get: function () {
return months.map(function(v) {
return dictionary.translate(v);
});
},
});
Object.defineProperty(Component, 'monthsShort', {
get: function () {
return months.map(function(v) {
return dictionary.translate(v).substring(0,3);
});
},
});
return Component;
}
/* harmony default export */ var helpers_date = (HelpersDate());
;// CONCATENATED MODULE: ./src/plugins/mask.js
function Mask() {
// Currency
var tokens = {
// Text
text: [ '@' ],
// Currency tokens
currency: [ '#(.{1})##0?(.{1}0+)?( ?;(.*)?)?', '#' ],
// Scientific
scientific: [ '0{1}(.{1}0+)?E{1}\\+0+' ],
// Percentage
percentage: [ '0{1}(.{1}0+)?%' ],
// Number
numeric: [ '0{1}(.{1}0+)?' ],
// Data tokens
datetime: [ 'YYYY', 'YYY', 'YY', 'MMMMM', 'MMMM', 'MMM', 'MM', 'DDDDD', 'DDDD', 'DDD', 'DD', 'DY', 'DAY', 'WD', 'D', 'Q', 'MONTH', 'MON', 'HH24', 'HH12', 'HH', '\\[H\\]', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'Y', 'M' ],
// Other
general: [ 'A', '0', '[0-9a-zA-Z\$]+', '.']
}
var getDate = function() {
if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
var m = 0;
if (this.date[4]) {
m = parseFloat(this.date[4] / 60);
}
var v = parseInt(this.date[3]) + m;
v /= 24;
} else if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
v = helpers.two(this.date[3]) + ':' + helpers.two(this.date[4]) + ':' + helpers.two(this.date[5])
} else {
if (this.date[0] && this.date[1] && ! this.date[2]) {
this.date[2] = 1;
}
v = helpers.two(this.date[0]) + '-' + helpers.two(this.date[1]) + '-' + helpers.two(this.date[2]);
if (this.date[3] || this.date[4] || this.date[5]) {
v += ' ' + helpers.two(this.date[3]) + ':' + helpers.two(this.date[4]) + ':' + helpers.two(this.date[5]);
}
}
return v;
}
var extractDate = function() {
var v = '';
if (! (this.date[0] && this.date[1] && this.date[2]) && (this.date[3] || this.date[4])) {
if (this.mask.toLowerCase().indexOf('[h]') !== -1) {
v = parseInt(this.date[3]);
} else {
let h = parseInt(this.date[3]);
if (h < 13 && this.values.indexOf('PM') !== -1) {
v = (h+12) % 24;
} else {
v = h % 24;
}
}
if (this.date[4]) {
v += parseFloat(this.date[4] / 60);
}
if (this.date[5]) {
v += parseFloat(this.date[5] / 3600);
}
v /= 24;
} else if (this.date[0] || this.date[1] || this.date[2] || this.date[3] || this.date[4] || this.date[5]) {
if (this.date[0] && this.date[1] && ! this.date[2]) {
this.date[2] = 1;
}
var t = helpers_date.now(this.date);
v = helpers_date.dateToNum(t);
}
if (isNaN(v)) {
v = '';
}
return v;
}
var isBlank = function(v) {
return v === null || v === '' || v === undefined ? true : false;
}
var isFormula = function(value) {
var v = (''+value)[0];
return v == '=' ? true : false;
}
var isNumeric = function(t) {
return t === 'currency' || t === 'percentage' || t === 'scientific' || t === 'numeric' ? true : false;
}
/**
* Get the decimal defined in the mask configuration
*/
var getDecimal = function(v) {
if (v && Number(v) == v) {
return '.';
} else {
if (this.options.decimal) {
return this.options.decimal;
} else {
if (this.locale) {
var t = Intl.NumberFormat(this.locale).format(1.1);
return this.options.decimal = t[1];
} else {
if (! v) {
v = this.mask;
}
var e = new RegExp('0{1}(.{1})0+', 'ig');
var t = e.exec(v);
if (t && t[1] && t[1].length == 1) {
// Save decimal
this.options.decimal = t[1];
// Return decimal
return t[1];
} else {
// Did not find any decimal last resort the default
var e = new RegExp('#{1}(.{1})#+', 'ig');
var t = e.exec(v);
if (t && t[1] && t[1].length == 1) {
if (t[1] === ',') {
this.options.decimal = '.';
} else {
this.options.decimal = ',';
}
} else {
this.options.decimal = '1.1'.toLocaleString().substring(1,2);
}
}
}
}
}
if (this.options.decimal) {
return this.options.decimal;
} else {
return null;
}
}
var ParseValue = function(v, decimal) {
if (v == '') {
return '';
}
// Get decimal
if (! decimal) {
decimal = getDecimal.call(this);
}
// New value
v = (''+v).split(decimal);
// Signal
var signal = v[0].match(/[-]+/g);
if (signal && signal.length) {
signal = true;
} else {
signal = false;
}
v[0] = v[0].match(/[0-9]+/g);
if (v[0]) {
if (signal) {
v[0].unshift('-');
}
v[0] = v[0].join('');
} else {
if (signal) {
v[0] = '-';
}
}
if (v[0] || v[1]) {
if (v[1] !== undefined) {
v[1] = v[1].match(/[0-9]+/g);
if (v[1]) {
v[1] = v[1].join('');
} else {
v[1] = '';
}
}
} else {
return '';
}
return v;
}
var FormatValue = function(v, event) {
if (v === '') {
return '';
}
// Get decimal
var d = getDecimal.call(this);
// Convert value
var o = this.options;
// Parse value
v = ParseValue.call(this, v);
if (v === '') {
return '';
}
var t = null;
// Temporary value
if (v[0]) {
if (o.style === 'percent') {
t = parseFloat(v[0]) / 100;
} else {
t = parseFloat(v[0] + '.1');
}
}
if ((v[0] === '-' || v[0] === '-00') && ! v[1] && (event && event.inputType == "deleteContentBackward")) {
return '';
}
var n = new Intl.NumberFormat(this.locale, o).format(t);
n = n.split(d);
if (o.style === 'percent') {
if (n[0].indexOf('%') !== -1) {
n[0] = n[0].replace('%', '');
n[2] = '%';
}
}
if (typeof(n[1]) !== 'undefined') {
var s = n[1].replace(/[0-9]*/g, '');
if (s) {
n[2] = s;
}
}
if (v[1] !== undefined) {
n[1] = d + v[1];
} else {
n[1] = '';
}
return n.join('');
}
var Format = function(e, event) {
var v = Value.call(e);
if (! v) {
return;
}
// Get decimal
var n = FormatValue.call(this, v, event);
var t = (n.length) - v.length;
var index = Caret.call(e) + t;
// Set value and update caret
Value.call(e, n, index, true);
}
var Extract = function(v) {
// Keep the raw value
var current = ParseValue.call(this, v);
if (current) {
// Negative values
if (current[0] === '-') {
current[0] = '-0';
}
return parseFloat(current.join('.'));
}
return null;
}
/**
* Caret getter and setter methods
*/
var Caret = function(index, adjustNumeric) {
if (index === undefined) {
if (this.tagName == 'DIV') {
var pos = 0;
var s = window.getSelection();
if (s) {
if (s.rangeCount !== 0) {
var r = s.getRangeAt(0);
var p = r.cloneRange();
p.selectNodeContents(this);
p.setEnd(r.endContainer, r.endOffset);
pos = p.toString().length;
}
}
return pos;
} else {
return this.selectionStart;
}
} else {
// Get the current value
var n = Value.call(this);
// Review the position
if (adjustNumeric) {
var p = null;
for (var i = 0; i < n.length; i++) {
if (n[i].match(/[\-0-9]/g) || n[i] === '.' || n[i] === ',') {
p = i;
}
}
// If the string has no numbers
if (p === null) {
p = n.indexOf(' ');
}
if (index >= p) {
index = p + 1;
}
}
// Do not update caret
if (index > n.length) {
index = n.length;
}
if (index) {
// Set caret
if (this.tagName == 'DIV') {
var s = window.getSelection();
var r = document.createRange();
if (this.childNodes[0]) {
r.setStart(this.childNodes[0], index);
s.removeAllRanges();
s.addRange(r);
}
} else {
this.selectionStart = index;
this.selectionEnd = index;
}
}
}
}
/**
* Value getter and setter method
*/
var Value = function(v, updateCaret, adjustNumeric) {
if (this.tagName == 'DIV') {
if (v === undefined) {
var v = this.innerText;
if (this.value && this.value.length > v.length) {
v = this.value;
}
return v;
} else {
if (this.innerText !== v) {
this.innerText = v;
if (updateCaret) {
Caret.call(this, updateCaret, adjustNumeric);
}
}
}
} else {
if (v === undefined) {
return this.value;
} else {
if (this.value !== v) {
this.value = v;
if (updateCaret) {
Caret.call(this, updateCaret, adjustNumeric);
}
}
}
}
}
// Labels
var weekDaysFull = helpers_date.weekdays;
var weekDays = helpers_date.weekdaysShort;
var monthsFull = helpers_date.months;
var months = helpers_date.monthsShort;
var parser = {
'YEAR': function(v, s) {
var y = ''+new Date().getFullYear();
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
if (parseInt(v) >= 0 && parseInt(v) <= 10) {
if (this.values[this.index].length < s) {
this.values[this.index] += v;
}
}
if (this.values[this.index].length == s) {
if (s == 2) {
var y = y.substr(0,2) + this.values[this.index];
} else if (s == 3) {
var y = y.substr(0,1) + this.values[this.index];
} else if (s == 4) {
var y = this.values[this.index];
}
this.date[0] = y;
this.index++;
}
},
'YYYY': function(v) {
parser.YEAR.call(this, v, 4);
},
'YYY': function(v) {
parser.YEAR.call(this, v, 3);
},
'YY': function(v) {
parser.YEAR.call(this, v, 2);
},
'FIND': function(v, a) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
if (this.event && this.event.inputType && this.event.inputType.indexOf('delete') > -1) {
this.values[this.index] += v;
return;
}
var pos = 0;
var count = 0;
var value = (this.values[this.index] + v).toLowerCase();
for (var i = 0; i < a.length; i++) {
if (a[i].toLowerCase().indexOf(value) == 0) {
pos = i;
count++;
}
}
if (count > 1) {
this.values[this.index] += v;
} else if (count == 1) {
// Jump number of chars
var t = (a[pos].length - this.values[this.index].length) - 1;
this.position += t;
this.values[this.index] = a[pos];
this.index++;
return pos;
}
},
'MMM': function(v) {
var ret = parser.FIND.call(this, v, months);
if (ret !== undefined) {
this.date[1] = ret + 1;
}
},
'MON': function(v) {
parser['MMM'].call(this, v);
},
'MMMM': function(v) {
var ret = parser.FIND.call(this, v, monthsFull);
if (ret !== undefined) {
this.date[1] = ret + 1;
}
},
'MONTH': function(v) {
parser['MMMM'].call(this, v);
},
'MMMMM': function(v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
var pos = 0;
var count = 0;
var value = (this.values[this.index] + v).toLowerCase();
for (var i = 0; i < monthsFull.length; i++) {
if (monthsFull[i][0].toLowerCase().indexOf(value) == 0) {
this.values[this.index] = monthsFull[i][0];
this.date[1] = i + 1;
this.index++;
break;
}
}
},
'MM': function(v) {
if (isBlank(this.values[this.index])) {
if (parseInt(v) > 1 && parseInt(v) < 10) {
this.date[1] = this.values[this.index] = '0' + v;
this.index++;
} else if (parseInt(v) < 2) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 1 && parseInt(v) < 3) {
this.date[1] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
this.date[1] = this.values[this.index] += v;
this.index++;
}
}
},
'M': function(v) {
var test = false;
if (parseInt(v) >= 0 && parseInt(v) < 10) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = v;
if (v > 1) {
this.date[1] = this.values[this.index];
this.index++;
}
} else {
if (this.values[this.index] == 1 && parseInt(v) < 3) {
this.date[1] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] == 0 && parseInt(v) > 0) {
this.date[1] = this.values[this.index] += v;
this.index++;
} else {
var test = true;
}
}
} else {
var test = true;
}
// Re-test
if (test == true) {
var t = parseInt(this.values[this.index]);
if (t > 0 && t < 12) {
this.date[1] = this.values[this.index];
this.index++;
// Repeat the character
this.position--;
}
}
},
'D': function(v) {
var test = false;
if (parseInt(v) >= 0 && parseInt(v) < 10) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = v;
if (parseInt(v) > 3) {
this.date[2] = this.values[this.index];
this.index++;
}
} else {
if (this.values[this.index] == 3 && parseInt(v) < 2) {
this.date[2] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] == 1 || this.values[this.index] == 2) {
this.date[2] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] == 0 && parseInt(v) > 0) {
this.date[2] = this.values[this.index] += v;
this.index++;
} else {
var test = true;
}
}
} else {
var test = true;
}
// Re-test
if (test == true) {
var t = parseInt(this.values[this.index]);
if (t > 0 && t < 32) {
this.date[2] = this.values[this.index];
this.index++;
// Repeat the character
this.position--;
}
}
},
'DD': function(v) {
if (isBlank(this.values[this.index])) {
if (parseInt(v) > 3 && parseInt(v) < 10) {
this.date[2] = this.values[this.index] = '0' + v;
this.index++;
} else if (parseInt(v) < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 3 && parseInt(v) < 2) {
this.date[2] = this.values[this.index] += v;
this.index++;
} else if ((this.values[this.index] == 1 || this.values[this.index] == 2) && parseInt(v) < 10) {
this.date[2] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] == 0 && parseInt(v) > 0 && parseInt(v) < 10) {
this.date[2] = this.values[this.index] += v;
this.index++;
}
}
},
'DDD': function(v) {
parser.FIND.call(this, v, weekDays);
},
'DY': function(v) {
parser['DDD'].call(this, v);
},
'DDDD': function(v) {
parser.FIND.call(this, v, weekDaysFull);
},
'DAY': function(v) {
parser['DDDD'].call(this, v);
},
'HH12': function(v, two) {
if (isBlank(this.values[this.index])) {
if (parseInt(v) > 1 && parseInt(v) < 10) {
if (two) {
v = 0 + v;
}
this.date[3] = this.values[this.index] = v;
this.index++;
} else if (parseInt(v) < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 1 && parseInt(v) < 3) {
this.date[3] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] < 1 && parseInt(v) < 10) {
this.date[3] = this.values[this.index] += v;
this.index++;
}
}
},
'HH24': function(v, two) {
if (parseInt(v) >= 0 && parseInt(v) < 10) {
if (this.values[this.index] == null || this.values[this.index] == '') {
if (parseInt(v) > 2 && parseInt(v) < 10) {
if (two) {
v = 0 + v;
}
this.date[3] = this.values[this.index] = v;
this.index++;
} else if (parseInt(v) < 10) {
this.values[this.index] = v;
}
} else {
if (this.values[this.index] == 2 && parseInt(v) < 4) {
if (! two && this.values[this.index] === '0') {
this.values[this.index] = '';
}
this.date[3] = this.values[this.index] += v;
this.index++;
} else if (this.values[this.index] < 2 && parseInt(v) < 10) {
if (! two && this.values[this.index] === '0') {
this.values[this.index] = '';
}
this.date[3] = this.values[this.index] += v;
this.index++;
}
}
}
},
'HH': function(v) {
parser['HH24'].call(this, v, 1);
},
'H': function(v) {
parser['HH24'].call(this, v, 0);
},
'\\[H\\]': function(v) {
if (this.values[this.index] == undefined) {
this.values[this.index] = '';
}
if (v.match(/[0-9]/g)) {
this.date[3] = this.values[this.index] += v;
} else {
if (this.values[this.index].match(/[0-9]/g)) {
this.date[3] = this.values[this.index];
this.index++;
// Repeat the character
this.position--;
}
}
},
'N60': function(v, i) {
if (this.values[this.index] == null || this.values[this.index] == '') {
if (parseInt(v) > 5 && parseInt(v) < 10) {
this.date[i] = this.values[this.index] = '0' + v;
this.index++;
} else if (parseInt(v) < 10) {
this.values[this.index] = v;
}
} else {
if (parseInt(v) < 10) {
this.date[i] = this.values[this.index] += v;
this.index++;
}
}
},
'MI': function(v) {
parser.N60.call(this, v, 4);
},
'SS': function(v) {
parser.N60.call(this, v, 5);
},
'AM/PM': function(v) {
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
if (this.values[this.index] === '') {
if (v.match(/a/i) && this.date[3] < 13) {
this.values[this.index] += 'A';
} else if (v.match(/p/i)) {
this.values[this.index] += 'P';
}
} else if (this.values[this.index] === 'A' || this.values[this.index] === 'P') {
this.values[this.index] += 'M';
this.index++;
}
},
'WD': function(v) {
if (typeof(this.values[this.index]) === 'undefined') {
this.values[this.index] = '';
}
if (parseInt(v) >= 0 && parseInt(v) < 7) {
this.values[this.index] = v;
}
if (this.values[this.index].length == 1) {
this.index++;
}
},
'0{1}(.{1}0+)?': function(v) {
// Get decimal
var decimal = getDecimal.call(this);
// Negative number
var neg = false;
// Create if is blank
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
} else {
if (this.values[this.index] == '-') {
neg = true;
}
}
var current = ParseValue.call(this, this.values[this.index], decimal);
if (current) {
this.values[this.index] = current.join(decimal);
}
// New entry
if (parseInt(v) >= 0 && parseInt(v) < 10) {
// Replace the zero for a number
if (this.values[this.index] == '0' && v > 0) {
this.values[this.index] = '';
} else if (this.values[this.index] == '-0' && v > 0) {
this.values[this.index] = '-';
}
// Don't add up zeros because does not mean anything here
if ((this.values[this.index] != '0' && this.values[this.index] != '-0') || v == decimal) {
this.values[this.index] += v;
}
} else if (decimal && v == decimal) {
if (this.values[this.index].indexOf(decimal) == -1) {
if (! this.values[this.index]) {
this.values[this.index] = '0';
}
this.values[this.index] += v;
}
} else if (v == '-') {
// Negative signed
neg = true;
}
if (neg === true && this.values[this.index][0] !== '-') {
this.values[this.index] = '-' + this.values[this.index];
}
},
'0{1}(.{1}0+)?E{1}\\+0+': function(v) {
parser['0{1}(.{1}0+)?'].call(this, v);
},
'0{1}(.{1}0+)?%': function(v) {
parser['0{1}(.{1}0+)?'].call(this, v);
if (this.values[this.index].match(/[\-0-9]/g)) {
if (this.values[this.index] && this.values[this.index].indexOf('%') == -1) {
this.values[this.index] += '%';
}
} else {
this.values[this.index] = '';
}
},
'#(.{1})##0?(.{1}0+)?( ?;(.*)?)?': function(v) {
// Parse number
parser['0{1}(.{1}0+)?'].call(this, v);
// Get decimal
var decimal = getDecimal.call(this);
// Get separator
var separator = this.tokens[this.index].substr(1,1);
// Negative
var negative = this.values[this.index][0] === '-' ? true : false;
// Current value
var current = ParseValue.call(this, this.values[this.index], decimal);
// Get main and decimal parts
if (current !== '') {
// Format number
var n = current[0].match(/[0-9]/g);
if (n) {
// Format
n = n.join('');
var t = [];
var s = 0;
for (var j = n.length - 1; j >= 0 ; j--) {
t.push(n[j]);
s++;
if (! (s % 3)) {
t.push(separator);
}
}
t = t.reverse();
current[0] = t.join('');
if (current[0].substr(0,1) == separator) {
current[0] = current[0].substr(1);
}
} else {
current[0] = '';
}
// Value
this.values[this.index] = current.join(decimal);
// Negative
if (negative) {
this.values[this.index] = '-' + this.values[this.index];
}
}
},
'0': function(v) {
if (v.match(/[0-9]/g)) {
this.values[this.index] = v;
this.index++;
}
},
'[0-9a-zA-Z$]+': function(v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
var t = this.tokens[this.index];
var s = this.values[this.index];
var i = s.length;
if (t[i] == v) {
this.values[this.index] += v;
if (this.values[this.index] == t) {
this.index++;
}
} else {
this.values[this.index] = t;
this.index++;
if (v.match(/[\-0-9]/g)) {
// Repeat the character
this.position--;
}
}
},
'A': function(v) {
if (v.match(/[a-zA-Z]/gi)) {
this.values[this.index] = v;
this.index++;
}
},
'.': function(v) {
parser['[0-9a-zA-Z$]+'].call(this, v);
},
'@': function(v) {
if (isBlank(this.values[this.index])) {
this.values[this.index] = '';
}
this.values[this.index] += v;
}
}
/**
* Get the tokens in the mask string
*/
var getTokens = function(str) {
if (this.type == 'general') {
var t = [].concat(tokens.general);
} else {
var t = [].concat(tokens.currency, tokens.datetime, tokens.percentage, tokens.scientific, tokens.numeric, tokens.text, tokens.general);
}
// Expression to extract all tokens from the string
var e = new RegExp(t.join('|'), 'gi');
// Extract
return str.match(e);
}
/**
* Get the method of one given token
*/
var getMethod = function(str) {
if (! this.type) {
var types = Object.keys(tokens);
} else if (this.type == 'text') {
var types = [ 'text' ];
} else if (this.type == 'general') {
var types = [ 'general' ];
} else if (this.type == 'datetime') {
var types = [ 'numeric', 'datetime', 'general' ];
} else {
var types = [ 'currency', 'percentage', 'scientific', 'numeric', 'general' ];
}
// Found
for (var i = 0; i < types.length; i++) {
var type = types[i];
for (var j = 0; j < tokens[type].length; j++) {
var e = new RegExp(tokens[type][j], 'gi');
var r = str.match(e);
if (r) {
return { type: type, method: tokens[type][j] }
}
}
}
}
/**
* Identify each method for each token
*/
var getMethods = function(t) {
var result = [];
for (var i = 0; i < t.length; i++) {
var m = getMethod.call(this, t[i]);
if (m) {
result.push(m.method);
} else {
result.push(null);
}
}
// Compatibility with excel
for (var i = 0; i < result.length; i++) {
if (result[i] == 'MM') {
// Not a month, correct to minutes
if (result[i-1] && result[i-1].indexOf('H') >= 0) {
result[i] = 'MI';
} else if (result[i-2] && result[i-2].indexOf('H') >= 0) {
result[i] = 'MI';
} else if (result[i+1] && result[i+1].indexOf('S') >= 0) {
result[i] = 'MI';
} else if (result[i+2] && result[i+2].indexOf('S') >= 0) {
result[i] = 'MI';
}
}
}
return result;
}
/**
* Get the type for one given token
*/
var getType = function(str) {
var m = getMethod.call(this, str);
if (m) {
var type = m.type;
}
if (type) {
var numeric = 0;
// Make sure the correct type
var t = getTokens.call(this, str);
for (var i = 0; i < t.length; i++) {
m = getMethod.call(this, t[i]);
if (m && isNumeric(m.type)) {
numeric++;
}
}
if (numeric > 1) {
type = 'general';
}
}
return type;
}
/**
* Parse character per character using the detected tokens in the mask
*/
var parse = function() {
// Parser method for this position
if (typeof(parser[this.methods[this.index]]) == 'function') {
parser[this.methods[this.index]].call(this, this.value[this.position]);
this.position++;
} else {
this.values[this.index] = this.tokens[this.index];
this.index++;
}
}
var toPlainString = function(num) {
return (''+ +num).replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/,
function(a,b,c,d,e) {
return e < 0
? b + '0.' + Array(1-e-c.length).join(0) + c + d
: b + c + d + Array(e-d.length+1).join(0);
});
}
/**
* Mask function
* @param {mixed|string} JS input or a string to be parsed
* @param {object|string} When the first param is a string, the second is the mask or object with the mask options
*/
var obj = function(e, config, returnObject) {
// Options
var r = null;
var t = null;
var o = {
// Element
input: null,
// Current value
value: null,
// Mask options
options: {},
// New values for each token found
values: [],
// Token position
index: 0,
// Character position
position: 0,
// Date raw values
date: [0,0,0,0,0,0],
// Raw number for the numeric values
number: 0,
}
// This is a JavaScript Event
if (typeof(e) == 'object') {
// Element
o.input = e.target;
// Current value
o.value = Value.call(e.target);
// Current caret position
o.caret = Caret.call(e.target);
// Mask
if (t = e.target.getAttribute('data-mask')) {
o.mask = t;
}
// Type
if (t = e.target.getAttribute('data-type')) {
o.type = t;
}
// Options
if (e.target.mask) {
if (e.target.mask.options) {
o.options = e.target.mask.options;
}
if (e.target.mask.locale) {
o.locale = e.target.mask.locale;
}
} else {
// Locale
if (t = e.target.getAttribute('data-locale')) {
o.locale = t;
if (o.mask) {
o.options.style = o.mask;
}
}
}
// Extra configuration
if (e.target.attributes && e.target.attributes.length) {
for (var i = 0; i < e.target.attributes.length; i++) {
var k = e.target.attributes[i].name;
var v = e.target.attributes[i].value;
if (k.substr(0,4) == 'data') {
o.options[k.substr(5)] = v;
}
}
}
} else {
// Options
if (typeof(config) == 'string') {
// Mask
o.mask = config;
} else {
// Mask
var k = Object.keys(config);
for (var i = 0; i < k.length; i++) {
o[k[i]] = config[k[i]];
}
}
if (typeof(e) === 'number') {
// Get decimal
getDecimal.call(o, o.mask);
// Replace to the correct decimal
e = (''+e).replace('.', o.options.decimal);
}
// Current
o.value = e;
if (o.input) {
// Value
Value.call(o.input, e);
// Focus
helpers.focus(o.input);
// Caret
o.caret = Caret.call(o.input);
}
}
// Mask detected start the process
if (! isFormula(o.value) && (o.mask || o.locale)) {
// Compatibility fixes
if (o.mask) {
// Remove []
o.mask = o.mask.replace(new RegExp(/\[h]/),'|h|');
o.mask = o.mask.replace(new RegExp(/\[.*?\]/),'');
o.mask = o.mask.replace(new RegExp(/\|h\|/),'[h]');
if (o.mask.indexOf(';') !== -1) {
var t = o.mask.split(';');
o.mask = t[0];
}
// Excel mask TODO: Improve
if (o.mask.indexOf('##') !== -1) {
var d = o.mask.split(';');
if (d[0]) {
if (typeof(e) == 'object') {
d[0] = d[0].replace(new RegExp(/_\)/g), '');
d[0] = d[0].replace(new RegExp(/_\(/g), '');
}
d[0] = d[0].replace('*', '\t');
d[0] = d[0].replace(new RegExp(/_-/g), '');
d[0] = d[0].replace(new RegExp(/_/g), '');
d[0] = d[0].replace(new RegExp(/"/g), '');
d[0] = d[0].replace('##0.###','##0.000');
d[0] = d[0].replace('##0.##','##0.00');
d[0] = d[0].replace('##0.#','##0.0');
d[0] = d[0].replace('##0,###','##0,000');
d[0] = d[0].replace('##0,##','##0,00');
d[0] = d[0].replace('##0,#','##0,0');
}
o.mask = d[0];
}
// Remove back slashes
if (o.mask.indexOf('\\') !== -1) {
var d = o.mask.split(';');
d[0] = d[0].replace(new RegExp(/\\/g), '');
o.mask = d[0];
}
// Get type
if (! o.type) {
o.type = getType.call(o, o.mask);
}
// Get tokens
o.tokens = getTokens.call(o, o.mask);
}
// On new input
if (typeof(e) !== 'object' || ! e.inputType || ! e.inputType.indexOf('insert') || ! e.inputType.indexOf('delete')) {
// Start transformation
if (o.locale) {
if (o.input) {
Format.call(o, o.input, e);
} else {
var newValue = FormatValue.call(o, o.value);
}
} else {
// Get tokens
o.methods = getMethods.call(o, o.tokens);
o.event = e;
// Go through all tokes
while (o.position < o.value.length && typeof(o.tokens[o.index]) !== 'undefined') {
// Get the appropriate parser
parse.call(o);
}
// New value
var newValue = o.values.join('');
// Add tokens to the end of string only if string is not empty
if (isNumeric(o.type) && newValue !== '') {
// Complement things in the end of the mask
while (typeof(o.tokens[o.index]) !== 'undefined') {
var t = getMethod.call(o, o.tokens[o.index]);
if (t && t.type == 'general') {
o.values[o.index] = o.tokens[o.index];
}
o.index++;
}
var adjustNumeric = true;
} else {
var adjustNumeric = false;
}
// New value
newValue = o.values.join('');
// Reset value
if (o.input) {
t = newValue.length - o.value.length;
if (t > 0) {
var caret = o.caret + t;
} else {
var caret = o.caret;
}
Value.call(o.input, newValue, caret, adjustNumeric);
}
}
}
// Update raw data
if (o.input) {
var label = null;
if (isNumeric(o.type)) {
let v = Value.call(o.input);
// Extract the number
o.number = Extract.call(o, v);
// Keep the raw data as a property of the tag
if (o.type == 'percentage' && (''+v).indexOf('%') !== -1) {
label = obj.adjustPrecision(o.number / 100);
} else {
label = o.number;
}
} else if (o.type == 'datetime') {
label = getDate.call(o);
if (o.date[0] && o.date[1] && o.date[2]) {
o.input.setAttribute('data-completed', true);
}
}
if (label) {
o.input.setAttribute('data-value', label);
}
}
if (newValue !== undefined) {
if (returnObject) {
return o;
} else {
return newValue;
}
}
}
}
obj.adjustPrecision = function(num) {
if (typeof num === 'number' && !Number.isInteger(num)) {
const v = num.toString().split('.');
if (v[1] && v[1].length > 10) {
let t0 = 0;
const t1 = v[1][v[1].length - 2];
if (t1 == 0 || t1 == 9) {
for (let i = v[1].length - 2; i > 0; i--) {
if (t0 >= 0 && v[1][i] == t1) {
t0++;
if (t0 > 6) {
break;
}
} else {
t0 = 0;
break;
}
}
if (t0) {
return parseFloat(parseFloat(num).toFixed(v[1].length - 1));
}
}
}
}
return num;
}
// Get the type of the mask
obj.getType = getType;
// Extract the tokens from a mask
obj.prepare = function(str, o) {
if (! o) {
o = {};
}
return getTokens.call(o, str);
}
/**
* Apply the mask to a element (legacy)
*/
obj.apply = function(e) {
var v = Value.call(e.target);
if (e.key.length == 1) {
v += e.key;
}
Value.call(e.target, obj(v, e.target.getAttribute('data-mask')));
}
/**
* Legacy support
*/
obj.run = function(value, mask, decimal) {
return obj(value, { mask: mask, decimal: decimal });
}
/**
* Extract number from masked string
*/
obj.extract = function(v, options, returnObject) {
if (isBlank(v)) {
return v;
}
if (typeof(options) != 'object') {
return v;
} else {
options = Object.assign({}, options);
if (! options.options) {
options.options = {};
}
}
// Compatibility
if (! options.mask && options.format) {
options.mask = options.format;
}
// Remove []
if (options.mask) {
if (options.mask.indexOf(';') !== -1) {
var t = options.mask.split(';');
options.mask = t[0];
}
options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
}
// Get decimal
getDecimal.call(options, options.mask);
var type = null;
var value = null;
if (options.type == 'percent' || options.options.style == 'percent') {
type = 'percentage';
} else if (options.mask) {
type = getType.call(options, options.mask);
}
if (type === 'text') {
var o = {};
value = v;
} else if (type === 'general') {
var o = obj(v, options, true);
value = v;
} else if (type === 'datetime') {
if (v instanceof Date) {
v = obj.getDateString(v, options.mask);
}
var o = obj(v, options, true);
if (helpers.isNumeric(v)) {
value = v;
} else {
value = extractDate.call(o);
}
} else if (type === 'scientific') {
value = v;
if (typeof(v) === 'string') {
value = Number(value);
}
var o = options;
} else {
value = Extract.call(options, v);
// Percentage
if (type === 'percentage' && (''+v).indexOf('%') !== -1) {
value /= 100;
}
var o = options;
}
o.value = value;
if (! o.type && type) {
o.type = type;
}
if (returnObject) {
return o;
} else {
return value;
}
}
/**
* Render
*/
obj.render = function(value, options, fullMask, strict) {
if (isBlank(value)) {
return value;
}
if (typeof(options) != 'object') {
return value;
} else {
options = Object.assign({}, options);
if (! options.options) {
options.options = {};
}
}
// Compatibility
if (! options.mask && options.format) {
options.mask = options.format;
}
// Remove []
if (options.mask) {
if (options.mask.indexOf(';') !== -1) {
var t = options.mask.split(';');
if (! fullMask) {
t[0] = t[0].replace(new RegExp(/_\)/g), '');
t[0] = t[0].replace(new RegExp(/_\(/g), '');
}
options.mask = t[0];
}
options.mask = options.mask.replace(new RegExp(/\[h]/),'|h|');
options.mask = options.mask.replace(new RegExp(/\[.*?\]/),'');
options.mask = options.mask.replace(new RegExp(/\|h\|/),'[h]');
}
var type = null;
if (options.type == 'percent' || options.options.style == 'percent') {
type = 'percentage';
} else if (options.mask) {
type = getType.call(options, options.mask);
} else if (value instanceof Date) {
type = 'datetime';
}
// Fill with blanks
var fillWithBlanks = false;
if (type =='datetime' || options.type == 'calendar') {
var t = obj.getDateString(value, options.mask);
if (t) {
value = t;
}
if (options.mask && fullMask) {
fillWithBlanks = true;
}
} else if (type === 'text') {
// Parse number
if (typeof(value) === 'number') {
value = value.toString();
}
} else {
// Parse number
if (typeof(value) === 'string' && jSuites.isNumeric(value)) {
value = Number(value);
}
// Percentage
if (type === 'percentage') {
value = obj.adjustPrecision(value*100);
}
// Number of decimal places
if (typeof(value) === 'number') {
var t = null;
if (options.mask && fullMask) {
var d = getDecimal.call(options, options.mask);
if (type === 'scientific') {
if (options.mask.indexOf(d) !== -1) {
let exp = options.mask.split('E');
exp = exp[0].split(d);
exp = ('' + exp[1].match(/[0-9]+/g))
exp = exp.length;
t = value.toExponential(exp);
} else {
t = value.toExponential(0);
}
} else {
if (options.mask.indexOf(d) !== -1) {
d = options.mask.split(d);
d = (''+d[1].match(/[0-9]+/g))
d = d.length;
t = value.toFixed(d);
let n = value.toString().split('.');
let fraction = n[1];
if (fraction && fraction.length > d && fraction[fraction.length-1] === '5') {
t = parseFloat(n[0] + '.' + fraction + '1').toFixed(d);
}
} else {
t = value.toFixed(0);
}
// Handle scientific notation
if ((''+t).indexOf('e') !== -1) {
t = toPlainString(t);
}
}
} else if (options.locale && fullMask) {
// Append zeros
var d = (''+value).split('.');
if (options.options) {
if (typeof(d[1]) === 'undefined') {
d[1] = '';
}
var len = d[1].length;
if (options.options.minimumFractionDigits > len) {
for (var i = 0; i < options.options.minimumFractionDigits - len; i++) {
d[1] += '0';
}
}
}
if (! d[1].length) {
t = d[0]
} else {
t = d.join('.');
}
var len = d[1].length;
if (options.options && options.options.maximumFractionDigits < len) {
t = parseFloat(t).toFixed(options.options.maximumFractionDigits);
}
} else {
t = toPlainString(value);
}
if (t !== null) {
value = t;
// Get decimal
getDecimal.call(options, options.mask);
// Replace to the correct decimal
if (options.options.decimal) {
value = value.replace('.', options.options.decimal);
}
}
} else {
if (options.mask && fullMask) {
fillWithBlanks = true;
}
}
}
if (fillWithBlanks) {
var s = options.mask.length - value.length;
if (s > 0) {
for (var i = 0; i < s; i++) {
value += ' ';
}
}
}
if (type === 'scientific') {
if (! fullMask) {
value = toPlainString(value);
} else {
return value;
}
}
if (type === 'numeric' && strict === false && typeof(value) === 'string') {
return value;
}
value = obj(value, options);
// Numeric mask, number of zeros
if (fullMask && type === 'numeric') {
var maskZeros = options.mask.match(new RegExp(/^[0]+$/gm));
if (maskZeros && maskZeros.length === 1) {
var maskLength = maskZeros[0].length;
if (maskLength > 3) {
value = '' + value;
while (value.length < maskLength) {
value = '0' + value;
}
}
}
}
return value;
}
obj.set = function(e, m) {
if (m) {
e.setAttribute('data-mask', m);
// Reset the value
var event = new Event('input', {
bubbles: true,
cancelable: true,
});
e.dispatchEvent(event);
}
}
// Helper to extract date from a string
obj.extractDateFromString = function (date, format) {
var o = obj(date, { mask: format }, true);
// Check if in format Excel (Need difference with format date or type detected is numeric)
if (date > 0 && Number(date) == date && (o.values.join("") !== o.value || o.type == "numeric")) {
var d = new Date(Math.round((date - 25569) * 86400 * 1000));
return d.getFullYear() + "-" + helpers.two(d.getMonth()) + "-" + helpers.two(d.getDate()) + ' 00:00:00';
}
var complete = false;
if (o.values && o.values.length === o.tokens.length && o.values[o.values.length - 1].length >= o.tokens[o.tokens.length - 1].length) {
complete = true;
}
if (o.date[0] && o.date[1] && (o.date[2] || complete)) {
if (!o.date[2]) {
o.date[2] = 1;
}
return o.date[0] + '-' + helpers.two(o.date[1]) + '-' + helpers.two(o.date[2]) + ' ' + helpers.two(o.date[3]) + ':' + helpers.two(o.date[4]) + ':' + helpers.two(o.date[5]);
}
return '';
}
// Helper to convert date into string
obj.getDateString = function (value, options) {
if (!options) {
var options = {};
}
// Labels
if (options && typeof (options) == 'object') {
if (options.format) {
var format = options.format;
} else if (options.mask) {
var format = options.mask;
}
} else {
var format = options;
}
if (!format) {
format = 'YYYY-MM-DD';
}
// Convert to number of hours
if (format.indexOf('[h]') >= 0) {
var result = 0;
if (value && helpers.isNumeric(value)) {
result = parseFloat(24 * Number(value));
if (format.indexOf('mm') >= 0) {
var h = ('' + result).split('.');
if (h[1]) {
var d = 60 * parseFloat('0.' + h[1])
d = parseFloat(d.toFixed(2));
} else {
var d = 0;
}
result = parseInt(h[0]) + ':' + helpers.two(d);
}
}
return result;
}
// Date instance
if (value instanceof Date) {
value = helpers_date.now(value);
} else if (value && helpers.isNumeric(value)) {
value = helpers_date.numToDate(value);
}
// Tokens
var tokens = ['DAY', 'WD', 'DDDD', 'DDD', 'DD', 'D', 'Q', 'HH24', 'HH12', 'HH', 'H', 'AM/PM', 'MI', 'SS', 'MS', 'YYYY', 'YYY', 'YY', 'Y', 'MONTH', 'MON', 'MMMMM', 'MMMM', 'MMM', 'MM', 'M', '.'];
// Expression to extract all tokens from the string
var e = new RegExp(tokens.join('|'), 'gi');
// Extract
var t = format.match(e);
// Compatibility with excel
for (var i = 0; i < t.length; i++) {
if (t[i].toUpperCase() == 'MM') {
// Not a month, correct to minutes
if (t[i - 1] && t[i - 1].toUpperCase().indexOf('H') >= 0) {
t[i] = 'mi';
} else if (t[i - 2] && t[i - 2].toUpperCase().indexOf('H') >= 0) {
t[i] = 'mi';
} else if (t[i + 1] && t[i + 1].toUpperCase().indexOf('S') >= 0) {
t[i] = 'mi';
} else if (t[i + 2] && t[i + 2].toUpperCase().indexOf('S') >= 0) {
t[i] = 'mi';
}
}
}
// Object
var o = {
tokens: t
}
// Value
if (value) {
var d = '' + value;
var splitStr = (d.indexOf('T') !== -1) ? 'T' : ' ';
d = d.split(splitStr);
var h = 0;
var m = 0;
var s = 0;
if (d[1]) {
h = d[1].split(':');
m = h[1] ? h[1] : 0;
s = h[2] ? h[2] : 0;
h = h[0] ? h[0] : 0;
}
d = d[0].split('-');
let day = new Date(d[0], d[1], 0).getDate();
if (d[0] && d[1] && d[2] && d[0] > 0 && d[1] > 0 && d[1] < 13 && d[2] > 0 && d[2] <= day) {
// Data
o.data = [d[0], d[1], d[2], h, m, s];
// Value
o.value = [];
// Calendar instance
var calendar = new Date(o.data[0], o.data[1] - 1, o.data[2], o.data[3], o.data[4], o.data[5]);
// Get method
var get = function (i) {
// Token
var t = this.tokens[i];
// Case token
var s = t.toUpperCase();
var v = null;
if (s === 'YYYY') {
v = this.data[0];
} else if (s === 'YYY') {
v = this.data[0].substring(1, 4);
} else if (s === 'YY') {
v = this.data[0].substring(2, 4);
} else if (s === 'Y') {
v = this.data[0].substring(3, 4);
} else if (t === 'MON') {
v = helpers_date.months[calendar.getMonth()].substr(0, 3).toUpperCase();
} else if (t === 'mon') {
v = helpers_date.months[calendar.getMonth()].substr(0, 3).toLowerCase();
} else if (t === 'MONTH') {
v = helpers_date.months[calendar.getMonth()].toUpperCase();
} else if (t === 'month') {
v = helpers_date.months[calendar.getMonth()].toLowerCase();
} else if (s === 'MMMMM') {
v = helpers_date.months[calendar.getMonth()].substr(0, 1);
} else if (s === 'MMMM' || t === 'Month') {
v = helpers_date.months[calendar.getMonth()];
} else if (s === 'MMM' || t == 'Mon') {
v = helpers_date.months[calendar.getMonth()].substr(0, 3);
} else if (s === 'MM') {
v = helpers.two(this.data[1]);
} else if (s === 'M') {
v = calendar.getMonth() + 1;
} else if (t === 'DAY') {
v = helpers_date.weekdays[calendar.getDay()].toUpperCase();
} else if (t === 'day') {
v = helpers_date.weekdays[calendar.getDay()].toLowerCase();
} else if (s === 'DDDD' || t == 'Day') {
v = helpers_date.weekdays[calendar.getDay()];
} else if (s === 'DDD') {
v = helpers_date.weekdays[calendar.getDay()].substr(0, 3);
} else if (s === 'DD') {
v = helpers.two(this.data[2]);
} else if (s === 'D') {
v = parseInt(this.data[2]);
} else if (s === 'Q') {
v = Math.floor((calendar.getMonth() + 3) / 3);
} else if (s === 'HH24' || s === 'HH') {
v = this.data[3];
if (v > 12 && this.tokens.indexOf('am/pm') !== -1) {
v -= 12;
}
v = helpers.two(v);
} else if (s === 'HH12') {
if (this.data[3] > 12) {
v = helpers.two(this.data[3] - 12);
} else {
v = helpers.two(this.data[3]);
}
} else if (s === 'H') {
v = this.data[3];
if (v > 12 && this.tokens.indexOf('am/pm') !== -1) {
v -= 12;
v = helpers.two(v);
}
} else if (s === 'MI') {
v = helpers.two(this.data[4]);
} else if (s === 'SS') {
v = helpers.two(this.data[5]);
} else if (s === 'MS') {
v = calendar.getMilliseconds();
} else if (s === 'AM/PM') {
if (this.data[3] >= 12) {
v = 'PM';
} else {
v = 'AM';
}
} else if (s === 'WD') {
v = helpers_date.weekdays[calendar.getDay()];
}
if (v === null) {
this.value[i] = this.tokens[i];
} else {
this.value[i] = v;
}
}
for (var i = 0; i < o.tokens.length; i++) {
get.call(o, i);
}
// Put pieces together
value = o.value.join('');
} else {
value = '';
}
}
return value;
}
return obj;
}
/* harmony default export */ var mask = (Mask());
;// CONCATENATED MODULE: ./src/plugins/calendar.js
function Calendar() {
var Component = (function (el, options) {
// Already created, update options
if (el.calendar) {
return el.calendar.setOptions(options, true);
}
// New instance
var obj = {type: 'calendar'};
obj.options = {};
// Date
obj.date = null;
/**
* Update options
*/
obj.setOptions = function (options, reset) {
// Default configuration
var defaults = {
// Render type: [ default | year-month-picker ]
type: 'default',
// Restrictions
validRange: null,
// Starting weekday - 0 for sunday, 6 for saturday
startingDay: null,
// Date format
format: 'DD/MM/YYYY',
// Allow keyboard date entry
readonly: true,
// Today is default
today: false,
// Show timepicker
time: false,
// Show the reset button
resetButton: true,
// Placeholder
placeholder: '',
// Translations can be done here
months: helpers_date.monthsShort,
monthsFull: helpers_date.months,
weekdays: helpers_date.weekdays,
textDone: dictionary.translate('Done'),
textReset: dictionary.translate('Reset'),
textUpdate: dictionary.translate('Update'),
// Value
value: null,
// Fullscreen (this is automatic set for screensize < 800)
fullscreen: false,
// Create the calendar closed as default
opened: false,
// Events
onopen: null,
onclose: null,
onchange: null,
onupdate: null,
// Internal mode controller
mode: null,
position: null,
// Data type
dataType: null,
// Controls
controls: true,
// Auto select
autoSelect: true,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof (obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Reset button
if (obj.options.resetButton == false) {
calendarReset.style.display = 'none';
} else {
calendarReset.style.display = '';
}
// Readonly
if (obj.options.readonly) {
el.setAttribute('readonly', 'readonly');
} else {
el.removeAttribute('readonly');
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('placeholder', obj.options.placeholder);
} else {
el.removeAttribute('placeholder');
}
if (helpers.isNumeric(obj.options.value) && obj.options.value > 0) {
obj.options.value = Component.numToDate(obj.options.value);
// Data type numeric
obj.options.dataType = 'numeric';
}
// Texts
calendarReset.innerHTML = obj.options.textReset;
calendarConfirm.innerHTML = obj.options.textDone;
calendarControlsUpdateButton.innerHTML = obj.options.textUpdate;
// Define mask
if (obj.options.format) {
el.setAttribute('data-mask', obj.options.format.toLowerCase());
}
// Value
if (!obj.options.value && obj.options.today) {
var value = Component.now();
} else {
var value = obj.options.value;
}
// Set internal date
if (value) {
// Force the update
obj.options.value = null;
// New value
obj.setValue(value);
}
return obj;
}
/**
* Open the calendar
*/
obj.open = function (value) {
if (!calendar.classList.contains('jcalendar-focus')) {
if (!calendar.classList.contains('jcalendar-inline')) {
// Current
Component.current = obj;
// Start tracking
tracking(obj, true);
// Create the days
obj.getDays();
// Render months
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
}
// Get time
if (obj.options.time) {
calendarSelectHour.value = obj.date[3];
calendarSelectMin.value = obj.date[4];
}
// Show calendar
calendar.classList.add('jcalendar-focus');
// Get the position of the corner helper
if (helpers.getWindowWidth() < 800 || obj.options.fullscreen) {
calendar.classList.add('jcalendar-fullsize');
// Animation
animation.slideBottom(calendarContent, 1);
} else {
calendar.classList.remove('jcalendar-fullsize');
var rect = el.getBoundingClientRect();
var rectContent = calendarContent.getBoundingClientRect();
if (obj.options.position) {
calendarContainer.style.position = 'fixed';
if (window.innerHeight < rect.bottom + rectContent.height) {
calendarContainer.style.top = (rect.top - (rectContent.height + 2)) + 'px';
} else {
calendarContainer.style.top = (rect.top + rect.height + 2) + 'px';
}
calendarContainer.style.left = rect.left + 'px';
} else {
if (window.innerHeight < rect.bottom + rectContent.height) {
var d = -1 * (rect.height + rectContent.height + 2);
if (d + rect.top < 0) {
d = -1 * (rect.top + rect.height);
}
calendarContainer.style.top = d + 'px';
} else {
calendarContainer.style.top = 2 + 'px';
}
if (window.innerWidth < rect.left + rectContent.width) {
var d = window.innerWidth - (rect.left + rectContent.width + 20);
calendarContainer.style.left = d + 'px';
} else {
calendarContainer.style.left = '0px';
}
}
}
// Events
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
}
}
obj.close = function (ignoreEvents, update) {
if (obj.options.autoSelect !== true && typeof(update) === 'undefined') {
update = false;
}
if (calendar.classList.contains('jcalendar-focus')) {
if (update !== false) {
var element = calendar.querySelector('.jcalendar-selected');
if (typeof (update) == 'string') {
var value = update;
} else if (!element || element.classList.contains('jcalendar-disabled')) {
var value = obj.options.value
} else {
var value = obj.getValue();
}
obj.setValue(value);
} else {
let value = obj.options.value || '';
obj.options.value = null;
obj.setValue(value)
}
// Events
if (!ignoreEvents && typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
// Hide
calendar.classList.remove('jcalendar-focus');
// Stop tracking
tracking(obj, false);
// Current
Component.current = null;
}
return obj.options.value;
}
obj.prev = function () {
// Check if the visualization is the days picker or years picker
if (obj.options.mode == 'years') {
obj.date[0] = obj.date[0] - 12;
// Update picker table of days
obj.getYears();
} else if (obj.options.mode == 'months') {
obj.date[0] = parseInt(obj.date[0]) - 1;
// Update picker table of months
obj.getMonths();
} else {
// Go to the previous month
if (obj.date[1] < 2) {
obj.date[0] = obj.date[0] - 1;
obj.date[1] = 12;
} else {
obj.date[1] = obj.date[1] - 1;
}
// Update picker table of days
obj.getDays();
}
}
obj.next = function () {
// Check if the visualization is the days picker or years picker
if (obj.options.mode == 'years') {
obj.date[0] = parseInt(obj.date[0]) + 12;
// Update picker table of days
obj.getYears();
} else if (obj.options.mode == 'months') {
obj.date[0] = parseInt(obj.date[0]) + 1;
// Update picker table of months
obj.getMonths();
} else {
// Go to the previous month
if (obj.date[1] > 11) {
obj.date[0] = parseInt(obj.date[0]) + 1;
obj.date[1] = 1;
} else {
obj.date[1] = parseInt(obj.date[1]) + 1;
}
// Update picker table of days
obj.getDays();
}
}
/**
* Set today
*/
obj.setToday = function () {
// Today
var value = new Date().toISOString().substr(0, 10);
// Change value
obj.setValue(value);
// Value
return value;
}
obj.setValue = function (val) {
if (!val) {
val = '' + val;
}
// Values
var newValue = val;
var oldValue = obj.options.value;
if (oldValue != newValue) {
// Set label
if (!newValue) {
obj.date = null;
var val = '';
el.classList.remove('jcalendar_warning');
el.title = '';
} else {
var value = obj.setLabel(newValue, obj.options);
var date = newValue.split(' ');
if (!date[1]) {
date[1] = '00:00:00';
}
var time = date[1].split(':')
var date = date[0].split('-');
var y = parseInt(date[0]);
var m = parseInt(date[1]);
var d = parseInt(date[2]);
var h = parseInt(time[0]);
var i = parseInt(time[1]);
obj.date = [y, m, d, h, i, 0];
var val = obj.setLabel(newValue, obj.options);
// Current selection day
var current = Component.now(new Date(y, m - 1, d), true);
// Available ranges
if (obj.options.validRange) {
if (!obj.options.validRange[0] || current >= obj.options.validRange[0]) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1]) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
el.classList.add('jcalendar_warning');
el.title = dictionary.translate('Date outside the valid range');
} else {
el.classList.remove('jcalendar_warning');
el.title = '';
}
} else {
el.classList.remove('jcalendar_warning');
el.title = '';
}
}
// New value
obj.options.value = newValue;
if (typeof (obj.options.onchange) == 'function') {
obj.options.onchange(el, newValue, oldValue);
}
// Lemonade JS
if (el.value != val) {
el.value = val;
if (typeof (el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
if (obj.date) {
obj.getDays();
// Render months
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
}
}
}
obj.getValue = function () {
if (obj.date) {
if (obj.options.time) {
return helpers.two(obj.date[0]) + '-' + helpers.two(obj.date[1]) + '-' + helpers.two(obj.date[2]) + ' ' + helpers.two(obj.date[3]) + ':' + helpers.two(obj.date[4]) + ':' + helpers.two(0);
} else {
return helpers.two(obj.date[0]) + '-' + helpers.two(obj.date[1]) + '-' + helpers.two(obj.date[2]) + ' ' + helpers.two(0) + ':' + helpers.two(0) + ':' + helpers.two(0);
}
} else {
return "";
}
}
/**
* Calendar
*/
obj.update = function (element, v) {
if (element.classList.contains('jcalendar-disabled')) {
// Do nothing
} else {
var elements = calendar.querySelector('.jcalendar-selected');
if (elements) {
elements.classList.remove('jcalendar-selected');
}
element.classList.add('jcalendar-selected');
if (element.classList.contains('jcalendar-set-month')) {
obj.date[1] = v;
obj.date[2] = 1; // first day of the month
} else {
obj.date[2] = element.innerText;
}
if (!obj.options.time) {
obj.close(null, true);
} else {
obj.date[3] = calendarSelectHour.value;
obj.date[4] = calendarSelectMin.value;
}
}
// Update
updateActions();
}
/**
* Set to blank
*/
obj.reset = function () {
// Close calendar
obj.setValue('');
obj.date = null;
obj.close(false, false);
}
/**
* Get calendar days
*/
obj.getDays = function () {
// Mode
obj.options.mode = 'days';
// Setting current values in case of NULLs
var date = new Date();
// Current selection
var year = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
var month = obj.date && helpers.isNumeric(obj.date[1]) ? obj.date[1] : parseInt(date.getMonth()) + 1;
var day = obj.date && helpers.isNumeric(obj.date[2]) ? obj.date[2] : parseInt(date.getDate());
var hour = obj.date && helpers.isNumeric(obj.date[3]) ? obj.date[3] : parseInt(date.getHours());
var min = obj.date && helpers.isNumeric(obj.date[4]) ? obj.date[4] : parseInt(date.getMinutes());
// Selection container
obj.date = [year, month, day, hour, min, 0];
// Update title
calendarLabelYear.innerHTML = year;
calendarLabelMonth.innerHTML = obj.options.months[month - 1];
// Current month and Year
var isCurrentMonthAndYear = (date.getMonth() == month - 1) && (date.getFullYear() == year) ? true : false;
var currentDay = date.getDate();
// Number of days in the month
var date = new Date(year, month, 0, 0, 0);
var numberOfDays = date.getDate();
// First day
var date = new Date(year, month - 1, 0, 0, 0);
var firstDay = date.getDay() + 1;
// Index value
var index = obj.options.startingDay || 0;
// First of day relative to the starting calendar weekday
firstDay = firstDay - index;
// Reset table
calendarBody.innerHTML = '';
// Weekdays Row
var row = document.createElement('tr');
row.setAttribute('align', 'center');
calendarBody.appendChild(row);
// Create weekdays row
for (var i = 0; i < 7; i++) {
var cell = document.createElement('td');
cell.classList.add('jcalendar-weekday')
cell.innerHTML = obj.options.weekdays[index].substr(0, 1);
row.appendChild(cell);
// Next week day
index++;
// Restart index
if (index > 6) {
index = 0;
}
}
// Index of days
var index = 0;
var d = 0;
// Calendar table
for (var j = 0; j < 6; j++) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
row.style.height = '34px';
// Create cells
for (var i = 0; i < 7; i++) {
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-day');
if (index >= firstDay && index < (firstDay + numberOfDays)) {
// Day cell
d++;
cell.innerHTML = d;
// Selected
if (d == day) {
cell.classList.add('jcalendar-selected');
}
// Current selection day is today
if (isCurrentMonthAndYear && currentDay == d) {
cell.style.fontWeight = 'bold';
}
// Current selection day
var current = Component.now(new Date(year, month - 1, d), true);
// Available ranges
if (obj.options.validRange) {
if (!obj.options.validRange[0] || current >= obj.options.validRange[0]) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1]) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
cell.classList.add('jcalendar-disabled');
}
}
}
// Day cell
row.appendChild(cell);
// Index
index++;
}
// Add cell to the calendar body
calendarBody.appendChild(row);
}
// Show time controls
if (obj.options.time) {
calendarControlsTime.style.display = '';
} else {
calendarControlsTime.style.display = 'none';
}
// Update
updateActions();
}
obj.getMonths = function () {
// Mode
obj.options.mode = 'months';
// Loading month labels
var months = obj.options.months;
// Value
var value = obj.options.value;
// Current date
var date = new Date();
var currentYear = parseInt(date.getFullYear());
var currentMonth = parseInt(date.getMonth()) + 1;
var selectedYear = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : currentYear;
var selectedMonth = obj.date && helpers.isNumeric(obj.date[1]) ? obj.date[1] : currentMonth;
// Update title
calendarLabelYear.innerHTML = obj.date[0];
calendarLabelMonth.innerHTML = months[selectedMonth - 1];
// Table
var table = document.createElement('table');
table.setAttribute('width', '100%');
// Row
var row = null;
// Calendar table
for (var i = 0; i < 12; i++) {
if (!(i % 4)) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
table.appendChild(row);
}
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-month');
cell.setAttribute('data-value', i + 1);
cell.innerText = months[i];
if (obj.options.validRange) {
var current = selectedYear + '-' + helpers.two(i + 1);
if (!obj.options.validRange[0] || current >= obj.options.validRange[0].substr(0, 7)) {
var test1 = true;
} else {
var test1 = false;
}
if (!obj.options.validRange[1] || current <= obj.options.validRange[1].substr(0, 7)) {
var test2 = true;
} else {
var test2 = false;
}
if (!(test1 && test2)) {
cell.classList.add('jcalendar-disabled');
}
}
if (i + 1 == selectedMonth) {
cell.classList.add('jcalendar-selected');
}
if (currentYear == selectedYear && i + 1 == currentMonth) {
cell.style.fontWeight = 'bold';
}
row.appendChild(cell);
}
calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
calendarBody.children[0].children[0].appendChild(table);
// Update
updateActions();
}
obj.getYears = function () {
// Mode
obj.options.mode = 'years';
// Current date
var date = new Date();
var currentYear = date.getFullYear();
var selectedYear = obj.date && helpers.isNumeric(obj.date[0]) ? obj.date[0] : parseInt(date.getFullYear());
// Array of years
var y = [];
for (var i = 0; i < 25; i++) {
y[i] = parseInt(obj.date[0]) + (i - 12);
}
// Assembling the year tables
var table = document.createElement('table');
table.setAttribute('width', '100%');
for (var i = 0; i < 25; i++) {
if (!(i % 5)) {
// Reset cells container
var row = document.createElement('tr');
row.setAttribute('align', 'center');
table.appendChild(row);
}
// Create cell
var cell = document.createElement('td');
cell.classList.add('jcalendar-set-year');
cell.innerText = y[i];
if (selectedYear == y[i]) {
cell.classList.add('jcalendar-selected');
}
if (currentYear == y[i]) {
cell.style.fontWeight = 'bold';
}
row.appendChild(cell);
}
calendarBody.innerHTML = '<tr><td colspan="7"></td></tr>';
calendarBody.firstChild.firstChild.appendChild(table);
// Update
updateActions();
}
obj.setLabel = function (value, mixed) {
return Component.getDateString(value, mixed);
}
obj.fromFormatted = function (value, format) {
return Component.extractDateFromString(value, format);
}
var mouseUpControls = function (e) {
var element = helpers.findElement(e.target, 'jcalendar-container');
if (element) {
var action = e.target.className;
// Object id
if (action == 'jcalendar-prev') {
obj.prev();
} else if (action == 'jcalendar-next') {
obj.next();
} else if (action == 'jcalendar-month') {
obj.getMonths();
} else if (action == 'jcalendar-year') {
obj.getYears();
} else if (action == 'jcalendar-set-year') {
obj.date[0] = e.target.innerText;
if (obj.options.type == 'year-month-picker') {
obj.getMonths();
} else {
obj.getDays();
}
} else if (e.target.classList.contains('jcalendar-set-month')) {
var month = parseInt(e.target.getAttribute('data-value'));
if (obj.options.type == 'year-month-picker') {
obj.update(e.target, month);
} else {
obj.date[1] = month;
obj.getDays();
}
} else if (action == 'jcalendar-confirm' || action == 'jcalendar-update' || action == 'jcalendar-close') {
obj.close(null, true);
} else if (action == 'jcalendar-backdrop') {
obj.close(false, false);
} else if (action == 'jcalendar-reset') {
obj.reset();
} else if (e.target.classList.contains('jcalendar-set-day') && e.target.innerText) {
obj.update(e.target);
}
} else {
obj.close(false, false);
}
}
var keyUpControls = function (e) {
if (e.target.value && e.target.value.length > 3) {
var test = Component.extractDateFromString(e.target.value, obj.options.format);
if (test) {
obj.setValue(test);
}
}
}
// Update actions button
var updateActions = function () {
var currentDay = calendar.querySelector('.jcalendar-selected');
if (currentDay && currentDay.classList.contains('jcalendar-disabled')) {
calendarControlsUpdateButton.setAttribute('disabled', 'disabled');
calendarSelectHour.setAttribute('disabled', 'disabled');
calendarSelectMin.setAttribute('disabled', 'disabled');
} else {
calendarControlsUpdateButton.removeAttribute('disabled');
calendarSelectHour.removeAttribute('disabled');
calendarSelectMin.removeAttribute('disabled');
}
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
var calendar = null;
var calendarReset = null;
var calendarConfirm = null;
var calendarContainer = null;
var calendarContent = null;
var calendarLabelYear = null;
var calendarLabelMonth = null;
var calendarTable = null;
var calendarBody = null;
var calendarControls = null;
var calendarControlsTime = null;
var calendarControlsUpdate = null;
var calendarControlsUpdateButton = null;
var calendarSelectHour = null;
var calendarSelectMin = null;
var init = function () {
// Get value from initial element if that is an input
if (el.tagName == 'INPUT' && el.value) {
options.value = el.value;
}
// Calendar DOM elements
calendarReset = document.createElement('div');
calendarReset.className = 'jcalendar-reset';
calendarConfirm = document.createElement('div');
calendarConfirm.className = 'jcalendar-confirm';
calendarControls = document.createElement('div');
calendarControls.className = 'jcalendar-controls'
calendarControls.style.borderBottom = '1px solid #ddd';
calendarControls.appendChild(calendarReset);
calendarControls.appendChild(calendarConfirm);
calendarContainer = document.createElement('div');
calendarContainer.className = 'jcalendar-container';
calendarContent = document.createElement('div');
calendarContent.className = 'jcalendar-content';
calendarContainer.appendChild(calendarContent);
// Main element
if (el.tagName == 'DIV') {
calendar = el;
calendar.classList.add('jcalendar-inline');
} else {
// Add controls to the screen
calendarContent.appendChild(calendarControls);
calendar = document.createElement('div');
calendar.className = 'jcalendar';
}
calendar.classList.add('jcalendar-container');
calendar.appendChild(calendarContainer);
// Table container
var calendarTableContainer = document.createElement('div');
calendarTableContainer.className = 'jcalendar-table';
calendarContent.appendChild(calendarTableContainer);
// Previous button
var calendarHeaderPrev = document.createElement('td');
calendarHeaderPrev.setAttribute('colspan', '2');
calendarHeaderPrev.className = 'jcalendar-prev';
// Header with year and month
calendarLabelYear = document.createElement('span');
calendarLabelYear.className = 'jcalendar-year';
calendarLabelMonth = document.createElement('span');
calendarLabelMonth.className = 'jcalendar-month';
var calendarHeaderTitle = document.createElement('td');
calendarHeaderTitle.className = 'jcalendar-header';
calendarHeaderTitle.setAttribute('colspan', '3');
calendarHeaderTitle.appendChild(calendarLabelMonth);
calendarHeaderTitle.appendChild(calendarLabelYear);
var calendarHeaderNext = document.createElement('td');
calendarHeaderNext.setAttribute('colspan', '2');
calendarHeaderNext.className = 'jcalendar-next';
var calendarHeader = document.createElement('thead');
var calendarHeaderRow = document.createElement('tr');
calendarHeaderRow.appendChild(calendarHeaderPrev);
calendarHeaderRow.appendChild(calendarHeaderTitle);
calendarHeaderRow.appendChild(calendarHeaderNext);
calendarHeader.appendChild(calendarHeaderRow);
calendarTable = document.createElement('table');
calendarBody = document.createElement('tbody');
calendarTable.setAttribute('cellpadding', '0');
calendarTable.setAttribute('cellspacing', '0');
calendarTable.appendChild(calendarHeader);
calendarTable.appendChild(calendarBody);
calendarTableContainer.appendChild(calendarTable);
calendarSelectHour = document.createElement('select');
calendarSelectHour.className = 'jcalendar-select';
calendarSelectHour.onchange = function () {
obj.date[3] = this.value;
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
for (var i = 0; i < 24; i++) {
var element = document.createElement('option');
element.value = i;
element.innerHTML = helpers.two(i);
calendarSelectHour.appendChild(element);
}
calendarSelectMin = document.createElement('select');
calendarSelectMin.className = 'jcalendar-select';
calendarSelectMin.onchange = function () {
obj.date[4] = this.value;
// Event
if (typeof (obj.options.onupdate) == 'function') {
obj.options.onupdate(el, obj.getValue());
}
}
for (var i = 0; i < 60; i++) {
var element = document.createElement('option');
element.value = i;
element.innerHTML = helpers.two(i);
calendarSelectMin.appendChild(element);
}
// Footer controls
var calendarControlsFooter = document.createElement('div');
calendarControlsFooter.className = 'jcalendar-controls';
calendarControlsTime = document.createElement('div');
calendarControlsTime.className = 'jcalendar-time';
calendarControlsTime.style.maxWidth = '140px';
calendarControlsTime.appendChild(calendarSelectHour);
calendarControlsTime.appendChild(calendarSelectMin);
calendarControlsUpdateButton = document.createElement('button');
calendarControlsUpdateButton.setAttribute('type', 'button');
calendarControlsUpdateButton.className = 'jcalendar-update';
calendarControlsUpdate = document.createElement('div');
calendarControlsUpdate.style.flexGrow = '10';
calendarControlsUpdate.appendChild(calendarControlsUpdateButton);
calendarControlsFooter.appendChild(calendarControlsTime);
// Only show the update button for input elements
if (el.tagName == 'INPUT') {
calendarControlsFooter.appendChild(calendarControlsUpdate);
}
calendarContent.appendChild(calendarControlsFooter);
var calendarBackdrop = document.createElement('div');
calendarBackdrop.className = 'jcalendar-backdrop';
calendar.appendChild(calendarBackdrop);
// Handle events
el.addEventListener("keyup", keyUpControls);
// Add global events
calendar.addEventListener("swipeleft", function (e) {
animation.slideLeft(calendarTable, 0, function () {
obj.next();
animation.slideRight(calendarTable, 1);
});
e.preventDefault();
e.stopPropagation();
});
calendar.addEventListener("swiperight", function (e) {
animation.slideRight(calendarTable, 0, function () {
obj.prev();
animation.slideLeft(calendarTable, 1);
});
e.preventDefault();
e.stopPropagation();
});
if ('ontouchend' in document.documentElement === true) {
calendar.addEventListener("touchend", mouseUpControls);
el.addEventListener("touchend", obj.open);
} else {
calendar.addEventListener("mouseup", mouseUpControls);
el.addEventListener("mouseup", obj.open);
}
// Global controls
if (!Component.hasEvents) {
// Execute only one time
Component.hasEvents = true;
// Enter and Esc
document.addEventListener("keydown", Component.keydown);
}
// Set configuration
obj.setOptions(options);
// Append element to the DOM
if (el.tagName == 'INPUT') {
el.parentNode.insertBefore(calendar, el.nextSibling);
// Add properties
el.setAttribute('autocomplete', 'off');
// Element
el.classList.add('jcalendar-input');
// Value
el.value = obj.setLabel(obj.getValue(), obj.options);
} else {
// Get days
obj.getDays();
// Hour
if (obj.options.time) {
calendarSelectHour.value = obj.date[3];
calendarSelectMin.value = obj.date[4];
}
}
// Default opened
if (obj.options.opened == true) {
obj.open();
}
// Controls
if (obj.options.controls == false) {
calendarContainer.classList.add('jcalendar-hide-controls');
}
// Change method
el.change = obj.setValue;
// Global generic value handler
el.val = function (val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.calendar = calendar.calendar = obj;
}
init();
return obj;
});
Component.keydown = function (e) {
var calendar = null;
if (calendar = Component.current) {
if (e.which == 13) {
// ENTER
calendar.close(false, true);
} else if (e.which == 27) {
// ESC
calendar.close(false, false);
}
}
}
Component.prettify = function (d, texts) {
if (!texts) {
var texts = {
justNow: 'Just now',
xMinutesAgo: '{0}m ago',
xHoursAgo: '{0}h ago',
xDaysAgo: '{0}d ago',
xWeeksAgo: '{0}w ago',
xMonthsAgo: '{0} mon ago',
xYearsAgo: '{0}y ago',
}
}
if (d.indexOf('GMT') === -1 && d.indexOf('Z') === -1) {
d += ' GMT';
}
var d1 = new Date();
var d2 = new Date(d);
var total = parseInt((d1 - d2) / 1000 / 60);
String.prototype.format = function (o) {
return this.replace('{0}', o);
}
if (total == 0) {
var text = texts.justNow;
} else if (total < 90) {
var text = texts.xMinutesAgo.format(total);
} else if (total < 1440) { // One day
var text = texts.xHoursAgo.format(Math.round(total / 60));
} else if (total < 20160) { // 14 days
var text = texts.xDaysAgo.format(Math.round(total / 1440));
} else if (total < 43200) { // 30 days
var text = texts.xWeeksAgo.format(Math.round(total / 10080));
} else if (total < 1036800) { // 24 months
var text = texts.xMonthsAgo.format(Math.round(total / 43200));
} else { // 24 months+
var text = texts.xYearsAgo.format(Math.round(total / 525600));
}
return text;
}
Component.prettifyAll = function () {
var elements = document.querySelectorAll('.prettydate');
for (var i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('data-date')) {
elements[i].innerHTML = Component.prettify(elements[i].getAttribute('data-date'));
} else {
if (elements[i].innerHTML) {
elements[i].setAttribute('title', elements[i].innerHTML);
elements[i].setAttribute('data-date', elements[i].innerHTML);
elements[i].innerHTML = Component.prettify(elements[i].innerHTML);
}
}
}
}
Component.now = helpers_date.now;
Component.toArray = helpers_date.toArray;
Component.dateToNum = helpers_date.dateToNum
Component.numToDate = helpers_date.numToDate;
Component.weekdays = helpers_date.weekdays;
Component.months = helpers_date.months;
Component.weekdaysShort = helpers_date.weekdaysShort;
Component.monthsShort = helpers_date.monthsShort;
// Legacy shortcut
Component.extractDateFromString = mask.extractDateFromString;
Component.getDateString = mask.getDateString;
return Component;
}
/* harmony default export */ var calendar = (Calendar());
;// CONCATENATED MODULE: ./src/plugins/palette.js
// More palettes https://coolors.co/ or https://gka.github.io/palettes/#/10|s|003790,005647,ffffe0|ffffe0,ff005e,93003a|1|1
function Palette() {
var palette = {
material: [
["#ffebee", "#fce4ec", "#f3e5f5", "#e8eaf6", "#e3f2fd", "#e0f7fa", "#e0f2f1", "#e8f5e9", "#f1f8e9", "#f9fbe7", "#fffde7", "#fff8e1", "#fff3e0", "#fbe9e7", "#efebe9", "#fafafa", "#eceff1"],
["#ffcdd2", "#f8bbd0", "#e1bee7", "#c5cae9", "#bbdefb", "#b2ebf2", "#b2dfdb", "#c8e6c9", "#dcedc8", "#f0f4c3", "#fff9c4", "#ffecb3", "#ffe0b2", "#ffccbc", "#d7ccc8", "#f5f5f5", "#cfd8dc"],
["#ef9a9a", "#f48fb1", "#ce93d8", "#9fa8da", "#90caf9", "#80deea", "#80cbc4", "#a5d6a7", "#c5e1a5", "#e6ee9c", "#fff59d", "#ffe082", "#ffcc80", "#ffab91", "#bcaaa4", "#eeeeee", "#b0bec5"],
["#e57373", "#f06292", "#ba68c8", "#7986cb", "#64b5f6", "#4dd0e1", "#4db6ac", "#81c784", "#aed581", "#dce775", "#fff176", "#ffd54f", "#ffb74d", "#ff8a65", "#a1887f", "#e0e0e0", "#90a4ae"],
["#ef5350", "#ec407a", "#ab47bc", "#5c6bc0", "#42a5f5", "#26c6da", "#26a69a", "#66bb6a", "#9ccc65", "#d4e157", "#ffee58", "#ffca28", "#ffa726", "#ff7043", "#8d6e63", "#bdbdbd", "#78909c"],
["#f44336", "#e91e63", "#9c27b0", "#3f51b5", "#2196f3", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b"],
["#e53935", "#d81b60", "#8e24aa", "#3949ab", "#1e88e5", "#00acc1", "#00897b", "#43a047", "#7cb342", "#c0ca33", "#fdd835", "#ffb300", "#fb8c00", "#f4511e", "#6d4c41", "#757575", "#546e7a"],
["#d32f2f", "#c2185b", "#7b1fa2", "#303f9f", "#1976d2", "#0097a7", "#00796b", "#388e3c", "#689f38", "#afb42b", "#fbc02d", "#ffa000", "#f57c00", "#e64a19", "#5d4037", "#616161", "#455a64"],
["#c62828", "#ad1457", "#6a1b9a", "#283593", "#1565c0", "#00838f", "#00695c", "#2e7d32", "#558b2f", "#9e9d24", "#f9a825", "#ff8f00", "#ef6c00", "#d84315", "#4e342e", "#424242", "#37474f"],
["#b71c1c", "#880e4f", "#4a148c", "#1a237e", "#0d47a1", "#006064", "#004d40", "#1b5e20", "#33691e", "#827717", "#f57f17", "#ff6f00", "#e65100", "#bf360c", "#3e2723", "#212121", "#263238"],
],
fire: [
["0b1a6d", "840f38", "b60718", "de030b", "ff0c0c", "fd491c", "fc7521", "faa331", "fbb535", "ffc73a"],
["071147", "5f0b28", "930513", "be0309", "ef0000", "fa3403", "fb670b", "f9991b", "faad1e", "ffc123"],
["03071e", "370617", "6a040f", "9d0208", "d00000", "dc2f02", "e85d04", "f48c06", "faa307", "ffba08"],
["020619", "320615", "61040d", "8c0207", "bc0000", "c82a02", "d05203", "db7f06", "e19405", "efab00"],
["020515", "2d0513", "58040c", "7f0206", "aa0000", "b62602", "b94903", "c57205", "ca8504", "d89b00"],
],
baby: [
["eddcd2", "fff1e6", "fde2e4", "fad2e1", "c5dedd", "dbe7e4", "f0efeb", "d6e2e9", "bcd4e6", "99c1de"],
["e1c4b3", "ffd5b5", "fab6ba", "f5a8c4", "aacecd", "bfd5cf", "dbd9d0", "baceda", "9dc0db", "7eb1d5"],
["daa990", "ffb787", "f88e95", "f282a9", "8fc4c3", "a3c8be", "cec9b3", "9dbcce", "82acd2", "649dcb"],
["d69070", "ff9c5e", "f66770", "f05f8f", "74bbb9", "87bfae", "c5b993", "83aac3", "699bca", "4d89c2"],
["c97d5d", "f58443", "eb4d57", "e54a7b", "66a9a7", "78ae9c", "b5a67e", "7599b1", "5c88b7", "4978aa"],
],
chart: [
['#C1D37F', '#4C5454', '#FFD275', '#66586F', '#D05D5B', '#C96480', '#95BF8F', '#6EA240', '#0F0F0E', '#EB8258', '#95A3B3', '#995D81'],
],
}
var Component = function (o) {
// Otherwise get palette value
if (palette[o]) {
return palette[o];
} else {
return palette.material;
}
}
Component.get = function (o) {
// Otherwise get palette value
if (palette[o]) {
return palette[o];
} else {
return palette;
}
}
Component.set = function (o, v) {
palette[o] = v;
}
return Component;
}
/* harmony default export */ var palette = (Palette());
;// CONCATENATED MODULE: ./src/plugins/tabs.js
function Tabs(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
data: [],
position: null,
allowCreate: false,
allowChangePosition: false,
onclick: null,
onload: null,
onchange: null,
oncreate: null,
ondelete: null,
onbeforecreate: null,
onchangeposition: null,
animation: false,
hideHeaders: false,
padding: null,
palette: null,
maxWidth: null,
}
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class
el.classList.add('jtabs');
var prev = null;
var next = null;
var border = null;
// Helpers
const setBorder = function(index) {
if (obj.options.animation) {
setTimeout(function() {
let rect = obj.headers.children[index].getBoundingClientRect();
if (obj.options.palette === 'modern') {
border.style.width = rect.width - 4 + 'px';
border.style.left = obj.headers.children[index].offsetLeft + 2 + 'px';
} else {
border.style.width = rect.width + 'px';
border.style.left = obj.headers.children[index].offsetLeft + 'px';
}
if (obj.options.position === 'bottom') {
border.style.top = '0px';
} else {
border.style.bottom = '0px';
}
}, 50);
}
}
var updateControls = function(x) {
if (typeof(obj.headers.scrollTo) == 'function') {
obj.headers.scrollTo({
left: x,
behavior: 'smooth',
});
} else {
obj.headers.scrollLeft = x;
}
if (x <= 1) {
prev.classList.add('disabled');
} else {
prev.classList.remove('disabled');
}
if (x >= obj.headers.scrollWidth - obj.headers.offsetWidth) {
next.classList.add('disabled');
} else {
next.classList.remove('disabled');
}
if (obj.headers.scrollWidth <= obj.headers.offsetWidth) {
prev.style.display = 'none';
next.style.display = 'none';
} else {
prev.style.display = '';
next.style.display = '';
}
}
obj.setBorder = setBorder;
// Set value
obj.open = function(index) {
// This is to force safari to update the children
const items = Array.from(obj.content.children);
if (! obj.content.children[index]) {
return;
}
var previous = null;
for (var i = 0; i < obj.headers.children.length; i++) {
if (obj.headers.children[i].classList.contains('jtabs-selected')) {
// Current one
previous = i;
}
// Remote selected
obj.headers.children[i].classList.remove('jtabs-selected');
obj.headers.children[i].removeAttribute('aria-selected')
if (obj.content.children[i]) {
obj.content.children[i].classList.remove('jtabs-selected');
}
}
obj.headers.children[index].classList.add('jtabs-selected');
obj.headers.children[index].setAttribute('aria-selected', 'true')
if (obj.content.children[index]) {
obj.content.children[index].classList.add('jtabs-selected');
}
if (previous != index && typeof(obj.options.onchange) == 'function') {
if (obj.content.children[index]) {
obj.options.onchange(el, obj, index, obj.headers.children[index], obj.content.children[index]);
}
}
// Hide
if (obj.options.hideHeaders == true && (obj.headers.children.length < 3 && obj.options.allowCreate == false)) {
obj.headers.parentNode.style.display = 'none';
} else {
obj.headers.parentNode.style.display = '';
var x1 = obj.headers.children[index].offsetLeft;
var x2 = x1 + obj.headers.children[index].offsetWidth;
var r1 = obj.headers.scrollLeft;
var r2 = r1 + obj.headers.offsetWidth;
if (! (r1 <= x1 && r2 >= x2)) {
// Out of the viewport
updateControls(x1 - 1);
}
// Set border
setBorder(index);
}
}
obj.selectIndex = function(a) {
var index = Array.prototype.indexOf.call(obj.headers.children, a);
if (index >= 0) {
obj.open(index);
}
return index;
}
obj.rename = function(i, title) {
if (! title) {
title = prompt('New title', obj.headers.children[i].innerText);
}
obj.headers.children[i].innerText = title;
setBorder(obj.getActive());
}
obj.create = function(title, url) {
if (typeof(obj.options.onbeforecreate) == 'function') {
var ret = obj.options.onbeforecreate(el, title);
if (ret === false) {
return false;
} else {
title = ret;
}
}
var div = obj.appendElement(title);
if (typeof(obj.options.oncreate) == 'function') {
obj.options.oncreate(el, div)
}
setBorder(obj.getActive());
return div;
}
obj.remove = function(index) {
return obj.deleteElement(index);
}
obj.nextNumber = function() {
var num = 0;
for (var i = 0; i < obj.headers.children.length; i++) {
var tmp = obj.headers.children[i].innerText.match(/[0-9].*/);
if (tmp > num) {
num = parseInt(tmp);
}
}
if (! num) {
num = 1;
} else {
num++;
}
return num;
}
obj.deleteElement = function(index) {
let current = obj.getActive();
if (! obj.headers.children[index]) {
return false;
} else {
obj.headers.removeChild(obj.headers.children[index]);
obj.content.removeChild(obj.content.children[index]);
}
if (current === index) {
obj.open(0);
} else {
let current = obj.getActive() || 0;
setBorder(current);
}
if (typeof(obj.options.ondelete) == 'function') {
obj.options.ondelete(el, index)
}
}
obj.appendElement = function(title, cb, openTab, position) {
if (! title) {
var title = prompt('Title?', '');
}
if (title) {
let headerId = helpers.guid();
let contentId = helpers.guid();
// Add content
var div = document.createElement('div');
div.setAttribute('id', contentId);
div.setAttribute('role', 'tabpanel');
div.setAttribute('aria-labelledby', headerId);
// Add headers
var h = document.createElement('div');
h.setAttribute('id', headerId);
h.setAttribute('role', 'tab');
h.setAttribute('aria-controls', contentId);
h.innerHTML = title;
h.content = div;
if (typeof(position) === 'undefined') {
obj.content.appendChild(div);
obj.headers.insertBefore(h, obj.headers.lastChild);
} else {
let r = obj.content.children[position];
if (r) {
obj.content.insertBefore(div, r);
} else {
obj.content.appendChild(div);
}
r = obj.headers.children[position] || obj.headers.lastChild;
obj.headers.insertBefore(h, r);
}
// Sortable
if (obj.options.allowChangePosition) {
h.setAttribute('draggable', 'true');
}
// Open new tab
if (openTab !== false) {
// Open new tab
obj.selectIndex(h);
}
// Callback
if (typeof(cb) == 'function') {
cb(div, h);
}
// Return element
return div;
}
}
obj.getActive = function() {
for (var i = 0; i < obj.headers.children.length; i++) {
if (obj.headers.children[i].classList.contains('jtabs-selected')) {
return i;
}
}
return false;
}
obj.updateContent = function(position, newContent) {
if (typeof newContent !== 'string') {
var contentItem = newContent;
} else {
var contentItem = document.createElement('div');
contentItem.innerHTML = newContent;
}
if (obj.content.children[position].classList.contains('jtabs-selected')) {
newContent.classList.add('jtabs-selected');
}
obj.content.replaceChild(newContent, obj.content.children[position]);
setBorder();
}
obj.updatePosition = function(f, t, ignoreEvents, openTab) {
// Ondrop update position of content
if (f > t) {
obj.content.insertBefore(obj.content.children[f], obj.content.children[t]);
} else {
obj.content.insertBefore(obj.content.children[f], obj.content.children[t].nextSibling);
}
// Open destination tab
if (openTab !== false) {
obj.open(t);
} else {
const activeIndex = obj.getActive();
if (t < activeIndex) {
obj.setBorder(activeIndex);
}
}
// Call event
if (! ignoreEvents && typeof(obj.options.onchangeposition) == 'function') {
obj.options.onchangeposition(obj.headers, f, t);
}
}
obj.move = function(f, t, ignoreEvents, openTab) {
if (f > t) {
obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t]);
} else {
obj.headers.insertBefore(obj.headers.children[f], obj.headers.children[t].nextSibling);
}
obj.updatePosition(f, t, ignoreEvents, openTab);
}
obj.setBorder = setBorder;
obj.init = function() {
el.innerHTML = '';
// Make sure the component is blank
obj.headers = document.createElement('div');
obj.content = document.createElement('div');
obj.headers.classList.add('jtabs-headers');
obj.headers.setAttribute('role', 'tablist');
obj.content.classList.add('jtabs-content');
obj.content.setAttribute('role', 'region');
obj.content.setAttribute('aria-label', 'Tab Panels');
if (obj.options.palette) {
el.classList.add('jtabs-modern');
} else {
el.classList.remove('jtabs-modern');
}
// Padding
if (obj.options.padding) {
obj.content.style.padding = parseInt(obj.options.padding) + 'px';
}
// Header
var header = document.createElement('div');
header.className = 'jtabs-headers-container';
header.appendChild(obj.headers);
if (obj.options.maxWidth) {
header.style.maxWidth = parseInt(obj.options.maxWidth) + 'px';
}
// Controls
var controls = document.createElement('div');
controls.className = 'jtabs-controls';
controls.setAttribute('draggable', 'false');
header.appendChild(controls);
// Append DOM elements
if (obj.options.position == 'bottom') {
el.appendChild(obj.content);
el.appendChild(header);
} else {
el.appendChild(header);
el.appendChild(obj.content);
}
// New button
if (obj.options.allowCreate == true) {
var add = document.createElement('div');
add.className = 'jtabs-add';
add.onclick = function() {
obj.create();
}
controls.appendChild(add);
}
prev = document.createElement('div');
prev.className = 'jtabs-prev';
prev.onclick = function() {
updateControls(obj.headers.scrollLeft - obj.headers.offsetWidth);
}
controls.appendChild(prev);
next = document.createElement('div');
next.className = 'jtabs-next';
next.onclick = function() {
updateControls(obj.headers.scrollLeft + obj.headers.offsetWidth);
}
controls.appendChild(next);
// Data
for (var i = 0; i < obj.options.data.length; i++) {
// Title
if (obj.options.data[i].titleElement) {
var headerItem = obj.options.data[i].titleElement;
} else {
var headerItem = document.createElement('div');
}
// Icon
if (obj.options.data[i].icon) {
var iconContainer = document.createElement('div');
var icon = document.createElement('i');
icon.classList.add('material-icons');
icon.innerHTML = obj.options.data[i].icon;
iconContainer.appendChild(icon);
headerItem.appendChild(iconContainer);
}
// Title
if (obj.options.data[i].title) {
var title = document.createTextNode(obj.options.data[i].title);
headerItem.appendChild(title);
}
// Width
if (obj.options.data[i].width) {
headerItem.style.width = obj.options.data[i].width;
}
// Content
if (obj.options.data[i].contentElement) {
var contentItem = obj.options.data[i].contentElement;
} else {
var contentItem = document.createElement('div');
contentItem.innerHTML = obj.options.data[i].content;
}
obj.headers.appendChild(headerItem);
obj.content.appendChild(contentItem);
}
// Animation
border = document.createElement('div');
border.className = 'jtabs-border';
obj.headers.appendChild(border);
if (obj.options.animation) {
el.classList.add('jtabs-animation');
}
// Events
obj.headers.addEventListener("click", function(e) {
if (e.target.parentNode.classList.contains('jtabs-headers')) {
var target = e.target;
} else {
if (e.target.tagName == 'I') {
var target = e.target.parentNode.parentNode;
} else {
var target = e.target.parentNode;
}
}
var index = obj.selectIndex(target);
if (typeof(obj.options.onclick) == 'function') {
obj.options.onclick(el, obj, index, obj.headers.children[index], obj.content.children[index]);
}
});
obj.headers.addEventListener("contextmenu", function(e) {
obj.selectIndex(e.target);
});
if (obj.headers.children.length) {
// Open first tab
obj.open(0);
}
// Update controls
updateControls(0);
if (obj.options.allowChangePosition == true) {
Sorting(obj.headers, {
direction: 1,
ondrop: function(a,b,c) {
obj.updatePosition(b,c);
},
});
}
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
}
// Loading existing nodes as the data
if (el.children[0] && el.children[0].children.length) {
// Create from existing elements
for (var i = 0; i < el.children[0].children.length; i++) {
var item = obj.options.data && obj.options.data[i] ? obj.options.data[i] : {};
if (el.children[1] && el.children[1].children[i]) {
item.titleElement = el.children[0].children[i];
item.contentElement = el.children[1].children[i];
} else {
item.contentElement = el.children[0].children[i];
}
obj.options.data[i] = item;
}
}
// Remote controller flag
var loadingRemoteData = false;
// Create from data
if (obj.options.data) {
// Append children
for (var i = 0; i < obj.options.data.length; i++) {
if (obj.options.data[i].url) {
ajax({
url: obj.options.data[i].url,
type: 'GET',
dataType: 'text/html',
index: i,
success: function(result) {
obj.options.data[this.index].content = result;
},
complete: function() {
obj.init();
}
});
// Flag loading
loadingRemoteData = true;
}
}
}
if (! loadingRemoteData) {
obj.init();
}
el.tabs = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/color.js
function Color(el, options) {
// Already created, update options
if (el.color) {
return el.color.setOptions(options, true);
}
// New instance
var obj = { type: 'color' };
obj.options = {};
var container = null;
var backdrop = null;
var content = null;
var resetButton = null;
var closeButton = null;
var tabs = null;
var jsuitesTabs = null;
/**
* Update options
*/
obj.setOptions = function(options, reset) {
/**
* @typedef {Object} defaults
* @property {(string|Array)} value - Initial value of the compontent
* @property {string} placeholder - The default instruction text on the element
* @property {requestCallback} onchange - Method to be execute after any changes on the element
* @property {requestCallback} onclose - Method to be execute when the element is closed
* @property {string} doneLabel - Label for button done
* @property {string} resetLabel - Label for button reset
* @property {string} resetValue - Value for button reset
* @property {Bool} showResetButton - Active or note for button reset - default false
*/
var defaults = {
placeholder: '',
value: null,
onopen: null,
onclose: null,
onchange: null,
closeOnChange: true,
palette: null,
position: null,
doneLabel: 'Done',
resetLabel: 'Reset',
fullscreen: false,
opened: false,
}
if (! options) {
options = {};
}
if (options && ! options.palette) {
// Default palette
options.palette = palette();
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Update the text of the controls, if they have already been created
if (resetButton) {
resetButton.innerHTML = obj.options.resetLabel;
}
if (closeButton) {
closeButton.innerHTML = obj.options.doneLabel;
}
// Update the pallete
if (obj.options.palette && jsuitesTabs) {
jsuitesTabs.updateContent(0, table());
}
// Value
if (typeof obj.options.value === 'string') {
el.value = obj.options.value;
if (el.tagName === 'INPUT') {
el.style.color = el.value;
el.style.backgroundColor = el.value;
}
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('placeholder', obj.options.placeholder);
} else {
if (el.getAttribute('placeholder')) {
el.removeAttribute('placeholder');
}
}
return obj;
}
obj.select = function(color) {
// Remove current selected mark
var selected = container.querySelector('.jcolor-selected');
if (selected) {
selected.classList.remove('jcolor-selected');
}
// Mark cell as selected
if (obj.values[color]) {
obj.values[color].classList.add('jcolor-selected');
}
obj.options.value = color;
}
/**
* Open color pallete
*/
obj.open = function() {
if (! container.classList.contains('jcolor-focus')) {
// Start tracking
tracking(obj, true);
// Show color picker
container.classList.add('jcolor-focus');
// Select current color
if (obj.options.value) {
obj.select(obj.options.value);
}
// Reset margin
content.style.marginTop = '';
content.style.marginLeft = '';
var rectContent = content.getBoundingClientRect();
var availableWidth = helpers.getWindowWidth();
var availableHeight = helpers.getWindowHeight();
if (availableWidth < 800 || obj.options.fullscreen == true) {
content.classList.add('jcolor-fullscreen');
animation.slideBottom(content, 1);
backdrop.style.display = 'block';
} else {
if (content.classList.contains('jcolor-fullscreen')) {
content.classList.remove('jcolor-fullscreen');
backdrop.style.display = '';
}
if (obj.options.position) {
content.style.position = 'fixed';
} else {
content.style.position = '';
}
if (rectContent.left + rectContent.width > availableWidth) {
content.style.marginLeft = -1 * (rectContent.left + rectContent.width - (availableWidth - 20)) + 'px';
}
if (rectContent.top + rectContent.height > availableHeight) {
content.style.marginTop = -1 * (rectContent.top + rectContent.height - (availableHeight - 20)) + 'px';
}
}
if (typeof(obj.options.onopen) == 'function') {
obj.options.onopen(el, obj);
}
jsuitesTabs.setBorder(jsuitesTabs.getActive());
// Update sliders
if (obj.options.value) {
var rgb = HexToRgb(obj.options.value);
rgbInputs.forEach(function(rgbInput, index) {
rgbInput.value = rgb[index];
rgbInput.dispatchEvent(new Event('input'));
});
}
}
}
/**
* Close color pallete
*/
obj.close = function(ignoreEvents) {
if (container.classList.contains('jcolor-focus')) {
// Remove focus
container.classList.remove('jcolor-focus');
// Make sure backdrop is hidden
backdrop.style.display = '';
// Call related events
if (! ignoreEvents && typeof(obj.options.onclose) == 'function') {
obj.options.onclose(el, obj);
}
// Stop the object
tracking(obj, false);
}
return obj.options.value;
}
/**
* Set value
*/
obj.setValue = function(color) {
if (! color) {
color = '';
}
if (color != obj.options.value) {
obj.options.value = color;
slidersResult = color;
// Remove current selecded mark
obj.select(color);
// Onchange
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, color, obj);
}
// Changes
if (el.value != obj.options.value) {
// Set input value
el.value = obj.options.value;
if (el.tagName === 'INPUT') {
el.style.color = el.value;
el.style.backgroundColor = el.value;
}
// Element onchange native
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
if (obj.options.closeOnChange == true) {
obj.close();
}
}
}
/**
* Get value
*/
obj.getValue = function() {
return obj.options.value;
}
var backdropClickControl = false;
// Converts a number in decimal to hexadecimal
var decToHex = function(num) {
var hex = num.toString(16);
return hex.length === 1 ? "0" + hex : hex;
}
// Converts a color in rgb to hexadecimal
var rgbToHex = function(r, g, b) {
return "#" + decToHex(r) + decToHex(g) + decToHex(b);
}
// Converts a number in hexadecimal to decimal
var hexToDec = function(hex) {
return parseInt('0x' + hex);
}
// Converts a color in hexadecimal to rgb
var HexToRgb = function(hex) {
return [hexToDec(hex.substr(1, 2)), hexToDec(hex.substr(3, 2)), hexToDec(hex.substr(5, 2))]
}
var table = function() {
// Content of the first tab
var tableContainer = document.createElement('div');
tableContainer.className = 'jcolor-grid';
// Cells
obj.values = [];
// Table pallete
var t = document.createElement('table');
t.setAttribute('cellpadding', '7');
t.setAttribute('cellspacing', '0');
for (var j = 0; j < obj.options.palette.length; j++) {
var tr = document.createElement('tr');
for (var i = 0; i < obj.options.palette[j].length; i++) {
var td = document.createElement('td');
var color = obj.options.palette[j][i];
if (color.length < 7 && color.substr(0,1) !== '#') {
color = '#' + color;
}
td.style.backgroundColor = color;
td.setAttribute('data-value', color);
td.innerHTML = '';
tr.appendChild(td);
// Selected color
if (obj.options.value == color) {
td.classList.add('jcolor-selected');
}
// Possible values
obj.values[color] = td;
}
t.appendChild(tr);
}
// Append to the table
tableContainer.appendChild(t);
return tableContainer;
}
// Canvas where the image will be rendered
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 160;
var context = canvas.getContext("2d");
var resizeCanvas = function() {
// Specifications necessary to correctly obtain colors later in certain positions
var m = tabs.firstChild.getBoundingClientRect();
canvas.width = m.width - 14;
gradient()
}
var gradient = function() {
var g = context.createLinearGradient(0, 0, canvas.width, 0);
// Create color gradient
g.addColorStop(0, "rgb(255,0,0)");
g.addColorStop(0.15, "rgb(255,0,255)");
g.addColorStop(0.33, "rgb(0,0,255)");
g.addColorStop(0.49, "rgb(0,255,255)");
g.addColorStop(0.67, "rgb(0,255,0)");
g.addColorStop(0.84, "rgb(255,255,0)");
g.addColorStop(1, "rgb(255,0,0)");
context.fillStyle = g;
context.fillRect(0, 0, canvas.width, canvas.height);
g = context.createLinearGradient(0, 0, 0, canvas.height);
g.addColorStop(0, "rgba(255,255,255,1)");
g.addColorStop(0.5, "rgba(255,255,255,0)");
g.addColorStop(0.5, "rgba(0,0,0,0)");
g.addColorStop(1, "rgba(0,0,0,1)");
context.fillStyle = g;
context.fillRect(0, 0, canvas.width, canvas.height);
}
var hsl = function() {
var element = document.createElement('div');
element.className = "jcolor-hsl";
var point = document.createElement('div');
point.className = 'jcolor-point';
var div = document.createElement('div');
div.appendChild(canvas);
div.appendChild(point);
element.appendChild(div);
// Moves the marquee point to the specified position
var update = function(buttons, x, y) {
if (buttons === 1) {
var rect = element.getBoundingClientRect();
var left = x - rect.left;
var top = y - rect.top;
if (left < 0) {
left = 0;
}
if (top < 0) {
top = 0;
}
if (left > rect.width) {
left = rect.width;
}
if (top > rect.height) {
top = rect.height;
}
point.style.left = left + 'px';
point.style.top = top + 'px';
var pixel = context.getImageData(left, top, 1, 1).data;
slidersResult = rgbToHex(pixel[0], pixel[1], pixel[2]);
}
}
// Applies the point's motion function to the div that contains it
element.addEventListener('mousedown', function(e) {
update(e.buttons, e.clientX, e.clientY);
});
element.addEventListener('mousemove', function(e) {
update(e.buttons, e.clientX, e.clientY);
});
element.addEventListener('touchmove', function(e) {
update(1, e.changedTouches[0].clientX, e.changedTouches[0].clientY);
});
return element;
}
var slidersResult = '';
var rgbInputs = [];
var changeInputColors = function() {
if (slidersResult !== '') {
for (var j = 0; j < rgbInputs.length; j++) {
var currentColor = HexToRgb(slidersResult);
currentColor[j] = 0;
var newGradient = 'linear-gradient(90deg, rgb(';
newGradient += currentColor.join(', ');
newGradient += '), rgb(';
currentColor[j] = 255;
newGradient += currentColor.join(', ');
newGradient += '))';
rgbInputs[j].style.backgroundImage = newGradient;
}
}
}
var sliders = function() {
// Content of the third tab
var slidersElement = document.createElement('div');
slidersElement.className = 'jcolor-sliders';
var slidersBody = document.createElement('div');
// Creates a range-type input with the specified name
var createSliderInput = function(name) {
var inputContainer = document.createElement('div');
inputContainer.className = 'jcolor-sliders-input-container';
var label = document.createElement('label');
label.innerText = name;
var subContainer = document.createElement('div');
subContainer.className = 'jcolor-sliders-input-subcontainer';
var input = document.createElement('input');
input.type = 'range';
input.min = 0;
input.max = 255;
input.value = 0;
input.setAttribute('aria-label', "Color value");
input.setAttribute('aria-valuemin', "0");
input.setAttribute('aria-valuemax', "255");
input.setAttribute('aria-valuenow', "0");
inputContainer.appendChild(label);
subContainer.appendChild(input);
var value = document.createElement('div');
value.innerText = input.value;
input.addEventListener('input', function() {
value.innerText = input.value;
});
subContainer.appendChild(value);
inputContainer.appendChild(subContainer);
slidersBody.appendChild(inputContainer);
return input;
}
// Creates red, green and blue inputs
rgbInputs = [
createSliderInput('Red'),
createSliderInput('Green'),
createSliderInput('Blue'),
];
slidersElement.appendChild(slidersBody);
// Element that prints the current color
var slidersResultColor = document.createElement('div');
slidersResultColor.className = 'jcolor-sliders-final-color';
var resultElement = document.createElement('div');
resultElement.style.visibility = 'hidden';
resultElement.innerText = 'a';
slidersResultColor.appendChild(resultElement)
// Update the element that prints the current color
var updateResult = function() {
var resultColor = rgbToHex(parseInt(rgbInputs[0].value), parseInt(rgbInputs[1].value), parseInt(rgbInputs[2].value));
resultElement.innerText = resultColor;
resultElement.style.color = resultColor;
resultElement.style.removeProperty('visibility');
slidersResult = resultColor;
}
// Apply the update function to color inputs
rgbInputs.forEach(function(rgbInput) {
rgbInput.addEventListener('input', function() {
updateResult();
changeInputColors();
});
});
slidersElement.appendChild(slidersResultColor);
return slidersElement;
}
var init = function() {
// Initial options
obj.setOptions(options);
// Add a proper input tag when the element is an input
if (el.tagName == 'INPUT') {
el.classList.add('jcolor-input');
el.readOnly = true;
}
// Table container
container = document.createElement('div');
container.className = 'jcolor';
// Table container
backdrop = document.createElement('div');
backdrop.className = 'jcolor-backdrop';
container.appendChild(backdrop);
// Content
content = document.createElement('div');
content.className = 'jcolor-content';
// Controls
var controls = document.createElement('div');
controls.className = 'jcolor-controls';
content.appendChild(controls);
// Reset button
resetButton = document.createElement('div');
resetButton.className = 'jcolor-reset';
resetButton.innerHTML = obj.options.resetLabel;
controls.appendChild(resetButton);
// Close button
closeButton = document.createElement('div');
closeButton.className = 'jcolor-close';
closeButton.innerHTML = obj.options.doneLabel;
controls.appendChild(closeButton);
// Element that will be used to create the tabs
tabs = document.createElement('div');
content.appendChild(tabs);
// Starts the jSuites tabs component
jsuitesTabs = Tabs(tabs, {
animation: true,
data: [
{
title: 'Grid',
contentElement: table(),
},
{
title: 'Spectrum',
contentElement: hsl(),
},
{
title: 'Sliders',
contentElement: sliders(),
}
],
onchange: function(element, instance, index) {
if (index === 1) {
resizeCanvas();
} else {
var color = slidersResult !== '' ? slidersResult : obj.getValue();
if (index === 2 && color) {
var rgb = HexToRgb(color);
rgbInputs.forEach(function(rgbInput, index) {
rgbInput.value = rgb[index];
rgbInput.dispatchEvent(new Event('input'));
});
}
}
},
palette: 'modern',
});
container.appendChild(content);
// Insert picker after the element
if (el.tagName == 'INPUT') {
el.parentNode.insertBefore(container, el.nextSibling);
} else {
el.appendChild(container);
}
container.addEventListener("click", function(e) {
if (e.target.tagName == 'TD') {
var value = e.target.getAttribute('data-value');
if (value) {
obj.setValue(value);
}
} else if (e.target.classList.contains('jcolor-reset')) {
obj.setValue('');
obj.close();
} else if (e.target.classList.contains('jcolor-close')) {
if (jsuitesTabs.getActive() > 0) {
obj.setValue(slidersResult);
}
obj.close();
} else if (e.target.classList.contains('jcolor-backdrop')) {
obj.close();
} else {
obj.open();
}
});
/**
* If element is focus open the picker
*/
el.addEventListener("mouseup", function(e) {
obj.open();
});
// If the picker is open on the spectrum tab, it changes the canvas size when the window size is changed
window.addEventListener('resize', function() {
if (container.classList.contains('jcolor-focus') && jsuitesTabs.getActive() == 1) {
resizeCanvas();
}
});
// Default opened
if (obj.options.opened == true) {
obj.open();
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.color = obj;
// Container shortcut
container.color = obj;
}
obj.toHex = function(rgb) {
var hex = function(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
if (rgb) {
if (/^#[0-9A-F]{6}$/i.test(rgb)) {
return rgb;
} else {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
if (rgb && rgb.length) {
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
} else {
return "";
}
}
}
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/contextmenu.js
function Contextmenu() {
var Component = function(el, options) {
// New instance
var obj = {type: 'contextmenu'};
obj.options = {};
// Default configuration
var defaults = {
items: null,
onclick: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class definition
el.classList.add('jcontextmenu');
/**
* Open contextmenu
*/
obj.open = function (e, items) {
if (items) {
// Update content
obj.options.items = items;
// Create items
obj.create(items);
}
// Close current contextmenu
if (Component.current) {
Component.current.close();
}
// Add to the opened components monitor
tracking(obj, true);
// Show context menu
el.classList.add('jcontextmenu-focus');
// Current
Component.current = obj;
// Coordinates
if ((obj.options.items && obj.options.items.length > 0) || el.children.length) {
if (e.target) {
if (e.changedTouches && e.changedTouches[0]) {
x = e.changedTouches[0].clientX;
y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
} else {
var x = e.x;
var y = e.y;
}
var rect = el.getBoundingClientRect();
if (window.innerHeight < y + rect.height) {
var h = y - rect.height;
if (h < 0) {
h = 0;
}
el.style.top = h + 'px';
} else {
el.style.top = y + 'px';
}
if (window.innerWidth < x + rect.width) {
if (x - rect.width > 0) {
el.style.left = (x - rect.width) + 'px';
} else {
el.style.left = '10px';
}
} else {
el.style.left = x + 'px';
}
}
}
obj.isOpened = function () {
return el.classList.contains('jcontextmenu-focus') ? true : false;
}
/**
* Close menu
*/
obj.close = function () {
if (el.classList.contains('jcontextmenu-focus')) {
el.classList.remove('jcontextmenu-focus');
}
tracking(obj, false);
}
/**
* Create items based on the declared objectd
* @param {object} items - List of object
*/
obj.create = function (items) {
// Update content
el.innerHTML = '';
// Add header contextmenu
var itemHeader = createHeader();
el.appendChild(itemHeader);
// Append items
for (var i = 0; i < items.length; i++) {
var itemContainer = createItemElement(items[i]);
el.appendChild(itemContainer);
}
}
/**
* createHeader for context menu
* @private
* @returns {HTMLElement}
*/
function createHeader() {
var header = document.createElement('div');
header.classList.add("header");
header.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
});
var title = document.createElement('a');
title.classList.add("title");
title.innerHTML = dictionary.translate("Menu");
header.appendChild(title);
var closeButton = document.createElement('a');
closeButton.classList.add("close");
closeButton.innerHTML = dictionary.translate("close");
closeButton.addEventListener("click", function (e) {
obj.close();
});
header.appendChild(closeButton);
return header;
}
/**
* Private function for create a new Item element
* @param {type} item
* @returns {jsuitesL#15.jSuites.contextmenu.createItemElement.itemContainer}
*/
function createItemElement(item) {
if (item.type && (item.type == 'line' || item.type == 'divisor')) {
var itemContainer = document.createElement('hr');
} else {
var itemContainer = document.createElement('div');
var itemText = document.createElement('a');
itemText.innerHTML = item.title;
if (item.tooltip) {
itemContainer.setAttribute('title', item.tooltip);
}
if (item.icon) {
itemContainer.setAttribute('data-icon', item.icon);
}
if (item.id) {
itemContainer.id = item.id;
}
if (item.disabled) {
itemContainer.className = 'jcontextmenu-disabled';
} else if (item.onclick) {
itemContainer.method = item.onclick;
itemContainer.addEventListener("mousedown", function (e) {
e.preventDefault();
});
itemContainer.addEventListener("mouseup", function (e) {
// Execute method
this.method(this, e);
});
}
itemContainer.appendChild(itemText);
if (item.submenu) {
var itemIconSubmenu = document.createElement('span');
itemIconSubmenu.innerHTML = "►";
itemContainer.appendChild(itemIconSubmenu);
itemContainer.classList.add('jcontexthassubmenu');
var el_submenu = document.createElement('div');
// Class definition
el_submenu.classList.add('jcontextmenu');
// Focusable
el_submenu.setAttribute('tabindex', '900');
// Append items
var submenu = item.submenu;
for (var i = 0; i < submenu.length; i++) {
var itemContainerSubMenu = createItemElement(submenu[i]);
el_submenu.appendChild(itemContainerSubMenu);
}
itemContainer.appendChild(el_submenu);
} else if (item.shortcut) {
var itemShortCut = document.createElement('span');
itemShortCut.innerHTML = item.shortcut;
itemContainer.appendChild(itemShortCut);
}
}
return itemContainer;
}
if (typeof (obj.options.onclick) == 'function') {
el.addEventListener('click', function (e) {
obj.options.onclick(obj, e);
});
}
// Create items
if (obj.options.items) {
obj.create(obj.options.items);
}
window.addEventListener("mousewheel", function () {
obj.close();
});
el.contextmenu = obj;
return obj;
}
return Component;
}
/* harmony default export */ var contextmenu = (Contextmenu());
;// CONCATENATED MODULE: ./src/plugins/dropdown.js
function Dropdown() {
var Component = (function (el, options) {
// Already created, update options
if (el.dropdown) {
return el.dropdown.setOptions(options, true);
}
// New instance
var obj = {type: 'dropdown'};
obj.options = {};
// Success
var success = function (data, val) {
// Set data
if (data && data.length) {
// Sort
if (obj.options.sortResults !== false) {
if (typeof obj.options.sortResults == "function") {
data.sort(obj.options.sortResults);
} else {
data.sort(sortData);
}
}
obj.setData(data);
}
// Onload method
if (typeof (obj.options.onload) == 'function') {
obj.options.onload(el, obj, data, val);
}
// Set value
if (val) {
applyValue(val);
}
// Component value
if (val === undefined || val === null) {
obj.options.value = '';
}
el.value = obj.options.value;
// Open dropdown
if (obj.options.opened == true) {
obj.open();
}
}
// Default sort
var sortData = function (itemA, itemB) {
var testA, testB;
if (typeof itemA == "string") {
testA = itemA;
} else {
if (itemA.text) {
testA = itemA.text;
} else if (itemA.name) {
testA = itemA.name;
}
}
if (typeof itemB == "string") {
testB = itemB;
} else {
if (itemB.text) {
testB = itemB.text;
} else if (itemB.name) {
testB = itemB.name;
}
}
if (typeof testA == "string" || typeof testB == "string") {
if (typeof testA != "string") {
testA = "" + testA;
}
if (typeof testB != "string") {
testB = "" + testB;
}
return testA.localeCompare(testB);
} else {
return testA - testB;
}
}
/**
* Reset the options for the dropdown
*/
var resetValue = function () {
// Reset value container
obj.value = {};
// Remove selected
for (var i = 0; i < obj.items.length; i++) {
if (obj.items[i].selected == true) {
if (obj.items[i].element) {
obj.items[i].element.classList.remove('jdropdown-selected')
}
obj.items[i].selected = null;
}
}
// Reset options
obj.options.value = '';
// Reset value
el.value = '';
}
/**
* Apply values to the dropdown
*/
var applyValue = function (values) {
// Reset the current values
resetValue();
// Read values
if (values !== null) {
if (!values) {
if (typeof (obj.value['']) !== 'undefined') {
obj.value[''] = '';
}
} else {
if (!Array.isArray(values)) {
values = ('' + values).split(';');
}
for (var i = 0; i < values.length; i++) {
obj.value[values[i]] = '';
}
}
}
// Update the DOM
for (var i = 0; i < obj.items.length; i++) {
if (typeof (obj.value[Value(i)]) !== 'undefined') {
if (obj.items[i].element) {
obj.items[i].element.classList.add('jdropdown-selected')
}
obj.items[i].selected = true;
// Keep label
obj.value[Value(i)] = Text(i);
}
}
// Global value
obj.options.value = Object.keys(obj.value).join(';');
// Update labels
obj.header.value = obj.getText();
}
// Get the value of one item
var Value = function (k, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'value';
} else {
var property = 'id';
}
if (obj.items[k]) {
if (v !== undefined) {
return obj.items[k].data[property] = v;
} else {
return obj.items[k].data[property];
}
}
return '';
}
// Get the label of one item
var Text = function (k, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'text';
} else {
var property = 'name';
}
if (obj.items[k]) {
if (v !== undefined) {
return obj.items[k].data[property] = v;
} else {
return obj.items[k].data[property];
}
}
return '';
}
var getValue = function () {
return Object.keys(obj.value);
}
var getText = function () {
var data = [];
var k = Object.keys(obj.value);
for (var i = 0; i < k.length; i++) {
data.push(obj.value[k[i]]);
}
return data;
}
obj.setOptions = function (options, reset) {
if (!options) {
options = {};
}
// Default configuration
var defaults = {
url: null,
data: [],
format: 0,
multiple: false,
autocomplete: false,
remoteSearch: false,
lazyLoading: false,
type: null,
width: null,
maxWidth: null,
opened: false,
value: null,
placeholder: '',
newOptions: false,
position: false,
onchange: null,
onload: null,
onopen: null,
onclose: null,
onfocus: null,
onblur: null,
oninsert: null,
onbeforeinsert: null,
onsearch: null,
onbeforesearch: null,
sortResults: false,
autofocus: false,
prompt: null,
allowEmpty: true,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof (obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Force autocomplete search
if (obj.options.remoteSearch == true || obj.options.type === 'searchbar') {
obj.options.autocomplete = true;
}
// New options
if (obj.options.newOptions == true) {
obj.header.classList.add('jdropdown-add');
} else {
obj.header.classList.remove('jdropdown-add');
}
// Autocomplete
if (obj.options.autocomplete == true) {
obj.header.removeAttribute('readonly');
} else {
obj.header.setAttribute('readonly', 'readonly');
}
// Place holder
if (obj.options.placeholder) {
obj.header.setAttribute('placeholder', obj.options.placeholder);
} else {
obj.header.removeAttribute('placeholder');
}
// Remove specific dropdown typing to add again
el.classList.remove('jdropdown-searchbar');
el.classList.remove('jdropdown-picker');
el.classList.remove('jdropdown-list');
if (obj.options.type == 'searchbar') {
el.classList.add('jdropdown-searchbar');
} else if (obj.options.type == 'list') {
el.classList.add('jdropdown-list');
} else if (obj.options.type == 'picker') {
el.classList.add('jdropdown-picker');
} else {
if (helpers.getWindowWidth() < 800) {
if (obj.options.autocomplete) {
el.classList.add('jdropdown-searchbar');
obj.options.type = 'searchbar';
} else {
el.classList.add('jdropdown-picker');
obj.options.type = 'picker';
}
} else {
if (obj.options.width) {
el.style.width = obj.options.width;
el.style.minWidth = obj.options.width;
} else {
el.style.removeProperty('width');
el.style.removeProperty('min-width');
}
el.classList.add('jdropdown-default');
obj.options.type = 'default';
}
}
// Close button
if (obj.options.type == 'searchbar') {
containerHeader.appendChild(closeButton);
} else {
container.insertBefore(closeButton, container.firstChild);
}
// Load the content
if (obj.options.url && !options.data) {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
success: function (data) {
if (data) {
success(data, obj.options.value);
}
}
});
} else {
success(obj.options.data, obj.options.value);
}
// Return the instance
return obj;
}
// Helpers
var containerHeader = null;
var container = null;
var content = null;
var closeButton = null;
var resetButton = null;
var backdrop = null;
var keyTimer = null;
/**
* Init dropdown
*/
var init = function () {
// Do not accept null
if (!options) {
options = {};
}
// If the element is a SELECT tag, create a configuration object
if (el.tagName == 'SELECT') {
var ret = Component.extractFromDom(el, options);
el = ret.el;
options = ret.options;
}
// Place holder
if (!options.placeholder && el.getAttribute('placeholder')) {
options.placeholder = el.getAttribute('placeholder');
}
// Value container
obj.value = {};
// Containers
obj.items = [];
obj.groups = [];
// Search options
obj.search = '';
obj.results = null;
// Create dropdown
el.classList.add('jdropdown');
// Header container
containerHeader = document.createElement('div');
containerHeader.className = 'jdropdown-container-header';
// Header
obj.header = document.createElement('input');
obj.header.className = 'jdropdown-header jss_object';
obj.header.type = 'text';
obj.header.setAttribute('autocomplete', 'off');
obj.header.onfocus = function () {
if (typeof (obj.options.onfocus) == 'function') {
obj.options.onfocus(el);
}
}
obj.header.onblur = function () {
if (typeof (obj.options.onblur) == 'function') {
obj.options.onblur(el);
}
}
obj.header.onkeyup = function (e) {
if (obj.options.autocomplete == true && !keyTimer) {
if (obj.search != obj.header.value.trim()) {
keyTimer = setTimeout(function () {
obj.find(obj.header.value.trim());
keyTimer = null;
}, 400);
}
if (!el.classList.contains('jdropdown-focus')) {
obj.open();
}
} else {
if (!obj.options.autocomplete) {
obj.next(e.key);
}
}
}
// Global controls
if (!Component.hasEvents) {
// Execute only one time
Component.hasEvents = true;
// Enter and Esc
document.addEventListener("keydown", Component.keydown);
}
// Container
container = document.createElement('div');
container.className = 'jdropdown-container';
// Dropdown content
content = document.createElement('div');
content.className = 'jdropdown-content';
// Close button
closeButton = document.createElement('div');
closeButton.className = 'jdropdown-close';
closeButton.textContent = 'Done';
// Reset button
resetButton = document.createElement('div');
resetButton.className = 'jdropdown-reset';
resetButton.textContent = 'x';
resetButton.onclick = function () {
obj.reset();
obj.close();
}
// Create backdrop
backdrop = document.createElement('div');
backdrop.className = 'jdropdown-backdrop';
// Append elements
containerHeader.appendChild(obj.header);
container.appendChild(content);
el.appendChild(containerHeader);
el.appendChild(container);
el.appendChild(backdrop);
// Set the otiptions
obj.setOptions(options);
if ('ontouchsend' in document.documentElement === true) {
el.addEventListener('touchsend', Component.mouseup);
} else {
el.addEventListener('mouseup', Component.mouseup);
}
// Lazyloading
if (obj.options.lazyLoading == true) {
LazyLoading(content, {
loadUp: obj.loadUp,
loadDown: obj.loadDown,
});
}
content.onwheel = function (e) {
e.stopPropagation();
}
// Change method
el.change = obj.setValue;
// Global generic value handler
el.val = function (val) {
if (val === undefined) {
return obj.getValue(obj.options.multiple ? true : false);
} else {
obj.setValue(val);
}
}
// Keep object available from the node
el.dropdown = obj;
}
/**
* Get the current remote source of data URL
*/
obj.getUrl = function () {
return obj.options.url;
}
/**
* Set the new data from a remote source
* @param {string} url - url from the remote source
* @param {function} callback - callback when the data is loaded
*/
obj.setUrl = function (url, callback) {
obj.options.url = url;
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
success: function (data) {
obj.setData(data);
// Callback
if (typeof (callback) == 'function') {
callback(obj);
}
}
});
}
/**
* Set ID for one item
*/
obj.setId = function (item, v) {
// Legacy purposes
if (!obj.options.format) {
var property = 'value';
} else {
var property = 'id';
}
if (typeof (item) == 'object') {
item[property] = v;
} else {
obj.items[item].data[property] = v;
}
}
const add = function(title, id) {
if (! title) {
let current = obj.options.autocomplete == true ? obj.header.value : '';
title = prompt(dictionary.translate('Add A New Option'), current);
if (! title) {
return false;
}
}
// Id
if (! id) {
id = helpers.guid();
}
// Create new item
if (!obj.options.format) {
var item = {
value: id,
text: title,
}
} else {
var item = {
id: id,
name: title,
}
}
// Callback
if (typeof (obj.options.onbeforeinsert) == 'function') {
let ret = obj.options.onbeforeinsert(obj, item);
if (ret === false) {
return false;
} else if (ret) {
item = ret;
}
}
// Add item to the main list
obj.options.data.push(item);
// Create DOM
var newItem = obj.createItem(item);
// Append DOM to the list
content.appendChild(newItem.element);
// Callback
if (typeof (obj.options.oninsert) == 'function') {
obj.options.oninsert(obj, item, newItem);
}
// Show content
if (content.style.display == 'none') {
content.style.display = '';
}
// Search?
if (obj.results) {
obj.results.push(newItem);
}
return item;
}
/**
* Add a new item
* @param {string} title - title of the new item
* @param {string} id - value/id of the new item
*/
obj.add = function (title, id) {
if (typeof (obj.options.prompt) == 'function') {
return obj.options.prompt.call(obj, add);
}
return add(title, id);
}
/**
* Create a new item
*/
obj.createItem = function (data, group, groupName) {
// Keep the correct source of data
if (!obj.options.format) {
if (!data.value && data.id !== undefined) {
data.value = data.id;
//delete data.id;
}
if (!data.text && data.name !== undefined) {
data.text = data.name;
//delete data.name;
}
} else {
if (!data.id && data.value !== undefined) {
data.id = data.value;
//delete data.value;
}
if (!data.name && data.text !== undefined) {
data.name = data.text
//delete data.text;
}
}
// Create item
var item = {};
item.element = document.createElement('div');
item.element.className = 'jdropdown-item';
item.element.indexValue = obj.items.length;
item.data = data;
// Groupd DOM
if (group) {
item.group = group;
}
// Id
if (data.id) {
item.element.setAttribute('id', data.id);
}
// Disabled
if (data.disabled == true) {
item.element.setAttribute('data-disabled', true);
}
// Tooltip
if (data.tooltip) {
item.element.setAttribute('title', data.tooltip);
}
// Image
if (data.image) {
var image = document.createElement('img');
image.className = 'jdropdown-image';
image.src = data.image;
if (!data.title) {
image.classList.add('jdropdown-image-small');
}
item.element.appendChild(image);
} else if (data.icon) {
var icon = document.createElement('span');
icon.className = "jdropdown-icon material-icons";
icon.innerText = data.icon;
if (!data.title) {
icon.classList.add('jdropdown-icon-small');
}
if (data.color) {
icon.style.color = data.color;
}
item.element.appendChild(icon);
} else if (data.color) {
var color = document.createElement('div');
color.className = 'jdropdown-color';
color.style.backgroundColor = data.color;
item.element.appendChild(color);
}
// Set content
if (!obj.options.format) {
var text = data.text;
} else {
var text = data.name;
}
var node = document.createElement('div');
node.className = 'jdropdown-description';
node.textContent = text || ' ';
// Title
if (data.title) {
var title = document.createElement('div');
title.className = 'jdropdown-title';
title.innerText = data.title;
node.appendChild(title);
}
// Set content
if (!obj.options.format) {
var val = data.value;
} else {
var val = data.id;
}
// Value
if (obj.value[val]) {
item.element.classList.add('jdropdown-selected');
item.selected = true;
}
// Keep DOM accessible
obj.items.push(item);
// Add node to item
item.element.appendChild(node);
return item;
}
obj.appendData = function (data) {
// Create elements
if (data.length) {
// Helpers
var items = [];
var groups = [];
// Prepare data
for (var i = 0; i < data.length; i++) {
// Process groups
if (data[i].group) {
if (!groups[data[i].group]) {
groups[data[i].group] = [];
}
groups[data[i].group].push(i);
} else {
items.push(i);
}
}
// Number of items counter
var counter = 0;
// Groups
var groupNames = Object.keys(groups);
// Append groups in case exists
if (groupNames.length > 0) {
for (var i = 0; i < groupNames.length; i++) {
// Group container
var group = document.createElement('div');
group.className = 'jdropdown-group';
// Group name
var groupName = document.createElement('div');
groupName.className = 'jdropdown-group-name';
groupName.textContent = groupNames[i];
// Group arrow
var groupArrow = document.createElement('i');
groupArrow.className = 'jdropdown-group-arrow jdropdown-group-arrow-down';
groupName.appendChild(groupArrow);
// Group items
var groupContent = document.createElement('div');
groupContent.className = 'jdropdown-group-items';
for (var j = 0; j < groups[groupNames[i]].length; j++) {
var item = obj.createItem(data[groups[groupNames[i]][j]], group, groupNames[i]);
if (obj.options.lazyLoading == false || counter < 200) {
groupContent.appendChild(item.element);
counter++;
}
}
// Group itens
group.appendChild(groupName);
group.appendChild(groupContent);
// Keep group DOM
obj.groups.push(group);
// Only add to the screen if children on the group
if (groupContent.children.length > 0) {
// Add DOM to the content
content.appendChild(group);
}
}
}
if (items.length) {
for (var i = 0; i < items.length; i++) {
var item = obj.createItem(data[items[i]]);
if (obj.options.lazyLoading == false || counter < 200) {
content.appendChild(item.element);
counter++;
}
}
}
}
}
obj.setData = function (data) {
// Reset current value
resetValue();
// Make sure the content container is blank
content.textContent = '';
// Reset
obj.header.value = '';
// Reset items and values
obj.items = [];
// Prepare data
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
// Compatibility
if (typeof (data[i]) != 'object') {
// Correct format
if (!obj.options.format) {
data[i] = {
value: data[i],
text: data[i]
}
} else {
data[i] = {
id: data[i],
name: data[i]
}
}
}
}
// Append data
obj.appendData(data);
// Update data
obj.options.data = data;
} else {
// Update data
obj.options.data = [];
}
obj.close();
}
obj.getData = function () {
return obj.options.data;
}
/**
* Get position of the item
*/
obj.getPosition = function (val) {
for (var i = 0; i < obj.items.length; i++) {
if (Value(i) == val) {
return i;
}
}
return false;
}
/**
* Get dropdown current text
*/
obj.getText = function (asArray) {
// Get value
var v = getText();
// Return value
if (asArray) {
return v;
} else {
return v.join('; ');
}
}
/**
* Get dropdown current value
*/
obj.getValue = function (asArray) {
// Get value
var v = getValue();
// Return value
if (asArray) {
return v;
} else {
return v.join(';');
}
}
/**
* Change event
*/
var change = function (oldValue) {
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof (el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
// Events
if (typeof (obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, oldValue, obj.options.value);
}
}
/**
* Set value
*/
obj.setValue = function (newValue) {
// Current value
var oldValue = obj.getValue();
// New value
if (Array.isArray(newValue)) {
newValue = newValue.join(';')
}
if (oldValue !== newValue) {
// Set value
applyValue(newValue);
// Change
change(oldValue);
}
}
obj.resetSelected = function () {
obj.setValue(null);
}
obj.selectIndex = function (index, force) {
// Make sure is a number
var index = parseInt(index);
// Only select those existing elements
if (obj.items && obj.items[index] && (force === true || obj.items[index].data.disabled !== true)) {
// Reset cursor to a new position
obj.setCursor(index, false);
// Behaviour
if (!obj.options.multiple) {
// Update value
if (obj.items[index].selected) {
if (obj.options.allowEmpty !== false) {
obj.setValue(null);
}
} else {
obj.setValue(Value(index));
}
// Close component
obj.close();
} else {
// Old value
var oldValue = obj.options.value;
// Toggle option
if (obj.items[index].selected) {
obj.items[index].element.classList.remove('jdropdown-selected');
obj.items[index].selected = false;
delete obj.value[Value(index)];
} else {
// Select element
obj.items[index].element.classList.add('jdropdown-selected');
obj.items[index].selected = true;
// Set value
obj.value[Value(index)] = Text(index);
}
// Global value
obj.options.value = Object.keys(obj.value).join(';');
// Update labels for multiple dropdown
if (obj.options.autocomplete == false) {
obj.header.value = getText().join('; ');
}
// Events
change(oldValue);
}
}
}
obj.selectItem = function (item) {
obj.selectIndex(item.indexValue);
}
var exists = function (k, result) {
for (var j = 0; j < result.length; j++) {
if (!obj.options.format) {
if (result[j].value == k) {
return true;
}
} else {
if (result[j].id == k) {
return true;
}
}
}
return false;
}
obj.find = function (str) {
if (obj.search == str.trim()) {
return false;
}
// Search term
obj.search = str;
// Reset index
obj.setCursor();
// Remove nodes from all groups
if (obj.groups.length) {
for (var i = 0; i < obj.groups.length; i++) {
obj.groups[i].lastChild.textContent = '';
}
}
// Remove all nodes
content.textContent = '';
// Remove current items in the remote search
if (obj.options.remoteSearch == true) {
// Reset results
obj.results = null;
// URL
var url = obj.options.url;
// Ajax call
let o = {
url: url,
method: 'GET',
data: { q: str },
dataType: 'json',
success: function (result) {
// Reset items
obj.items = [];
// Add the current selected items to the results in case they are not there
var current = Object.keys(obj.value);
if (current.length) {
for (var i = 0; i < current.length; i++) {
if (!exists(current[i], result)) {
if (!obj.options.format) {
result.unshift({value: current[i], text: obj.value[current[i]]});
} else {
result.unshift({id: current[i], name: obj.value[current[i]]});
}
}
}
}
// Append data
obj.appendData(result);
// Show or hide results
if (!result.length) {
content.style.display = 'none';
} else {
content.style.display = '';
}
if (typeof(obj.options.onsearch) === 'function') {
obj.options.onsearch(obj, result);
}
}
}
if (typeof(obj.options.onbeforesearch) === 'function') {
let ret = obj.options.onbeforesearch(obj, o);
if (ret === false) {
return;
} else if (typeof(ret) === 'object') {
o = ret;
}
}
// Remote search
ajax(o);
} else {
// Search terms
str = new RegExp(str, 'gi');
// Reset search
var results = [];
// Append options
for (var i = 0; i < obj.items.length; i++) {
// Item label
var label = Text(i);
// Item title
var title = obj.items[i].data.title || '';
// Group name
var groupName = obj.items[i].data.group || '';
// Synonym
var synonym = obj.items[i].data.synonym || '';
if (synonym) {
synonym = synonym.join(' ');
}
if (str == null || obj.items[i].selected == true || label.toString().match(str) || title.match(str) || groupName.match(str) || synonym.match(str)) {
results.push(obj.items[i]);
}
}
if (!results.length) {
content.style.display = 'none';
// Results
obj.results = null;
} else {
content.style.display = '';
// Results
obj.results = results;
// Show 200 items at once
var number = results.length || 0;
// Lazyloading
if (obj.options.lazyLoading == true && number > 200) {
number = 200;
}
for (var i = 0; i < number; i++) {
if (obj.results[i].group) {
if (!obj.results[i].group.parentNode) {
content.appendChild(obj.results[i].group);
}
obj.results[i].group.lastChild.appendChild(obj.results[i].element);
} else {
content.appendChild(obj.results[i].element);
}
}
}
}
// Auto focus
if (obj.options.autofocus == true) {
obj.first();
}
}
obj.open = function () {
// Focus
if (!el.classList.contains('jdropdown-focus')) {
// Current dropdown
Component.current = obj;
// Start tracking
tracking(obj, true);
// Add focus
el.classList.add('jdropdown-focus');
// Animation
if (helpers.getWindowWidth() < 800) {
if (obj.options.type == null || obj.options.type == 'picker') {
animation.slideBottom(container, 1);
}
}
// Filter
if (obj.options.autocomplete == true) {
obj.header.value = obj.search;
obj.header.focus();
}
// Set cursor for the first or first selected element
var k = getValue();
if (k[0]) {
var cursor = obj.getPosition(k[0]);
if (cursor !== false) {
obj.setCursor(cursor);
}
}
// Container Size
if (!obj.options.type || obj.options.type == 'default') {
var rect = el.getBoundingClientRect();
var rectContainer = container.getBoundingClientRect();
if (obj.options.position) {
container.style.position = 'fixed';
if (window.innerHeight < rect.bottom + rectContainer.height) {
container.style.top = '';
container.style.bottom = (window.innerHeight - rect.top) + 1 + 'px';
} else {
container.style.top = rect.bottom + 'px';
container.style.bottom = '';
}
container.style.left = rect.left + 'px';
} else {
if (window.innerHeight < rect.bottom + rectContainer.height) {
container.style.top = '';
container.style.bottom = rect.height + 1 + 'px';
} else {
container.style.top = '';
container.style.bottom = '';
}
}
container.style.minWidth = rect.width + 'px';
if (obj.options.maxWidth) {
container.style.maxWidth = obj.options.maxWidth;
}
if (!obj.items.length && obj.options.autocomplete == true) {
content.style.display = 'none';
} else {
content.style.display = '';
}
}
}
// Events
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
obj.close = function (ignoreEvents) {
if (el.classList.contains('jdropdown-focus')) {
// Update labels
obj.header.value = obj.getText();
// Remove cursor
obj.setCursor();
// Events
if (!ignoreEvents && typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
// Blur
if (obj.header.blur) {
obj.header.blur();
}
// Remove focus
el.classList.remove('jdropdown-focus');
// Start tracking
tracking(obj, false);
// Current dropdown
Component.current = null;
}
return obj.getValue();
}
/**
* Set cursor
*/
obj.setCursor = function (index, setPosition) {
// Remove current cursor
if (obj.currentIndex != null) {
// Remove visual cursor
if (obj.items && obj.items[obj.currentIndex]) {
obj.items[obj.currentIndex].element.classList.remove('jdropdown-cursor');
}
}
if (index == undefined) {
obj.currentIndex = null;
} else {
index = parseInt(index);
// Cursor only for visible items
if (obj.items[index].element.parentNode) {
obj.items[index].element.classList.add('jdropdown-cursor');
obj.currentIndex = index;
// Update scroll to the cursor element
if (setPosition !== false && obj.items[obj.currentIndex].element) {
var container = content.scrollTop;
var element = obj.items[obj.currentIndex].element;
content.scrollTop = element.offsetTop - element.scrollTop + element.clientTop - 95;
}
}
}
}
// Compatibility
obj.resetCursor = obj.setCursor;
obj.updateCursor = obj.setCursor;
/**
* Reset cursor and selected items
*/
obj.reset = function () {
// Reset cursor
obj.setCursor();
// Reset selected
obj.setValue(null);
}
/**
* First available item
*/
obj.first = function () {
if (obj.options.lazyLoading === true) {
obj.loadFirst();
}
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
var newIndex = items[0].indexValue;
obj.setCursor(newIndex);
}
}
/**
* Last available item
*/
obj.last = function () {
if (obj.options.lazyLoading === true) {
obj.loadLast();
}
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
var newIndex = items[items.length - 1].indexValue;
obj.setCursor(newIndex);
}
}
obj.next = function (letter) {
var newIndex = null;
if (letter) {
if (letter.length == 1) {
// Current index
var current = obj.currentIndex || -1;
// Letter
letter = letter.toLowerCase();
var e = null;
var l = null;
var items = content.querySelectorAll('.jdropdown-item');
if (items.length) {
for (var i = 0; i < items.length; i++) {
if (items[i].indexValue > current) {
if (e = obj.items[items[i].indexValue]) {
if (l = e.element.innerText[0]) {
l = l.toLowerCase();
if (letter == l) {
newIndex = items[i].indexValue;
break;
}
}
}
}
}
obj.setCursor(newIndex);
}
}
} else {
if (obj.currentIndex == undefined || obj.currentIndex == null) {
obj.first();
} else {
var element = obj.items[obj.currentIndex].element;
var next = element.nextElementSibling;
if (next) {
if (next.classList.contains('jdropdown-group')) {
next = next.lastChild.firstChild;
}
newIndex = next.indexValue;
} else {
if (element.parentNode.classList.contains('jdropdown-group-items')) {
if (next = element.parentNode.parentNode.nextElementSibling) {
if (next.classList.contains('jdropdown-group')) {
next = next.lastChild.firstChild;
} else if (next.classList.contains('jdropdown-item')) {
newIndex = next.indexValue;
} else {
next = null;
}
}
if (next) {
newIndex = next.indexValue;
}
}
}
if (newIndex !== null) {
obj.setCursor(newIndex);
}
}
}
}
obj.prev = function () {
var newIndex = null;
if (obj.currentIndex === null) {
obj.first();
} else {
var element = obj.items[obj.currentIndex].element;
var prev = element.previousElementSibling;
if (prev) {
if (prev.classList.contains('jdropdown-group')) {
prev = prev.lastChild.lastChild;
}
newIndex = prev.indexValue;
} else {
if (element.parentNode.classList.contains('jdropdown-group-items')) {
if (prev = element.parentNode.parentNode.previousElementSibling) {
if (prev.classList.contains('jdropdown-group')) {
prev = prev.lastChild.lastChild;
} else if (prev.classList.contains('jdropdown-item')) {
newIndex = prev.indexValue;
} else {
prev = null
}
}
if (prev) {
newIndex = prev.indexValue;
}
}
}
}
if (newIndex !== null) {
obj.setCursor(newIndex);
}
}
obj.loadFirst = function () {
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
// Show 200 items at once
var number = results.length || 0;
// Lazyloading
if (obj.options.lazyLoading == true && number > 200) {
number = 200;
}
// Reset container
content.textContent = '';
// First 200 items
for (var i = 0; i < number; i++) {
if (results[i].group) {
if (!results[i].group.parentNode) {
content.appendChild(results[i].group);
}
results[i].group.lastChild.appendChild(results[i].element);
} else {
content.appendChild(results[i].element);
}
}
// Scroll go to the begin
content.scrollTop = 0;
}
obj.loadLast = function () {
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
// Show first page
var number = results.length;
// Max 200 items
if (number > 200) {
number = number - 200;
// Reset container
content.textContent = '';
// First 200 items
for (var i = number; i < results.length; i++) {
if (results[i].group) {
if (!results[i].group.parentNode) {
content.appendChild(results[i].group);
}
results[i].group.lastChild.appendChild(results[i].element);
} else {
content.appendChild(results[i].element);
}
}
// Scroll go to the begin
content.scrollTop = content.scrollHeight;
}
}
obj.loadUp = function () {
var test = false;
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
var items = content.querySelectorAll('.jdropdown-item');
var fistItem = items[0].indexValue;
fistItem = obj.items[fistItem];
var index = results.indexOf(fistItem) - 1;
if (index > 0) {
var number = 0;
while (index > 0 && results[index] && number < 200) {
if (results[index].group) {
if (!results[index].group.parentNode) {
content.insertBefore(results[index].group, content.firstChild);
}
results[index].group.lastChild.insertBefore(results[index].element, results[index].group.lastChild.firstChild);
} else {
content.insertBefore(results[index].element, content.firstChild);
}
index--;
number++;
}
// New item added
test = true;
}
return test;
}
obj.loadDown = function () {
var test = false;
// Search
if (obj.results) {
var results = obj.results;
} else {
var results = obj.items;
}
var items = content.querySelectorAll('.jdropdown-item');
var lastItem = items[items.length - 1].indexValue;
lastItem = obj.items[lastItem];
var index = results.indexOf(lastItem) + 1;
if (index < results.length) {
var number = 0;
while (index < results.length && results[index] && number < 200) {
if (results[index].group) {
if (!results[index].group.parentNode) {
content.appendChild(results[index].group);
}
results[index].group.lastChild.appendChild(results[index].element);
} else {
content.appendChild(results[index].element);
}
index++;
number++;
}
// New item added
test = true;
}
return test;
}
init();
return obj;
});
Component.keydown = function (e) {
var dropdown = null;
if (dropdown = Component.current) {
if (e.which == 13 || e.which == 9) { // enter or tab
if (dropdown.header.value && dropdown.currentIndex == null && dropdown.options.newOptions) {
// if they typed something in, but it matched nothing, and newOptions are allowed, start that flow
dropdown.add();
} else {
// Quick Select/Filter
if (dropdown.currentIndex == null && dropdown.options.autocomplete == true && dropdown.header.value != "") {
dropdown.find(dropdown.header.value);
}
dropdown.selectIndex(dropdown.currentIndex);
}
} else if (e.which == 38) { // up arrow
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex > 0) {
dropdown.prev();
}
e.preventDefault();
} else if (e.which == 40) { // down arrow
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
dropdown.next();
}
e.preventDefault();
} else if (e.which == 36) {
dropdown.first();
if (!e.target.classList.contains('jdropdown-header')) {
e.preventDefault();
}
} else if (e.which == 35) {
dropdown.last();
if (!e.target.classList.contains('jdropdown-header')) {
e.preventDefault();
}
} else if (e.which == 27) {
dropdown.close();
} else if (e.which == 33) { // page up
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex > 0) {
for (var i = 0; i < 7; i++) {
dropdown.prev()
}
}
e.preventDefault();
} else if (e.which == 34) { // page down
if (dropdown.currentIndex == null) {
dropdown.first();
} else if (dropdown.currentIndex + 1 < dropdown.items.length) {
for (var i = 0; i < 7; i++) {
dropdown.next()
}
}
e.preventDefault();
}
}
}
Component.mouseup = function (e) {
var element = helpers.findElement(e.target, 'jdropdown');
if (element) {
var dropdown = element.dropdown;
if (e.target.classList.contains('jdropdown-header')) {
if (element.classList.contains('jdropdown-focus') && element.classList.contains('jdropdown-default')) {
var rect = element.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 30) {
if (e.target.classList.contains('jdropdown-add')) {
dropdown.add();
} else {
dropdown.close();
}
} else {
if (dropdown.options.autocomplete == false) {
dropdown.close();
}
}
} else {
dropdown.open();
}
} else if (e.target.classList.contains('jdropdown-group-name')) {
var items = e.target.nextSibling.children;
if (e.target.nextSibling.style.display != 'none') {
for (var i = 0; i < items.length; i++) {
if (items[i].style.display != 'none') {
dropdown.selectItem(items[i]);
}
}
}
} else if (e.target.classList.contains('jdropdown-group-arrow')) {
if (e.target.classList.contains('jdropdown-group-arrow-down')) {
e.target.classList.remove('jdropdown-group-arrow-down');
e.target.classList.add('jdropdown-group-arrow-up');
e.target.parentNode.nextSibling.style.display = 'none';
} else {
e.target.classList.remove('jdropdown-group-arrow-up');
e.target.classList.add('jdropdown-group-arrow-down');
e.target.parentNode.nextSibling.style.display = '';
}
} else if (e.target.classList.contains('jdropdown-item')) {
dropdown.selectItem(e.target);
} else if (e.target.classList.contains('jdropdown-image')) {
dropdown.selectItem(e.target.parentNode);
} else if (e.target.classList.contains('jdropdown-description')) {
dropdown.selectItem(e.target.parentNode);
} else if (e.target.classList.contains('jdropdown-title')) {
dropdown.selectItem(e.target.parentNode.parentNode);
} else if (e.target.classList.contains('jdropdown-close') || e.target.classList.contains('jdropdown-backdrop')) {
dropdown.close();
}
}
}
Component.extractFromDom = function (el, options) {
// Keep reference
var select = el;
if (!options) {
options = {};
}
// Prepare configuration
if (el.getAttribute('multiple') && (!options || options.multiple == undefined)) {
options.multiple = true;
}
if (el.getAttribute('placeholder') && (!options || options.placeholder == undefined)) {
options.placeholder = el.getAttribute('placeholder');
}
if (el.getAttribute('data-autocomplete') && (!options || options.autocomplete == undefined)) {
options.autocomplete = true;
}
if (!options || options.width == undefined) {
options.width = el.offsetWidth;
}
if (el.value && (!options || options.value == undefined)) {
options.value = el.value;
}
if (!options || options.data == undefined) {
options.data = [];
for (var j = 0; j < el.children.length; j++) {
if (el.children[j].tagName == 'OPTGROUP') {
for (var i = 0; i < el.children[j].children.length; i++) {
options.data.push({
value: el.children[j].children[i].value,
text: el.children[j].children[i].textContent,
group: el.children[j].getAttribute('label'),
});
}
} else {
options.data.push({
value: el.children[j].value,
text: el.children[j].textContent,
});
}
}
}
if (!options || options.onchange == undefined) {
options.onchange = function (a, b, c, d) {
if (options.multiple == true) {
if (obj.items[b].classList.contains('jdropdown-selected')) {
select.options[b].setAttribute('selected', 'selected');
} else {
select.options[b].removeAttribute('selected');
}
} else {
select.value = d;
}
}
}
// Create DIV
var div = document.createElement('div');
el.parentNode.insertBefore(div, el);
el.style.display = 'none';
el = div;
return {el: el, options: options};
}
return Component;
}
/* harmony default export */ var dropdown = (Dropdown());
;// CONCATENATED MODULE: ./src/plugins/picker.js
function Picker(el, options) {
// Already created, update options
if (el.picker) {
return el.picker.setOptions(options, true);
}
// New instance
var obj = { type: 'picker' };
obj.options = {};
var dropdownHeader = null;
var dropdownContent = null;
/**
* The element passed is a DOM element
*/
var isDOM = function(o) {
return (o instanceof Element || o instanceof HTMLDocument);
}
/**
* Create the content options
*/
var createContent = function() {
dropdownContent.innerHTML = '';
// Create items
var keys = Object.keys(obj.options.data);
// Go though all options
for (var i = 0; i < keys.length; i++) {
// Item
var dropdownItem = document.createElement('div');
dropdownItem.classList.add('jpicker-item');
dropdownItem.setAttribute('role', 'option');
dropdownItem.k = keys[i];
dropdownItem.v = obj.options.data[keys[i]];
// Label
var item = obj.getLabel(keys[i], dropdownItem);
if (isDOM(item)) {
dropdownItem.appendChild(item);
} else {
dropdownItem.innerHTML = item;
}
// Append
dropdownContent.appendChild(dropdownItem);
}
}
/**
* Set or reset the options for the picker
*/
obj.setOptions = function(options, reset) {
// Default configuration
var defaults = {
value: 0,
data: null,
render: null,
onchange: null,
onmouseover: null,
onselect: null,
onopen: null,
onclose: null,
onload: null,
width: null,
header: true,
right: false,
bottom: false,
content: false,
columns: null,
grid: null,
height: null,
}
// Legacy purpose only
if (options && options.options) {
options.data = options.options;
}
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Start using the options
if (obj.options.header === false) {
dropdownHeader.style.display = 'none';
} else {
dropdownHeader.style.display = '';
}
// Width
if (obj.options.width) {
dropdownHeader.style.width = parseInt(obj.options.width) + 'px';
} else {
dropdownHeader.style.width = '';
}
// Height
if (obj.options.height) {
dropdownContent.style.maxHeight = obj.options.height + 'px';
dropdownContent.style.overflow = 'scroll';
} else {
dropdownContent.style.overflow = '';
}
if (obj.options.columns > 0) {
if (! obj.options.grid) {
dropdownContent.classList.add('jpicker-columns');
dropdownContent.style.width = obj.options.width ? obj.options.width : 36 * obj.options.columns + 'px';
} else {
dropdownContent.classList.add('jpicker-grid');
dropdownContent.style.gridTemplateColumns = 'repeat(' + obj.options.grid + ', 1fr)';
}
}
if (isNaN(parseInt(obj.options.value))) {
obj.options.value = 0;
}
// Create list from data
createContent();
// Set value
obj.setValue(obj.options.value);
// Set options all returns the own instance
return obj;
}
obj.getValue = function() {
return obj.options.value;
}
obj.setValue = function(k, e) {
// Set label
obj.setLabel(k);
// Update value
obj.options.value = String(k);
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
if (dropdownContent.children[k] && dropdownContent.children[k].getAttribute('type') !== 'generic') {
obj.close();
}
// Call method
if (e) {
if (typeof (obj.options.onchange) == 'function') {
var v = obj.options.data[k];
obj.options.onchange(el, obj, v, v, k, e);
}
}
}
obj.getLabel = function(v, item) {
var label = obj.options.data[v] || null;
if (typeof(obj.options.render) == 'function') {
label = obj.options.render(label, item);
}
return label;
}
obj.setLabel = function(v) {
var item;
if (obj.options.content) {
item = '<i class="material-icons">' + obj.options.content + '</i>';
} else {
item = obj.getLabel(v, null);
}
// Label
if (isDOM(item)) {
dropdownHeader.innerHTML = '';
dropdownHeader.appendChild(item);
} else {
dropdownHeader.innerHTML = item;
}
}
obj.open = function() {
if (! el.classList.contains('jpicker-focus')) {
// Start tracking the element
tracking(obj, true);
// Open picker
el.classList.add('jpicker-focus');
el.focus();
var top = 0;
var left = 0;
dropdownContent.style.marginLeft = '';
var rectHeader = dropdownHeader.getBoundingClientRect();
var rectContent = dropdownContent.getBoundingClientRect();
if (window.innerHeight < rectHeader.bottom + rectContent.height || obj.options.bottom) {
top = -1 * (rectContent.height + 4);
} else {
top = rectHeader.height + 4;
}
if (obj.options.right === true) {
left = -1 * rectContent.width + rectHeader.width;
}
if (rectContent.left + left < 0) {
left = left + rectContent.left + 10;
}
if (rectContent.left + rectContent.width > window.innerWidth) {
left = -1 * (10 + rectContent.left + rectContent.width - window.innerWidth);
}
dropdownContent.style.marginTop = parseInt(top) + 'px';
dropdownContent.style.marginLeft = parseInt(left) + 'px';
//dropdownContent.style.marginTop
if (typeof obj.options.onopen == 'function') {
obj.options.onopen(el, obj);
}
}
}
obj.close = function() {
if (el.classList.contains('jpicker-focus')) {
el.classList.remove('jpicker-focus');
// Start tracking the element
tracking(obj, false);
if (typeof obj.options.onclose == 'function') {
obj.options.onclose(el, obj);
}
}
}
/**
* Create floating picker
*/
var init = function() {
let id = helpers.guid();
// Class
el.classList.add('jpicker');
el.setAttribute('role', 'combobox');
el.setAttribute('aria-haspopup', 'listbox');
el.setAttribute('aria-expanded', 'false');
el.setAttribute('aria-controls', id);
el.setAttribute('tabindex', '0');
el.onmousedown = function(e) {
if (! el.classList.contains('jpicker-focus')) {
obj.open();
}
}
// Dropdown Header
dropdownHeader = document.createElement('div');
dropdownHeader.classList.add('jpicker-header');
// Dropdown content
dropdownContent = document.createElement('div');
dropdownContent.setAttribute('id', id);
dropdownContent.setAttribute('role', 'listbox');
dropdownContent.classList.add('jpicker-content');
dropdownContent.onclick = function(e) {
var item = helpers.findElement(e.target, 'jpicker-item');
if (item) {
if (item.parentNode === dropdownContent) {
// Update label
obj.setValue(item.k, e);
}
}
}
// Append content and header
el.appendChild(dropdownHeader);
el.appendChild(dropdownContent);
// Default value
el.value = options.value || 0;
// Set options
obj.setOptions(options);
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.picker = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/toolbar.js
function Toolbar(el, options) {
// New instance
var obj = { type:'toolbar' };
obj.options = {};
// Default configuration
var defaults = {
app: null,
container: false,
badge: false,
title: false,
responsive: false,
maxWidth: null,
bottom: true,
items: [],
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
if (! el && options.app && options.app.el) {
el = document.createElement('div');
options.app.el.appendChild(el);
}
// Arrow
var toolbarArrow = document.createElement('div');
toolbarArrow.classList.add('jtoolbar-item');
toolbarArrow.classList.add('jtoolbar-arrow');
var toolbarFloating = document.createElement('div');
toolbarFloating.classList.add('jtoolbar-floating');
toolbarArrow.appendChild(toolbarFloating);
obj.selectItem = function(element) {
var elements = toolbarContent.children;
for (var i = 0; i < elements.length; i++) {
if (element != elements[i]) {
elements[i].classList.remove('jtoolbar-selected');
}
}
element.classList.add('jtoolbar-selected');
}
obj.hide = function() {
animation.slideBottom(el, 0, function() {
el.style.display = 'none';
});
}
obj.show = function() {
el.style.display = '';
animation.slideBottom(el, 1);
}
obj.get = function() {
return el;
}
obj.setBadge = function(index, value) {
toolbarContent.children[index].children[1].firstChild.innerHTML = value;
}
obj.destroy = function() {
toolbar.remove();
el.innerHTML = '';
}
obj.update = function(a, b) {
for (var i = 0; i < toolbarContent.children.length; i++) {
// Toolbar element
var toolbarItem = toolbarContent.children[i];
// State management
if (typeof(toolbarItem.updateState) == 'function') {
toolbarItem.updateState(el, obj, toolbarItem, a, b);
}
}
for (var i = 0; i < toolbarFloating.children.length; i++) {
// Toolbar element
var toolbarItem = toolbarFloating.children[i];
// State management
if (typeof(toolbarItem.updateState) == 'function') {
toolbarItem.updateState(el, obj, toolbarItem, a, b);
}
}
}
obj.create = function(items) {
// Reset anything in the toolbar
toolbarContent.innerHTML = '';
// Create elements in the toolbar
for (var i = 0; i < items.length; i++) {
var toolbarItem = document.createElement('div');
toolbarItem.classList.add('jtoolbar-item');
if (items[i].width) {
toolbarItem.style.width = parseInt(items[i].width) + 'px';
}
if (items[i].k) {
toolbarItem.k = items[i].k;
}
if (items[i].tooltip) {
toolbarItem.setAttribute('title', items[i].tooltip);
toolbarItem.setAttribute('aria-label', items[i].tooltip);
}
// Id
if (items[i].id) {
toolbarItem.setAttribute('id', items[i].id);
}
// Selected
if (items[i].updateState) {
toolbarItem.updateState = items[i].updateState;
}
if (items[i].active) {
toolbarItem.classList.add('jtoolbar-active');
}
if (items[i].disabled) {
toolbarItem.classList.add('jtoolbar-disabled');
}
if (items[i].type == 'select' || items[i].type == 'dropdown') {
Picker(toolbarItem, items[i]);
} else if (items[i].type == 'divisor') {
toolbarItem.classList.add('jtoolbar-divisor');
} else if (items[i].type == 'label') {
toolbarItem.classList.add('jtoolbar-label');
toolbarItem.innerHTML = items[i].content;
} else {
// Material icons
var toolbarIcon = document.createElement('i');
if (typeof(items[i].class) === 'undefined') {
toolbarIcon.classList.add('material-icons');
} else {
var c = items[i].class.split(' ');
for (var j = 0; j < c.length; j++) {
toolbarIcon.classList.add(c[j]);
}
}
toolbarIcon.innerHTML = items[i].content ? items[i].content : '';
toolbarItem.setAttribute('role', 'button');
toolbarItem.appendChild(toolbarIcon);
// Badge options
if (obj.options.badge == true) {
var toolbarBadge = document.createElement('div');
toolbarBadge.classList.add('jbadge');
var toolbarBadgeContent = document.createElement('div');
toolbarBadgeContent.innerHTML = items[i].badge ? items[i].badge : '';
toolbarBadge.appendChild(toolbarBadgeContent);
toolbarItem.appendChild(toolbarBadge);
}
// Title
if (items[i].title) {
if (obj.options.title == true) {
var toolbarTitle = document.createElement('span');
toolbarTitle.innerHTML = items[i].title;
toolbarItem.appendChild(toolbarTitle);
} else {
toolbarItem.setAttribute('title', items[i].title);
}
}
if (obj.options.app && items[i].route) {
// Route
toolbarItem.route = items[i].route;
// Onclick for route
toolbarItem.onclick = function() {
obj.options.app.pages(this.route);
}
// Create pages
obj.options.app.pages(items[i].route, {
toolbarItem: toolbarItem,
closed: true
});
}
// Render
if (typeof(items[i].render) === 'function') {
items[i].render(toolbarItem, items[i]);
}
}
if (items[i].onclick) {
toolbarItem.onclick = items[i].onclick.bind(items[i], el, obj, toolbarItem);
}
toolbarContent.appendChild(toolbarItem);
}
// Fits to the page
setTimeout(function() {
obj.refresh();
}, 0);
}
obj.open = function() {
toolbarArrow.classList.add('jtoolbar-arrow-selected');
var rectElement = el.getBoundingClientRect();
var rect = toolbarFloating.getBoundingClientRect();
if (rect.bottom > window.innerHeight || obj.options.bottom) {
toolbarFloating.style.bottom = '0';
} else {
toolbarFloating.style.removeProperty('bottom');
}
toolbarFloating.style.right = '0';
toolbarArrow.children[0].focus();
// Start tracking
tracking(obj, true);
}
obj.close = function() {
toolbarArrow.classList.remove('jtoolbar-arrow-selected')
// End tracking
tracking(obj, false);
}
obj.refresh = function() {
if (obj.options.responsive == true) {
// Width of the c
var rect = el.parentNode.getBoundingClientRect();
if (! obj.options.maxWidth) {
obj.options.maxWidth = rect.width;
}
// Available parent space
var available = parseInt(obj.options.maxWidth);
// Remove arrow
if (toolbarArrow.parentNode) {
toolbarArrow.parentNode.removeChild(toolbarArrow);
}
// Move all items to the toolbar
while (toolbarFloating.firstChild) {
toolbarContent.appendChild(toolbarFloating.firstChild);
}
// Toolbar is larger than the parent, move elements to the floating element
if (available < toolbarContent.offsetWidth) {
// Give space to the floating element
available -= 50;
// Move to the floating option
while (toolbarContent.lastChild && available < toolbarContent.offsetWidth) {
toolbarFloating.insertBefore(toolbarContent.lastChild, toolbarFloating.firstChild);
}
}
// Show arrow
if (toolbarFloating.children.length > 0) {
toolbarContent.appendChild(toolbarArrow);
}
}
}
obj.setReadonly = function(state) {
state = state ? 'add' : 'remove';
el.classList[state]('jtoolbar-disabled');
}
el.onclick = function(e) {
var element = helpers.findElement(e.target, 'jtoolbar-item');
if (element) {
obj.selectItem(element);
}
if (e.target.classList.contains('jtoolbar-arrow')) {
obj.open();
}
}
window.addEventListener('resize', function() {
obj.refresh();
});
// Toolbar
el.classList.add('jtoolbar');
// Reset content
el.innerHTML = '';
// Container
if (obj.options.container == true) {
el.classList.add('jtoolbar-container');
}
// Content
var toolbarContent = document.createElement('div');
el.appendChild(toolbarContent);
// Special toolbar for mobile applications
if (obj.options.app) {
el.classList.add('jtoolbar-mobile');
}
// Create toolbar
obj.create(obj.options.items);
// Shortcut
el.toolbar = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/editor.js
function Editor() {
var Component = (function(el, options) {
// New instance
var obj = { type:'editor' };
obj.options = {};
// Default configuration
var defaults = {
// Load data from a remove location
url: null,
// Initial HTML content
value: '',
// Initial snippet
snippet: null,
// Add toolbar
toolbar: true,
toolbarOnTop: false,
// Website parser is to read websites and images from cross domain
remoteParser: null,
// Placeholder
placeholder: null,
// Parse URL
filterPaste: true,
// Accept drop files
dropZone: true,
dropAsSnippet: false,
acceptImages: true,
acceptFiles: false,
maxFileSize: 5000000,
allowImageResize: true,
// Style
maxHeight: null,
height: null,
focus: false,
// Events
onclick: null,
onfocus: null,
onblur: null,
onload: null,
onkeyup: null,
onkeydown: null,
onchange: null,
extensions: null,
type: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Private controllers
var editorTimer = null;
var editorAction = null;
var files = [];
// Keep the reference for the container
obj.el = el;
if (typeof(obj.options.onclick) == 'function') {
el.onclick = function(e) {
obj.options.onclick(el, obj, e);
}
}
// Prepare container
el.classList.add('jeditor-container');
// Snippet
var snippet = document.createElement('div');
snippet.className = 'jsnippet';
snippet.setAttribute('contenteditable', false);
// Toolbar
var toolbar = document.createElement('div');
toolbar.className = 'jeditor-toolbar';
obj.editor = document.createElement('div');
obj.editor.setAttribute('contenteditable', true);
obj.editor.setAttribute('spellcheck', false);
obj.editor.classList.add('jeditor');
// Placeholder
if (obj.options.placeholder) {
obj.editor.setAttribute('data-placeholder', obj.options.placeholder);
}
// Max height
if (obj.options.maxHeight || obj.options.height) {
obj.editor.style.overflowY = 'auto';
if (obj.options.maxHeight) {
obj.editor.style.maxHeight = obj.options.maxHeight;
}
if (obj.options.height) {
obj.editor.style.height = obj.options.height;
}
}
// Set editor initial value
if (obj.options.url) {
ajax({
url: obj.options.url,
dataType: 'html',
success: function(result) {
obj.editor.innerHTML = result;
Component.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
}
})
} else {
if (obj.options.value) {
obj.editor.innerHTML = obj.options.value;
} else {
// Create from existing elements
for (var i = 0; i < el.children.length; i++) {
obj.editor.appendChild(el.children[i]);
}
}
}
// Make sure element is empty
el.innerHTML = '';
/**
* Onchange event controllers
*/
var change = function(e) {
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, e);
}
// Update value
obj.options.value = obj.getData();
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
/**
* Extract images from a HTML string
*/
var extractImageFromHtml = function(html) {
// Create temp element
var div = document.createElement('div');
div.innerHTML = html;
// Extract images
var img = div.querySelectorAll('img');
if (img.length) {
for (var i = 0; i < img.length; i++) {
obj.addImage(img[i].src);
}
}
}
/**
* Insert node at caret
*/
var insertNodeAtCaret = function(newNode) {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
var selectedText = range.toString();
range.deleteContents();
range.insertNode(newNode);
// move the cursor after element
range.setStartAfter(newNode);
range.setEndAfter(newNode);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
var updateTotalImages = function() {
var o = null;
if (o = snippet.children[0]) {
// Make sure is a grid
if (! o.classList.contains('jslider-grid')) {
o.classList.add('jslider-grid');
}
// Quantify of images
var number = o.children.length;
// Set the configuration of the grid
o.setAttribute('data-number', number > 4 ? 4 : number);
// Total of images inside the grid
if (number > 4) {
o.setAttribute('data-total', number - 4);
} else {
o.removeAttribute('data-total');
}
}
}
/**
* Append image to the snippet
*/
var appendImage = function(image) {
if (! snippet.innerHTML) {
obj.appendSnippet({});
}
snippet.children[0].appendChild(image);
updateTotalImages();
}
/**
* Append snippet
* @Param object data
*/
obj.appendSnippet = function(data) {
// Reset snippet
snippet.innerHTML = '';
// Attributes
var a = [ 'image', 'title', 'description', 'host', 'url' ];
for (var i = 0; i < a.length; i++) {
var div = document.createElement('div');
div.className = 'jsnippet-' + a[i];
div.setAttribute('data-k', a[i]);
snippet.appendChild(div);
if (data[a[i]]) {
if (a[i] == 'image') {
if (! Array.isArray(data.image)) {
data.image = [ data.image ];
}
for (var j = 0; j < data.image.length; j++) {
var img = document.createElement('img');
img.src = data.image[j];
div.appendChild(img);
}
} else {
div.innerHTML = data[a[i]];
}
}
}
obj.editor.appendChild(document.createElement('br'));
obj.editor.appendChild(snippet);
}
/**
* Set editor value
*/
obj.setData = function(o) {
if (typeof(o) == 'object') {
obj.editor.innerHTML = o.content;
} else {
obj.editor.innerHTML = o;
}
if (obj.options.focus) {
Component.setCursor(obj.editor, true);
}
// Reset files container
files = [];
}
obj.getFiles = function() {
var f = obj.editor.querySelectorAll('.jfile');
var d = [];
for (var i = 0; i < f.length; i++) {
if (files[f[i].src]) {
d.push(files[f[i].src]);
}
}
return d;
}
obj.getText = function() {
return obj.editor.innerText;
}
/**
* Get editor data
*/
obj.getData = function(json) {
if (! json) {
var data = obj.editor.innerHTML;
} else {
var data = {
content : '',
}
// Get snippet
if (snippet.innerHTML) {
var index = 0;
data.snippet = {};
for (var i = 0; i < snippet.children.length; i++) {
// Get key from element
var key = snippet.children[i].getAttribute('data-k');
if (key) {
if (key == 'image') {
if (! data.snippet.image) {
data.snippet.image = [];
}
// Get all images
for (var j = 0; j < snippet.children[i].children.length; j++) {
data.snippet.image.push(snippet.children[i].children[j].getAttribute('src'))
}
} else {
data.snippet[key] = snippet.children[i].innerHTML;
}
}
}
}
// Get files
var f = Object.keys(files);
if (f.length) {
data.files = [];
for (var i = 0; i < f.length; i++) {
data.files.push(files[f[i]]);
}
}
// Get content
var d = document.createElement('div');
d.innerHTML = obj.editor.innerHTML;
var s = d.querySelector('.jsnippet');
if (s) {
s.remove();
}
var text = d.innerHTML;
text = text.replace(/<br>/g, "\n");
text = text.replace(/<\/div>/g, "<\/div>\n");
text = text.replace(/<(?:.|\n)*?>/gm, "");
data.content = text.trim();
// Process extensions
processExtensions('getData', data);
}
return data;
}
// Reset
obj.reset = function() {
obj.editor.innerHTML = '';
snippet.innerHTML = '';
files = [];
}
obj.addPdf = function(data) {
if (data.result.substr(0,4) != 'data') {
console.error('Invalid source');
} else {
var canvas = document.createElement('canvas');
canvas.width = 60;
canvas.height = 60;
var img = new Image();
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function(blob) {
var newImage = document.createElement('img');
newImage.src = window.URL.createObjectURL(blob);
newImage.title = data.name;
newImage.className = 'jfile pdf';
files[newImage.src] = {
file: newImage.src,
extension: 'pdf',
content: data.result,
}
//insertNodeAtCaret(newImage);
document.execCommand('insertHtml', false, newImage.outerHTML);
});
}
}
obj.addImage = function(src, asSnippet) {
if (! obj.options.acceptImages) {
return;
}
if (! src) {
src = '';
}
if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
console.error('remoteParser not defined in your initialization');
} else {
// This is to process cross domain images
if (src.substr(0,4) == 'data') {
var extension = src.split(';')
extension = extension[0].split('/');
extension = extension[1];
} else {
var extension = src.substr(src.lastIndexOf('.') + 1);
// Work for cross browsers
src = obj.options.remoteParser + src;
}
var img = new Image();
img.onload = function onload() {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(function(blob) {
var newImage = document.createElement('img');
newImage.src = window.URL.createObjectURL(blob);
newImage.classList.add('jfile');
newImage.setAttribute('tabindex', '900');
newImage.setAttribute('width', img.width);
newImage.setAttribute('height', img.height);
files[newImage.src] = {
file: newImage.src,
extension: extension,
content: canvas.toDataURL(),
}
if (obj.options.dropAsSnippet || asSnippet) {
appendImage(newImage);
// Just to understand the attachment is part of a snippet
files[newImage.src].snippet = true;
} else {
//insertNodeAtCaret(newImage);
document.execCommand('insertHtml', false, newImage.outerHTML);
}
change();
});
};
img.src = src;
}
}
obj.addFile = function(files) {
var reader = [];
for (var i = 0; i < files.length; i++) {
if (files[i].size > obj.options.maxFileSize) {
alert('The file is too big');
} else {
// Only PDF or Images
var type = files[i].type.split('/');
if (type[0] == 'image') {
type = 1;
} else if (type[1] == 'pdf') {
type = 2;
} else {
type = 0;
}
if (type) {
// Create file
reader[i] = new FileReader();
reader[i].index = i;
reader[i].type = type;
reader[i].name = files[i].name;
reader[i].date = files[i].lastModified;
reader[i].size = files[i].size;
reader[i].addEventListener("load", function (data) {
// Get result
if (data.target.type == 2) {
if (obj.options.acceptFiles == true) {
obj.addPdf(data.target);
}
} else {
obj.addImage(data.target.result);
}
}, false);
reader[i].readAsDataURL(files[i])
} else {
alert('The extension is not allowed');
}
}
}
}
// Destroy
obj.destroy = function() {
obj.editor.removeEventListener('mouseup', editorMouseUp);
obj.editor.removeEventListener('mousedown', editorMouseDown);
obj.editor.removeEventListener('mousemove', editorMouseMove);
obj.editor.removeEventListener('keyup', editorKeyUp);
obj.editor.removeEventListener('keydown', editorKeyDown);
obj.editor.removeEventListener('dragstart', editorDragStart);
obj.editor.removeEventListener('dragenter', editorDragEnter);
obj.editor.removeEventListener('dragover', editorDragOver);
obj.editor.removeEventListener('drop', editorDrop);
obj.editor.removeEventListener('paste', editorPaste);
obj.editor.removeEventListener('blur', editorBlur);
obj.editor.removeEventListener('focus', editorFocus);
el.editor = null;
el.classList.remove('jeditor-container');
toolbar.remove();
snippet.remove();
obj.editor.remove();
}
obj.upload = function() {
helpers.click(obj.file);
}
// Valid tags
const validTags = [
'html','body','address','span', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'b', 'i', 'blockquote',
'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre', 'hr', 'br', 'img',
'figure', 'picture', 'figcaption', 'iframe', 'table', 'thead', 'tbody', 'tfoot', 'tr',
'th', 'td', 'caption', 'u', 'del', 'ins', 'sub', 'sup', 'small', 'mark',
'input', 'textarea', 'select', 'option', 'button', 'label', 'fieldset',
'legend', 'audio', 'video', 'abbr', 'cite', 'kbd', 'section', 'article',
'nav', 'aside', 'header', 'footer', 'main', 'details', 'summary', 'svg', 'line', 'source'
];
// Valid properties
const validProperty = ['width', 'height', 'align', 'border', 'src', 'tabindex'];
// Valid CSS attributes
const validStyle = ['color', 'font-weight', 'font-size', 'background', 'background-color', 'margin'];
const parse = function(element) {
// Remove elements that are not white-listed
if (element.tagName && validTags.indexOf(element.tagName.toLowerCase()) === -1) {
if (element.innerText) {
element.innerHTML = element.innerText;
}
}
// Remove attributes
if (element.attributes && element.attributes.length) {
let style = null;
// Process style attribute
let elementStyle = element.getAttribute('style');
if (elementStyle) {
style = [];
let t = elementStyle.split(';');
for (let j = 0; j < t.length; j++) {
let v = t[j].trim().split(':');
if (validStyle.indexOf(v[0].trim()) >= 0) {
let k = v.shift();
v = v.join(':');
style.push(k + ':' + v);
}
}
}
// Process image
if (element.tagName.toUpperCase() === 'IMG') {
if (! obj.options.acceptImages || !element.src) {
element.parentNode.removeChild(element);
} else {
// Check if is data
element.setAttribute('tabindex', '900');
// Check attributes for persistence
obj.addImage(element.src);
}
}
// Remove attributes
let attr = [];
for (let i = 0; i < element.attributes.length; i++) {
attr.push(element.attributes[i].name);
}
if (attr.length) {
attr.forEach(function (v) {
if (validProperty.indexOf(v) === -1) {
element.removeAttribute(v);
} else {
// Protection XSS
if (element.attributes && element.attributes[i] && element.attributes[i].value.indexOf('<') !== -1) {
element.attributes[i].value.replace('<', '<');
}
}
});
}
element.style = '';
// Add valid style
if (style && style.length) {
element.setAttribute('style', style.join(';'));
}
}
// Parse children
if (element.children.length) {
for (let i = element.children.length; i > 0; i--) {
parse(element.children[i - 1]);
}
}
}
var select = function(e) {
var s = window.getSelection()
var r = document.createRange();
r.selectNode(e);
s.addRange(r)
}
var filter = function(data) {
if (data) {
data = data.replace(new RegExp('<!--(.*?)-->', 'gsi'), '');
}
var parser = new DOMParser();
var d = parser.parseFromString(data, "text/html");
parse(d);
var div = document.createElement('div');
div.innerHTML = d.firstChild.innerHTML;
return div;
}
var editorPaste = function(e) {
if (obj.options.filterPaste == true) {
if (e.clipboardData || e.originalEvent.clipboardData) {
var html = (e.originalEvent || e).clipboardData.getData('text/html');
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
var file = (e.originalEvent || e).clipboardData.files
} else if (window.clipboardData) {
var html = window.clipboardData.getData('Html');
var text = window.clipboardData.getData('Text');
var file = window.clipboardData.files
}
if (file.length) {
// Paste a image from the clipboard
obj.addFile(file);
} else {
if (! html) {
html = text.split('\r\n');
if (! e.target.innerText) {
html.map(function(v) {
var d = document.createElement('div');
d.innerText = v;
obj.editor.appendChild(d);
});
} else {
html = html.map(function(v) {
return '<div>' + v + '</div>';
});
document.execCommand('insertText', false, html.join(''));
}
} else {
var d = filter(html);
// Paste to the editor
//insertNodeAtCaret(d);
document.execCommand('insertHtml', false, d.innerHTML);
}
}
e.preventDefault();
}
}
var editorDragStart = function(e) {
if (editorAction && editorAction.e) {
e.preventDefault();
}
}
var editorDragEnter = function(e) {
if (editorAction || obj.options.dropZone == false) {
// Do nothing
} else {
el.classList.add('jeditor-dragging');
e.preventDefault();
}
}
var editorDragOver = function(e) {
if (editorAction || obj.options.dropZone == false) {
// Do nothing
} else {
if (editorTimer) {
clearTimeout(editorTimer);
}
editorTimer = setTimeout(function() {
el.classList.remove('jeditor-dragging');
}, 100);
e.preventDefault();
}
}
var editorDrop = function(e) {
if (editorAction || obj.options.dropZone == false) {
// Do nothing
} else {
// Position caret on the drop
var range = null;
if (document.caretRangeFromPoint) {
range=document.caretRangeFromPoint(e.clientX, e.clientY);
} else if (e.rangeParent) {
range=document.createRange();
range.setStart(e.rangeParent,e.rangeOffset);
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
sel.anchorNode.parentNode.focus();
var html = (e.originalEvent || e).dataTransfer.getData('text/html');
var text = (e.originalEvent || e).dataTransfer.getData('text/plain');
var file = (e.originalEvent || e).dataTransfer.files;
if (file.length) {
obj.addFile(file);
} else if (text) {
extractImageFromHtml(html);
}
el.classList.remove('jeditor-dragging');
e.preventDefault();
}
}
var editorBlur = function(e) {
// Process extensions
processExtensions('onevent', e);
// Apply changes
change(e);
// Blur
if (typeof(obj.options.onblur) == 'function') {
obj.options.onblur(el, obj, e);
}
}
var editorFocus = function(e) {
// Focus
if (typeof(obj.options.onfocus) == 'function') {
obj.options.onfocus(el, obj, e);
}
}
var editorKeyUp = function(e) {
if (! obj.editor.innerHTML) {
obj.editor.innerHTML = '<div><br></div>';
}
if (typeof(obj.options.onkeyup) == 'function') {
obj.options.onkeyup(el, obj, e);
}
}
var editorKeyDown = function(e) {
// Process extensions
processExtensions('onevent', e);
if (e.key == 'Delete') {
if (e.target.tagName == 'IMG') {
var parent = e.target.parentNode;
select(e.target);
if (parent.classList.contains('jsnippet-image')) {
updateTotalImages();
}
}
}
if (typeof(obj.options.onkeydown) == 'function') {
obj.options.onkeydown(el, obj, e);
}
}
var editorMouseUp = function(e) {
if (editorAction && editorAction.e) {
editorAction.e.classList.remove('resizing');
if (editorAction.e.changed == true) {
var image = editorAction.e.cloneNode()
image.width = parseInt(editorAction.e.style.width) || editorAction.e.getAttribute('width');
image.height = parseInt(editorAction.e.style.height) || editorAction.e.getAttribute('height');
editorAction.e.style.width = '';
editorAction.e.style.height = '';
select(editorAction.e);
document.execCommand('insertHtml', false, image.outerHTML);
}
}
editorAction = false;
}
var editorMouseDown = function(e) {
var close = function(snippet) {
var rect = snippet.getBoundingClientRect();
if (rect.width - (e.clientX - rect.left) < 40 && e.clientY - rect.top < 40) {
snippet.innerHTML = '';
snippet.remove();
}
}
if (e.target.tagName == 'IMG') {
if (e.target.style.cursor) {
var rect = e.target.getBoundingClientRect();
editorAction = {
e: e.target,
x: e.clientX,
y: e.clientY,
w: rect.width,
h: rect.height,
d: e.target.style.cursor,
}
if (! e.target.getAttribute('width')) {
e.target.setAttribute('width', rect.width)
}
if (! e.target.getAttribute('height')) {
e.target.setAttribute('height', rect.height)
}
var s = window.getSelection();
if (s.rangeCount) {
for (var i = 0; i < s.rangeCount; i++) {
s.removeRange(s.getRangeAt(i));
}
}
e.target.classList.add('resizing');
} else {
editorAction = true;
}
} else {
if (e.target.classList.contains('jsnippet')) {
close(e.target);
} else if (e.target.parentNode.classList.contains('jsnippet')) {
close(e.target.parentNode);
}
editorAction = true;
}
}
var editorMouseMove = function(e) {
if (e.target.tagName == 'IMG' && ! e.target.parentNode.classList.contains('jsnippet-image') && obj.options.allowImageResize == true) {
if (e.target.getAttribute('tabindex')) {
var rect = e.target.getBoundingClientRect();
if (e.clientY - rect.top < 5) {
if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'ne-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'nw-resize';
} else {
e.target.style.cursor = 'n-resize';
}
} else if (rect.height - (e.clientY - rect.top) < 5) {
if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'se-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'sw-resize';
} else {
e.target.style.cursor = 's-resize';
}
} else if (rect.width - (e.clientX - rect.left) < 5) {
e.target.style.cursor = 'e-resize';
} else if (e.clientX - rect.left < 5) {
e.target.style.cursor = 'w-resize';
} else {
e.target.style.cursor = '';
}
}
}
// Move
if (e.which == 1 && editorAction && editorAction.d) {
if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
editorAction.e.style.width = (editorAction.w + (e.clientX - editorAction.x));
if (e.shiftKey) {
var newHeight = (e.clientX - editorAction.x) * (editorAction.h / editorAction.w);
editorAction.e.style.height = editorAction.h + newHeight;
} else {
var newHeight = null;
}
}
if (! newHeight) {
if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
if (! e.shiftKey) {
editorAction.e.style.height = editorAction.h + (e.clientY - editorAction.y);
}
}
}
editorAction.e.changed = true;
}
}
var processExtensions = function(method, data) {
if (obj.options.extensions) {
var ext = Object.keys(obj.options.extensions);
if (ext.length) {
for (var i = 0; i < ext.length; i++)
if (obj.options.extensions[ext[i]] && typeof(obj.options.extensions[ext[i]][method]) == 'function') {
obj.options.extensions[ext[i]][method].call(obj, data);
}
}
}
}
var loadExtensions = function() {
if (obj.options.extensions) {
var ext = Object.keys(obj.options.extensions);
if (ext.length) {
for (var i = 0; i < ext.length; i++) {
if (obj.options.extensions[ext[i]] && typeof (obj.options.extensions[ext[i]]) == 'function') {
obj.options.extensions[ext[i]] = obj.options.extensions[ext[i]](el, obj);
}
}
}
}
}
document.addEventListener('mouseup', editorMouseUp);
document.addEventListener('mousemove', editorMouseMove);
obj.editor.addEventListener('mousedown', editorMouseDown);
obj.editor.addEventListener('keyup', editorKeyUp);
obj.editor.addEventListener('keydown', editorKeyDown);
obj.editor.addEventListener('dragstart', editorDragStart);
obj.editor.addEventListener('dragenter', editorDragEnter);
obj.editor.addEventListener('dragover', editorDragOver);
obj.editor.addEventListener('drop', editorDrop);
obj.editor.addEventListener('paste', editorPaste);
obj.editor.addEventListener('focus', editorFocus);
obj.editor.addEventListener('blur', editorBlur);
// Append editor to the container
el.appendChild(obj.editor);
// Snippet
if (obj.options.snippet) {
obj.appendSnippet(obj.options.snippet);
}
// Add toolbar
if (obj.options.toolbar) {
// Default toolbar configuration
if (Array.isArray(obj.options.toolbar)) {
var toolbarOptions = {
container: true,
responsive: true,
items: obj.options.toolbar
}
} else if (obj.options.toolbar === true) {
var toolbarOptions = {
container: true,
responsive: true,
items: [],
}
} else {
var toolbarOptions = obj.options.toolbar;
}
// Default items
if (! (toolbarOptions.items && toolbarOptions.items.length)) {
toolbarOptions.items = Component.getDefaultToolbar(obj);
}
if (obj.options.toolbarOnTop) {
// Add class
el.classList.add('toolbar-on-top');
// Append to the DOM
el.insertBefore(toolbar, el.firstChild);
} else {
// Add padding to the editor
obj.editor.style.padding = '15px';
// Append to the DOM
el.appendChild(toolbar);
}
// Create toolbar
Toolbar(toolbar, toolbarOptions);
toolbar.addEventListener('click', function() {
obj.editor.focus();
})
}
// Upload file
obj.file = document.createElement('input');
obj.file.style.display = 'none';
obj.file.type = 'file';
obj.file.setAttribute('accept', 'image/*');
obj.file.onchange = function() {
obj.addFile(this.files);
}
el.appendChild(obj.file);
// Focus to the editor
if (obj.options.focus) {
Component.setCursor(obj.editor, obj.options.focus == 'initial' ? true : false);
}
// Change method
el.change = obj.setData;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
// Data type
var o = el.getAttribute('data-html') === 'true' ? false : true;
return obj.getData(o);
} else {
obj.setData(val);
}
}
loadExtensions();
el.editor = obj;
// Onload
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj, obj.editor);
}
return obj;
});
Component.setCursor = function(element, first) {
element.focus();
document.execCommand('selectAll');
var sel = window.getSelection();
var range = sel.getRangeAt(0);
if (first == true) {
var node = range.startContainer;
var size = 0;
} else {
var node = range.endContainer;
var size = node.length;
}
range.setStart(node, size);
range.setEnd(node, size);
sel.removeAllRanges();
sel.addRange(range);
}
Component.getDefaultToolbar = function(obj) {
var color = function(a,b,c) {
if (! c.color) {
var t = null;
var colorPicker = Color(c, {
onchange: function(o, v) {
if (c.k === 'color') {
document.execCommand('foreColor', false, v);
} else {
document.execCommand('backColor', false, v);
}
}
});
c.color.open();
}
}
var items = [];
items.push({
content: 'undo',
onclick: function() {
document.execCommand('undo');
}
});
items.push({
content: 'redo',
onclick: function() {
document.execCommand('redo');
}
});
items.push({
type: 'divisor'
});
if (obj.options.toolbarOnTop) {
items.push({
type: 'select',
width: '140px',
options: ['Default', 'Verdana', 'Arial', 'Courier New'],
render: function (e) {
return '<span style="font-family:' + e + '">' + e + '</span>';
},
onchange: function (a,b,c,d,e) {
document.execCommand("fontName", false, d);
}
});
items.push({
type: 'select',
content: 'format_size',
options: ['x-small', 'small', 'medium', 'large', 'x-large'],
render: function (e) {
return '<span style="font-size:' + e + '">' + e + '</span>';
},
onchange: function (a,b,c,d,e) {
//var html = `<span style="font-size: ${c}">${text}</span>`;
//document.execCommand('insertHtml', false, html);
document.execCommand("fontSize", false, parseInt(e)+1);
//var f = window.getSelection().anchorNode.parentNode
//f.removeAttribute("size");
//f.style.fontSize = d;
}
});
items.push({
type: 'select',
options: ['format_align_left', 'format_align_center', 'format_align_right', 'format_align_justify'],
render: function (e) {
return '<i class="material-icons">' + e + '</i>';
},
onchange: function (a,b,c,d,e) {
var options = ['JustifyLeft','justifyCenter','justifyRight','justifyFull'];
document.execCommand(options[e]);
}
});
items.push({
type: 'divisor'
});
items.push({
content: 'format_color_text',
k: 'color',
onclick: color,
});
items.push({
content: 'format_color_fill',
k: 'background-color',
onclick: color,
});
}
items.push({
content: 'format_bold',
onclick: function(a,b,c) {
document.execCommand('bold');
if (document.queryCommandState("bold")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_italic',
onclick: function(a,b,c) {
document.execCommand('italic');
if (document.queryCommandState("italic")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_underline',
onclick: function(a,b,c) {
document.execCommand('underline');
if (document.queryCommandState("underline")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
type:'divisor'
});
items.push({
content: 'format_list_bulleted',
onclick: function(a,b,c) {
document.execCommand('insertUnorderedList');
if (document.queryCommandState("insertUnorderedList")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_list_numbered',
onclick: function(a,b,c) {
document.execCommand('insertOrderedList');
if (document.queryCommandState("insertOrderedList")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_indent_increase',
onclick: function(a,b,c) {
document.execCommand('indent', true, null);
if (document.queryCommandState("indent")) {
c.classList.add('selected');
} else {
c.classList.remove('selected');
}
}
});
items.push({
content: 'format_indent_decrease',
onclick: function() {
document.execCommand('outdent');
if (document.queryCommandState("outdent")) {
this.classList.add('selected');
} else {
this.classList.remove('selected');
}
}
});
if (obj.options.toolbarOnTop) {
items.push({
type: 'divisor'
});
items.push({
content: 'photo',
onclick: function () {
obj.upload();
}
});
items.push({
type: 'select',
content: 'table_view',
columns: 8,
grid: 8,
right: true,
options: [
'0x0', '1x0', '2x0', '3x0', '4x0', '5x0', '6x0', '7x0',
'0x1', '1x1', '2x1', '3x1', '4x1', '5x1', '6x1', '7x1',
'0x2', '1x2', '2x2', '3x2', '4x2', '5x2', '6x2', '7x2',
'0x3', '1x3', '2x3', '3x3', '4x3', '5x3', '6x3', '7x3',
'0x4', '1x4', '2x4', '3x4', '4x4', '5x4', '6x4', '7x4',
'0x5', '1x5', '2x5', '3x5', '4x5', '5x5', '6x5', '7x5',
'0x6', '1x6', '2x6', '3x6', '4x6', '5x6', '6x6', '7x6',
'0x7', '1x7', '2x7', '3x7', '4x7', '5x7', '6x7', '7x7',
],
render: function (e, item) {
if (item) {
item.onmouseover = this.onmouseover;
e = e.split('x');
item.setAttribute('data-x', e[0]);
item.setAttribute('data-y', e[1]);
}
var element = document.createElement('div');
item.style.margin = '1px';
item.style.border = '1px solid #ddd';
return element;
},
onmouseover: function (e) {
var x = parseInt(e.target.getAttribute('data-x'));
var y = parseInt(e.target.getAttribute('data-y'));
for (var i = 0; i < e.target.parentNode.children.length; i++) {
var element = e.target.parentNode.children[i];
var ex = parseInt(element.getAttribute('data-x'));
var ey = parseInt(element.getAttribute('data-y'));
if (ex <= x && ey <= y) {
element.style.backgroundColor = '#cae1fc';
element.style.borderColor = '#2977ff';
} else {
element.style.backgroundColor = '';
element.style.borderColor = '#ddd';
}
}
},
onchange: function (a, b, c) {
c = c.split('x');
var table = document.createElement('table');
var tbody = document.createElement('tbody');
for (var y = 0; y <= c[1]; y++) {
var tr = document.createElement('tr');
for (var x = 0; x <= c[0]; x++) {
var td = document.createElement('td');
td.innerHTML = '';
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
table.setAttribute('width', '100%');
table.setAttribute('cellpadding', '6');
table.setAttribute('cellspacing', '0');
document.execCommand('insertHTML', false, table.outerHTML);
}
});
}
return items;
}
return Component;
}
/* harmony default export */ var editor = (Editor());
;// CONCATENATED MODULE: ./src/plugins/floating.js
function Floating() {
var Component = (function (el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
type: 'big',
title: 'Untitled',
width: 510,
height: 472,
}
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Private methods
var setContent = function () {
var temp = document.createElement('div');
while (el.children[0]) {
temp.appendChild(el.children[0]);
}
obj.content = document.createElement('div');
obj.content.className = 'jfloating_content';
obj.content.innerHTML = el.innerHTML;
while (temp.children[0]) {
obj.content.appendChild(temp.children[0]);
}
obj.container = document.createElement('div');
obj.container.className = 'jfloating';
obj.container.appendChild(obj.content);
if (obj.options.title) {
obj.container.setAttribute('title', obj.options.title);
} else {
obj.container.classList.add('no-title');
}
// validate element dimensions
if (obj.options.width) {
obj.container.style.width = parseInt(obj.options.width) + 'px';
}
if (obj.options.height) {
obj.container.style.height = parseInt(obj.options.height) + 'px';
}
el.innerHTML = '';
el.appendChild(obj.container);
}
var setEvents = function () {
if (obj.container) {
obj.container.addEventListener('click', function (e) {
var rect = e.target.getBoundingClientRect();
if (e.target.classList.contains('jfloating')) {
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
setTimeout(function () {
obj.close();
}, 100);
} else {
obj.setState();
}
}
});
}
}
var setType = function () {
obj.container.classList.add('jfloating-' + obj.options.type);
}
obj.state = {
isMinized: false,
}
obj.setState = function () {
if (obj.state.isMinized) {
obj.container.classList.remove('jfloating-minimized');
} else {
obj.container.classList.add('jfloating-minimized');
}
obj.state.isMinized = !obj.state.isMinized;
}
obj.close = function () {
Components.elements.splice(Component.elements.indexOf(obj.container), 1);
obj.updatePosition();
el.remove();
}
obj.updatePosition = function () {
for (var i = 0; i < Component.elements.length; i++) {
var floating = Component.elements[i];
var prevFloating = Component.elements[i - 1];
floating.style.right = i * (prevFloating ? prevFloating.offsetWidth : floating.offsetWidth) * 1.01 + 'px';
}
}
obj.init = function () {
// Set content into root
setContent();
// Set dialog events
setEvents();
// Set dialog type
setType();
// Update floating position
Component.elements.push(obj.container);
obj.updatePosition();
el.floating = obj;
}
obj.init();
return obj;
});
Component.elements = [];
return Component;
}
/* harmony default export */ var floating = (Floating());
;// CONCATENATED MODULE: ./src/plugins/validations.js
function Validations() {
/**
* Options: Object,
* Properties:
* Constraint,
* Reference,
* Value
*/
const isNumeric = function(num) {
return !isNaN(num) && num !== null && (typeof num !== 'string' || num.trim() !== '');
}
const numberCriterias = {
'between': function(value, range) {
return value >= range[0] && value <= range[1];
},
'not between': function(value, range) {
return value < range[0] || value > range[1];
},
'<': function(value, range) {
return value < range[0];
},
'<=': function(value, range) {
return value <= range[0];
},
'>': function(value, range) {
return value > range[0];
},
'>=': function(value, range) {
return value >= range[0];
},
'=': function(value, range) {
return value == range[0];
},
'!=': function(value, range) {
return value != range[0];
},
}
const dateCriterias = {
'valid date': function() {
return true;
},
'=': function(value, range) {
return value === range[0];
},
'!=': function(value, range) {
return value !== range[0];
},
'<': function(value, range) {
return value < range[0];
},
'<=': function(value, range) {
return value <= range[0];
},
'>': function(value, range) {
return value > range[0];
},
'>=': function(value, range) {
return value >= range[0];
},
'between': function(value, range) {
return value >= range[0] && value <= range[1];
},
'not between': function(value, range) {
return value < range[0] || value > range[1];
},
}
const textCriterias = {
'contains': function(value, range) {
return value.includes(range[0]);
},
'not contains': function(value, range) {
return !value.includes(range[0]);
},
'begins with': function(value, range) {
return value.startsWith(range[0]);
},
'ends with': function(value, range) {
return value.endsWith(range[0]);
},
'=': function(value, range) {
return value === range[0];
},
'!=': function(value, range) {
return value !== range[0];
},
'valid email': function(value) {
var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
return pattern.test(value);
},
'valid url': function(value) {
var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
return pattern.test(value);
},
}
// Component router
const component = function(value, options) {
if (typeof(component[options.type]) === 'function') {
if (options.allowBlank && (typeof value === 'undefined' || value === '' || value === null)) {
return true;
}
return component[options.type].call(this, value, options);
}
return null;
}
component.url = function(data) {
var pattern = new RegExp(/(((https?:\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|]+)/ig);
return pattern.test(data) ? true : false;
}
component.email = function(data) {
var pattern = new RegExp(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
return data && pattern.test(data) ? true : false;
}
component.required = function(data) {
return data && data.trim() ? true : false;
}
component.empty = function(data) {
return typeof data === 'undefined' || data === null || (typeof data === 'string' && !data.toString().trim());
}
component['not exist'] = component.empty;
component.notEmpty = function(data) {
return !component.empty(data);
}
component.exist = component.notEmpty;
component.number = function(data, options) {
if (! isNumeric(data)) {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!numberCriterias[options.criteria]) {
return false;
}
let values = options.value.map(function(num) {
return parseFloat(num);
})
return numberCriterias[options.criteria](data, values);
};
component.login = function(data) {
let pattern = new RegExp(/^[a-zA-Z0-9._-]+$/);
return data && pattern.test(data) ? true : false;
}
component.list = function(data, options) {
let dataType = typeof data;
if (dataType !== 'string' && dataType !== 'number') {
return false;
}
let list;
if (typeof(options.value[0]) === 'string') {
if (options.source) {
list = options.source;
} else {
list = options.value[0].split(',');
}
} else {
list = options.value[0];
}
if (! Array.isArray(list)) {
return false;
} else {
let validOption = list.findIndex(function (item) {
return item == data;
});
return validOption > -1;
}
}
const getCurrentDateWithoutTime = function() {
let date = new Date();
date.setHours(0, 0, 0, 0);
return date;
}
const relativeDates = {
'one year ago': function() {
let date = getCurrentDateWithoutTime();
date.setFullYear(date.getFullYear() - 1);
return date;
},
'one month ago': function() {
let date = getCurrentDateWithoutTime();
date.setMonth(date.getMonth() - 1);
return date;
},
'one week ago': function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() - 7);
return date;
},
yesterday: function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() - 1);
return date;
},
today: getCurrentDateWithoutTime,
tomorrow: function() {
let date = getCurrentDateWithoutTime();
date.setDate(date.getDate() + 1);
return date;
},
};
component.date = function(data, options) {
if (isNumeric(data) && data > 0 && data < 1000000) {
data = helpers_date.numToDate(data);
}
if (new Date(data) == 'Invalid Date') {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!dateCriterias[options.criteria]) {
return false;
}
let values = options.value.map(function(date) {
if (typeof date === 'string' && relativeDates[date]) {
return relativeDates[date]().getTime();
}
return new Date(date).getTime();
});
return dateCriterias[options.criteria](new Date(data).getTime(), values);
}
component.text = function(data, options) {
if (typeof data === 'undefined' || data === null) {
data = '';
} else if (typeof data !== 'string') {
return false;
}
if (!options || !options.criteria) {
return true;
}
if (!textCriterias[options.criteria]) {
return false;
}
return textCriterias[options.criteria](data, options.value);
}
component.textLength = function(data, options) {
let textLength;
if (typeof data === 'string') {
textLength = data.length;
} else if (typeof data !== 'undefined' && data !== null && typeof data.toString === 'function') {
textLength = data.toString().length;
} else {
textLength = 0;
}
return component.number(textLength, options);
}
return component;
}
/* harmony default export */ var validations = (Validations());
;// CONCATENATED MODULE: ./src/plugins/form.js
function Form() {
var Component = (function(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
url: null,
message: 'Are you sure? There are unsaved information in your form',
ignore: false,
currentHash: null,
submitButton:null,
validations: null,
onbeforeload: null,
onload: null,
onbeforesave: null,
onsave: null,
onbeforeremove: null,
onremove: null,
onerror: function(el, message) {
alert(message);
}
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Validations
if (! obj.options.validations) {
obj.options.validations = {};
}
// Submit Button
if (! obj.options.submitButton) {
obj.options.submitButton = el.querySelector('input[type=submit]');
}
if (obj.options.submitButton && obj.options.url) {
obj.options.submitButton.onclick = function() {
obj.save();
}
}
if (! obj.options.validations.email) {
obj.options.validations.email = validations.email;
}
if (! obj.options.validations.length) {
obj.options.validations.length = validations.length;
}
if (! obj.options.validations.required) {
obj.options.validations.required = validations.required;
}
obj.setUrl = function(url) {
obj.options.url = url;
}
obj.load = function() {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'json',
queue: true,
success: function(data) {
// Overwrite values from the backend
if (typeof(obj.options.onbeforeload) == 'function') {
var ret = obj.options.onbeforeload(el, data);
if (ret) {
data = ret;
}
}
// Apply values to the form
Component.setElements(el, data);
// Onload methods
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, data);
}
}
});
}
obj.save = function() {
var test = obj.validate();
if (test) {
obj.options.onerror(el, test);
} else {
var data = Component.getElements(el, true);
if (typeof(obj.options.onbeforesave) == 'function') {
var data = obj.options.onbeforesave(el, data);
if (data === false) {
return;
}
}
ajax({
url: obj.options.url,
method: 'POST',
dataType: 'json',
data: data,
success: function(result) {
if (typeof(obj.options.onsave) == 'function') {
obj.options.onsave(el, data, result);
}
}
});
}
}
obj.remove = function() {
if (typeof(obj.options.onbeforeremove) == 'function') {
var ret = obj.options.onbeforeremove(el, obj);
if (ret === false) {
return false;
}
}
ajax({
url: obj.options.url,
method: 'DELETE',
dataType: 'json',
success: function(result) {
if (typeof(obj.options.onremove) == 'function') {
obj.options.onremove(el, obj, result);
}
obj.reset();
}
});
}
var addError = function(element) {
// Add error in the element
element.classList.add('error');
// Submit button
if (obj.options.submitButton) {
obj.options.submitButton.setAttribute('disabled', true);
}
// Return error message
var error = element.getAttribute('data-error') || 'There is an error in the form';
element.setAttribute('title', error);
return error;
}
var delError = function(element) {
var error = false;
// Remove class from this element
element.classList.remove('error');
element.removeAttribute('title');
// Get elements in the form
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
if (elements[i].getAttribute('data-validation')) {
if (elements[i].classList.contains('error')) {
error = true;
}
}
}
if (obj.options.submitButton) {
if (error) {
obj.options.submitButton.setAttribute('disabled', true);
} else {
obj.options.submitButton.removeAttribute('disabled');
}
}
}
obj.validateElement = function(element) {
// Test results
var test = false;
// Value
var value = Component.getValue(element);
// Validation
var validation = element.getAttribute('data-validation');
// Parse
if (typeof(obj.options.validations[validation]) == 'function' && ! obj.options.validations[validation](value, element)) {
// Not passed in the test
test = addError(element);
} else {
if (element.classList.contains('error')) {
delError(element);
}
}
return test;
}
obj.reset = function() {
// Get elements in the form
var name = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
if (name = elements[i].getAttribute('name')) {
if (elements[i].type == 'checkbox' || elements[i].type == 'radio') {
elements[i].checked = false;
} else {
if (typeof(elements[i].val) == 'function') {
elements[i].val('');
} else {
elements[i].value = '';
}
}
}
}
}
// Run form validation
obj.validate = function() {
var test = [];
// Get elements in the form
var elements = el.querySelectorAll("input, select, textarea, div[name]");
// Run all elements
for (var i = 0; i < elements.length; i++) {
// Required
if (elements[i].getAttribute('data-validation')) {
var res = obj.validateElement(elements[i]);
if (res) {
test.push(res);
}
}
}
if (test.length > 0) {
return test.join('<br>');
} else {
return false;
}
}
// Check the form
obj.getError = function() {
// Validation
return obj.validation() ? true : false;
}
// Return the form hash
obj.setHash = function() {
return obj.getHash(Component.getElements(el));
}
// Get the form hash
obj.getHash = function(str) {
var hash = 0, i, chr;
if (str.length === 0) {
return hash;
} else {
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
}
return hash;
}
// Is there any change in the form since start tracking?
obj.isChanged = function() {
var hash = obj.setHash();
return (obj.options.currentHash != hash);
}
// Restart tracking
obj.resetTracker = function() {
obj.options.currentHash = obj.setHash();
obj.options.ignore = false;
}
// Ignore flag
obj.setIgnore = function(ignoreFlag) {
obj.options.ignore = ignoreFlag ? true : false;
}
// Start tracking in one second
setTimeout(function() {
obj.options.currentHash = obj.setHash();
}, 1000);
// Validations
el.addEventListener("keyup", function(e) {
if (e.target.getAttribute('data-validation')) {
obj.validateElement(e.target);
}
});
// Alert
if (! Component.hasEvents) {
window.addEventListener("beforeunload", function (e) {
if (obj.isChanged() && obj.options.ignore == false) {
var confirmationMessage = obj.options.message? obj.options.message : "\o/";
if (confirmationMessage) {
if (typeof e == 'undefined') {
e = window.event;
}
if (e) {
e.returnValue = confirmationMessage;
}
return confirmationMessage;
} else {
return void(0);
}
}
});
Component.hasEvents = true;
}
el.form = obj;
return obj;
});
// Get value from one element
Component.getValue = function(element) {
var value = null;
if (element.type == 'checkbox') {
if (element.checked == true) {
value = element.value || true;
}
} else if (element.type == 'radio') {
if (element.checked == true) {
value = element.value;
}
} else if (element.type == 'file') {
value = element.files;
} else if (element.tagName == 'select' && element.multiple == true) {
value = [];
var options = element.querySelectorAll("options[selected]");
for (var j = 0; j < options.length; j++) {
value.push(options[j].value);
}
} else if (typeof(element.val) == 'function') {
value = element.val();
} else {
value = element.value || '';
}
return value;
}
// Get form elements
Component.getElements = function(el, asArray) {
var data = {};
var name = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
for (var i = 0; i < elements.length; i++) {
if (name = elements[i].getAttribute('name')) {
data[name] = Component.getValue(elements[i]) || '';
}
}
return asArray == true ? data : JSON.stringify(data);
}
//Get form elements
Component.setElements = function(el, data) {
var name = null;
var value = null;
var elements = el.querySelectorAll("input, select, textarea, div[name]");
for (var i = 0; i < elements.length; i++) {
// Attributes
var type = elements[i].getAttribute('type');
if (name = elements[i].getAttribute('name')) {
// Transform variable names in pathname
name = name.replace(new RegExp(/\[(.*?)\]/ig), '.$1');
value = null;
// Seach for the data in the path
if (name.match(/\./)) {
var tmp = Path.call(data, name) || '';
if (typeof(tmp) !== 'undefined') {
value = tmp;
}
} else {
if (typeof(data[name]) !== 'undefined') {
value = data[name];
}
}
// Set the values
if (value !== null) {
if (type == 'checkbox' || type == 'radio') {
elements[i].checked = value ? true : false;
} else if (type == 'file') {
// Do nothing
} else {
if (typeof (elements[i].val) == 'function') {
elements[i].val(value);
} else {
elements[i].value = value;
}
}
}
}
}
}
return Component;
}
/* harmony default export */ var plugins_form = (Form());
;// CONCATENATED MODULE: ./src/plugins/modal.js
function Modal() {
var Events = function() {
// Position
var tracker = null;
var keyDown = function (e) {
if (e.which == 27) {
var modals = document.querySelectorAll('.jmodal');
for (var i = 0; i < modals.length; i++) {
modals[i].parentNode.modal.close();
}
}
}
var mouseUp = function (e) {
let element = e.composedPath();
var item = helpers.findElement(element[0], 'jmodal');
if (item) {
// Get target info
var rect = item.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
item.parentNode.modal.close();
}
}
if (tracker) {
tracker.element.style.cursor = 'auto';
tracker = null;
}
}
var mouseDown = function (e) {
let element = e.composedPath();
var item = helpers.findElement(element[0], 'jmodal');
if (item) {
// Get target info
var rect = item.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
if (rect.width - (x - rect.left) < 50 && (y - rect.top) < 50) {
// Do nothing
} else {
if (y - rect.top < 50) {
if (document.selection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
tracker = {
left: rect.left,
top: rect.top,
x: e.clientX,
y: e.clientY,
width: rect.width,
height: rect.height,
element: item,
}
}
}
}
}
var mouseMove = function (e) {
if (tracker) {
e = e || window.event;
if (e.buttons) {
var mouseButton = e.buttons;
} else if (e.button) {
var mouseButton = e.button;
} else {
var mouseButton = e.which;
}
if (mouseButton) {
tracker.element.style.top = (tracker.top + (e.clientY - tracker.y) + (tracker.height / 2)) + 'px';
tracker.element.style.left = (tracker.left + (e.clientX - tracker.x) + (tracker.width / 2)) + 'px';
tracker.element.style.cursor = 'move';
} else {
tracker.element.style.cursor = 'auto';
}
}
}
document.addEventListener('keydown', keyDown);
document.addEventListener('mouseup', mouseUp);
document.addEventListener('mousedown', mouseDown);
document.addEventListener('mousemove', mouseMove);
}
var Component = (function (el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
url: null,
onopen: null,
onclose: null,
onload: null,
closed: false,
width: null,
height: null,
title: null,
padding: null,
backdrop: true,
icon: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Title
if (!obj.options.title && el.getAttribute('title')) {
obj.options.title = el.getAttribute('title');
}
var temp = document.createElement('div');
while (el.children[0]) {
temp.appendChild(el.children[0]);
}
obj.title = document.createElement('div');
obj.title.className = 'jmodal_title';
if (obj.options.icon) {
obj.title.setAttribute('data-icon', obj.options.icon);
}
obj.content = document.createElement('div');
obj.content.className = 'jmodal_content';
obj.content.innerHTML = el.innerHTML;
while (temp.children[0]) {
obj.content.appendChild(temp.children[0]);
}
obj.container = document.createElement('div');
obj.container.className = 'jmodal';
obj.container.appendChild(obj.title);
obj.container.appendChild(obj.content);
if (obj.options.padding) {
obj.content.style.padding = obj.options.padding;
}
if (obj.options.width) {
obj.container.style.width = obj.options.width;
}
if (obj.options.height) {
obj.container.style.height = obj.options.height;
}
if (obj.options.title) {
var title = document.createElement('h4');
title.innerText = obj.options.title;
obj.title.appendChild(title);
}
el.innerHTML = '';
el.style.display = 'none';
el.appendChild(obj.container);
// Backdrop
if (obj.options.backdrop) {
var backdrop = document.createElement('div');
backdrop.className = 'jmodal_backdrop';
backdrop.onclick = function () {
obj.close();
}
el.appendChild(backdrop);
}
obj.open = function () {
el.style.display = 'block';
// Fullscreen
var rect = obj.container.getBoundingClientRect();
if (helpers.getWindowWidth() < rect.width) {
obj.container.style.top = '';
obj.container.style.left = '';
obj.container.classList.add('jmodal_fullscreen');
animation.slideBottom(obj.container, 1);
} else {
if (obj.options.backdrop) {
backdrop.style.display = 'block';
}
}
// Event
if (typeof (obj.options.onopen) == 'function') {
obj.options.onopen(el, obj);
}
}
obj.resetPosition = function () {
obj.container.style.top = '';
obj.container.style.left = '';
}
obj.isOpen = function () {
return el.style.display != 'none' ? true : false;
}
obj.close = function () {
if (obj.isOpen()) {
el.style.display = 'none';
if (obj.options.backdrop) {
// Backdrop
backdrop.style.display = '';
}
// Remove fullscreen class
obj.container.classList.remove('jmodal_fullscreen');
// Event
if (typeof (obj.options.onclose) == 'function') {
obj.options.onclose(el, obj);
}
}
}
if (obj.options.url) {
ajax({
url: obj.options.url,
method: 'GET',
dataType: 'text/html',
success: function (data) {
obj.content.innerHTML = data;
if (!obj.options.closed) {
obj.open();
}
if (typeof (obj.options.onload) === 'function') {
obj.options.onload(obj);
}
}
});
} else {
if (!obj.options.closed) {
obj.open();
}
if (typeof (obj.options.onload) === 'function') {
obj.options.onload(obj);
}
}
// Keep object available from the node
el.modal = obj;
// Create events when the first modal is create only
Events();
// Execute the events only once
Events = function() {};
return obj;
});
return Component;
}
/* harmony default export */ var modal = (Modal());
;// CONCATENATED MODULE: ./src/plugins/notification.js
function Notification() {
var Component = function (options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
icon: null,
name: 'Notification',
date: null,
error: null,
title: null,
message: null,
timeout: 4000,
autoHide: true,
closeable: true,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
var notification = document.createElement('div');
notification.className = 'jnotification';
if (obj.options.error) {
notification.classList.add('jnotification-error');
}
var notificationContainer = document.createElement('div');
notificationContainer.className = 'jnotification-container';
notification.appendChild(notificationContainer);
var notificationHeader = document.createElement('div');
notificationHeader.className = 'jnotification-header';
notificationContainer.appendChild(notificationHeader);
var notificationImage = document.createElement('div');
notificationImage.className = 'jnotification-image';
notificationHeader.appendChild(notificationImage);
if (obj.options.icon) {
var notificationIcon = document.createElement('img');
notificationIcon.src = obj.options.icon;
notificationImage.appendChild(notificationIcon);
}
var notificationName = document.createElement('div');
notificationName.className = 'jnotification-name';
notificationName.innerHTML = obj.options.name;
notificationHeader.appendChild(notificationName);
if (obj.options.closeable == true) {
var notificationClose = document.createElement('div');
notificationClose.className = 'jnotification-close';
notificationClose.onclick = function () {
obj.hide();
}
notificationHeader.appendChild(notificationClose);
}
var notificationDate = document.createElement('div');
notificationDate.className = 'jnotification-date';
notificationHeader.appendChild(notificationDate);
var notificationContent = document.createElement('div');
notificationContent.className = 'jnotification-content';
notificationContainer.appendChild(notificationContent);
if (obj.options.title) {
var notificationTitle = document.createElement('div');
notificationTitle.className = 'jnotification-title';
notificationTitle.innerHTML = obj.options.title;
notificationContent.appendChild(notificationTitle);
}
var notificationMessage = document.createElement('div');
notificationMessage.className = 'jnotification-message';
notificationMessage.innerHTML = obj.options.message;
notificationContent.appendChild(notificationMessage);
obj.show = function () {
document.body.appendChild(notification);
if (helpers.getWindowWidth() > 800) {
animation.fadeIn(notification);
} else {
animation.slideTop(notification, 1);
}
}
obj.hide = function () {
if (helpers.getWindowWidth() > 800) {
animation.fadeOut(notification, function () {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
if (notificationTimeout) {
clearTimeout(notificationTimeout);
}
}
});
} else {
animation.slideTop(notification, 0, function () {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
if (notificationTimeout) {
clearTimeout(notificationTimeout);
}
}
});
}
};
obj.show();
if (obj.options.autoHide == true) {
var notificationTimeout = setTimeout(function () {
obj.hide();
}, obj.options.timeout);
}
if (helpers.getWindowWidth() < 800) {
notification.addEventListener("swipeup", function (e) {
obj.hide();
e.preventDefault();
e.stopPropagation();
});
}
return obj;
}
Component.isVisible = function () {
var j = document.querySelector('.jnotification');
return j && j.parentNode ? true : false;
}
return Component;
}
/* harmony default export */ var notification = (Notification());
;// CONCATENATED MODULE: ./src/plugins/progressbar.js
function Progressbar(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
value: 0,
onchange: null,
width: null,
};
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Class
el.classList.add('jprogressbar');
el.setAttribute('tabindex', 1);
el.setAttribute('data-value', obj.options.value);
var bar = document.createElement('div');
bar.style.width = obj.options.value + '%';
bar.style.color = '#fff';
el.appendChild(bar);
if (obj.options.width) {
el.style.width = obj.options.width;
}
// Set value
obj.setValue = function(value) {
value = parseInt(value);
obj.options.value = value;
bar.style.width = value + '%';
el.setAttribute('data-value', value + '%');
if (value < 6) {
el.style.color = '#000';
} else {
el.style.color = '#fff';
}
// Update value
obj.options.value = value;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, value);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
obj.getValue = function() {
return obj.options.value;
}
var action = function(e) {
if (e.which) {
// Get target info
var rect = el.getBoundingClientRect();
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
obj.setValue(Math.round((x - rect.left) / rect.width * 100));
}
}
// Events
if ('touchstart' in document.documentElement === true) {
el.addEventListener('touchstart', action);
el.addEventListener('touchend', action);
} else {
el.addEventListener('mousedown', action);
el.addEventListener("mousemove", action);
}
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.progressbar = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/rating.js
function Rating(el, options) {
// Already created, update options
if (el.rating) {
return el.rating.setOptions(options, true);
}
// New instance
var obj = {};
obj.options = {};
obj.setOptions = function(options, reset) {
// Default configuration
var defaults = {
number: 5,
value: 0,
tooltip: [ 'Very bad', 'Bad', 'Average', 'Good', 'Very good' ],
onchange: null,
};
// Loop through the initial configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Make sure the container is empty
el.innerHTML = '';
// Add elements
for (var i = 0; i < obj.options.number; i++) {
var div = document.createElement('div');
div.setAttribute('data-index', (i + 1))
div.setAttribute('title', obj.options.tooltip[i])
el.appendChild(div);
}
// Selected option
if (obj.options.value) {
for (var i = 0; i < obj.options.number; i++) {
if (i < obj.options.value) {
el.children[i].classList.add('jrating-selected');
}
}
}
return obj;
}
// Set value
obj.setValue = function(index) {
for (var i = 0; i < obj.options.number; i++) {
if (i < index) {
el.children[i].classList.add('jrating-selected');
} else {
el.children[i].classList.remove('jrating-over');
el.children[i].classList.remove('jrating-selected');
}
}
obj.options.value = index;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, index);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
obj.getValue = function() {
return obj.options.value;
}
var init = function() {
// Start plugin
obj.setOptions(options);
// Class
el.classList.add('jrating');
// Events
el.addEventListener("click", function(e) {
var index = e.target.getAttribute('data-index');
if (index != undefined) {
if (index == obj.options.value) {
obj.setValue(0);
} else {
obj.setValue(index);
}
}
});
el.addEventListener("mouseover", function(e) {
var index = e.target.getAttribute('data-index');
for (var i = 0; i < obj.options.number; i++) {
if (i < index) {
el.children[i].classList.add('jrating-over');
} else {
el.children[i].classList.remove('jrating-over');
}
}
});
el.addEventListener("mouseout", function(e) {
for (var i = 0; i < obj.options.number; i++) {
el.children[i].classList.remove('jrating-over');
}
});
// Change
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
// Reference
el.rating = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/search.js
function Search(el, options) {
if (el.search) {
return el.search;
}
var index = null;
var select = function(e) {
if (e.target.classList.contains('jsearch_item')) {
var element = e.target;
} else {
var element = e.target.parentNode;
}
obj.selectIndex(element);
e.preventDefault();
}
var createList = function(data) {
if (typeof(obj.options.onsearch) == 'function') {
var ret = obj.options.onsearch(obj, data);
if (ret) {
data = ret;
}
}
// Reset container
container.innerHTML = '';
// Print results
if (! data.length) {
// Show container
el.style.display = '';
} else {
// Show container
el.style.display = 'block';
// Show items (only 10)
var len = data.length < 11 ? data.length : 10;
for (var i = 0; i < len; i++) {
if (typeof(data[i]) == 'string') {
var text = data[i];
var value = data[i];
} else {
// Legacy
var text = data[i].text;
if (! text && data[i].name) {
text = data[i].name;
}
var value = data[i].value;
if (! value && data[i].id) {
value = data[i].id;
}
}
var div = document.createElement('div');
div.setAttribute('data-value', value);
div.setAttribute('data-text', text);
div.className = 'jsearch_item';
if (data[i].id) {
div.setAttribute('id', data[i].id)
}
if (obj.options.forceSelect && i == 0) {
div.classList.add('selected');
}
var img = document.createElement('img');
if (data[i].image) {
img.src = data[i].image;
} else {
img.style.display = 'none';
}
div.appendChild(img);
var item = document.createElement('div');
item.innerHTML = text;
div.appendChild(item);
// Append item to the container
container.appendChild(div);
}
}
}
var execute = function(str) {
if (str != obj.terms) {
// New terms
obj.terms = str;
// New index
if (obj.options.forceSelect) {
index = 0;
} else {
index = null;
}
// Array or remote search
if (Array.isArray(obj.options.data)) {
var test = function(o) {
if (typeof(o) == 'string') {
if ((''+o).toLowerCase().search(str.toLowerCase()) >= 0) {
return true;
}
} else {
for (var key in o) {
var value = o[key];
if ((''+value).toLowerCase().search(str.toLowerCase()) >= 0) {
return true;
}
}
}
return false;
}
var results = obj.options.data.filter(function(item) {
return test(item);
});
// Show items
createList(results);
} else {
// Get remove results
ajax({
url: obj.options.data + str,
method: 'GET',
dataType: 'json',
success: function(data) {
// Show items
createList(data);
}
});
}
}
}
// Search timer
var timer = null;
// Search methods
var obj = function(str) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
execute(str);
}, 500);
}
if(options.forceSelect === null) {
options.forceSelect = true;
}
obj.options = {
data: options.data || null,
input: options.input || null,
searchByNode: options.searchByNode || null,
onselect: options.onselect || null,
forceSelect: options.forceSelect,
onsearch: options.onsearch || null,
onbeforesearch: options.onbeforesearch || null,
};
obj.selectIndex = function(item) {
var id = item.getAttribute('id');
var text = item.getAttribute('data-text');
var value = item.getAttribute('data-value');
var image = item.children[0].src || '';
// Onselect
if (typeof(obj.options.onselect) == 'function') {
obj.options.onselect(obj, text, value, id, image);
}
// Close container
obj.close();
}
obj.open = function() {
el.style.display = 'block';
}
obj.close = function() {
if (timer) {
clearTimeout(timer);
}
// Current terms
obj.terms = '';
// Remove results
container.innerHTML = '';
// Hide
el.style.display = '';
}
obj.isOpened = function() {
return el.style.display ? true : false;
}
obj.keydown = function(e) {
if (obj.isOpened()) {
if (e.key == 'Enter') {
// Enter
if (index!==null && container.children[index]) {
obj.selectIndex(container.children[index]);
e.preventDefault();
} else {
obj.close();
}
} else if (e.key === 'ArrowUp') {
// Up
if (index!==null && container.children[0]) {
container.children[index].classList.remove('selected');
if(!obj.options.forceSelect && index === 0) {
index = null;
} else {
index = Math.max(0, index-1);
container.children[index].classList.add('selected');
}
}
e.preventDefault();
} else if (e.key === 'ArrowDown') {
// Down
if(index == null) {
index = -1;
} else {
container.children[index].classList.remove('selected');
}
if (index < 9 && container.children[index+1]) {
index++;
}
container.children[index].classList.add('selected');
e.preventDefault();
}
}
}
obj.keyup = function(e) {
if (! obj.options.searchByNode && obj.options.input) {
if (obj.options.input.tagName === 'DIV') {
var terms = obj.options.input.innerText;
} else {
var terms = obj.options.input.value;
}
} else {
// Current node
var node = helpers.getNode();
if (node) {
var terms = node.innerText;
}
}
if (typeof(obj.options.onbeforesearch) == 'function') {
var ret = obj.options.onbeforesearch(obj, terms);
if (ret) {
terms = ret;
} else {
if (ret === false) {
// Ignore event
return;
}
}
}
obj(terms);
}
obj.blur = function(e) {
obj.close();
}
// Add events
if (obj.options.input) {
obj.options.input.addEventListener("keyup", obj.keyup);
obj.options.input.addEventListener("keydown", obj.keydown);
obj.options.input.addEventListener("blur", obj.blur);
}
// Append element
var container = document.createElement('div');
container.classList.add('jsearch_container');
container.onmousedown = select;
el.appendChild(container);
el.classList.add('jsearch');
el.search = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/slider.js
function Slider(el, options) {
var obj = {};
obj.options = {};
obj.currentImage = null;
if (options) {
obj.options = options;
}
// Focus
el.setAttribute('tabindex', '900')
// Items
obj.options.items = [];
if (! el.classList.contains('jslider')) {
el.classList.add('jslider');
el.classList.add('unselectable');
if (obj.options.height) {
el.style.minHeight = parseInt(obj.options.height) + 'px';
}
if (obj.options.width) {
el.style.width = parseInt(obj.options.width) + 'px';
}
if (obj.options.grid) {
el.classList.add('jslider-grid');
var number = el.children.length;
if (number > 4) {
el.setAttribute('data-total', number - 4);
}
el.setAttribute('data-number', (number > 4 ? 4 : number));
}
// Add slider counter
var counter = document.createElement('div');
counter.classList.add('jslider-counter');
// Move children inside
if (el.children.length > 0) {
// Keep children items
for (var i = 0; i < el.children.length; i++) {
obj.options.items.push(el.children[i]);
// counter click event
var item = document.createElement('div');
item.onclick = function() {
var index = Array.prototype.slice.call(counter.children).indexOf(this);
obj.show(obj.currentImage = obj.options.items[index]);
}
counter.appendChild(item);
}
}
// Add caption
var caption = document.createElement('div');
caption.className = 'jslider-caption';
// Add close buttom
var controls = document.createElement('div');
var close = document.createElement('div');
close.className = 'jslider-close';
close.innerHTML = '';
close.onclick = function() {
obj.close();
}
controls.appendChild(caption);
controls.appendChild(close);
}
obj.updateCounter = function(index) {
for (var i = 0; i < counter.children.length; i ++) {
if (counter.children[i].classList.contains('jslider-counter-focus')) {
counter.children[i].classList.remove('jslider-counter-focus');
break;
}
}
counter.children[index].classList.add('jslider-counter-focus');
}
obj.show = function(target) {
if (! target) {
var target = el.children[0];
}
// Focus element
el.classList.add('jslider-focus');
el.classList.remove('jslider-grid');
el.appendChild(controls);
el.appendChild(counter);
// Update counter
var index = obj.options.items.indexOf(target);
obj.updateCounter(index);
// Remove display
for (var i = 0; i < el.children.length; i++) {
el.children[i].style.display = '';
}
target.style.display = 'block';
// Is there any previous
if (target.previousElementSibling) {
el.classList.add('jslider-left');
} else {
el.classList.remove('jslider-left');
}
// Is there any next
if (target.nextElementSibling && target.nextElementSibling.tagName == 'IMG') {
el.classList.add('jslider-right');
} else {
el.classList.remove('jslider-right');
}
obj.currentImage = target;
// Vertical image
if (obj.currentImage.offsetHeight > obj.currentImage.offsetWidth) {
obj.currentImage.classList.add('jslider-vertical');
}
controls.children[0].innerText = obj.currentImage.getAttribute('title');
}
obj.open = function() {
obj.show();
// Event
if (typeof(obj.options.onopen) == 'function') {
obj.options.onopen(el);
}
}
obj.close = function() {
// Remove control classes
el.classList.remove('jslider-focus');
el.classList.remove('jslider-left');
el.classList.remove('jslider-right');
// Show as a grid depending on the configuration
if (obj.options.grid) {
el.classList.add('jslider-grid');
}
// Remove display
for (var i = 0; i < el.children.length; i++) {
el.children[i].style.display = '';
}
// Remove controls from the component
counter.remove();
controls.remove();
// Current image
obj.currentImage = null;
// Event
if (typeof(obj.options.onclose) == 'function') {
obj.options.onclose(el);
}
}
obj.reset = function() {
el.innerHTML = '';
}
obj.next = function() {
var nextImage = obj.currentImage.nextElementSibling;
if (nextImage && nextImage.tagName === 'IMG') {
obj.show(obj.currentImage.nextElementSibling);
}
}
obj.prev = function() {
if (obj.currentImage.previousElementSibling) {
obj.show(obj.currentImage.previousElementSibling);
}
}
var mouseUp = function(e) {
// Open slider
if (e.target.tagName == 'IMG') {
obj.show(e.target);
} else if (! e.target.classList.contains('jslider-close') && ! (e.target.parentNode.classList.contains('jslider-counter') || e.target.classList.contains('jslider-counter'))){
// Arrow controls
var offsetX = e.offsetX || e.changedTouches[0].clientX;
if (e.target.clientWidth - offsetX < 40) {
// Show next image
obj.next();
} else if (offsetX < 40) {
// Show previous image
obj.prev();
}
}
}
if ('ontouchend' in document.documentElement === true) {
el.addEventListener('touchend', mouseUp);
} else {
el.addEventListener('mouseup', mouseUp);
}
// Add global events
el.addEventListener("swipeleft", function(e) {
obj.next();
e.preventDefault();
e.stopPropagation();
});
el.addEventListener("swiperight", function(e) {
obj.prev();
e.preventDefault();
e.stopPropagation();
});
el.addEventListener('keydown', function(e) {
if (e.which == 27) {
obj.close();
}
});
el.slider = obj;
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/tags.js
function Tags(el, options) {
// Redefine configuration
if (el.tags) {
return el.tags.setOptions(options, true);
}
var obj = { type:'tags' };
obj.options = {};
// Limit
var limit = function() {
return obj.options.limit && el.children.length >= obj.options.limit ? true : false;
}
// Search helpers
var search = null;
var searchContainer = null;
obj.setOptions = function(options, reset) {
/**
* @typedef {Object} defaults
* @property {(string|Array)} value - Initial value of the compontent
* @property {number} limit - Max number of tags inside the element
* @property {string} search - The URL for suggestions
* @property {string} placeholder - The default instruction text on the element
* @property {validation} validation - Method to validate the tags
* @property {requestCallback} onbeforechange - Method to be execute before any changes on the element
* @property {requestCallback} onchange - Method to be execute after any changes on the element
* @property {requestCallback} onfocus - Method to be execute when on focus
* @property {requestCallback} onblur - Method to be execute when on blur
* @property {requestCallback} onload - Method to be execute when the element is loaded
*/
var defaults = {
value: '',
limit: null,
search: null,
placeholder: null,
validation: null,
onbeforepaste: null,
onbeforechange: null,
onremoveitem: null,
onlimit: null,
onchange: null,
onfocus: null,
onblur: null,
onload: null,
}
// Loop through though the default configuration
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
if (typeof(obj.options[property]) == 'undefined' || reset === true) {
obj.options[property] = defaults[property];
}
}
}
// Placeholder
if (obj.options.placeholder) {
el.setAttribute('data-placeholder', obj.options.placeholder);
} else {
el.removeAttribute('data-placeholder');
}
el.placeholder = obj.options.placeholder;
// Update value
obj.setValue(obj.options.value);
// Validate items
filter();
// Create search box
if (obj.options.search) {
if (! searchContainer) {
searchContainer = document.createElement('div');
el.parentNode.insertBefore(searchContainer, el.nextSibling);
// Create container
search = Search(searchContainer, {
data: obj.options.search,
onselect: function(a,b,c) {
obj.selectIndex(b,c);
}
});
}
} else {
if (searchContainer) {
search = null;
searchContainer.remove();
searchContainer = null;
}
}
return obj;
}
/**
* Add a new tag to the element
* @param {(?string|Array)} value - The value of the new element
*/
obj.add = function(value, focus) {
if (typeof(obj.options.onbeforechange) == 'function') {
var ret = obj.options.onbeforechange(el, obj, obj.options.value, value);
if (ret === false) {
return false;
} else {
if (ret != null) {
value = ret;
}
}
}
// Make sure search is closed
if (search) {
search.close();
}
if (limit()) {
if (typeof(obj.options.onlimit) == 'function') {
obj.options.onlimit(obj, obj.options.limit);
} else {
alert(dictionary.translate('You reach the limit number of entries') + ' ' + obj.options.limit);
}
} else {
// Get node
var node = helpers.getNode();
if (node && node.parentNode && node.parentNode.classList.contains('jtags') &&
node.nextSibling && (! (node.nextSibling.innerText && node.nextSibling.innerText.trim()))) {
div = node.nextSibling;
} else {
// Remove not used last item
if (el.lastChild) {
if (! el.lastChild.innerText.trim()) {
el.removeChild(el.lastChild);
}
}
// Mix argument string or array
if (! value || typeof(value) == 'string') {
var div = createElement(value, value, node);
} else {
for (var i = 0; i <= value.length; i++) {
if (! limit()) {
if (! value[i] || typeof(value[i]) == 'string') {
var t = value[i] || '';
var v = null;
} else {
var t = value[i].text;
var v = value[i].value;
}
// Add element
var div = createElement(t, v);
}
}
}
// Change
change();
}
// Place caret
if (focus) {
setFocus(div);
}
}
}
obj.setLimit = function(limit) {
obj.options.limit = limit;
var n = el.children.length - limit;
while (el.children.length > limit) {
el.removeChild(el.lastChild);
}
}
// Remove a item node
obj.remove = function(node) {
// Remove node
node.parentNode.removeChild(node);
// Make sure element is not blank
if (! el.children.length) {
obj.add('', true);
} else {
change();
}
if (typeof(obj.options.onremoveitem) == 'function') {
obj.options.onremoveitem(el, obj, node);
}
}
/**
* Get all tags in the element
* @return {Array} data - All tags as an array
*/
obj.getData = function() {
var data = [];
for (var i = 0; i < el.children.length; i++) {
// Get value
var text = el.children[i].innerText.replace("\n", "");
// Get id
var value = el.children[i].getAttribute('data-value');
if (! value) {
value = text;
}
// Item
if (text || value) {
data.push({ text: text, value: value });
}
}
return data;
}
/**
* Get the value of one tag. Null for all tags
* @param {?number} index - Tag index number. Null for all tags.
* @return {string} value - All tags separated by comma
*/
obj.getValue = function(index) {
var value = null;
if (index != null) {
// Get one individual value
value = el.children[index].getAttribute('data-value');
if (! value) {
value = el.children[index].innerText.replace("\n", "");
}
} else {
// Get all
var data = [];
for (var i = 0; i < el.children.length; i++) {
value = el.children[i].innerText.replace("\n", "");
if (value) {
data.push(obj.getValue(i));
}
}
value = data.join(',');
}
return value;
}
/**
* Set the value of the element based on a string separeted by (,|;|\r\n)
* @param {mixed} value - A string or array object with values
*/
obj.setValue = function(mixed) {
if (! mixed) {
obj.reset();
} else {
if (el.value != mixed) {
if (Array.isArray(mixed)) {
obj.add(mixed);
} else {
// Remove whitespaces
var text = (''+mixed).trim();
// Tags
var data = extractTags(text);
// Reset
el.innerHTML = '';
// Add tags to the element
obj.add(data);
}
}
}
}
/**
* Reset the data from the element
*/
obj.reset = function() {
// Empty class
el.classList.add('jtags-empty');
// Empty element
el.innerHTML = '<div></div>';
// Execute changes
change();
}
/**
* Verify if all tags in the element are valid
* @return {boolean}
*/
obj.isValid = function() {
var test = 0;
for (var i = 0; i < el.children.length; i++) {
if (el.children[i].classList.contains('jtags_error')) {
test++;
}
}
return test == 0 ? true : false;
}
/**
* Add one element from the suggestions to the element
* @param {object} item - Node element in the suggestions container
*/
obj.selectIndex = function(text, value) {
var node = helpers.getNode();
if (node) {
// Append text to the caret
node.innerText = text;
// Set node id
if (value) {
node.setAttribute('data-value', value);
}
// Remove any error
node.classList.remove('jtags_error');
if (! limit()) {
// Add new item
obj.add('', true);
}
}
}
/**
* Search for suggestions
* @param {object} node - Target node for any suggestions
*/
obj.search = function(node) {
// Search for
var terms = node.innerText;
}
// Destroy tags element
obj.destroy = function() {
// Bind events
el.removeEventListener('mouseup', tagsMouseUp);
el.removeEventListener('keydown', tagsKeyDown);
el.removeEventListener('keyup', tagsKeyUp);
el.removeEventListener('paste', tagsPaste);
el.removeEventListener('focus', tagsFocus);
el.removeEventListener('blur', tagsBlur);
// Remove element
el.parentNode.removeChild(el);
}
var setFocus = function(node) {
if (el.children.length) {
var range = document.createRange();
var sel = window.getSelection();
if (! node) {
var node = el.childNodes[el.childNodes.length-1];
}
range.setStart(node, node.length)
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
el.scrollLeft = el.scrollWidth;
}
}
var createElement = function(label, value, node) {
var div = document.createElement('div');
div.textContent = label ? label : '';
if (value) {
div.setAttribute('data-value', value);
}
if (node && node.parentNode.classList.contains('jtags')) {
el.insertBefore(div, node.nextSibling);
} else {
el.appendChild(div);
}
return div;
}
var change = function() {
// Value
var value = obj.getValue();
if (value != obj.options.value) {
obj.options.value = value;
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, obj, obj.options.value);
}
// Lemonade JS
if (el.value != obj.options.value) {
el.value = obj.options.value;
if (typeof(el.oninput) == 'function') {
el.oninput({
type: 'input',
target: el,
value: el.value
});
}
}
}
filter();
}
/**
* Filter tags
*/
var filter = function() {
for (var i = 0; i < el.children.length; i++) {
if (el.children[i].tagName === 'DIV') {
// Create label design
if (!obj.getValue(i)) {
el.children[i].classList.remove('jtags_label');
} else {
el.children[i].classList.add('jtags_label');
// Validation in place
if (typeof (obj.options.validation) == 'function') {
if (obj.getValue(i)) {
if (!obj.options.validation(el.children[i], el.children[i].innerText, el.children[i].getAttribute('data-value'))) {
el.children[i].classList.add('jtags_error');
} else {
el.children[i].classList.remove('jtags_error');
}
} else {
el.children[i].classList.remove('jtags_error');
}
} else {
el.children[i].classList.remove('jtags_error');
}
}
}
}
isEmpty();
}
var isEmpty = function() {
// Can't be empty
if (! el.innerText.trim()) {
if (! el.children.length || el.children[0].tagName === 'BR') {
el.innerHTML = '';
setFocus(createElement());
}
} else {
el.classList.remove('jtags-empty');
}
}
/**
* Extract tags from a string
* @param {string} text - Raw string
* @return {Array} data - Array with extracted tags
*/
var extractTags = function(text) {
/** @type {Array} */
var data = [];
/** @type {string} */
var word = '';
// Remove whitespaces
text = text.trim();
if (text) {
for (var i = 0; i < text.length; i++) {
if (text[i] == ',' || text[i] == ';' || text[i] == '\n') {
if (word) {
data.push(word.trim());
word = '';
}
} else {
word += text[i];
}
}
if (word) {
data.push(word);
}
}
return data;
}
/** @type {number} */
var anchorOffset = 0;
/**
* Processing event keydown on the element
* @param e {object}
*/
var tagsKeyDown = function(e) {
// Anchoroffset
anchorOffset = window.getSelection().anchorOffset;
// Verify if is empty
isEmpty();
// Comma
if (e.key === 'Tab' || e.key === ';' || e.key === ',') {
var n = window.getSelection().anchorOffset;
if (n > 1) {
if (limit()) {
if (typeof(obj.options.onlimit) == 'function') {
obj.options.onlimit(obj, obj.options.limit)
}
} else {
obj.add('', true);
}
}
e.preventDefault();
} else if (e.key == 'Enter') {
if (! search || ! search.isOpened()) {
var n = window.getSelection().anchorOffset;
if (n > 1) {
if (! limit()) {
obj.add('', true);
}
}
e.preventDefault();
}
} else if (e.key == 'Backspace') {
// Back space - do not let last item to be removed
if (el.children.length == 1 && window.getSelection().anchorOffset < 1) {
e.preventDefault();
}
}
// Search events
if (search) {
search.keydown(e);
}
// Verify if is empty
isEmpty();
}
/**
* Processing event keyup on the element
* @param e {object}
*/
var tagsKeyUp = function(e) {
if (e.which == 39) {
// Right arrow
var n = window.getSelection().anchorOffset;
if (n > 1 && n == anchorOffset) {
obj.add('', true);
}
} else if (e.which == 13 || e.which == 38 || e.which == 40) {
e.preventDefault();
} else {
if (search) {
search.keyup(e);
}
}
filter();
}
/**
* Processing event paste on the element
* @param e {object}
*/
var tagsPaste = function(e) {
if (e.clipboardData || e.originalEvent.clipboardData) {
var text = (e.originalEvent || e).clipboardData.getData('text/plain');
} else if (window.clipboardData) {
var text = window.clipboardData.getData('Text');
}
var data = extractTags(text);
if (typeof(obj.options.onbeforepaste) == 'function') {
var ret = obj.options.onbeforepaste(el, obj, data);
if (ret === false) {
e.preventDefault();
return false;
} else {
if (ret) {
data = ret;
}
}
}
if (data.length > 1) {
obj.add(data, true);
e.preventDefault();
} else if (data[0]) {
document.execCommand('insertText', false, data[0])
e.preventDefault();
}
}
/**
* Processing event mouseup on the element
* @param e {object}
*/
var tagsMouseUp = function(e) {
if (e.target.parentNode && e.target.parentNode.classList.contains('jtags')) {
if (e.target.classList.contains('jtags_label') || e.target.classList.contains('jtags_error')) {
var rect = e.target.getBoundingClientRect();
if (rect.width - (e.clientX - rect.left) < 16) {
obj.remove(e.target);
}
}
}
// Set focus in the last item
if (e.target == el) {
setFocus();
}
}
var tagsFocus = function() {
if (! el.classList.contains('jtags-focus')) {
if (! el.children.length || obj.getValue(el.children.length - 1)) {
if (! limit()) {
createElement('');
}
}
if (typeof(obj.options.onfocus) == 'function') {
obj.options.onfocus(el, obj, obj.getValue());
}
el.classList.add('jtags-focus');
}
}
var tagsBlur = function() {
if (el.classList.contains('jtags-focus')) {
if (search) {
search.close();
}
for (var i = 0; i < el.children.length - 1; i++) {
// Create label design
if (! obj.getValue(i)) {
el.removeChild(el.children[i]);
}
}
change();
el.classList.remove('jtags-focus');
if (typeof(obj.options.onblur) == 'function') {
obj.options.onblur(el, obj, obj.getValue());
}
}
}
var init = function() {
// Bind events
if ('touchend' in document.documentElement === true) {
el.addEventListener('touchend', tagsMouseUp);
} else {
el.addEventListener('mouseup', tagsMouseUp);
}
el.addEventListener('keydown', tagsKeyDown);
el.addEventListener('keyup', tagsKeyUp);
el.addEventListener('paste', tagsPaste);
el.addEventListener('focus', tagsFocus);
el.addEventListener('blur', tagsBlur);
// Editable
el.setAttribute('contenteditable', true);
// Prepare container
el.classList.add('jtags');
// Initial options
obj.setOptions(options);
if (typeof(obj.options.onload) == 'function') {
obj.options.onload(el, obj);
}
// Change methods
el.change = obj.setValue;
// Global generic value handler
el.val = function(val) {
if (val === undefined) {
return obj.getValue();
} else {
obj.setValue(val);
}
}
el.tags = obj;
}
init();
return obj;
}
;// CONCATENATED MODULE: ./src/plugins/upload.js
function Upload(el, options) {
var obj = {};
obj.options = {};
// Default configuration
var defaults = {
type: 'image',
extension: '*',
input: false,
minWidth: false,
maxWidth: null,
maxHeight: null,
maxJpegSizeBytes: null, // For example, 350Kb would be 350000
onchange: null,
multiple: false,
remoteParser: null,
};
// Loop through our object
for (var property in defaults) {
if (options && options.hasOwnProperty(property)) {
obj.options[property] = options[property];
} else {
obj.options[property] = defaults[property];
}
}
// Multiple
if (obj.options.multiple == true) {
el.setAttribute('data-multiple', true);
}
// Container
el.content = [];
// Upload icon
el.classList.add('jupload');
if (obj.options.input == true) {
el.classList.add('input');
}
obj.add = function(data) {
// Reset container for single files
if (obj.options.multiple == false) {
el.content = [];
el.innerText = '';
}
// Append to the element
if (obj.options.type == 'image') {
var img = document.createElement('img');
img.setAttribute('src', data.file);
img.setAttribute('tabindex', -1);
if (! el.getAttribute('name')) {
img.className = 'jfile';
img.content = data;
}
el.appendChild(img);
} else {
if (data.name) {
var name = data.name;
} else {
var name = data.file;
}
var div = document.createElement('div');
div.innerText = name || obj.options.type;
div.classList.add('jupload-item');
div.setAttribute('tabindex', -1);
el.appendChild(div);
}
if (data.content) {
data.file = helpers.guid();
}
// Push content
el.content.push(data);
// Onchange
if (typeof(obj.options.onchange) == 'function') {
obj.options.onchange(el, data);
}
}
obj.addFromFile = function(file) {
var type = file.type.split('/');
if (type[0] == obj.options.type) {
var readFile = new FileReader();
readFile.addEventListener("load", function (v) {
var data = {
file: v.srcElement.result,
extension: file.name.substr(file.name.lastIndexOf('.') + 1),
name: file.name,
size: file.size,
lastmodified: file.lastModified,
content: v.srcElement.result,
}
obj.add(data);
});
readFile.readAsDataURL(file);
} else {
alert(dictionary.translate('This extension is not allowed'));
}
}
obj.addFromUrl = function(src) {
if (src.substr(0,4) != 'data' && ! obj.options.remoteParser) {
console.error('remoteParser not defined in your initialization');
} else {
// This is to process cross domain images
if (src.substr(0,4) == 'data') {
var extension = src.split(';')
extension = extension[0].split('/');
var type = extension[0].replace('data:','');
if (type == obj.options.type) {
var data = {
file: src,
name: '',
extension: extension[1],
content: src,
}
obj.add(data);
} else {
alert(obj.options.text.extensionNotAllowed);
}
} else {
var extension = src.substr(src.lastIndexOf('.') + 1);
// Work for cross browsers
src = obj.options.remoteParser + src;
// Get remove content
ajax({
url: src,
type: 'GET',
dataType: 'blob',
success: function(data) {
//add(extension[0].replace('data:',''), data);
}
})
}
}
}
var getDataURL = function(canvas, type) {
var compression = 0.92;
var lastContentLength = null;
var content = canvas.toDataURL(type, compression);
while (obj.options.maxJpegSizeBytes && type === 'image/jpeg' &&
content.length > obj.options.maxJpegSizeBytes && content.length !== lastContentLength) {
// Apply the compression
compression *= 0.9;
lastContentLength = content.length;
content = canvas.toDataURL(type, compression);
}
return content;
}
var mime = obj.options.type + '/' + obj.options.extension;
var input = document.createElement('input');
input.type = 'file';
input.setAttribute('accept', mime);
input.onchange = function() {
for (var i = 0; i < this.files.length; i++) {
obj.addFromFile(this.files[i]);
}
}
// Allow multiple files
if (obj.options.multiple == true) {
input.setAttribute('multiple', true);
}
var current = null;
el.addEventListener("click", function(e) {
current = null;
if (! el.children.length || e.target === el) {
helpers.click(input);
} else {
if (e.target.parentNode == el) {
current = e.target;
}
}
});
el.addEventListener("dblclick", function(e) {
helpers.click(input);
});
el.addEventListener('dragenter', function(e) {
el.style.border = '1px dashed #000';
});
el.addEventListener('dragleave', function(e) {
el.style.border = '1px solid #eee';
});
el.addEventListener('dragstop', function(e) {
el.style.border = '1px solid #eee';
});
el.addEventListener('dragover', function(e) {
e.preventDefault();
});
el.addEventListener('keydown', function(e) {
if (current && e.which == 46) {
var index = Array.prototype.indexOf.call(el.children, current);
if (index >= 0) {
el.content.splice(index, 1);
current.remove();
current = null;
}
}
});
el.addEventListener('drop', function(e) {
e.preventDefault();
e.stopPropagation();
var html = (e.originalEvent || e).dataTransfer.getData('text/html');
var file = (e.originalEvent || e).dataTransfer.files;
if (file.length) {
for (var i = 0; i < e.dataTransfer.files.length; i++) {
obj.addFromFile(e.dataTransfer.files[i]);
}
} else if (html) {
if (obj.options.multiple == false) {
el.innerText = '';
}
// Create temp element
var div = document.createElement('div');
div.innerHTML = html;
// Extract images
var img = div.querySelectorAll('img');
if (img.length) {
for (var i = 0; i < img.length; i++) {
obj.addFromUrl(img[i].src);
}
}
}
el.style.border = '1px solid #eee';
return false;
});
el.val = function(val) {
if (val === undefined) {
return el.content && el.content.length ? el.content : null;
} else {
// Reset
el.innerText = '';
el.content = [];
if (val) {
if (Array.isArray(val)) {
for (var i = 0; i < val.length; i++) {
if (typeof(val[i]) == 'string') {
obj.add({ file: val[i] });
} else {
obj.add(val[i]);
}
}
} else if (typeof(val) == 'string') {
obj.add({ file: val });
}
}
}
}
el.upload = el.image = obj;
return obj;
}
// EXTERNAL MODULE: ./packages/sha512/sha512.js
var sha512 = __webpack_require__(195);
var sha512_default = /*#__PURE__*/__webpack_require__.n(sha512);
;// CONCATENATED MODULE: ./src/jsuites.js
var jsuites_jSuites = {
// Helpers
...dictionary,
...helpers,
/** Current version */
version: '5.8.6',
/** Bind new extensions to Jsuites */
setExtensions: function(o) {
if (typeof(o) == 'object') {
var k = Object.keys(o);
for (var i = 0; i < k.length; i++) {
jsuites_jSuites[k[i]] = o[k[i]];
}
}
},
tracking: tracking,
path: Path,
sorting: Sorting,
lazyLoading: LazyLoading,
// Plugins
ajax: ajax,
animation: animation,
calendar: calendar,
color: Color,
contextmenu: contextmenu,
dropdown: dropdown,
editor: editor,
floating: floating,
form: plugins_form,
mask: mask,
modal: modal,
notification: notification,
palette: palette,
picker: Picker,
progressbar: Progressbar,
rating: Rating,
search: Search,
slider: Slider,
tabs: Tabs,
tags: Tags,
toolbar: Toolbar,
upload: Upload,
validations: validations,
}
// Legacy
jsuites_jSuites.image = Upload;
jsuites_jSuites.image.create = function(data) {
var img = document.createElement('img');
img.setAttribute('src', data.file);
img.className = 'jfile';
img.setAttribute('tabindex', -1);
img.content = data;
return img;
}
jsuites_jSuites.tracker = plugins_form;
jsuites_jSuites.loading = animation.loading;
jsuites_jSuites.sha512 = (sha512_default());
/** Core events */
const Events = function() {
if (typeof(window['jSuitesStateControl']) === 'undefined') {
window['jSuitesStateControl'] = [];
} else {
// Do nothing
return;
}
const find = function(DOMElement, component) {
if (DOMElement[component.type] && DOMElement[component.type] == component) {
return true;
}
if (DOMElement.component && DOMElement.component == component) {
return true;
}
if (DOMElement.parentNode) {
return find(DOMElement.parentNode, component);
}
return false;
}
const isOpened = function(e) {
let state = window['jSuitesStateControl'];
if (state && state.length > 0) {
for (let i = 0; i < state.length; i++) {
if (state[i] && ! find(e, state[i])) {
state[i].close();
}
}
}
}
// Width of the border
let cornerSize = 15;
// Current element
let element = null;
// Controllers
let editorAction = false;
// Event state
let state = {
x: null,
y: null,
}
// Tooltip element
let tooltip = document.createElement('div')
tooltip.classList.add('jtooltip');
const isWebcomponent = function(e) {
return e && (e.shadowRoot || (e.tagName && e.tagName.includes('-')));
}
const getElement = function(e) {
let d;
let element;
// Which component I am clicking
let path = e.path || (e.composedPath && e.composedPath());
// If path available get the first element in the chain
if (path) {
element = path[0];
// Adjustment sales force
if (element && isWebcomponent(element) && ! element.shadowRoot && e.toElement) {
element = e.toElement;
}
} else {
// Try to guess using the coordinates
if (e.target && isWebcomponent(e.target)) {
d = e.target.shadowRoot;
} else {
d = document;
}
// Get the first target element
element = d.elementFromPoint(x, y);
}
return element;
}
// Events
const mouseDown = function(e) {
// Verify current components tracking
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
let element = getElement(e);
// Editable
let editable = element && element.tagName === 'DIV' && element.getAttribute('contentEditable');
// Check if this is the floating
let item = jsuites_jSuites.findElement(element, 'jpanel');
// Jfloating found
if (item && ! item.classList.contains("readonly") && ! editable) {
// Keep the tracking information
let rect = item.getBoundingClientRect();
let angle = 0;
if (item.style.rotate) {
// Extract the angle value from the match and convert it to a number
angle = parseFloat(item.style.rotate);
}
let action = 'move';
if (element.getAttribute('data-action')) {
action = element.getAttribute('data-action');
} else {
if (item.style.cursor) {
action = 'resize';
} else {
item.style.cursor = 'move';
}
}
// Action
editorAction = {
action: action,
a: angle,
e: item,
x: x,
y: y,
l: rect.left,
t: rect.top,
b: rect.bottom,
r: rect.right,
w: rect.width,
h: rect.height,
d: item.style.cursor,
actioned: false,
}
// Make sure width and height styling is OK
if (! item.style.width) {
item.style.width = rect.width + 'px';
}
if (! item.style.height) {
item.style.height = rect.height + 'px';
}
} else {
// No floating action found
editorAction = false;
}
isOpened(element);
focus(e);
}
const calculateAngle = function(x1, y1, x2, y2, x3, y3) {
// Calculate dx and dy for the first line
const dx1 = x2 - x1;
const dy1 = y2 - y1;
// Calculate dx and dy for the second line
const dx2 = x3 - x1;
const dy2 = y3 - y1;
// Calculate the angle for the first line
let angle1 = Math.atan2(dy1, dx1);
// Calculate the angle for the second line
let angle2 = Math.atan2(dy2, dx2);
// Calculate the angle difference in radians
let angleDifference = angle2 - angle1;
// Convert the angle difference to degrees
angleDifference = angleDifference * (180 / Math.PI);
// Normalize the angle difference to be within [0, 360) degrees
if (angleDifference < 0) {
angleDifference += 360;
}
return angleDifference;
}
const mouseUp = function(e) {
if (editorAction && editorAction.e) {
if (typeof(editorAction.e.refresh) == 'function' && state.actioned) {
editorAction.e.refresh();
}
editorAction.e.style.cursor = '';
}
// Reset
state = {
x: null,
y: null,
}
editorAction = false;
}
const mouseMove = function(e) {
if (editorAction) {
let x = e.clientX || e.pageX;
let y = e.clientY || e.pageY;
if (state.x == null && state.y == null) {
state.x = x;
state.y = y;
}
// Action on going
if (editorAction.action === 'move') {
var dx = x - state.x;
var dy = y - state.y;
var top = editorAction.e.offsetTop + dy;
var left = editorAction.e.offsetLeft + dx;
// Update position
editorAction.e.style.top = top + 'px';
editorAction.e.style.left = left + 'px';
// Update element
if (typeof (editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('position', top, left);
}
} else if (editorAction.action === 'rotate') {
let ox = editorAction.l+editorAction.w/2;
let oy = editorAction.t+editorAction.h/2;
let angle = calculateAngle(ox, oy, editorAction.x, editorAction.y, x, y);
angle = angle + editorAction.a % 360;
angle = Math.round(angle / 2) * 2;
editorAction.e.style.rotate = `${angle}deg`;
// Update element
if (typeof (editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('rotate', angle);
}
} else if (editorAction.action === 'resize') {
let top = null;
let left = null;
let width = null;
let height = null;
if (editorAction.d == 'e-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'se-resize') {
width = editorAction.e.offsetWidth + (x - state.x);
if (e.shiftKey) {
height = editorAction.e.offsetHeight + (x - state.x) * (editorAction.e.offsetHeight / editorAction.e.offsetWidth);
}
} else if (editorAction.d === 'w-resize' || editorAction.d == 'nw-resize'|| editorAction.d == 'sw-resize') {
left = editorAction.e.offsetLeft + (x - state.x);
width = editorAction.e.offsetLeft + editorAction.e.offsetWidth - left;
if (e.shiftKey) {
height = editorAction.e.offsetHeight - (x - state.x) * (editorAction.e.offsetHeight / editorAction.e.offsetWidth);
}
}
if (editorAction.d == 's-resize' || editorAction.d == 'se-resize' || editorAction.d == 'sw-resize') {
if (! height) {
height = editorAction.e.offsetHeight + (y - state.y);
}
} else if (editorAction.d === 'n-resize' || editorAction.d == 'ne-resize' || editorAction.d == 'nw-resize') {
top = editorAction.e.offsetTop + (y - state.y);
height = editorAction.e.offsetTop + editorAction.e.offsetHeight - top;
}
if (top) {
editorAction.e.style.top = top + 'px';
}
if (left) {
editorAction.e.style.left = left + 'px';
}
if (width) {
editorAction.e.style.width = width + 'px';
}
if (height) {
editorAction.e.style.height = height + 'px';
}
// Update element
if (typeof(editorAction.e.refresh) == 'function') {
state.actioned = true;
editorAction.e.refresh('dimensions', width, height);
}
}
state.x = x;
state.y = y;
} else {
let element = getElement(e);
// Resize action
let item = jsuites_jSuites.findElement(element, 'jpanel');
// Found eligible component
if (item) {
// Resizing action
let controls = item.classList.contains('jpanel-controls');
if (controls) {
let position = element.getAttribute('data-position');
if (position) {
item.style.cursor = position;
} else {
item.style.cursor = '';
}
} else if (item.getAttribute('tabindex')) {
let rect = item.getBoundingClientRect();
//console.log(e.clientY - rect.top, rect.width - (e.clientX - rect.left), cornerSize)
if (e.clientY - rect.top < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'ne-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'nw-resize';
} else {
item.style.cursor = 'n-resize';
}
} else if (rect.height - (e.clientY - rect.top) < cornerSize) {
if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'se-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'sw-resize';
} else {
item.style.cursor = 's-resize';
}
} else if (rect.width - (e.clientX - rect.left) < cornerSize) {
item.style.cursor = 'e-resize';
} else if (e.clientX - rect.left < cornerSize) {
item.style.cursor = 'w-resize';
} else {
item.style.cursor = '';
}
}
}
}
}
let position = ['n','ne','e','se','s','sw','w','nw','rotate'];
position.forEach(function(v, k) {
position[k] = document.createElement('div');
position[k].classList.add('jpanel-action');
if (v === 'rotate') {
position[k].setAttribute('data-action', 'rotate');
} else {
position[k].setAttribute('data-action', 'resize');
position[k].setAttribute('data-position', v + '-resize');
}
});
let currentElement;
const focus = function(e) {
let element = getElement(e);
// Check if this is the floating
let item = jsuites_jSuites.findElement(element, 'jpanel');
if (item && ! item.classList.contains("readonly") && item.classList.contains('jpanel-controls')) {
item.append(...position);
if (! item.classList.contains('jpanel-rotate')) {
position[position.length-1].remove();
}
currentElement = item;
} else {
blur(e);
}
}
const blur = function(e) {
if (currentElement) {
position.forEach(function(v) {
v.remove();
});
currentElement = null;
}
}
const mouseOver = function(e) {
let element = getElement(e);
var message = element.getAttribute('data-tooltip');
if (message) {
// Instructions
tooltip.innerText = message;
// Position
if (e.changedTouches && e.changedTouches[0]) {
var x = e.changedTouches[0].clientX;
var y = e.changedTouches[0].clientY;
} else {
var x = e.clientX;
var y = e.clientY;
}
tooltip.style.top = y + 'px';
tooltip.style.left = x + 'px';
document.body.appendChild(tooltip);
} else if (tooltip.innerText) {
tooltip.innerText = '';
document.body.removeChild(tooltip);
}
}
const contextMenu = function(e) {
var item = document.activeElement;
if (item && typeof(item.contextmenu) == 'function') {
// Create edition
item.contextmenu(e);
e.preventDefault();
e.stopImmediatePropagation();
} else {
// Search for possible context menus
item = jsuites_jSuites.findElement(e.target, function(o) {
return o.tagName && o.getAttribute('aria-contextmenu-id');
});
if (item) {
var o = document.querySelector('#' + item);
if (! o) {
console.error('JSUITES: contextmenu id not found: ' + item);
} else {
o.contextmenu.open(e);
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
}
const keyDown = function(e) {
let item = document.activeElement;
if (item) {
if (e.key === "Delete" && typeof(item.delete) == 'function') {
item.delete();
e.preventDefault();
e.stopImmediatePropagation();
}
}
let state = window['jSuitesStateControl'];
if (state && state.length > 0) {
item = state[state.length - 1];
if (item) {
if (e.key === "Escape" && typeof(item.isOpened) == 'function' && typeof(item.close) == 'function') {
if (item.isOpened()) {
item.close();
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
}
}
const input = function(e) {
if (e.target.getAttribute('data-mask') || e.target.mask) {
jsuites_jSuites.mask(e);
}
}
document.addEventListener('focusin', focus);
document.addEventListener('mouseup', mouseUp);
document.addEventListener("mousedown", mouseDown);
document.addEventListener('mousemove', mouseMove);
document.addEventListener('mouseover', mouseOver);
document.addEventListener('keydown', keyDown);
document.addEventListener('contextmenu', contextMenu);
document.addEventListener('input', input);
}
if (typeof(document) !== "undefined") {
Events();
}
/* harmony default export */ var jsuites = (jsuites_jSuites);
}();
jSuites = __webpack_exports__["default"];
/******/ })()
;
return jSuites;
})));
//}}}
//{{{
(function() {
var css = store.getTiddlerText("jsuites.js##CSS").replace(/\* \//g, "*/");
css = css.substring(css.indexOf("//{{{") + "//{{{".length, css.lastIndexOf("//}}}"));
css = css.replace(".jcalendar-prev {", ".jcalendar-prev, .viewer .jcalendar-prev {");
css = css.replace(".jcalendar-next {", ".jcalendar-next, .viewer .jcalendar-next {");
css = css +
".viewer .jcalendar table, .viewer .jcolor table { border: unset; margin: 0; }\n" +
".viewer .jcalendar td, .viewer .jcalendar tr { border: unset; background-color: unset; }\n" +
".viewer .jcolor td, .viewer .jcolor tr { border: unset; }\n";
setStylesheet(css, "jsuites.js-stylesheet");
})();
//}}}
/***
|Name|paste.js|
|Source|https://github.com/layerssss/paste.js/blob/master/paste.js|
|Documentation|https://github.com/layerssss/paste.js|
|Version|commit ceec57ca49dcbc30aa4d9ae6d3393c97d68e96fb 2019-04-17 https://raw.githubusercontent.com/layerssss/paste.js/master/paste.js|
|License|[[The MIT License (MIT)|https://github.com/layerssss/paste.js/blob/master/LICENSE]]|
|Description|paste.js is an interface to read data ( text / image ) from clipboard in different browsers|
!!!!!Code
//{{{
// Generated by CoffeeScript 1.12.7
/*
paste.js is an interface to read data ( text / image ) from clipboard in different browsers. It also contains several hacks.
https://github.com/layerssss/paste.js
*/
(function() {
var $, Paste, createHiddenEditable, dataURLtoBlob, isFocusable;
$ = window.jQuery;
$.paste = function(pasteContainer) {
var pm;
if (typeof console !== "undefined" && console !== null) {
console.log("DEPRECATED: This method is deprecated. Please use $.fn.pastableNonInputable() instead.");
}
pm = Paste.mountNonInputable(pasteContainer);
return pm._container;
};
$.fn.pastableNonInputable = function() {
var el, j, len, ref;
ref = this;
for (j = 0, len = ref.length; j < len; j++) {
el = ref[j];
if (el._pastable || $(el).is('textarea, input:text, [contenteditable]')) {
continue;
}
Paste.mountNonInputable(el);
el._pastable = true;
}
return this;
};
$.fn.pastableTextarea = function() {
var el, j, len, ref;
ref = this;
for (j = 0, len = ref.length; j < len; j++) {
el = ref[j];
if (el._pastable || $(el).is(':not(textarea, input:text)')) {
continue;
}
Paste.mountTextarea(el);
el._pastable = true;
}
return this;
};
$.fn.pastableContenteditable = function() {
var el, j, len, ref;
ref = this;
for (j = 0, len = ref.length; j < len; j++) {
el = ref[j];
if (el._pastable || $(el).is(':not([contenteditable])')) {
continue;
}
Paste.mountContenteditable(el);
el._pastable = true;
}
return this;
};
dataURLtoBlob = function(dataURL, sliceSize) {
var b64Data, byteArray, byteArrays, byteCharacters, byteNumbers, contentType, i, m, offset, ref, slice;
if (sliceSize == null) {
sliceSize = 512;
}
if (!(m = dataURL.match(/^data\:([^\;]+)\;base64\,(.+)$/))) {
return null;
}
ref = m, m = ref[0], contentType = ref[1], b64Data = ref[2];
byteCharacters = atob(b64Data);
byteArrays = [];
offset = 0;
while (offset < byteCharacters.length) {
slice = byteCharacters.slice(offset, offset + sliceSize);
byteNumbers = new Array(slice.length);
i = 0;
while (i < slice.length) {
byteNumbers[i] = slice.charCodeAt(i);
i++;
}
byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
offset += sliceSize;
}
return new Blob(byteArrays, {
type: contentType
});
};
createHiddenEditable = function() {
return $(document.createElement('div')).attr('contenteditable', true).attr('aria-hidden', true).attr('tabindex', -1).css({
width: 1,
height: 1,
position: 'fixed',
left: -100,
overflow: 'hidden',
opacity: 1e-17
});
};
isFocusable = function(element, hasTabindex) {
var fieldset, focusableIfVisible, img, map, mapName, nodeName;
map = void 0;
mapName = void 0;
img = void 0;
focusableIfVisible = void 0;
fieldset = void 0;
nodeName = element.nodeName.toLowerCase();
if ('area' === nodeName) {
map = element.parentNode;
mapName = map.name;
if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
return false;
}
img = $('img[usemap=\'#' + mapName + '\']');
return img.length > 0 && img.is(':visible');
}
if (/^(input|select|textarea|button|object)$/.test(nodeName)) {
focusableIfVisible = !element.disabled;
if (focusableIfVisible) {
fieldset = $(element).closest('fieldset')[0];
if (fieldset) {
focusableIfVisible = !fieldset.disabled;
}
}
} else if ('a' === nodeName) {
focusableIfVisible = element.href || hasTabindex;
} else {
focusableIfVisible = hasTabindex;
}
focusableIfVisible = focusableIfVisible || $(element).is('[contenteditable]');
return focusableIfVisible && $(element).is(':visible');
};
Paste = (function() {
Paste.prototype._target = null;
Paste.prototype._container = null;
Paste.mountNonInputable = function(nonInputable) {
var paste;
paste = new Paste(createHiddenEditable().appendTo(nonInputable), nonInputable);
$(nonInputable).on('click', (function(_this) {
return function(ev) {
if (!(isFocusable(ev.target, false) || window.getSelection().toString())) {
return paste._container.focus();
}
};
})(this));
paste._container.on('focus', (function(_this) {
return function() {
return $(nonInputable).addClass('pastable-focus');
};
})(this));
return paste._container.on('blur', (function(_this) {
return function() {
return $(nonInputable).removeClass('pastable-focus');
};
})(this));
};
Paste.mountTextarea = function(textarea) {
var ctlDown, paste, ref, ref1;
if ((typeof DataTransfer !== "undefined" && DataTransfer !== null ? DataTransfer.prototype : void 0) && ((ref = Object.getOwnPropertyDescriptor) != null ? (ref1 = ref.call(Object, DataTransfer.prototype, 'items')) != null ? ref1.get : void 0 : void 0)) {
return this.mountContenteditable(textarea);
}
paste = new Paste(createHiddenEditable().insertBefore(textarea), textarea);
ctlDown = false;
$(textarea).on('keyup', function(ev) {
var ref2;
if ((ref2 = ev.keyCode) === 17 || ref2 === 224) {
ctlDown = false;
}
return null;
});
$(textarea).on('keydown', function(ev) {
var ref2;
if ((ref2 = ev.keyCode) === 17 || ref2 === 224) {
ctlDown = true;
}
if ((ev.ctrlKey != null) && (ev.metaKey != null)) {
ctlDown = ev.ctrlKey || ev.metaKey;
}
if (ctlDown && ev.keyCode === 86) {
paste._textarea_focus_stolen = true;
paste._container.focus();
paste._paste_event_fired = false;
setTimeout((function(_this) {
return function() {
if (!paste._paste_event_fired) {
$(textarea).focus();
return paste._textarea_focus_stolen = false;
}
};
})(this), 1);
}
return null;
});
$(textarea).on('paste', (function(_this) {
return function() {};
})(this));
$(textarea).on('focus', (function(_this) {
return function() {
if (!paste._textarea_focus_stolen) {
return $(textarea).addClass('pastable-focus');
}
};
})(this));
$(textarea).on('blur', (function(_this) {
return function() {
if (!paste._textarea_focus_stolen) {
return $(textarea).removeClass('pastable-focus');
}
};
})(this));
$(paste._target).on('_pasteCheckContainerDone', (function(_this) {
return function() {
$(textarea).focus();
return paste._textarea_focus_stolen = false;
};
})(this));
return $(paste._target).on('pasteText', (function(_this) {
return function(ev, data) {
var content, curEnd, curStart;
curStart = $(textarea).prop('selectionStart');
curEnd = $(textarea).prop('selectionEnd');
content = $(textarea).val();
$(textarea).val("" + content.slice(0, curStart) + data.text + content.slice(curEnd));
$(textarea)[0].setSelectionRange(curStart + data.text.length, curStart + data.text.length);
return $(textarea).trigger('change');
};
})(this));
};
Paste.mountContenteditable = function(contenteditable) {
var paste;
paste = new Paste(contenteditable, contenteditable);
$(contenteditable).on('focus', (function(_this) {
return function() {
return $(contenteditable).addClass('pastable-focus');
};
})(this));
return $(contenteditable).on('blur', (function(_this) {
return function() {
return $(contenteditable).removeClass('pastable-focus');
};
})(this));
};
function Paste(_container, _target) {
this._container = _container;
this._target = _target;
this._container = $(this._container);
this._target = $(this._target).addClass('pastable');
this._container.on('paste', (function(_this) {
return function(ev) {
var _i, clipboardData, file, fileType, item, j, k, l, len, len1, len2, pastedFilename, reader, ref, ref1, ref2, ref3, ref4, stringIsFilename, text;
_this.originalEvent = (ev.originalEvent !== null ? ev.originalEvent : null);
_this._paste_event_fired = true;
if (((ref = ev.originalEvent) != null ? ref.clipboardData : void 0) != null) {
clipboardData = ev.originalEvent.clipboardData;
if (clipboardData.items) {
pastedFilename = null;
_this.originalEvent.pastedTypes = [];
ref1 = clipboardData.items;
for (j = 0, len = ref1.length; j < len; j++) {
item = ref1[j];
if (item.type.match(/^text\/(plain|rtf|html)/)) {
_this.originalEvent.pastedTypes.push(item.type);
}
}
ref2 = clipboardData.items;
for (_i = k = 0, len1 = ref2.length; k < len1; _i = ++k) {
item = ref2[_i];
if (item.type.match(/^image\//)) {
reader = new FileReader();
reader.onload = function(event) {
return _this._handleImage(event.target.result, _this.originalEvent, pastedFilename);
};
try {
reader.readAsDataURL(item.getAsFile());
} catch (error) {}
ev.preventDefault();
break;
}
if (item.type === 'text/plain') {
if (_i === 0 && clipboardData.items.length > 1 && clipboardData.items[1].type.match(/^image\//)) {
stringIsFilename = true;
fileType = clipboardData.items[1].type;
}
item.getAsString(function(string) {
if (stringIsFilename) {
pastedFilename = string;
return _this._target.trigger('pasteText', {
text: string,
isFilename: true,
fileType: fileType,
originalEvent: _this.originalEvent
});
} else {
return _this._target.trigger('pasteText', {
text: string,
originalEvent: _this.originalEvent
});
}
});
}
if (item.type === 'text/rtf') {
item.getAsString(function(string) {
return _this._target.trigger('pasteTextRich', {
text: string,
originalEvent: _this.originalEvent
});
});
}
if (item.type === 'text/html') {
item.getAsString(function(string) {
return _this._target.trigger('pasteTextHtml', {
text: string,
originalEvent: _this.originalEvent
});
});
}
}
} else {
if (-1 !== Array.prototype.indexOf.call(clipboardData.types, 'text/plain')) {
text = clipboardData.getData('Text');
setTimeout(function() {
return _this._target.trigger('pasteText', {
text: text,
originalEvent: _this.originalEvent
});
}, 1);
}
_this._checkImagesInContainer(function(src) {
return _this._handleImage(src, _this.originalEvent);
});
}
}
if (clipboardData = window.clipboardData) {
if ((ref3 = (text = clipboardData.getData('Text'))) != null ? ref3.length : void 0) {
setTimeout(function() {
_this._target.trigger('pasteText', {
text: text,
originalEvent: _this.originalEvent
});
return _this._target.trigger('_pasteCheckContainerDone');
}, 1);
} else {
ref4 = clipboardData.files;
for (l = 0, len2 = ref4.length; l < len2; l++) {
file = ref4[l];
_this._handleImage(URL.createObjectURL(file), _this.originalEvent);
}
_this._checkImagesInContainer(function(src) {
return _this._handleImage(src, _this.originalEvent);
});
}
}
return null;
};
})(this));
}
Paste.prototype._handleImage = function(src, e, name) {
var loader;
if (src.match(/^webkit\-fake\-url\:\/\//)) {
return this._target.trigger('pasteImageError', {
message: "You are trying to paste an image in Safari, however we are unable to retieve its data."
});
}
this._target.trigger('pasteImageStart');
loader = new Image();
loader.crossOrigin = "anonymous";
loader.onload = (function(_this) {
return function() {
var blob, canvas, ctx, dataURL;
canvas = document.createElement('canvas');
canvas.width = loader.width;
canvas.height = loader.height;
ctx = canvas.getContext('2d');
ctx.drawImage(loader, 0, 0, canvas.width, canvas.height);
dataURL = null;
try {
dataURL = canvas.toDataURL('image/png');
blob = dataURLtoBlob(dataURL);
} catch (error) {}
if (dataURL) {
_this._target.trigger('pasteImage', {
blob: blob,
dataURL: dataURL,
width: loader.width,
height: loader.height,
originalEvent: e,
name: name
});
}
return _this._target.trigger('pasteImageEnd');
};
})(this);
loader.onerror = (function(_this) {
return function() {
_this._target.trigger('pasteImageError', {
message: "Failed to get image from: " + src,
url: src
});
return _this._target.trigger('pasteImageEnd');
};
})(this);
return loader.src = src;
};
Paste.prototype._checkImagesInContainer = function(cb) {
var img, j, len, ref, timespan;
timespan = Math.floor(1000 * Math.random());
ref = this._container.find('img');
for (j = 0, len = ref.length; j < len; j++) {
img = ref[j];
img["_paste_marked_" + timespan] = true;
}
return setTimeout((function(_this) {
return function() {
var k, len1, ref1;
ref1 = _this._container.find('img');
for (k = 0, len1 = ref1.length; k < len1; k++) {
img = ref1[k];
if (!img["_paste_marked_" + timespan]) {
cb(img.src);
$(img).remove();
}
}
return _this._target.trigger('_pasteCheckContainerDone');
};
})(this), 1);
};
return Paste;
})();
}).call(this);
//}}}
//{{{
config.shadowTiddlers.IgnoredTagsRegExp =
"/^\\d\\d\\d\\d-\\d\\d-\\d\\d.*$/,\n" +
"/^\\d\\d\\d\\d-[w|s]\\d\\d$/,\n" +
"/^AbegoExtensions$/,\n" +
"/^alpha$/,\n" +
"/^attachment$/,\n" +
"/^basic$/,\n" +
"/^DataTiddlerProject$/,\n" +
"/^Decrypt\\(.*$/,\n" +
"/^examples$/,\n" +
"/^ForEachTiddler.*$/,\n" +
"/^ics.*/,\n" +
"/^iTW$/,\n" +
"/^package$/,\n" +
"/^.*Package$/,\n" +
"/^Plugin.*$/i,\n" +
"/^format:default$/,\n" +
"/^formatting$/,\n" +
"/^FormTiddlerProject$/,\n" +
"/^macros$/,\n" +
"/^My Plugins$/,\n" +
"/^noTitle$/,\n" +
"/^patch$/,\n" +
"/^sample$/,\n" +
"/^settings$/,\n" +
"/^spreadsheet$/,\n" +
"/^story$/,\n" +
"/^syntax$/,\n" +
"/^systemList$/,\n" +
"/^systemServer$/,\n" +
"/^TiddlyHome$/,\n" +
"/^TiddlyTools$/,\n" +
"/^timezone:.*$/,\n" +
"/^twab.*$/,\n" +
"/^Trash$/,\n" +
"/^UdoBorkowski$/,\n" +
"/^upload$/,\n"
//}}}
//{{{
config.macros.tagChooser.includeTag = function (tag) {
var ignoredTagsRegExpText = store.getTiddlerText('IgnoredTagsRegExp');
if (ignoredTagsRegExpText != config.macros.tagChooser.ignoredTagsRegExpText) {
try {
eval("config.macros.tagChooser.ignoredTagsRegExp = [" + ignoredTagsRegExpText + "]");
config.macros.tagChooser.ignoredTagsRegExpText = ignoredTagsRegExpText;
} catch (e) {}
}
config.macros.tagChooser.ignoredTagsRegExp = config.macros.tagChooser.ignoredTagsRegExp ? config.macros.tagChooser.ignoredTagsRegExp : [];
var includeTag = true;
for (var r = 0; r < config.macros.tagChooser.ignoredTagsRegExp.length; r++) {
if (tag.match(config.macros.tagChooser.ignoredTagsRegExp[r])) {
includeTag = false;
break;
}
}
if (includeTag) {
var tiddler = store.getTiddler(tag);
if (tiddler && tiddler.tags && ((tiddler.tags.indexOf("meeting") > -1) || (tiddler.tags.indexOf("task") > -1))) {
includeTag = false;
}
}
return includeTag;
}
config.macros.tagChooser.onClick = function (ev)
{
var e = ev || window.event;
var lingo = config.views.editor.tagChooser;
var popup = Popup.create(this);
var tags = store.getTags(this.getAttribute("tags"));
if(tags.length == 0)
jQuery("<li/>").text(lingo.popupNone).appendTo(popup);
for(var t=0; t<tags.length; t++) {
if (config.macros.tagChooser.includeTag(tags[t][0])) {
var tag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
tag.setAttribute("tag",tags[t][0]);
tag.setAttribute("tiddler",this.getAttribute("tiddler"));
}
}
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
}
//}}}