Skip to content

Instantly share code, notes, and snippets.

@maditnerd
Created February 1, 2013 11:28
Show Gist options
  • Save maditnerd/4690773 to your computer and use it in GitHub Desktop.
Save maditnerd/4690773 to your computer and use it in GitHub Desktop.
function wysiwyg() {
this.currentElmt;
this.toolbar;
this.toolbarWidgets;
this.widgets = [];
this.selector;
this.toolbarDocument;
this.currentDocument;
this.enable;
this.isMultiple = false;
this.textarea;
this.allowedTags = {
"div":["style","dir","lang","title"],
"span":["style","dir","lang","title"],
"p":["style","dir","lang","title"],
"pre":["style","dir","lang","title"],
"br":["style"]
};
this.allowedStyles = {},
/* Build and init toolbar */
this.init = function (selector, toolbarWidgets, toolbarDocument, currentDocument) {
/* Init WYSIWYG vars*/
this.selector = selector;
this.toolbarWidgets = toolbarWidgets;
if(typeof toolbarDocument == "undefined") this.toolbarDocument = document;
else this.toolbarDocument = toolbarDocument;
if(typeof currentDocument == "undefined") this.currentDocument = document;
else this.currentDocument = currentDocument;
var $this = this;
/* Select element to convert into WYSIWYG */
var el = $(this.selector,this.currentDocument);
/*if(el[0].tagName == "TEXTAREA") {
this.toolbarWidgets.push("code");
}*/
/* If it's not already done */
if($('.HTML5editorToolbar[data-selector="' + this.selector + '"]',this.toolbarDocument).length == 0){
/* We build a toolbar which is associated with the WYSIWYG */
this.toolbar = this.buildToolbar();
/* If textarea mode : simple WYSIWYG conneted with a textarea */
if(el[0].tagName == "TEXTAREA") {
this.textarea = el;
this.textarea.before(this.toolbar);
el[0].style.margin = "0";
var singleEditor = $("<div>",this.toolbarDocument);
singleEditor.attr("id", "editor" + ($(".HTML5editorToolbar",this.currentDocument).length + 1))
.attr("contenteditable","true").attr("spellcheck", "false").attr("data-textarea",this.selector)
.addClass("HTML5Editor")
.html(this.textarea.val())
.get(0).style = el[0].style
this.currentElmt = singleEditor[0];
this.textarea.hide()
.before(singleEditor);
this.selector = "#" + singleEditor.attr("id");
}else{
/* If multiple mode: one toolbar for severals contenteditable divs */
$("body",this.toolbarDocument).append(this.toolbar);
this.toolbar.addClass("multiple");
this.isMultiple = true;
}
}
this.toolbar.show();
$(this.currentDocument).on("mouseup",this.selector,function(e){
$this.checkCommands();
$(".popover").hide();
})
.on("paste drop",this.selector,function(e){
var eltm = this;
setTimeout(function() {$this.sanitize(eltm)},20);
});
$(this.selector,this.currentDocument).each(function(){
if($this.isMultiple){
this.setAttribute("spellcheck", "false");
this.setAttribute("contenteditable", "true");
/* Listen focus event on all div that matches selector in order to position and resize wysiwyg */
$(this).on("focus",function (e) {
if($this.enable){
$this.setDIV(e.target);
this.innerHTML = $this.format(this.innerHTML);
}
})
.on("keyup drop paste",function(e) {
this.setAttribute("data-modified","1");
$this.checkCommands();
});
}else{
/* Copy the content of WYSIWYG to the textarea */
$(this).on("keyup change",function(e) {
$this.textarea[0].value = $this.currentElmt.innerHTML;
$this.checkCommands();
e.preventDefault();
})
.on("blur",function(e) {
this.innerHTML = $this.format(this.innerHTML);
if(this.isMultiple) $($this.toolbar,this.toolbarDocument).hide();
});
/* Copy the content of textarea to the WYSIWYG */
$this.textarea.on("keyup blur",function(e) {
$this.currentElmt.innerHTML = $this.textarea[0].value;
e.preventDefault();
})
}
});
/* Init for first use */
this.setCommand("styleWithCSS");
if(this.isMultiple) $this.setDIV($(this.selector, this.currentDocument).get(0));
this.enable = true;
}
this.buildToolbar = function(){
var toolbar = $("<div>");
toolbar.addClass("HTML5editorToolbar")
.data("selector",this.selector)
.html('<iframe src="about:blank" class="popover"></iframe><ul class="tabs"></ul><div class="commands"></div>');
for (var i=0; i < this.toolbarWidgets.length; i++) {
eval("var widget = new wysiwyg_" + this.toolbarWidgets[i] + "();");
var tag = $("<div>",this.toolbarDocument);
tag.addClass('btn');
tag.html(widget.getAdmin());
if(typeof widget.allowedTags != "undefined") jQuery.extend(this.allowedTags, widget.allowedTags);
if(typeof widget.allowedStyles != "undefined") jQuery.extend(this.allowedStyles, widget.allowedStyles);
this.widgets[widget.name] = widget;
if($(".toolbar_" + widget.category,toolbar).length == 0){
var tabTitle = $("<a>")
.attr("href", "#" + "toolbar_" + widget.category)
.html(widget.category);
var item = $("<li>");
item.append(tabTitle);
$(".tabs",toolbar).append(item);
var tab = $("<div>");
tab.addClass("toolbar_" + widget.category);
if(i==0) tab.css('display','block');
$(".commands",toolbar).append(tab);
}
$(".toolbar_" + widget.category,toolbar).append(tag);
}
var $this = this;
/* Manage Tabs */
$(toolbar).on("mousedown",".tabs a",function(e){
$(this).addClass("current");
$(".commands > div",$this.toolbar).hide();
if(this.textContent == "code"){
var toolbar = $this.toolbar[0];
toolbar.nextSibling.nextSibling.style.display = "block";
toolbar.nextSibling.style.display = "none";
}else{
if(!$this.isMultiple) {
var toolbar = $this.toolbar[0];
toolbar.nextSibling.nextSibling.style.display = "none";
toolbar.nextSibling.style.display = "block";
}
$(".tabs a",$this.toolbar).removeClass("current");
$(e.target.getAttribute("href").replace("#","."),$this.toolbar).show();
}
e.preventDefault();
})
.on("mousedown","*",function(e){
//$(this).triggerHandler("click");
e.preventDefault();
})
/* Listen click action on BTN && Listen change action on selects*/
.on("mousedown change",".HTML5editorAction",function(e){
$this.widgets[this.getAttribute("data-name")].onClick(e, $this);
e.preventDefault();
});
return toolbar;
}
this.format = function( code ) {
var html = '';
var pad = 0;
code = code.replace(/(>)\s*(<)(\/*)/g, '$1\r\n$2$3');
$.each(code.split('\r\n'), function(index, node) {
var indent = 0;
if (node.match( /.+<\/\w[^>]*>$/ )) {
indent = 0;
} else if (node.match( /^<\/\w/ )) {
if (pad != 0) pad -= 1;
} else if (node.match( /^<\w[^>]*[^\/]>.*$/ ) && !node.match( /^<(br|img).*>\s?$/ ) ) {
indent = 1;
}
var padding = '';
for (var i = 0; i < pad; i++) padding += ' ';
html += padding + node + '\r\n';
pad += indent;
});
return html;
}
/* position and resize wysiwyg */
this.setDIV = function (currentElmt) {
this.currentElmt = currentElmt;
if(this.isMultiple) this.toolbar.css("width",$(this.currentElmt).width());
var offset = $(this.currentElmt).offset();
var top = offset.top - 55;
if(top < 0){
top = offset.top + $(this.currentElmt).height() + 30;
}
this.toolbar.css("top",top + "px");
this.toolbar.css("left", offset.left + "px");
$(".HTML5editorToolbar").show();
}
/* Exec a command on current active contenteditable div */
this.setCommand = function (command, value) {
if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1) this.currentDocument.execCommand("styleWithCSS", false, value); // fix firefox
this.currentDocument.execCommand(command, false, value);
$(this.currentElmt).attr("data-modified","1").trigger('keyup');
this.checkCommands();
}
/* Check wich command could be exec or not */
this.checkCommands = function () {
var $this = this;
$("[data-command]",this.toolbar).each(function(){
var el = $(this.parentNode);
var enabled = $this.currentDocument.queryCommandEnabled(this.getAttribute("data-command"));
if(enabled){
el.removeClass("inactive");
}else{
if(!el.hasClass("inactive")) el.addClass("inactive");
}
if(enabled && $this.currentDocument.queryCommandState(this.getAttribute("data-command"))){
$this.widgets[this.getAttribute("data-name")].setCurrentValue(el, $this.currentDocument.queryCommandValue(this.getAttribute("data-command")));
if(!el.hasClass("active")) el.addClass("active");
}else{
el.removeClass("active");
}
});
}
this.sanitize = function(elmt) {
for(var a = 0; a < elmt.childNodes.length; a++) {
node = elmt.childNodes[a];
if(node.nodeType == 1){
if(typeof this.allowedTags[node.tagName.toLowerCase()] == "undefined") {
if(window.getComputedStyle(node,null).getPropertyValue("display") == 'block') var span = document.createElement("div");// todo ie x.currentStyle[styleProp];
else var span = document.createElement("span");
var attrs = node.attributes;
for(var i=0;i < attrs.length;i++) span.setAttribute(attrs[i].nodeName,attrs[i].nodeValue);
span.innerHTML = node.innerHTML;// do with dom, not innerHTML
var newNode = node.parentNode.insertBefore(span,node);
node.parentNode.removeChild(node);
node = newNode;
}
var attrs = node.attributes;
for(var i=0;i < attrs.length;i++){
if(this.allowedTags[node.tagName.toLowerCase()].indexOf(attrs[i].nodeName) == -1) { //indexOf not for ie8
node.removeAttribute(attrs[i].nodeName);
i--;
}else if(attrs[i].nodeName == "style"){
var styles = node.style;
for(var y=0;y < styles.length;y++){
if(typeof this.allowedStyles[styles[y]] == "undefined") {
styles.removeProperty(styles[y]); //removeProperty not for ie8 : removeAttribute
y--;
}
}
if(styles.length == 0) node.removeAttribute("style");
else node.setAttribute("style", styles.cssText);
}
}
/* if the node is empty and useless */
if(node.tagName != "BR" && node.getBoundingClientRect().height == 0) {
node.parentNode.removeChild(node);
a--;
}
/* if the node is useless */
else if((node.tagName == "SPAN" && !node.hasAttribute("style")) || (!node.hasAttribute("style") && node.parentNode.childNodes.length == 1)){
for(var z = 0; z < node.childNodes.length; z++) {
var childNode = node.childNodes[z];
node.parentNode.insertBefore(childNode,node);
z--
}
node.parentNode.removeChild(node);
a--;
}
if (node.hasChildNodes()) this.sanitize(node);
}
}
},
this.disable = function () {
this.enable = false;
if(typeof this.toolbar != "undefined") {
this.toolbar.hide();
$(this.selector,this.currentDocument).attr("contenteditable", "false");
}
}
}
function wysiwyg_btn() {
this.category = "format";
this.getAdmin = function(){
return '<div style="background:url(' + this.icon + ') no-repeat center;width:25px;height:25px;border:0;" class="HTML5editorAction" data-name="' + this.name + '" data-command="' + this.command + '"></div>';
}
this.onClick = function(e, editor){
editor.setCommand(this.command);
}
this.setCurrentValue = function(elmt, value){}
}
function wysiwyg_bold() {
wysiwyg_btn.call(this);
this.name = this.command = "bold";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=";
this.allowedStyles = {
"font-weight": /.*/
};
}
function wysiwyg_underline() {
wysiwyg_btn.call(this);
this.name = this.command = "underline";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALAAAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA7";
this.allowedStyles = {
"text-decoration": /.*/
};
}
function wysiwyg_italic() {
wysiwyg_btn.call(this);
this.name = this.command = "italic";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALAAAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==";
this.allowedStyles = {
"font-style": /.*/
};
}
function wysiwyg_justifyLeft() {
wysiwyg_btn.call(this);
this.name = this.command = "justifyLeft";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==";
this.allowedStyles = {
"text-align": /.*/
};
}
function wysiwyg_justifyCenter() {
wysiwyg_btn.call(this);
this.name = this.command = "justifyCenter";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7";
this.allowedStyles = {
"text-align": /.*/
};
}
function wysiwyg_justifyRight() {
wysiwyg_btn.call(this);
this.name = this.command = "justifyRight";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==";
this.allowedStyles = {
"text-align": /.*/
};
}
function wysiwyg_justifyFull() {
wysiwyg_btn.call(this);
this.name = this.command = "justifyFull";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALBJREFUeNpi/P//PwMlgImBQsACNoUJbE4jEGsBMSMUM2FhM0HxZSAu/ffvHwMjyAtQA0gGIANYkPhrsdiGbCsyG4QdQZqo6oKNBGxEx9ZUd8E2ImyFYWYgNqHIBSB9IIzsgt1E2MwG1MQOpDmAWIFkF6Ane7gLgAxmRkbGyUCmLhZbWYHyHFBbQbazAvEFIPYF4u9gFwA1gwKFF4g5oQFJCPwB4s9AvRADBjQ3AgQYAIOVSAdZa5U/AAAAAElFTkSuQmCC";
this.allowedStyles = {
"text-align": /.*/
};
}
function wysiwyg_strikeThrough() {
wysiwyg_btn.call(this);
this.name = this.command = "strikeThrough";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAACfSURBVCjPY/jPgB8yUFNBiWDBzOy01PKEmZG7sSrIe5dVDqIjygP/Y1GQm5b2P7kDwvbAZkK6S8L/6P8hM32N/zPYu2C1InJ36P/A/x7/bc+YoSooLy3/D4Px/23+SyC5G8kEf0EIbZSmfdfov9wZDCvc0uzLYWyZ/2J3MRTYppn/14eaIvKOvxxDgUma7ju1M/LlkmnC5bwdNIoL7BAAWzr8P9A5d4gAAAAASUVORK5CYII=";
this.allowedStyles = {
"text-decoration": /.*/
};
}
function wysiwyg_subscript() {
wysiwyg_btn.call(this);
this.name = this.command = "subscript";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAARhJREFUeNrsU4FtgzAQfHcDM4I9AiuYEegIMAKMACOYEfAI8QjOCHgEPIL7b+SoIUoaJW2kSjnpxfsRp/vzAfDGvwXbD4ZhiPtZXdcwTROEENIZe/YT8cd+oJQC730qznkqKSUTQgBV13WPKSY45+I4jmeKrbV3Kb2q+M88ztBaR2PM6TzPMxRF8bxiWv876PJ+RXHTNLEsy9Rnv3ELqKqKLeuakmPslhK8X2gryW4Sr/gRqaM0tG3LcgSzYrLEer5tpTjYYwCP/OPnOfGFFX3fp6hRZikdVJSIHDd6r0RIJdFzH16ciqegD0ts9BLdssa7L+8WDm4jsuhDhz4fPYDYbMe/dIvkQ8Skkp4ne7ExvWTwxkvxJcAAeyp5PYg93M0AAAAASUVORK5CYII=";
this.allowedStyles = {
"vertical-align": /.*/
};
}
function wysiwyg_superscript() {
wysiwyg_btn.call(this);
this.name = this.command = "superscript";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAARVJREFUeNrsU4FtwyAQfEddAI9gRrBHsEdgBToCHsEeAY9gRggjOCPACDDClyeKFSdK1Fpqqko56cSDxPPc3wO88W9R7LnkQkBajY15zxjAZ8c3uT72JL4kFC0De4rg4y98Wc0Oibfnh5dpPAzD3etCCJimCWI8/znF+Z4+Ojx5AJkkaXhZPK24bVvw3mey1BUi57yoqgqISik4LgGJpO0gGNB7zgUkPnXFsiw4juOmYmvtWqnUZ03XnqXA9FtXPLSb1hqNMet+nmcoy/Lb9nzYPKryGqTxT3B4+eRJKbGu6xxf9E7yQNd1u6YVQhpXshxpvA6BUpgckUmN3VUxVUorWYusR+j7/tbreW2apoA3/gxfAgwA01J5qh+9fJUAAAAASUVORK5CYII=";
this.allowedStyles = {
"vertical-align": /.*/
};
}
function wysiwyg_orderedList() {
wysiwyg_btn.call(this);
this.name = "orderedList";
this.command = "insertOrderedList";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P///////yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEOzyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=";
this.allowedTags = {
"ol":["id","class","style","dir","lang","title"],
"li":["id","class","style","dir","lang","title"]
};
this.allowedStyles = {
"list-style": /.*/,
"list-style-image": /.*/,
"list-style-position": /.*/,
"list-style-type": /.*/
};
}
function wysiwyg_unOrderedList() {
wysiwyg_btn.call(this);
this.name = "unOrderedList";
this.command = "insertUnorderedList";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv///////yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud64UwiuKnigGQliQuWOyKQykgAAOw==";
this.allowedTags = {
"ul":["id","class","style","dir","lang","title"],
"li":["id","class","style","dir","lang","title"]
};
this.allowedStyles = {
"list-style": /.*/,
"list-style-image": /.*/,
"list-style-position": /.*/,
"list-style-type": /.*/
};
}
function wysiwyg_undo() {
wysiwyg_btn.call(this);
this.name = this.command = "undo";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7/I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq7046807TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfEwhdRIH4fh/DZMICe3/C4nBQBADs=";
}
function wysiwyg_redo() {
wysiwyg_btn.call(this);
this.name = this.command = "redo";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1////yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9NaEDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw==";
}
function wysiwyg_copy() {
wysiwyg_btn.call(this);
this.name = this.command = "copy";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OPs++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAAGrpy+wsbKzIiEAOw==";
this.onClick = function(e, editor){
if(typeof(window.clipboardData)=="undefined") {
alert("Your navigateur preferences don't allow this action. Please use CTRL + C");
}else{
editor.setCommand("copy");
}
}
}
function wysiwyg_paste() {
wysiwyg_btn.call(this);
this.name = this.command = "paste";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrpqbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f///////////////////////////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvBSyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQo9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAAMOaK+bLAOrtLUyt7i5uiUhADs=";
this.onClick = function(e, editor){
if(typeof(window.clipboardData)=="undefined") {
alert("Your navigateur preferences don't allow this action. Please use CTRL + V");
}else{
editor.setCommand("copy");
}
}
}
function wysiwyg_cut() {
wysiwyg_btn.call(this);
this.name = this.command = "cut";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P///////////////////////////////////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnEbGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==";
this.onClick = function(e, editor){
if(typeof(window.clipboardData)=="undefined") {
alert("Your navigateur preferences don't allow this action. Please use CTRL + X");
}else{
editor.setCommand("copy");
}
}
}
function wysiwyg_outdent() {
wysiwyg_btn.call(this);
this.name = this.command = "outdent";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMczsYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=";
this.allowedTags = {
"blockquote":["id","class","style","dir","lang","title"]
};
this.allowedStyles = {
"margin": /.*/,
"border": /.*/,
"padding": /.*/
};
}
function wysiwyg_indent() {
wysiwyg_btn.call(this);
this.name = this.command = "indent";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3NHa7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==";
this.allowedTags = {
"blockquote":["id","class","style","dir","lang","title"]
};
this.allowedStyles = {
"margin": /.*/,
"border": /.*/,
"padding": /.*/
};
}
function wysiwyg_removeFormat() {
wysiwyg_btn.call(this);
this.name = this.command = "removeFormat";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB501ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqGXz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMzdeiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2ClrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tAwMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwffj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrjb/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCcXmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc666s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg==";
this.onClick = function(e, editor){
editor.setCommand("removeFormat");
editor.setCommand("backColor","transparent");
}
}
function wysiwyg_createLink() {
wysiwyg_btn.call(this);
this.name = this.command = "createLink";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb/I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq7046827/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghGi9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7";
this.category = "insert";
this.allowedTags = {
"a":["id","class","style","dir","lang","title","accesskey","tabindex","charset","coords","href","hreflang","name","rel","rev","shape","target"]
};
this.onClick = function(e, editor){
var link = prompt('URL','http:\/\/');
if(link && link.length > 0){
editor.setCommand("createLink", link);
}
}
}
function wysiwyg_unlink() {
wysiwyg_btn.call(this);
this.name = this.command = "unlink";
this.category = "insert";
this.icon = "data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb/I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq7046827/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghGi9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7";
}
function wysiwyg_selectSquel() {
this.name = this.command = "formatBlock";
this.category = "format";
this.values = {
"p":"Paragraph",
"h1":"Heading 1",
"h2":"Heading 2",
"h3":"Heading 3",
"h4":"Heading 4",
"h5":"Heading 5",
"h6":"Heading 6",
"pre":"Preformatted",
"blockquote":"Blockquote"
};
this.allowedTags = {
"h1":["id","class","style","dir","lang","title"],
"h2":["id","class","style","dir","lang","title"],
"h3":["id","class","style","dir","lang","title"],
"h4":["id","class","style","dir","lang","title"],
"h5":["id","class","style","dir","lang","title"],
"h6":["id","class","style","dir","lang","title"]
};
this.template = function(i, val){
return '<div class="option" data-option="' + i + '"><' + i + '>' + val + '</' + i + '></div>';
};
this.defaultText = 'Font Family';
this.getAdmin = function(){
return '<div style=";height:25px;border:0;" class="HTML5editorAction select" data-name="' + this.name + '" data-command="' + this.command + '"><span class="temoin temoinSelect">' + this.defaultText + '</span></div>';
}
this.onClick = function(e, editor){
var html = "<style>.option{cursor:pointer}</style>";
for (i in this.values) {
html += this.template(i, this.values[i]);
}
var prop = $('.popover',editor.toolbar).css({
width:"190px",
height:"200px"
}).appendTo($(e.target).closest(".btn")).show().contents().find("body").empty().append(html);
var $this = this;
$(".option",prop).on("mousedown",function(e){
editor.setCommand($this.command,$(this).data("option"));
$('.popover',editor.toolbar).hide();
$(".option",prop).off("mousedown");
});
}
this.setCurrentValue = function(elmt, type){
$(".temoin", elmt).text(this.values[type]);
}
}
function wysiwyg_formatBlock() {
wysiwyg_selectSquel.call(this);
this.name = this.command = "formatBlock";
this.category = "format";
this.values = {
"p":"Paragraph",
"h1":"Heading 1",
"h2":"Heading 2",
"h3":"Heading 3",
"h4":"Heading 4",
"h5":"Heading 5",
"h6":"Heading 6",
"pre":"Preformatted",
"blockquote":"Blockquote"
};
this.allowedTags = {
"h1":["id","class","style","dir","lang","title"],
"h2":["id","class","style","dir","lang","title"],
"h3":["id","class","style","dir","lang","title"],
"h4":["id","class","style","dir","lang","title"],
"h5":["id","class","style","dir","lang","title"],
"h6":["id","class","style","dir","lang","title"]
};
this.template = function(i, val){
return '<div class="option" data-option="' + i + '"><' + i + '>' + val + '</' + i + '></div>';
};
this.defaultText = 'Format';
}
function wysiwyg_fontName() {
wysiwyg_selectSquel.call(this);
this.name = this.command = "fontName";
this.category = "format";
this.values = {
"arial, helvetica, sans-serif":"Arial",
"'arial black', avant garde;":"Arial Black",
"'book antiqua', palatino":"Book Antiqua",
"'comic sans ms', sans-serif":"Comic Sans MS",
"courier new, courier":"Courier New",
"georgia, palatino":"Georgia",
"helvetica":"Helvetica",
"impact, chicago":"Impact",
"symbol":"Symbol",
"tahoma, arial, helvetica, sans-serif":"Tahoma",
"terminal, monaco":"Terminal",
"'times new roman', times":"Times New Roman",
"'trebuchet ms', geneva":"Trebuchet MS",
"verdana, geneva":"Verdana",
"webdings":"Webdings",
"wingdings, 'zapf dingbats'":"Wingdings"
};
this.template = function(i, val){
return '<div class="option" data-option="' + i + '" style="font-family:' + i + '">' + val + '</div>';
};
this.defaultText = 'Font Family';
this.allowedStyles = {
"font-family": /.*/
};
}
function wysiwyg_fontSize() {
wysiwyg_selectSquel.call(this);
this.name = this.command = "fontSize";
this.category = "format";
this.values = {
"1":"8",
"2":"10",
"3":"12",
"4":"14",
"5":"18",
"6":"24",
"7":"36"
};
this.template = function(i, val){
return '<div class="option" data-option="' + i + '" style="font-size:' + val + 'pt">' + val + '</div>';
};
this.defaultText = 'Font Size';
this.allowedStyles = {
"font-size": /.*/
};
}
function wysiwyg_colorPicker() {
this.pickerColorImg = "data:image/png;base64,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";
this.pickerColorImgObj;
this.palette = ["#000000","#434343","#666666","#999999","#B7B7B7","#cccccc","#D9D9D9","#EFEFEF","#F3F3F3","#ffffff",
"#980000","red","#F90","yellow","lime","cyan","#4A86E8","blue","#90F","magenta",
"#E6B8AF","#F4CCCC","#FCE5CD","#FFF2CC","#D9EAD3","#D0E0E3","#C9DAF8","#CFE2F3","#D9D2E9","#EAD1DC",
"#DD7E6B","#EA9999","#F9CB9C","#FFE599","#B6D7A8","#A2C4C9","#A4C2F4","#9FC5E8","#B4A7D6","#D5A6BD",
"#CC4125","#E06666","#F6B26B","#FFD966","#93C47D","#76A5AF","#6D9EEB","#6FA8DC","#8E7CC3","#C27BA0",
"#A61C00","#C00","#E69138","#F1C232","#6AA84F","#45818E","#3C78D8","#3D85C6","#674EA7","#A64D79",
"#85200C","#900"," #B45F06"," #BF9000","#38761D","#134F5C","#15C","#0B5394","#351C75","#741B47",
"#5B0F00","#600","#783F04","#7F6000","#274E13","#0C343D","#1C4587","#073763","#20124D","#4C1130"];
this.getAdmin = function(){
this.pickerColorImgObj = new Image();
this.pickerColorImgObj.src = this.pickerColorImg;
return '<div class="HTML5editorAction" style="position:relative;width:25px;height:25px;position:relative;background:url(' + this.icon + ') no-repeat center 2px;" data-name="' + this.name + '"data-command="' + this.command + '"><div class="temoin" style="position:absolute;top:19px;height:3px;left:2px;right:3px;"></div></div>';
}
this.onClick = function(e, editor){
var html = '<style>.pick{float:left;width:16px;height:16px;margin:1px;cursor:pointer}.pick:hover{outline:1px solid #777}</style><div style="width: 180px;"><div class="pick" style="height:16px;width:auto;float:none;color:#ddd;font-size:11px;font-family:arial;padding:0 2px;background:transparent">Transparent</div>';
for (var i = 0; i < this.palette.length; i++) {
html += '<div class="pick" style="background:' + this.palette[i] + '"></div>';
}
html += '<div class="picker" style="display:none">';
html += '<div style="float:left;width:22px;height:22px;margin: 1px;border: 1px solid #999;" class="picked_color_preview"></div>';
html += '<div style="float:left;width:50px;height:22px;margin-left: 7px;" class="picked_color_rgb"></div><div style="clear:both"></div>';
html += '<canvas style="width:180px;height:180px;" class="mycolorpicker"></canvas></div>';
var prop = $('.popover',editor.toolbar).css({
width:"200px",
height:"185px"
}).appendTo($(e.target).closest(".btn")).show().contents().find("body").empty().append(html);
var temoin = $(".temoin",e.target);
var c = $('.mycolorpicker',prop).get(0);
var ctx = c.getContext('2d');
c.width = this.pickerColorImgObj.width;
c.height = this.pickerColorImgObj.height;
ctx.drawImage(this.pickerColorImgObj,0,0);
var $this = this;
$(c,prop).on("mousemove.Wcolorpicker", function(event){
var x = event.originalEvent.layerX - c.offsetLeft;
var y = event.originalEvent.layerY - c.offsetTop;
var img_data = ctx.getImageData(x, y, 1, 1).data;
var rgb = $this.rgbToHex(img_data[0],img_data[1],img_data[2]);
$('.picked_color_preview',prop).css("background-color",rgb);
$('.picked_color_rgb',prop).text(rgb);
});
$(c,prop).on("click.Wcolorpicker", function(event){
editor.setCommand($this.command,$('.picked_color_rgb',prop).text());
temoin.css("background-color",$('.picked_color_rgb',prop).text());
$('.popover',editor.toolbar).hide();
$(c,prop).off(".Wcolorpicker");
event.stopPropagation();
});
$(".pick",prop).on("mousedown.Wcolorpicker", function(event){
editor.setCommand($this.command,$(this).css("background-color"));
temoin.css("background-color",$(this).css("background-color"));
$('.popover',editor.toolbar).hide();
$(c,prop).off(".Wcolorpicker");
event.preventDefault();
});
}
this.setCurrentValue = function(elmt, color){
//$(".temoin", elmt).css("background-color",color);
}
this.rgbToHex = function(r, g, b){
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
}
function wysiwyg_foreColor() {
wysiwyg_colorPicker.call(this);
this.name = this.command = "foreColor";
this.category = "format";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gEEDyMK75EyNwAAAi5JREFUOMut0k9I02EYB/Dv+74/98fy56opYymUa61RQ1I6denfpRIMDLzoTcmDHYTA6NhFtB0qFEHsFOQxgyFUsA4aqbHTmC1xiX+alPsna7r22/u8HbzoXF3sOX+fz/s8vA9DmWruemnjnG0SKQ0AOGdFIlUTGu/MlGa1cgBj6L5xqYF3tfgAAOOBMH8/H+sG8ORAtszrXBN8x3//umk6/F0BQKOrhj1+MV0oSrKGxjtpb56XAiTlNZezWuhHTZgMLqjJ4IIC5zihWwRJebU0fwDQBJ7eueIRs5ENKCVXlJIrc5E4zrsdQhN49k/A1zFWb9KY1113HK+DEZnPF/ry+ULfTCgm9WodhlH0+jrG6v8KcODR7cvn2JfVNOI/MiBJUyRpKpHMYjO1A6fDzqDwsCzgbR8xS5LdF9wO9vbToiKSo5GJHiMy0WNIkqPRpXVlr6llUhbvedtHzAe+kaRsazzrZKmcxPJ6koyC0eu5+7wXAIqGgcxWVtoddcJisSL3K9sG4NU+QHA27DlTx5fi22i+6BMWs0ClWcMRs4BurUCVVROzX9NYP1Yrctns8D7A1er3mSq4TsKKdx8+y2QiKcodWGWVTZ5s8AoFpbta/b7Ymwfh3VNlGHI4nWJxNYXkZgJEZF4O9Bf2Np9uGTTlttLbxu88bHaHSG6sDQG4yXf3p6ZoJIq5mY9QRAOlzQCwHOgvQKnBWHgeP9e+QRE14X8UO3VrQB0G4Ied4A+X1/MuJnM+zQAAAABJRU5ErkJggg==";
this.allowedStyles = {
"color": /.*/
};
}
function wysiwyg_hiliteColor() {
wysiwyg_colorPicker.call(this);
this.name = this.command = "backColor";
this.category = "format";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe5JREFUeNqMUz1LI1EUPe9FRJGIsqAgmDjuBKZKoYuCilqk0EYrG3+AYiUWIhayKUQQwWA5hUHFzir+ghijRjEiFpIwJCAWwoZMxM6YzOydcSabHaPxwuFy7nvncO/7YKoK6Po/pNMdkOWx4ObmUVDT3mvOXB2cECXoNny+P3pnZ/l3dc2BcafBWCwm4fBwBKenklksl8vY2prG+fk7TyQk7OxM4+rK5B8MMDqaQnd3DtGoWDFYXo4gHhfx+PgDZ2ciFhcjGBhImeu5HHB/D1xeWgbh8CC83hxKpZK5oTobM9vciGIReH1FV3UHJ4riRibT/p8wGAxgeFiBx5PH0JCCjY0AdfITLy9oIpNjMpZtg3FN05ggqMzIxM1swOtVTe7xqMzvzxDPTJJBoK8P/b29mKFbmWtAnXh+JgdWgN9fmKDW1w3x0xMKNzfIUqNJ/pnw9vaB5i+YBtRuLfE8bUt+6GB7m2NvL1bhX4mN9YrB7m7REvJvi41gq6ucHk3NSX4JgrCWSChTTnE6DUQiQCpVQ7WwwOFyuXwEORQK6aIoXhwc4DocRv/KCiBJdU5dlhlsMef8mDG2RMKWukIr3PE4rvN52OJZqrURWgkthGZCo3V2xtzMeQtumhc0b7KnR9vPZnFnGbzVgPlt/gowAIULFFoZgfsOAAAAAElFTkSuQmCC";
this.allowedStyles = {
"background-color": /.*/,
};
}
function wysiwyg_insertImage() {
wysiwyg_btn.call(this);
this.name = this.command = "insertImage";
this.category = "insert";
this.icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIfSURBVDjLpZPNS5RRFMZ/577v+L5jmlmNoBgE4iLIWkgxmTtx4R8QLXLRB1GYG4lAwlWkCH1sShcRuIgWYUQoBIUVgojLyowWLSRhSCNtchzn672nxYxT6hRBD/cuzuW5D+c5H6Kq/A9cgM6+0VtBTk4tJwM/kS7BspvDsAc7w4w8uXGyxwUIrHRev9AcqYlERMRFAS3+E1RBdSNWglyGs9eenwbyAsuJwIvsjUjX7QfU7duF51gC9cBUYYT8NYJjhM8fZ+nvuUg2EClaSKbBGJfGhv0cjLbiGAfVAMQFEYwIIgZjDCHHYO2WGmzY9DwfP1yRz/cv0KLJLQLZTIpsah1EULVYDbDWIICq4khALpNE1W7PQBW+xmN8W4qTtTmsBvxIL5IJ6pECp8ZbYX0tDmpKC3xZLCe0kPr1oBFUU0XyCmEWFnT7HNgC3zhlGMcr6TtITJBLvKK6+jtX7z/ElDV4cGJzBn9COv6MPZXTNDcfpX53I6/nnrL+ftKPdtfddAHUWgRYmp8rKRAKPabtSAeBCThc287Eh1GiTS3Mfxq75OZnLd+coYG+YvQ7rtzpJyQVdBw4B8DltnuMzw4DY74LsDNs4jaXqqotl3wLC4KFw+panLnYNG9jU/S2jzD44gx+vlYpF2CHZx6dH3h5LJnVJmtL7dJxf+bdtNdyqJXx2WHKxGXqzSTAkPzrOke76waBLqASWAWGZ+7Gen8CJf/dMYh8E3AAAAAASUVORK5CYII=";
this.allowedStyles = {
"padding": /.*/,"float": /.*/
};
this.allowedTags = {
"img":["id","src","alt","title","class","style","dir","lang","title"]
};
this.onClick = function(e, editor){
var html = 'URL <input type="text" class="URLimg"><input type="button" value="Add" class="addIMG">';
var prop = $('.popover',editor.toolbar).css({
width:"300px",
height:"50px"
}).appendTo(e.target.parentNode).show().contents().find("body").empty().append(html);
var $this = this;
$(".addIMG",prop).on("mousedown",function(event){
console.dir($(".URLimg",prop).val());
editor.setCommand($this.command,$(".URLimg",prop).val());
$('.popover',editor.toolbar).hide();
$(".addIMG",prop).off("mousedown");
});
}
}
function wysiwyg_code() {
this.name = "code";
this.command = "bold"; // fix firefox
this.category = "code";
this.allowedTags = {
"code":["id","src","alt","title","class","style","dir","lang","title","data-language"]
};
this.getAdmin = function(){
return '';
}
this.onClick = function(e, editor){
}
}function blockAdminToolbar() {
this.initBefore = function () {
/* Tabs */
$('.sidebar').on('click','.icons', function(){
var elmt = this.parentNode;
var rel = elmt.getAttribute("rel");
var parent = $(elmt).closest(".contenttab");
$(".block",parent).hide();
if( elmt.classList.contains('active')) {
elmt.classList.remove('active');
rel = '';
}else {
$(".mainTab",parent).removeClass('active');
elmt.classList.add('active');
}
ParsimonyAdmin.setCookie($(elmt).closest(".sidebar").data("side") + "ToolbarPanel",rel,999);
});
$('#admin').on('click','.ssTab',function(){
var parent = $(this).parent().parent();
parent.find(".tabPanel").hide();
parent.find("." + this.getAttribute('rel')).show();
parent.find(".ssTab").removeClass('active');
this.classList.add('active');
});
}
}
ParsimonyAdmin.setPlugin(new blockAdminToolbar());function blockAdminMenu() {
this.initBefore = function () {
/* Orientation and resolution */
$("#toolbar").on('change','#changeres', function(e) {
var res = this.value;
$("#currentRes").text(res);
if(res == 'max'){
var height = ParsimonyAdmin.currentBody.offsetHeight + 250;
if(screen.height > height) height = screen.height - 28;
ParsimonyAdmin.$iframe.css({
"width": "100%",
"height": height + "px"
});
res = ["max","max"];
}else{
res = res.split(/x/);
if($("#changeorientation").length == 0 || ($("#changeorientation").val() == 'portrait' && ParsimonyAdmin.getCookie("landscape") == 'portrait')){
ParsimonyAdmin.$iframe.css({
"width": res[0] + "px",
"height": res[1] + "px"
});
}else{
ParsimonyAdmin.$iframe.css({
"width": res[1] + "px",
"height": res[0] + "px"
});
}
}
ParsimonyAdmin.setCookie("screenX",res[0],999);
ParsimonyAdmin.setCookie("screenY",res[1],999);
ParsimonyAdmin.setCookie("landscape",$("#changeorientation").val(),999);
ParsimonyAdmin.$currentBody.removeClass("landscape portrait").addClass($("#changeorientation").val());
})
.on('change','#changeorientation', function(e) {
ParsimonyAdmin.setCookie("landscape",$("#changeorientation").val(),999);
$("#changeres").trigger("change");
});
}
}
ParsimonyAdmin.setPlugin(new blockAdminMenu());function blockAdminModules() {
this.initBefore = function () {
$(".modeleajout").click(function(e){
e.preventDefault();
e.stopPropagation();
ParsimonyAdmin.displayConfBox(BASE_PATH + "admin/action",t(($(this).data('title') || $(this).attr('title') || $(this).data('tooltip'))),"TOKEN=" + TOKEN + "&model=" + $(this).attr('rel') + "&action=getViewAdminModel");
});
}
this.init = function () {
$("#left_sidebar").on('click','div.titleTab', function(){
var next = $(this).next();
if(next.is('div')) $(this).next().slideToggle('fast');
});
}
this.loadCreationMode = function () {
//highlight link on list page
var src = ParsimonyAdmin.currentWindow.location.href.toLocaleString().replace("http://","");
var src = src.substring(src.indexOf(BASE_PATH)).replace("?parsiframe=ok","").replace("parsiframe=ok","");
var itemLink = $('.sublist[data-url="' + src + '"]');
if(itemLink.length > 0){
$(".sublist.selected").removeClass('selected');
itemLink.addClass('selected');
}
}
}
ParsimonyAdmin.setPlugin(new blockAdminModules());function blockAdminBlocks() {
this.dragLastDomId = "";
this.dragMiddle = 0;
this.isAddBlock = false;
this.blocks = [];
this.startDragging = function () {
ParsimonyAdmin.$currentBody.append(document.getElementById("dropInPage" ));
$("#right_sidebar .mainTab").removeClass("active");
$(".paneltree").addClass("active");
}
this.changeBlockPosition = function (blockType, idBlock, idNextBlock, startIdParentBlock, stopIdParentBlock, startTypeCont, stopTypeCont, action, content){
if(typeof startIdParentBlock == "undefined" || typeof stopIdParentBlock == "undefined"){
alert(t('Error in your DOM, perhaps an HTML tag isn\'t closed.'));
return false
};
if(idNextBlock == undefined || idNextBlock==idBlock) idNextBlock = "last";
var contentToAdd = '';
if(typeof content != "undefined") contentToAdd = content;
ParsimonyAdmin.postData(BASE_PATH + "admin/" + action,{
TOKEN: TOKEN ,
popBlock: blockType ,
idBlock: idBlock,
id_next_block:idNextBlock ,
startParentBlock: startIdParentBlock ,
parentBlock:stopIdParentBlock ,
start_typecont:startTypeCont ,
stop_typecont:stopTypeCont ,
IDPage: $(".container_page",ParsimonyAdmin.currentBody).data('page'),
content: contentToAdd
},function(data){
ParsimonyAdmin.execResult(data);
ParsimonyAdmin.returnToShelter();
ParsimonyAdmin.updateUI();
});
}
this.loadEditMode = function () {
$this = this;
ParsimonyAdmin.$currentDocument.on('click.edit','.block',function(e){
var blockInst = (typeof $this.blocks["block_" + this.classList[1]] != "undefined") ? $this.blocks["block_" + this.classList[1]] : $this.blocks['block_block'];
blockInst.onClickEdit.apply(this, [e]);
});
}
this.loadCreationMode = function () {
$this = this;
//Dispatch menu action event : configure / design / delete
$(document).add('#config_tree_selector').on('click.creation',".config_destroy, .cssblock, .configure_block",function(e){
var blockInst = (typeof $this.blocks["block_" + this.classList[1]] != "undefined") ? $this.blocks["block_" + this.classList[1]] : $this.blocks['block_block'];
eval("blockInst." + $(this).data("action") + ".apply(this, [e]);");
})
/* Hide overlay when user don't pick a block */
.on('mouseover.creation',"body", function(event) {
document.getElementById("blockOverlay").style.display = "none";
})
.on('dragenter.creation',"#admintoolbar", function(e) {
e.stopPropagation();
ParsimonyAdmin.returnToShelter();
});
/* HTML5 drag n drop*/
$("#panelblocks").on('dragstart.creation',".admin_core_block", function( event ){
$this.isAddBlock = true;
var evt = event.originalEvent;
evt.dataTransfer.setDragImage(this,15,15);
evt.dataTransfer.setData("parsimony/addblock", JSON.stringify({blockType:this.id}));
evt.dataTransfer.effectAllowed = 'copy';
$this.startDragging();
});
$("#menu").add('#paneltree').on('dragstart.creation',".move_block",function( event ){
$this.isAddBlock = false;
var evt = event.originalEvent;
var elmt = $("#" + ParsimonyAdmin.inProgress,ParsimonyAdmin.currentBody);
evt.dataTransfer.setDragImage(elmt[0],15,15);
var startTypeCont = ParsimonyAdmin.whereIAm(ParsimonyAdmin.inProgress);
if(elmt.parent().closest(".container").hasClass("container_page")) startIdParentBlock = elmt.parent().closest(".container").data('page');
else startIdParentBlock = elmt.parent().closest(".container").attr('id');
if(startIdParentBlock == 'content') startIdParentBlock = $(".container_page",ParsimonyAdmin.currentBody).data('page');
evt.dataTransfer.setData("parsimony/moveblock", JSON.stringify({idBlock:ParsimonyAdmin.inProgress,startIdParentBlock:startIdParentBlock,startTypeCont:startTypeCont}));
evt.dataTransfer.effectAllowed = 'copyMove';
$this.startDragging();
});
$('#conf_box_content').on('click.creation',"#dialog-ok",function(e){
e.preventDefault();
var idBlock = $("#dialog-id").val();
var obj = JSON.parse($("#dialog-id-options").val());
if(idBlock != ''){
if( obj.idNextBlock == '' || obj.stopIdParentBlock == '' || ParsimonyAdmin.whereIAm("dropInTree") == '') alert("stop");
var content = '';
if(typeof obj.content != "undefined") content = obj.content;
$this.changeBlockPosition(obj.blockType,idBlock,obj.idNextBlock,'',obj.stopIdParentBlock,'',ParsimonyAdmin.whereIAm("dropInTree"),"addBlock",content);
}else{
alert(t('Please enter your ID'));
}
ParsimonyAdmin.closeConfBox();
});
ParsimonyAdmin.$currentBody.on('click.creation','.block',function(e){
var blockInst = (typeof $this.blocks["block_" + this.classList[1]] != "undefined") ? $this.blocks["block_" + this.classList[1]] : $this.blocks['block_block'];
blockInst.onClickCreation.apply(this, [e]);
})
.on('mouseover.creation',".block", function(event) {
event.stopImmediatePropagation();
var offset = this.getBoundingClientRect();
var leftOffsetFrame = document.getElementById("parsiframe").offsetLeft;
if(ParsimonyAdmin.inProgress != this.id) document.getElementById("blockOverlay").style.cssText = "display:block;top:" + offset.top + "px;left:" + (offset.left + leftOffsetFrame + 40) + "px;width:" + $(this).outerWidth() + "px;height:" + $(this).outerHeight() + "px";
else document.getElementById("blockOverlay").style.display = "none";
});
ParsimonyAdmin.$currentBody.add('#paneltree')
.on('dragenter.creation','.block,.tree_selector', function(e) {
if( e.originalEvent.dataTransfer.types != null){
e.stopImmediatePropagation();
//if(e.type == 'dragenter' || Math.floor ( Math.random() * 12 ) == 3) {
var isContainer = false;
if((this.classList.contains("container") && !this.classList.contains("tree_selector")) || this.id =='treedom_container') isContainer = true;
if(e.type == 'dragenter' || ($this.dragLastDomId != this.id ||
( $this.dragMiddlePos == 1 && (e.originalEvent.pageY > $this.dragMiddle)) ||
( $this.dragMiddlePos == 0 && (e.originalEvent.pageY < $this.dragMiddle)))){
var theBlock = this;
if(this.classList.contains("tree_selector")) theBlock = ParsimonyAdmin.currentDocument.getElementById(this.id.split("treedom_")[1]);
var theBlockTree = document.getElementById("treedom_" + theBlock.id);
var dropInPage = ParsimonyAdmin.currentDocument.getElementById("dropInPage") || $("#dropInPage").appendTo(ParsimonyAdmin.currentBody).get(0);
$this.dragLastDomId = this.id;
$this.dragMiddle = $(this).offset().top + this.offsetHeight/2;
if(e.originalEvent.pageY < $this.dragMiddle && !isContainer){
if(!$this.isAddBlock && ParsimonyAdmin.inProgress == theBlock.id) return true;
$this.dragMiddlePos = 1;
$(theBlock).before(dropInPage);
theBlockTree.parentNode.insertBefore(document.getElementById( "dropInTree" ),theBlockTree);
}else{
if(!$this.isAddBlock && theBlock.nextElementSibling.id == ParsimonyAdmin.inProgress) return true;
$this.dragMiddlePos = 0;
if(theBlock.classList.contains("container") && $(theBlock).children(".dropInContainer").length > 0){
if(!$this.isAddBlock && theBlock.id == ParsimonyAdmin.inProgress) return true;
$(".dropInContainerChild:first",theBlock).append(dropInPage);
theBlockTree.appendChild(document.getElementById( "dropInTree" ),theBlockTree);
}else if(theBlock.classList.contains("container") && !isContainer){
theBlock.parentNode.insertBefore(dropInPage,theBlock);
theBlockTree.parentNode.insertBefore(document.getElementById( "dropInTree" ),theBlockTree);
}else if(theBlock.parentNode.classList.contains("container") && !isContainer){
theBlock.parentNode.insertBefore(dropInPage,theBlock.nextSibling);
theBlockTree.parentNode.insertBefore(document.getElementById( "dropInTree" ),theBlockTree.nextSibling);
}
}
}
dropInPage = theBlock = theBlockTree = null;
return false;
}else{
return true;
}
})
.on('dragover.creation','.block,.tree_selector', function(e) {
if( e.originalEvent.dataTransfer.types != null){
e.stopImmediatePropagation();
e.preventDefault(); /* Firefox fix */
return false;
}else{
return true;
}
})
.on('dragover.creation dragenter.creation','.marqueurdragndrop', function(e) {
if( e.originalEvent.dataTransfer.types != null){
e.stopImmediatePropagation();
e.preventDefault(); /* Firefox fix */
return false;
}else{
return true;
}
})
.on('drop.creation','.container,.tree_selector',function( event ){
event.stopPropagation();
var evt = event.originalEvent;
evt.preventDefault(); /* Firefox fix */
evt.stopPropagation();
var elmt = $( "#dropInPage" ,ParsimonyAdmin.currentBody);
if(elmt.length > 0){
var stopIdParentBlock = "";
if(elmt.closest(".container").hasClass("container_page")) stopIdParentBlock = elmt.closest(".container").data('page');
else stopIdParentBlock = elmt.closest(".container").attr('id');
var idNextBlock = elmt.next(".block").attr('id');
/* Move block action */
if(evt.dataTransfer.getData("parsimony/moveblock").length > 0){
var obj = JSON.parse(evt.dataTransfer.getData("parsimony/moveblock"));
if(obj.idBlock == '' || idNextBlock == '' || obj.startIdParentBlock == '' || stopIdParentBlock == '' || obj.startTypeCont == '' || ParsimonyAdmin.whereIAm("dropInTree") == '') alert("stop");
$this.changeBlockPosition('',obj.idBlock,idNextBlock,obj.startIdParentBlock,stopIdParentBlock,obj.startTypeCont,ParsimonyAdmin.whereIAm("dropInTree"),"moveBlock");
}else{
/* Add a block or other types of things */
var obj;
if(evt.dataTransfer.getData("parsimony/addblock").length > 0){
obj = JSON.stringify({blockType:JSON.parse(evt.dataTransfer.getData("parsimony/addblock")).blockType,stopIdParentBlock:stopIdParentBlock,idNextBlock:idNextBlock});
}else if(evt.dataTransfer.getData("text/plain").length > 0){
obj = JSON.stringify({blockType:"core\\blocks\\wysiwyg",stopIdParentBlock:stopIdParentBlock,idNextBlock:idNextBlock,content:evt.dataTransfer.getData("text/plain")});
}
ParsimonyAdmin.displayConfBox("#dialog","Entrez un identifiant pour ce nouveau bloc");
$("#dialog-id-options").val(obj);
$("#dialog-id").val('').trigger("focus");
}
}
});
}
this.unloadCreationMode = function(){
ParsimonyAdmin.$currentBody.add('#paneltree').add('#conf_box_content').add(document).add('#config_tree_selector').add("#menu").off('.creation');
}
this.setBlock = function (block) {
this.blocks["block_" + block.name] = block;
}
}
function block() {
this.name = "block";
var me = this;
this.onClickEdit = function (e) {
e.stopPropagation();
}
this.onClickCreation = function (e) {
e.stopPropagation();
ParsimonyAdmin.selectBlock(this.id);
if(e.trad != true && e.link != true) ParsimonyAdmin.closeParsiadminMenu();
ParsimonyAdmin.addTitleParsiadminMenu('#' + ParsimonyAdmin.inProgress);
ParsimonyAdmin.addOptionParsiadminMenu('<a href="#" class="configure_block" rel="getViewConfigBlock" data-action="onConfigure" title="Configuration' + ' #' + ParsimonyAdmin.inProgress + '"><span class="ui-icon ui-icon-wrench floatleft"></span>'+ t('Configure') +'</a>');
var CSSProps = '';
if(typeof me.stylableElements != "undefined"){
$.each(me.stylableElements, function(index, value) {
CSSProps += '<a href="#" onclick="blockAdminCSS.displayCSSConf(CSSTHEMEPATH, \'#\' + ParsimonyAdmin.inProgress + \' ' + value + '\');return false;" data-css="' + value + '"><span class="ui-icon ui-icon-pencil floatleft"></span>'+ t('Design') + ' ' + t(index) + '</a>';
});
CSSProps = '<span class="ui-icon ui-icon-carat-1-e floatright"></span><div class="none CSSProps">' + CSSProps + '</a>';
}
ParsimonyAdmin.addOptionParsiadminMenu('<div class="CSSDesign"><a href="#" class="cssblock" data-action="onDesign"><span class="ui-icon ui-icon-pencil floatleft"></span>' + t('Design') + '</a>' + CSSProps + '</div>');
if(this.id != "container") ParsimonyAdmin.addOptionParsiadminMenu('<a href="#" draggable="true" class="move_block" style="cursor:move"><span class="ui-icon ui-icon-arrow-4 floatleft"></span>'+ t('Move') +'</a>');
if(this.id != "container" && this.id != "content") ParsimonyAdmin.addOptionParsiadminMenu('<a href="#" class="config_destroy" data-action="onDelete"><span class="ui-icon ui-icon-closethick floatleft"></span>'+ t('Delete') +'</a>');
ParsimonyAdmin.openParsiadminMenu(e.pageX || ($(window).width()/2),e.pageY || ($(window).height()/2));
}
this.onConfigure = function () {
var parentId = '';
var inProgress = $("#treedom_" + ParsimonyAdmin.inProgress);
if(inProgress.length > 0){
if(inProgress.parent().closest(".container").attr("id") == "treedom_content") parentId = inProgress.parent().closest("#treedom_content").data('page');
else parentId = inProgress.parent().closest(".container").attr('id').replace("treedom_","");
}
ParsimonyAdmin.displayConfBox(BASE_PATH + "admin/action",$(this).attr('title'),"TOKEN=" + TOKEN + "&idBlock=" + ParsimonyAdmin.inProgress + "&parentBlock=" + parentId + "&typeProgress=" + ParsimonyAdmin.typeProgress + "&action=" + $(this).attr('rel') +"&IDPage=" + $(".container_page",ParsimonyAdmin.currentBody).data('page'));
}
this.onDesign = function (e) {
e.preventDefault();
ParsimonyAdmin.selectBlock(ParsimonyAdmin.inProgress);
blockAdminCSS.displayCSSConf(CSSTHEMEPATH, "#" + ParsimonyAdmin.inProgress);
}
this.onCreate = function () {
}
this.onDelete = function (e) {
ParsimonyAdmin.destroyBlock();
}
this.onSaveConfig = function () {
}
}function blockAdminTree() {
this.initBefore = function () {
/* Help on Tree*/
$('#right_sidebar').on('click','#treelegend',function(){
$('#treelegend2').slideToggle();
}).on('click','.arrow_tree',function(event){
event.stopPropagation();
$(this).toggleClass('down');
$(this).nextAll('ul,li').toggleClass('none');
});
}
this.initIframe = function () {
$('#treedom_container').attr('title','Site structure');
$('#treedom_content').attr('title','Dynamic content');
}
this.init = function () {
$('#right_sidebar').on('click','.tree_selector', function(event){
event.stopPropagation();
ParsimonyAdmin.selectBlock(this.id.split("treedom_")[1]);
if($("#" + this.id.split("treedom_")[1],ParsimonyAdmin.currentBody).length > 0){
$("body").animate({
scrollTop : $("#" + this.id.split("treedom_")[1],ParsimonyAdmin.currentBody).offset().top -50
},"fast");
}
}).on('mouseenter','.tree_selector', function(event){
event.stopPropagation();
var ids = this.id.split("treedom_")[1];
$(".selection-block:not(#" + ParsimonyAdmin.inProgress + ")",ParsimonyAdmin.currentBody).removeClass("selection-block");
$("#" + ids,ParsimonyAdmin.currentBody).trigger('mouseover');
});
$("#treedom_content").on('mouseover mouseout',function(event){
var dom = $(".container_page",ParsimonyAdmin.currentBody).get(0);
if(typeof dom.style != "undefined"){
if (event.type == 'mouseover') {
dom.style.outline = '5px #c8007a solid';
} else {
dom.style.outline = 'none';
}
}
});
}
}
ParsimonyAdmin.setPlugin(new blockAdminTree());/**
* Drag n'Drop - jQuery Plugin
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to contact@parsimony-cms.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Parsimony to newer
* versions in the future. If you wish to customize Parsimony for your
* needs please refer to http://www.parsimony.mobi for more information.
*
* @authors Julien Gras et Benoît Lorillot
* @copyright Julien Gras et Benoît Lorillot
* @version Release: 1.0
* @category Drag n'Drop - jQuery Plugin
* Requires: jQuery v1.4.2+
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
(function( $ ){
var methods = {
init : function( options ) {
params = $.extend( {
stopDraggable: function() {},
stopResizable: function() {},
initPos:{}
}, options);
var initContext = $("#overlays");
params.initContext = initContext;
var context = this.closest("html");
$(document).off("mousemove").off("mouseup");
$(".parsimonyDND",initContext).off("click").off("mousedown").off("click",".parsimonyResize").find(".parsimonyResize").off("mousedown");
$(".parsimonyResize",initContext).remove();
$(".parsimonyDND",context).removeClass("parsimonyDND");
return this.each(function() {
var $this = $(this);
var doc = $this.closest("body");
$this.addClass("parsimonyDND");
params.initPos = {
left : isNaN(parseFloat($this.css("left"))) ? 'initial' : $this.css("left"),
top : isNaN(parseFloat($this.css("top"))) ? 'initial' : $this.css("top"),
width : isNaN(parseFloat($this.css("width"))) ? 'initial' : $this.css("width"),
height : isNaN(parseFloat($this.css("height"))) ? 'initial' : $this.css("height")
};
initContext.append('<div class="parsimonyDND"><div class="parsimonyResizeInfo"> <span class="parsimonyResizeReInit spanDND ui-icon-arrowrefresh-1-w" title="Reinit"></span> </a><span class="parsimonyResizeClose spanDND closedesign ui-icon-closethick"></span> </div><div class="parsimonyResize se"></div><div class="parsimonyResize nw"></div><div class="parsimonyResize ne"></div><div class="parsimonyResize sw"></div></div>');
var offset = $(this).offset();
var offsetFrame = ParsimonyAdmin.$iframe.offset();
$(".parsimonyDND",initContext).css({
position: "absolute",
left : (offset.left + offsetFrame.left + 40),
top : offset.top,
width : params.initPos.width,
height : params.initPos.height
});
var dnd = $(".parsimonyDND", initContext);
dnd.on("mousedown.parsimonyDND",function(e){
$("#overlays").css("pointer-events","all");
var $this = $(".parsimonyDND",context);
if($this.css('position')=="static"){
$this.css('position','relative');
$('#panelcss select[name="position"]').val('relative');
}
var dndstart = {
$this : $this,
left : isNaN(parseFloat($this.css("left"))) ? 0 : $this.css("left"),
top : isNaN(parseFloat($this.css("top"))) ? 0 : $this.css("top"),
pageX : e.pageX,
pageY : e.pageY
};
$(document).add(doc).on("mousemove.parsimonyDND",dndstart,function(e){
$this.css({
left: parseFloat(dndstart.left) + e.pageX - dndstart.pageX + "px",
top: parseFloat(dndstart.top) + e.pageY - dndstart.pageY + "px"
});
document.getElementById("box_top").value = $this.css("top");
document.getElementById("box_left").value = $this.css("left");
$this.parsimonyDND("updatePosition");
}).on("mouseup.parsimonyDND",dndstart,function(e){
$("#overlays").css("pointer-events","none");
params.stopDraggable(e,$this);
$(document).add(doc).off("mousemove").off("mouseup");
});
});
initContext.on("click.parsimonyDND",function(e){
e.stopImmediatePropagation();
});
initContext.on("click.parsimonyDND",".parsimonyResize",function(e){
e.stopImmediatePropagation();
});
initContext.find(".parsimonyResizeReInit").on("click.parsimonyDND",function(e){
e.preventDefault();
$this.closest(".parsimonyDND").parsimonyDND("reInit");
});
initContext.find(".parsimonyResizeClose").on("click.parsimonyDND",function(e){
e.preventDefault();
$this.closest(".parsimonyDND").parsimonyDND("destroy");
});
initContext.find(".parsimonyResize").on("mousedown.parsimonyDND",function(e){
e.stopImmediatePropagation();
$("#overlays").css("pointer-events","all");
if($this.css('position')=="static"){
$this.css('position','relative');
$('#panelcss select[name="position"]').val('relative');
}
var parent = $(".parsimonyDND",context);
var bounds = parent.get(0).getBoundingClientRect();
var dndstart = {
$this : parent,
width : bounds.width,
height : bounds.height,
top : isNaN(parseFloat(parent.css("top"))) ? 0 : parent.css("top"),//$(this).css('top'),
left : isNaN(parseFloat(parent.css("left"))) ? 0 : parent.css("left"),//$(this).css('left'),
pageX : e.pageX,
pageY : e.pageY,
dir : $(this).attr('class').replace("parsimonyResize ", "")
};
$(document).add(doc).on("mousemove.parsimonyDND",dndstart,function(e){
switch(dndstart.dir){
case "se":
$this.css({
width: parseFloat(dndstart.width) + (e.pageX - dndstart.pageX) + "px",
height: parseFloat(dndstart.height) + (e.pageY - dndstart.pageY) + "px"
});
break;
case "nw":
$this.css({
top: parseFloat(dndstart.top) + (e.pageY - dndstart.pageY) + "px",
left: parseFloat(dndstart.left) + (e.pageX - dndstart.pageX) + "px",
width: parseFloat(dndstart.width) - (e.pageX - dndstart.pageX) + "px",
height: parseFloat(dndstart.height) - (e.pageY - dndstart.pageY) + "px"
});
break;
case "ne":
$this.css({
top: parseFloat(dndstart.top) + (e.pageY - dndstart.pageY) + "px",
width: parseFloat(dndstart.width) + (e.pageX - dndstart.pageX) + "px",
height: parseFloat(dndstart.height) - (e.pageY - dndstart.pageY) + "px"
});
break;
case "sw":
$this.css({
left: parseFloat(dndstart.left) + (e.pageX - dndstart.pageX) + "px",
width: parseFloat(dndstart.width) - (e.pageX - dndstart.pageX) + "px",
height: parseFloat(dndstart.height) + (e.pageY - dndstart.pageY) + "px"
});
break;
}
$this.parsimonyDND("updatePosition");
document.getElementById("box_width").value = $this.width() + "px";
document.getElementById("box_height").value = $this.height() + "px";
document.getElementById("box_top").value = $this.css("top");
document.getElementById("box_left").value = $this.css("left");
}).on("mouseup",dndstart,function(e){
$("#overlays").css("pointer-events","none");
params.stopResizable(e,$this);
$(document).add(doc).off("mousemove").off("mouseup");
});
});
});
},
reInit : function( ) {
return this.each(function(){
$(this).css({
top: params.initPos.top,
left: params.initPos.left,
width: params.initPos.width,
height: params.initPos.height
});
$(this).parsimonyDND("updatePosition");
params.stopResizable("",$(this));
})
},
updatePosition : function( ) {
return this.each(function(){
var bounds1 = this.getBoundingClientRect();
var bounds = $(this).offset();
var offsetFrame = ParsimonyAdmin.$iframe.offset();
$(".parsimonyDND",params.initContext).css({
top: bounds.top + "px",
left: bounds.left + offsetFrame.left + 40 + "px",
width: bounds1.width + "px",
height: bounds1.height + "px"
});
})
},
destroy : function( ) {
return this.each(function(){
$(".parsimonyDND",params.initContext).remove();
$(this).removeClass('parsimonyDND');
$(this).off('.parsimonyDND');
})
}
};
$.fn.parsimonyDND = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
}
};
})( jQuery );// CodeMirror version 3.02
//
// CodeMirror is the only global var we claim
window.CodeMirror = (function() {
"use strict";
// BROWSER SNIFFING
// Crude, but necessary to handle a number of hard-to-feature-detect
// bugs and behavior differences.
var gecko = /gecko\/\d/i.test(navigator.userAgent);
var ie = /MSIE \d/.test(navigator.userAgent);
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
var chrome = /Chrome\//.test(navigator.userAgent);
var opera = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor);
var khtml = /KHTML\//.test(navigator.userAgent);
var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
var phantom = /PhantomJS/.test(navigator.userAgent);
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
// This is woefully incomplete. Suggestions for alternative methods welcome.
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
var mac = ios || /Mac/.test(navigator.platform);
var windows = /windows/i.test(navigator.platform);
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
if (opera_version) opera_version = Number(opera_version[1]);
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
// Optimize some code when these features are not used
var sawReadOnlySpans = false, sawCollapsedSpans = false;
// CONSTRUCTOR
function CodeMirror(place, options) {
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
this.options = options = options || {};
// Determine effective options based on given values and defaults.
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
options[opt] = defaults[opt];
setGuttersForLineNumbers(options);
var display = this.display = makeDisplay(place);
display.wrapper.CodeMirror = this;
updateGutters(this);
if (options.autofocus && !mobile) focusInput(this);
this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
this.nextOpId = 0;
loadMode(this);
themeChanged(this);
if (options.lineWrapping)
this.display.wrapper.className += " CodeMirror-wrap";
// Initialize the content.
this.setValue(options.value || "");
// Override magic textarea content restore that IE sometimes does
// on our hidden textarea on reload
if (ie) setTimeout(bind(resetInput, this, true), 20);
this.view.history = makeHistory();
registerEventHandlers(this);
// IE throws unspecified error in certain cases, when
// trying to access activeElement before onload
var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
else onBlur(this);
operation(this, function() {
for (var opt in optionHandlers)
if (optionHandlers.propertyIsEnumerable(opt))
optionHandlers[opt](this, options[opt], Init);
for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
})();
}
// DISPLAY CONSTRUCTOR
function makeDisplay(place) {
var d = {};
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
if (webkit) input.style.width = "1000px";
else input.setAttribute("wrap", "off");
input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
// Wraps and hides input textarea
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
// The actual fake scrollbars.
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
// DIVs containing the selection and the actual code
d.lineDiv = elt("div");
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
// Blinky cursor, and element used to ensure cursor fits at the end of a line
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
// Secondary cursor, shown when on a 'jump' in bi-directional text
d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
// Used to measure text size
d.measure = elt("div", null, "CodeMirror-measure");
// Wraps everything that needs to exist inside the vertically-padded coordinate system
d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
null, "position: relative; outline: none");
// Moved around its parent to cover visible view
d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
// Set to the height of the text, causes scrolling
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
// Will contain the gutters, if any
d.gutters = elt("div", null, "CodeMirror-gutters");
d.lineGutter = null;
// Helper element to properly size the gutter backgrounds
var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
// Provides scrolling
d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
d.scroller.setAttribute("tabIndex", "-1");
// The element in which the editor lives.
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
d.scrollbarFiller, d.scroller], "CodeMirror");
// Work around IE7 z-index bug
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
// Needed to hide big blue blinking cursor on Mobile Safari
if (ios) input.style.width = "0px";
if (!webkit) d.scroller.draggable = true;
// Needed to handle Tab key in KHTML
if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
// Current visible range (may be bigger than the view window).
d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
// Used to only resize the line number gutter when necessary (when
// the amount of lines crosses a boundary that makes its width change)
d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
// See readInput and resetInput
d.prevInput = "";
// Set to true when a non-horizontal-scrolling widget is added. As
// an optimization, widget aligning is skipped when d is false.
d.alignWidgets = false;
// Flag that indicates whether we currently expect input to appear
// (after some event like 'keypress' or 'input') and are polling
// intensively.
d.pollingFast = false;
// Self-resetting timeout for the poller
d.poll = new Delayed();
// True when a drag from the editor is active
d.draggingText = false;
d.cachedCharWidth = d.cachedTextHeight = null;
d.measureLineCache = [];
d.measureLineCachePos = 0;
// Tracks when resetInput has punted to just putting a short
// string instead of the (large) selection.
d.inaccurateSelection = false;
// Used to adjust overwrite behaviour when a paste has been
// detected
d.pasteIncoming = false;
// Used for measuring wheel scrolling granularity
d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
return d;
}
// VIEW CONSTRUCTOR
function makeView(doc) {
var selPos = {line: 0, ch: 0};
return {
doc: doc,
// frontier is the point up to which the content has been parsed,
frontier: 0, highlight: new Delayed(),
sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
scrollTop: 0, scrollLeft: 0,
overwrite: false, focused: false,
// Tracks the maximum line length so that
// the horizontal scrollbar can be kept
// static when scrolling.
maxLine: getLine(doc, 0),
maxLineLength: 0,
maxLineChanged: false,
suppressEdits: false,
goalColumn: null,
cantEdit: false,
keyMaps: [],
overlays: [],
modeGen: 0
};
}
// STATE UPDATES
// Used to get the editor into a consistent state again when options change.
function loadMode(cm) {
var doc = cm.view.doc;
cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
doc.iter(0, doc.size, function(line) {
if (line.stateAfter) line.stateAfter = null;
if (line.styles) line.styles = null;
});
cm.view.frontier = 0;
startWorker(cm, 100);
cm.view.modeGen++;
if (cm.curOp) regChange(cm, 0, doc.size);
}
function wrappingChanged(cm) {
var doc = cm.view.doc, th = textHeight(cm.display);
if (cm.options.lineWrapping) {
cm.display.wrapper.className += " CodeMirror-wrap";
var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
doc.iter(0, doc.size, function(line) {
if (line.height == 0) return;
var guess = Math.ceil(line.text.length / perLine) || 1;
if (guess != 1) updateLineHeight(line, guess * th);
});
cm.display.sizer.style.minWidth = "";
} else {
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
computeMaxLength(cm.view);
doc.iter(0, doc.size, function(line) {
if (line.height != 0) updateLineHeight(line, th);
});
}
regChange(cm, 0, doc.size);
clearCaches(cm);
setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
}
function keyMapChanged(cm) {
var style = keyMap[cm.options.keyMap].style;
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
(style ? " cm-keymap-" + style : "");
}
function themeChanged(cm) {
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
clearCaches(cm);
}
function guttersChanged(cm) {
updateGutters(cm);
updateDisplay(cm, true);
}
function updateGutters(cm) {
var gutters = cm.display.gutters, specs = cm.options.gutters;
removeChildren(gutters);
for (var i = 0; i < specs.length; ++i) {
var gutterClass = specs[i];
var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
if (gutterClass == "CodeMirror-linenumbers") {
cm.display.lineGutter = gElt;
gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
}
}
gutters.style.display = i ? "" : "none";
}
function lineLength(doc, line) {
if (line.height == 0) return 0;
var len = line.text.length, merged, cur = line;
while (merged = collapsedSpanAtStart(cur)) {
var found = merged.find();
cur = getLine(doc, found.from.line);
len += found.from.ch - found.to.ch;
}
cur = line;
while (merged = collapsedSpanAtEnd(cur)) {
var found = merged.find();
len -= cur.text.length - found.from.ch;
cur = getLine(doc, found.to.line);
len += cur.text.length - found.to.ch;
}
return len;
}
function computeMaxLength(view) {
view.maxLine = getLine(view.doc, 0);
view.maxLineLength = lineLength(view.doc, view.maxLine);
view.maxLineChanged = true;
view.doc.iter(1, view.doc.size, function(line) {
var len = lineLength(view.doc, line);
if (len > view.maxLineLength) {
view.maxLineLength = len;
view.maxLine = line;
}
});
}
// Make sure the gutters options contains the element
// "CodeMirror-linenumbers" when the lineNumbers option is true.
function setGuttersForLineNumbers(options) {
var found = false;
for (var i = 0; i < options.gutters.length; ++i) {
if (options.gutters[i] == "CodeMirror-linenumbers") {
if (options.lineNumbers) found = true;
else options.gutters.splice(i--, 1);
}
}
if (!found && options.lineNumbers)
options.gutters.push("CodeMirror-linenumbers");
}
// SCROLLBARS
// Re-synchronize the fake scrollbars with the actual size of the
// content. Optionally force a scrollTop.
function updateScrollbars(d /* display */, docHeight) {
var totalHeight = docHeight + 2 * paddingTop(d);
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
var needsV = scrollHeight > d.scroller.clientHeight;
if (needsV) {
d.scrollbarV.style.display = "block";
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
d.scrollbarV.firstChild.style.height =
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
} else d.scrollbarV.style.display = "";
if (needsH) {
d.scrollbarH.style.display = "block";
d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
d.scrollbarH.firstChild.style.width =
(d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
} else d.scrollbarH.style.display = "";
if (needsH && needsV) {
d.scrollbarFiller.style.display = "block";
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
} else d.scrollbarFiller.style.display = "";
if (mac_geLion && scrollbarWidth(d.measure) === 0)
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
}
function visibleLines(display, doc, viewPort) {
var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
if (typeof viewPort == "number") top = viewPort;
else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
top = Math.floor(top - paddingTop(display));
var bottom = Math.ceil(top + height);
return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
}
// LINE NUMBERS
function alignHorizontally(cm) {
var display = cm.display;
if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
var gutterW = display.gutters.offsetWidth, l = comp + "px";
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
}
if (cm.options.fixedGutter)
display.gutters.style.left = (comp + gutterW) + "px";
}
function maybeUpdateLineNumberWidth(cm) {
if (!cm.options.lineNumbers) return false;
var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
if (last.length != display.lineNumChars) {
var test = display.measure.appendChild(elt("div", [elt("div", last)],
"CodeMirror-linenumber CodeMirror-gutter-elt"));
var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
display.lineGutter.style.width = "";
display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
display.lineNumWidth = display.lineNumInnerWidth + padding;
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
display.lineGutter.style.width = display.lineNumWidth + "px";
return true;
}
return false;
}
function lineNumberFor(options, i) {
return String(options.lineNumberFormatter(i + options.firstLineNumber));
}
function compensateForHScroll(display) {
return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
}
// DISPLAY DRAWING
function updateDisplay(cm, changes, viewPort) {
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
var updated = updateDisplayInner(cm, changes, viewPort);
if (updated) {
signalLater(cm, cm, "update", cm);
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
}
updateSelection(cm);
updateScrollbars(cm.display, cm.view.doc.height);
return updated;
}
// Uses a set of changes plus the current scroll position to
// determine which DOM updates have to be made, and makes the
// updates.
function updateDisplayInner(cm, changes, viewPort) {
var display = cm.display, doc = cm.view.doc;
if (!display.wrapper.clientWidth) {
display.showingFrom = display.showingTo = display.viewOffset = 0;
return;
}
// Compute the new visible window
// If scrollTop is specified, use that to determine which lines
// to render instead of the current scrollbar position.
var visible = visibleLines(display, doc, viewPort);
// Bail out if the visible area is already rendered and nothing changed.
if (changes !== true && changes.length == 0 &&
visible.from > display.showingFrom && visible.to < display.showingTo)
return;
if (changes && maybeUpdateLineNumberWidth(cm))
changes = true;
var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
// When merged lines are present, the line that needs to be
// redrawn might not be the one that was changed.
if (changes !== true && sawCollapsedSpans)
for (var i = 0; i < changes.length; ++i) {
var ch = changes[i], merged;
while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
var from = merged.find().from.line;
if (ch.diff) ch.diff -= ch.from - from;
ch.from = from;
}
}
// Used to determine which lines need their line numbers updated
var positionsChangedFrom = changes === true ? 0 : Infinity;
if (cm.options.lineNumbers && changes && changes !== true)
for (var i = 0; i < changes.length; ++i)
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
var from = Math.max(visible.from - cm.options.viewportMargin, 0);
var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
if (sawCollapsedSpans) {
from = lineNo(visualLine(doc, getLine(doc, from)));
while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
}
// Create a range of theoretically intact lines, and punch holes
// in that using the change info.
var intact = changes === true ? [] :
computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
// Clip off the parts that won't be visible
var intactLines = 0;
for (var i = 0; i < intact.length; ++i) {
var range = intact[i];
if (range.from < from) range.from = from;
if (range.to > to) range.to = to;
if (range.from >= range.to) intact.splice(i--, 1);
else intactLines += range.to - range.from;
}
if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
return;
intact.sort(function(a, b) {return a.from - b.from;});
var focused = document.activeElement;
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
patchDisplay(cm, from, to, intact, positionsChangedFrom);
display.lineDiv.style.display = "";
if (document.activeElement != focused && focused.offsetHeight) focused.focus();
var different = from != display.showingFrom || to != display.showingTo ||
display.lastSizeC != display.wrapper.clientHeight;
// This is just a bogus formula that detects when the editor is
// resized or the font size changes.
if (different) display.lastSizeC = display.wrapper.clientHeight;
display.showingFrom = from; display.showingTo = to;
startWorker(cm, 100);
var prevBottom = display.lineDiv.offsetTop;
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
if (ie_lt8) {
var bot = node.offsetTop + node.offsetHeight;
height = bot - prevBottom;
prevBottom = bot;
} else {
var box = node.getBoundingClientRect();
height = box.bottom - box.top;
}
var diff = node.lineObj.height - height;
if (height < 2) height = textHeight(display);
if (diff > .001 || diff < -.001) {
updateLineHeight(node.lineObj, height);
var widgets = node.lineObj.widgets;
if (widgets) for (var i = 0; i < widgets.length; ++i)
widgets[i].height = widgets[i].node.offsetHeight;
}
}
display.viewOffset = heightAtLine(cm, getLine(doc, from));
// Position the mover div to align with the current virtual scroll position
display.mover.style.top = display.viewOffset + "px";
if (visibleLines(display, doc, viewPort).to >= to)
updateDisplayInner(cm, [], viewPort);
return true;
}
function computeIntact(intact, changes) {
for (var i = 0, l = changes.length || 0; i < l; ++i) {
var change = changes[i], intact2 = [], diff = change.diff || 0;
for (var j = 0, l2 = intact.length; j < l2; ++j) {
var range = intact[j];
if (change.to <= range.from && change.diff) {
intact2.push({from: range.from + diff, to: range.to + diff});
} else if (change.to <= range.from || change.from >= range.to) {
intact2.push(range);
} else {
if (change.from > range.from)
intact2.push({from: range.from, to: change.from});
if (change.to < range.to)
intact2.push({from: change.to + diff, to: range.to + diff});
}
}
intact = intact2;
}
return intact;
}
function getDimensions(cm) {
var d = cm.display, left = {}, width = {};
for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
left[cm.options.gutters[i]] = n.offsetLeft;
width[cm.options.gutters[i]] = n.offsetWidth;
}
return {fixedPos: compensateForHScroll(d),
gutterTotalWidth: d.gutters.offsetWidth,
gutterLeft: left,
gutterWidth: width,
wrapperWidth: d.wrapper.clientWidth};
}
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
var dims = getDimensions(cm);
var display = cm.display, lineNumbers = cm.options.lineNumbers;
if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
removeChildren(display.lineDiv);
var container = display.lineDiv, cur = container.firstChild;
function rm(node) {
var next = node.nextSibling;
if (webkit && mac && cm.display.currentWheelTarget == node) {
node.style.display = "none";
node.lineObj = null;
} else {
node.parentNode.removeChild(node);
}
return next;
}
var nextIntact = intact.shift(), lineNo = from;
cm.view.doc.iter(from, to, function(line) {
if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
if (lineIsHidden(line)) {
if (line.height != 0) updateLineHeight(line, 0);
if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
if (line.widgets[i].showIfHidden) {
var prev = cur.previousSibling;
if (prev.nodeType == "pre") {
var wrap = elt("div", null, null, "position: relative");
prev.parentNode.replaceChild(wrap, prev);
wrap.appendChild(prev);
prev = wrap;
}
prev.appendChild(buildLineWidget(line.widgets[i], prev, dims));
}
} else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
// This line is intact. Skip to the actual node. Update its
// line number if needed.
while (cur.lineObj != line) cur = rm(cur);
if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
cur = cur.nextSibling;
} else {
// This line needs to be generated.
var lineNode = buildLineElement(cm, line, lineNo, dims);
container.insertBefore(lineNode, cur);
lineNode.lineObj = line;
}
++lineNo;
});
while (cur) cur = rm(cur);
}
function buildLineElement(cm, line, lineNo, dims) {
var lineElement = lineContent(cm, line);
var markers = line.gutterMarkers, display = cm.display;
if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
(!line.widgets || !line.widgets.length)) return lineElement;
// Lines with gutter elements or a background class need
// to be wrapped again, and have the extra elements added
// to the wrapper div
var wrap = elt("div", null, line.wrapClass, "position: relative");
if (cm.options.lineNumbers || markers) {
var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
if (cm.options.fixedGutter) wrap.alignable = [gutterWrap];
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
wrap.lineNumber = gutterWrap.appendChild(
elt("div", lineNumberFor(cm.options, lineNo),
"CodeMirror-linenumber CodeMirror-gutter-elt",
"left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
+ display.lineNumInnerWidth + "px"));
if (markers)
for (var k = 0; k < cm.options.gutters.length; ++k) {
var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
if (found)
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
}
}
// Kludge to make sure the styled element lies behind the selection (by z-index)
if (line.bgClass)
wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
wrap.appendChild(lineElement);
if (line.widgets) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
var widget = ws[i], node = buildLineWidget(widget, wrap, dims);
if (widget.above)
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
else
wrap.appendChild(node);
}
if (ie_lt8) wrap.style.zIndex = 2;
return wrap;
}
function buildLineWidget(widget, wrap, dims) {
var node = elt("div", [widget.node], "CodeMirror-linewidget");
node.widget = widget;
if (widget.noHScroll) {
(wrap.alignable || (wrap.alignable = [])).push(node);
var width = dims.wrapperWidth;
node.style.left = dims.fixedPos + "px";
if (!widget.coverGutter) {
width -= dims.gutterTotalWidth;
node.style.paddingLeft = dims.gutterTotalWidth + "px";
}
node.style.width = width + "px";
}
if (widget.coverGutter) {
node.style.zIndex = 5;
node.style.position = "relative";
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
}
return node;
}
// SELECTION / CURSOR
function updateSelection(cm) {
var display = cm.display;
var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
if (collapsed || cm.options.showCursorWhenSelecting)
updateSelectionCursor(cm);
else
display.cursor.style.display = display.otherCursor.style.display = "none";
if (!collapsed)
updateSelectionRange(cm);
else
display.selectionDiv.style.display = "none";
// Move the hidden textarea near the cursor to prevent scrolling artifacts
var headPos = cursorCoords(cm, cm.view.sel.head, "div");
var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
headPos.top + lineOff.top - wrapOff.top)) + "px";
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
headPos.left + lineOff.left - wrapOff.left)) + "px";
}
// No selection, plain cursor
function updateSelectionCursor(cm) {
var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
display.cursor.style.left = pos.left + "px";
display.cursor.style.top = pos.top + "px";
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
display.cursor.style.display = "";
if (pos.other) {
display.otherCursor.style.display = "";
display.otherCursor.style.left = pos.other.left + "px";
display.otherCursor.style.top = pos.other.top + "px";
display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
} else { display.otherCursor.style.display = "none"; }
}
// Highlight selection
function updateSelectionRange(cm) {
var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
var fragment = document.createDocumentFragment();
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
function add(left, top, width, bottom) {
if (top < 0) top = 0;
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
"px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
"px; height: " + (bottom - top) + "px"));
}
function drawForLine(line, fromArg, toArg, retTop) {
var lineObj = getLine(doc, line);
var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
function coords(ch) {
return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
}
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
var leftPos = coords(dir == "rtl" ? to - 1 : from);
var rightPos = coords(dir == "rtl" ? from : to - 1);
var left = leftPos.left, right = rightPos.right;
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
add(left, leftPos.top, null, leftPos.bottom);
left = pl;
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
}
if (toArg == null && to == lineLen) right = clientWidth;
if (fromArg == null && from == 0) left = pl;
rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
if (left < pl + 1) left = pl;
add(left, rightPos.top, right - left, rightPos.bottom);
});
return rVal;
}
if (sel.from.line == sel.to.line) {
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
} else {
var fromObj = getLine(doc, sel.from.line);
var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
while (merged = collapsedSpanAtEnd(cur)) {
var found = merged.find();
path.push(found.from.ch, found.to.line, found.to.ch);
if (found.to.line == sel.to.line) {
path.push(sel.to.ch);
singleLine = true;
break;
}
cur = getLine(doc, found.to.line);
}
// This is a single, merged line
if (singleLine) {
for (var i = 0; i < path.length; i += 3)
drawForLine(path[i], path[i+1], path[i+2]);
} else {
var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
if (sel.from.ch)
// Draw the first line of selection.
middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
else
// Simply include it in the middle block.
middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
if (!sel.to.ch)
middleBot = heightAtLine(cm, toObj) - display.viewOffset;
else
middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
}
}
removeChildrenAndAdd(display.selectionDiv, fragment);
display.selectionDiv.style.display = "";
}
// Cursor-blinking
function restartBlink(cm) {
var display = cm.display;
clearInterval(display.blinker);
var on = true;
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
display.blinker = setInterval(function() {
if (!display.cursor.offsetHeight) return;
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
}, cm.options.cursorBlinkRate);
}
// HIGHLIGHT WORKER
function startWorker(cm, time) {
if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo)
cm.view.highlight.set(time, bind(highlightWorker, cm));
}
function highlightWorker(cm) {
var view = cm.view, doc = view.doc;
if (view.frontier >= cm.display.showingTo) return;
var end = +new Date + cm.options.workTime;
var state = copyState(view.mode, getStateBefore(cm, view.frontier));
var changed = [], prevChange;
doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
if (view.frontier >= cm.display.showingFrom) { // Visible
var oldStyles = line.styles;
line.styles = highlightLine(cm, line, state);
var ischange = !oldStyles || oldStyles.length != line.styles.length;
for (var i = 0; !ischange && i < oldStyles.length; ++i)
ischange = oldStyles[i] != line.styles[i];
if (ischange) {
if (prevChange && prevChange.end == view.frontier) prevChange.end++;
else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
}
line.stateAfter = copyState(view.mode, state);
} else {
processLine(cm, line, state);
line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
}
++view.frontier;
if (+new Date > end) {
startWorker(cm, cm.options.workDelay);
return true;
}
});
if (changed.length)
operation(cm, function() {
for (var i = 0; i < changed.length; ++i)
regChange(this, changed[i].start, changed[i].end);
})();
}
// Finds the line to start with when starting a parse. Tries to
// find a line with a stateAfter, so that it can start with a
// valid state. If that fails, it returns the line with the
// smallest indentation, which tends to need the least context to
// parse correctly.
function findStartLine(cm, n) {
var minindent, minline, doc = cm.view.doc;
for (var search = n, lim = n - 100; search > lim; --search) {
if (search == 0) return 0;
var line = getLine(doc, search-1);
if (line.stateAfter) return search;
var indented = countColumn(line.text, null, cm.options.tabSize);
if (minline == null || minindent > indented) {
minline = search - 1;
minindent = indented;
}
}
return minline;
}
function getStateBefore(cm, n) {
var view = cm.view;
if (!view.mode.startState) return true;
var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
if (!state) state = startState(view.mode);
else state = copyState(view.mode, state);
view.doc.iter(pos, n, function(line) {
processLine(cm, line, state);
var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
line.stateAfter = save ? copyState(view.mode, state) : null;
++pos;
});
return state;
}
// POSITION MEASUREMENT
function paddingTop(display) {return display.lineSpace.offsetTop;}
function paddingLeft(display) {
var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
return e.offsetLeft;
}
function measureChar(cm, line, ch, data) {
var dir = -1;
data = data || measureLine(cm, line);
for (var pos = ch;; pos += dir) {
var r = data[pos];
if (r) break;
if (dir < 0 && pos == 0) dir = 1;
}
return {left: pos < ch ? r.right : r.left,
right: pos > ch ? r.left : r.right,
top: r.top, bottom: r.bottom};
}
function measureLine(cm, line) {
// First look in the cache
var display = cm.display, cache = cm.display.measureLineCache;
for (var i = 0; i < cache.length; ++i) {
var memo = cache[i];
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
display.scroller.clientWidth == memo.width)
return memo.measure;
}
var measure = measureLineInner(cm, line);
// Store result in the cache
var memo = {text: line.text, width: display.scroller.clientWidth,
markedSpans: line.markedSpans, measure: measure};
if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
else cache.push(memo);
return measure;
}
function measureLineInner(cm, line) {
var display = cm.display, measure = emptyArray(line.text.length);
var pre = lineContent(cm, line, measure);
// IE does not cache element positions of inline elements between
// calls to getBoundingClientRect. This makes the loop below,
// which gathers the positions of all the characters on the line,
// do an amount of layout work quadratic to the number of
// characters. When line wrapping is off, we try to improve things
// by first subdividing the line into a bunch of inline blocks, so
// that IE can reuse most of the layout information from caches
// for those blocks. This does interfere with line wrapping, so it
// doesn't work when wrapping is on, but in that case the
// situation is slightly better, since IE does cache line-wrapping
// information and only recomputes per-line.
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
var fragment = document.createDocumentFragment();
var chunk = 10, n = pre.childNodes.length;
for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
var wrap = elt("div", null, null, "display: inline-block");
for (var j = 0; j < chunk && n; ++j) {
wrap.appendChild(pre.firstChild);
--n;
}
fragment.appendChild(wrap);
}
pre.appendChild(fragment);
}
removeChildrenAndAdd(display.measure, pre);
var outer = display.lineDiv.getBoundingClientRect();
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
var size = cur.getBoundingClientRect();
var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
for (var j = 0; j < vranges.length; j += 2) {
var rtop = vranges[j], rbot = vranges[j+1];
if (rtop > bot || rbot < top) continue;
if (rtop <= top && rbot >= bot ||
top <= rtop && bot >= rbot ||
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
vranges[j] = Math.min(top, rtop);
vranges[j+1] = Math.max(bot, rbot);
break;
}
}
if (j == vranges.length) vranges.push(top, bot);
data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
}
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
var vr = cur.top;
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
}
return data;
}
function clearCaches(cm) {
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
cm.view.maxLineChanged = true;
}
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
function intoCoordSystem(cm, lineObj, rect, context) {
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
var size = widgetHeight(lineObj.widgets[i]);
rect.top += size; rect.bottom += size;
}
if (context == "line") return rect;
if (!context) context = "local";
var yOff = heightAtLine(cm, lineObj);
if (context != "local") yOff -= cm.display.viewOffset;
if (context == "page") {
var lOff = cm.display.lineSpace.getBoundingClientRect();
yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
rect.left += xOff; rect.right += xOff;
}
rect.top += yOff; rect.bottom += yOff;
return rect;
}
function charCoords(cm, pos, context, lineObj) {
if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
}
function cursorCoords(cm, pos, context, lineObj, measurement) {
lineObj = lineObj || getLine(cm.view.doc, pos.line);
if (!measurement) measurement = measureLine(cm, lineObj);
function get(ch, right) {
var m = measureChar(cm, lineObj, ch, measurement);
if (right) m.left = m.right; else m.right = m.left;
return intoCoordSystem(cm, lineObj, m, context);
}
var order = getOrder(lineObj), ch = pos.ch;
if (!order) return get(ch);
var main, other, linedir = order[0].level;
for (var i = 0; i < order.length; ++i) {
var part = order[i], rtl = part.level % 2, nb, here;
if (part.from < ch && part.to > ch) return get(ch, rtl);
var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
if (left == ch) {
// Opera and IE return bogus offsets and widths for edges
// where the direction flips, but only for the side with the
// lower level. So we try to use the side with the higher
// level.
if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
else here = get(rtl && part.from != part.to ? ch - 1 : ch);
if (rtl == linedir) main = here; else other = here;
} else if (right == ch) {
var nb = i < order.length - 1 && order[i+1];
if (!rtl && nb && nb.from == nb.to) continue;
if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
else here = get(rtl ? ch : ch - 1, true);
if (rtl == linedir) main = here; else other = here;
}
}
if (linedir && !ch) other = get(order[0].to - 1);
if (!main) return other;
if (other) main.other = other;
return main;
}
// Coords must be lineSpace-local
function coordsChar(cm, x, y) {
var doc = cm.view.doc;
y += cm.display.viewOffset;
if (y < 0) return {line: 0, ch: 0, outside: true};
var lineNo = lineAtHeight(doc, y);
if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
if (x < 0) x = 0;
for (;;) {
var lineObj = getLine(doc, lineNo);
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
var merged = collapsedSpanAtEnd(lineObj);
var mergedPos = merged && merged.find();
if (merged && found.ch >= mergedPos.from.ch)
lineNo = mergedPos.to.line;
else
return found;
}
}
function coordsCharInner(cm, lineObj, lineNo, x, y) {
var innerOff = y - heightAtLine(cm, lineObj);
var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
var measurement = measureLine(cm, lineObj);
function getX(ch) {
var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
lineObj, measurement);
wrongLine = true;
if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
else if (innerOff < sp.top) return sp.left + cWidth;
else wrongLine = false;
return sp.left;
}
var bidi = getOrder(lineObj), dist = lineObj.text.length;
var from = lineLeft(lineObj), to = lineRight(lineObj);
var fromX = paddingLeft(cm.display), toX = getX(to);
if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
// Do a binary search between these bounds.
for (;;) {
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
var after = x - fromX < toX - x, ch = after ? from : to;
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
return {line: lineNo, ch: ch, after: after, outside: wrongLine};
}
var step = Math.ceil(dist / 2), middle = from + step;
if (bidi) {
middle = from;
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
}
var middleX = getX(middle);
if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
else {from = middle; fromX = middleX; dist = step;}
}
}
var measureText;
function textHeight(display) {
if (display.cachedTextHeight != null) return display.cachedTextHeight;
if (measureText == null) {
measureText = elt("pre");
// Measure a bunch of lines, for browsers that compute
// fractional heights.
for (var i = 0; i < 49; ++i) {
measureText.appendChild(document.createTextNode("x"));
measureText.appendChild(elt("br"));
}
measureText.appendChild(document.createTextNode("x"));
}
removeChildrenAndAdd(display.measure, measureText);
var height = measureText.offsetHeight / 50;
if (height > 3) display.cachedTextHeight = height;
removeChildren(display.measure);
return height || 1;
}
function charWidth(display) {
if (display.cachedCharWidth != null) return display.cachedCharWidth;
var anchor = elt("span", "x");
var pre = elt("pre", [anchor]);
removeChildrenAndAdd(display.measure, pre);
var width = anchor.offsetWidth;
if (width > 2) display.cachedCharWidth = width;
return width || 10;
}
// OPERATIONS
// Operations are used to wrap changes in such a way that each
// change won't have to update the cursor and display (which would
// be awkward, slow, and error-prone), but instead updates are
// batched and then all combined and executed at once.
function startOperation(cm) {
if (cm.curOp) ++cm.curOp.depth;
else cm.curOp = {
// Nested operations delay update until the outermost one
// finishes.
depth: 1,
// An array of ranges of lines that have to be updated. See
// updateDisplay.
changes: [],
delayedCallbacks: [],
updateInput: null,
userSelChange: null,
textChanged: null,
selectionChanged: false,
updateMaxLine: false,
id: ++cm.nextOpId
};
}
function endOperation(cm) {
var op = cm.curOp;
if (--op.depth) return;
cm.curOp = null;
var view = cm.view, display = cm.display;
if (op.updateMaxLine) computeMaxLength(view);
if (view.maxLineChanged && !cm.options.lineWrapping) {
var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
view.maxLineChanged = false;
var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
if (maxScrollLeft < view.scrollLeft)
setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
}
var newScrollPos, updated;
if (op.selectionChanged) {
var coords = cursorCoords(cm, view.sel.head);
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
}
if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
if (!updated && op.selectionChanged) updateSelection(cm);
if (newScrollPos) scrollCursorIntoView(cm);
if (op.selectionChanged) restartBlink(cm);
if (view.focused && op.updateInput)
resetInput(cm, op.userSelChange);
if (op.textChanged)
signal(cm, "change", cm, op.textChanged);
if (op.selectionChanged) signal(cm, "cursorActivity", cm);
for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
}
// Wraps a function in an operation. Returns the wrapped function.
function operation(cm1, f) {
return function() {
var cm = cm1 || this;
startOperation(cm);
try {var result = f.apply(cm, arguments);}
finally {endOperation(cm);}
return result;
};
}
function regChange(cm, from, to, lendiff) {
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
}
// INPUT HANDLING
function slowPoll(cm) {
if (cm.view.pollingFast) return;
cm.display.poll.set(cm.options.pollInterval, function() {
readInput(cm);
if (cm.view.focused) slowPoll(cm);
});
}
function fastPoll(cm) {
var missed = false;
cm.display.pollingFast = true;
function p() {
var changed = readInput(cm);
if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
else {cm.display.pollingFast = false; slowPoll(cm);}
}
cm.display.poll.set(20, p);
}
// prevInput is a hack to work with IME. If we reset the textarea
// on every change, that breaks IME. So we look for changes
// compared to the previous content instead. (Modern browsers have
// events that indicate IME taking place, but these are not widely
// supported or compatible enough yet to rely on.)
function readInput(cm) {
var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
var text = input.value;
if (text == prevInput && posEq(sel.from, sel.to)) return false;
startOperation(cm);
view.sel.shift = false;
var same = 0, l = Math.min(prevInput.length, text.length);
while (same < l && prevInput[same] == text[same]) ++same;
var from = sel.from, to = sel.to;
if (same < prevInput.length)
from = {line: from.line, ch: from.ch - (prevInput.length - same)};
else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
var updateInput = cm.curOp.updateInput;
updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
cm.curOp.updateInput = updateInput;
if (text.length > 1000) input.value = cm.display.prevInput = "";
else cm.display.prevInput = text;
endOperation(cm);
cm.display.pasteIncoming = false;
return true;
}
function resetInput(cm, user) {
var view = cm.view, minimal, selected;
if (!posEq(view.sel.from, view.sel.to)) {
cm.display.prevInput = "";
minimal = hasCopyEvent &&
(view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
if (minimal) cm.display.input.value = "-";
else cm.display.input.value = selected || cm.getSelection();
if (view.focused) selectInput(cm.display.input);
} else if (user) cm.display.prevInput = cm.display.input.value = "";
cm.display.inaccurateSelection = minimal;
}
function focusInput(cm) {
if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
cm.display.input.focus();
}
function isReadOnly(cm) {
return cm.options.readOnly || cm.view.cantEdit;
}
// EVENT HANDLERS
function registerEventHandlers(cm) {
var d = cm.display;
on(d.scroller, "mousedown", operation(cm, onMouseDown));
on(d.scroller, "dblclick", operation(cm, e_preventDefault));
on(d.lineSpace, "selectstart", function(e) {
if (!eventInWidget(d, e)) e_preventDefault(e);
});
// Gecko browsers fire contextmenu *after* opening the menu, at
// which point we can't mess with it anymore. Context menu is
// handled in onMouseDown for Gecko.
if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
on(d.scroller, "scroll", function() {
setScrollTop(cm, d.scroller.scrollTop);
setScrollLeft(cm, d.scroller.scrollLeft, true);
signal(cm, "scroll", cm);
});
on(d.scrollbarV, "scroll", function() {
setScrollTop(cm, d.scrollbarV.scrollTop);
});
on(d.scrollbarH, "scroll", function() {
setScrollLeft(cm, d.scrollbarH.scrollLeft);
});
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
on(d.scrollbarH, "mousedown", reFocus);
on(d.scrollbarV, "mousedown", reFocus);
// Prevent wrapper from ever scrolling
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
if (!window.registered) window.registered = 0;
++window.registered;
function onResize() {
// Might be a text scaling operation, clear size caches.
d.cachedCharWidth = d.cachedTextHeight = null;
clearCaches(cm);
updateDisplay(cm, true);
}
on(window, "resize", onResize);
// Above handler holds on to the editor and its data structures.
// Here we poll to unregister it when the editor is no longer in
// the document, so that it can be garbage-collected.
setTimeout(function unregister() {
for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
if (p) setTimeout(unregister, 5000);
else {--window.registered; off(window, "resize", onResize);}
}, 5000);
on(d.input, "keyup", operation(cm, function(e) {
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
}));
on(d.input, "input", bind(fastPoll, cm));
on(d.input, "keydown", operation(cm, onKeyDown));
on(d.input, "keypress", operation(cm, onKeyPress));
on(d.input, "focus", bind(onFocus, cm));
on(d.input, "blur", bind(onBlur, cm));
function drag_(e) {
if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
e_stop(e);
}
if (cm.options.dragDrop) {
on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
on(d.scroller, "dragenter", drag_);
on(d.scroller, "dragover", drag_);
on(d.scroller, "drop", operation(cm, onDrop));
}
on(d.scroller, "paste", function(e){
if (eventInWidget(d, e)) return;
focusInput(cm);
fastPoll(cm);
});
on(d.input, "paste", function() {
d.pasteIncoming = true;
fastPoll(cm);
});
function prepareCopy() {
if (d.inaccurateSelection) {
d.prevInput = "";
d.inaccurateSelection = false;
d.input.value = cm.getSelection();
selectInput(d.input);
}
}
on(d.input, "cut", prepareCopy);
on(d.input, "copy", prepareCopy);
// Needed to handle Tab key in KHTML
if (khtml) on(d.sizer, "mouseup", function() {
if (document.activeElement == d.input) d.input.blur();
focusInput(cm);
});
}
function eventInWidget(display, e) {
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
if (!n) return true;
if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
n.parentNode == display.sizer && n != display.mover) return true;
}
}
function posFromMouse(cm, e, liberal) {
var display = cm.display;
if (!liberal) {
var target = e_target(e);
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
target == display.scrollbarFiller) return null;
}
var x, y, space = display.lineSpace.getBoundingClientRect();
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
return coordsChar(cm, x - space.left, y - space.top);
}
var lastClick, lastDoubleClick;
function onMouseDown(e) {
var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
sel.shift = e_prop(e, "shiftKey");
if (eventInWidget(display, e)) {
if (!webkit) {
display.scroller.draggable = false;
setTimeout(function(){display.scroller.draggable = true;}, 100);
}
return;
}
if (clickInGutter(cm, e)) return;
var start = posFromMouse(cm, e);
switch (e_button(e)) {
case 3:
if (gecko) onContextMenu.call(cm, cm, e);
return;
case 2:
if (start) extendSelection(cm, start);
setTimeout(bind(focusInput, cm), 20);
e_preventDefault(e);
return;
}
// For button 1, if it was clicked inside the editor
// (posFromMouse returning non-null), we have to adjust the
// selection.
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
if (!view.focused) onFocus(cm);
var now = +new Date, type = "single";
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
type = "triple";
e_preventDefault(e);
setTimeout(bind(focusInput, cm), 20);
selectLine(cm, start.line);
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
type = "double";
lastDoubleClick = {time: now, pos: start};
e_preventDefault(e);
var word = findWordAt(getLine(doc, start.line).text, start);
extendSelection(cm, word.from, word.to);
} else { lastClick = {time: now, pos: start}; }
var last = start;
if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
var dragEnd = operation(cm, function(e2) {
if (webkit) display.scroller.draggable = false;
view.draggingText = false;
off(document, "mouseup", dragEnd);
off(display.scroller, "drop", dragEnd);
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
e_preventDefault(e2);
extendSelection(cm, start);
focusInput(cm);
}
});
// Let the drag handler handle this.
if (webkit) display.scroller.draggable = true;
view.draggingText = dragEnd;
// IE's approach to draggable
if (display.scroller.dragDrop) display.scroller.dragDrop();
on(document, "mouseup", dragEnd);
on(display.scroller, "drop", dragEnd);
return;
}
e_preventDefault(e);
if (type == "single") extendSelection(cm, clipPos(doc, start));
var startstart = sel.from, startend = sel.to;
function doSelect(cur) {
if (type == "single") {
extendSelection(cm, clipPos(doc, start), cur);
return;
}
startstart = clipPos(doc, startstart);
startend = clipPos(doc, startend);
if (type == "double") {
var word = findWordAt(getLine(doc, cur.line).text, cur);
if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
else extendSelection(cm, startstart, word.to);
} else if (type == "triple") {
if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
}
}
var editorSize = display.wrapper.getBoundingClientRect();
// Used to ensure timeout re-tries don't fire when another extend
// happened in the meantime (clearTimeout isn't reliable -- at
// least on Chrome, the timeouts still happen even when cleared,
// if the clear happens after their scheduled firing time).
var counter = 0;
function extend(e) {
var curCount = ++counter;
var cur = posFromMouse(cm, e, true);
if (!cur) return;
if (!posEq(cur, last)) {
if (!view.focused) onFocus(cm);
last = cur;
doSelect(cur);
var visible = visibleLines(display, doc);
if (cur.line >= visible.to || cur.line < visible.from)
setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
} else {
var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
if (outside) setTimeout(operation(cm, function() {
if (counter != curCount) return;
display.scroller.scrollTop += outside;
extend(e);
}), 50);
}
}
function done(e) {
counter = Infinity;
var cur = posFromMouse(cm, e);
if (cur) doSelect(cur);
e_preventDefault(e);
focusInput(cm);
off(document, "mousemove", move);
off(document, "mouseup", up);
}
var move = operation(cm, function(e) {
if (!ie && !e_button(e)) done(e);
else extend(e);
});
var up = operation(cm, done);
on(document, "mousemove", move);
on(document, "mouseup", up);
}
function onDrop(e) {
var cm = this;
if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
return;
e_preventDefault(e);
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
if (!pos || isReadOnly(cm)) return;
if (files && files.length && window.FileReader && window.File) {
var n = files.length, text = Array(n), read = 0;
var loadFile = function(file, i) {
var reader = new FileReader;
reader.onload = function() {
text[i] = reader.result;
if (++read == n) {
pos = clipPos(cm.view.doc, pos);
operation(cm, function() {
var end = replaceRange(cm, text.join(""), pos, pos, "paste");
setSelection(cm, pos, end);
})();
}
};
reader.readAsText(file);
};
for (var i = 0; i < n; ++i) loadFile(files[i], i);
} else {
// Don't do a replace if the drop happened inside of the selected text.
if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
cm.view.draggingText(e);
// Ensure the editor is re-focused
setTimeout(bind(focusInput, cm), 20);
return;
}
try {
var text = e.dataTransfer.getData("Text");
if (text) {
var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
setSelection(cm, pos, pos);
if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
cm.replaceSelection(text, null, "paste");
focusInput(cm);
onFocus(cm);
}
}
catch(e){}
}
}
function clickInGutter(cm, e) {
var display = cm.display;
try { var mX = e.clientX, mY = e.clientY; }
catch(e) { return false; }
if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
e_preventDefault(e);
if (!hasHandler(cm, "gutterClick")) return true;
var lineBox = display.lineDiv.getBoundingClientRect();
if (mY > lineBox.bottom) return true;
mY -= lineBox.top - display.viewOffset;
for (var i = 0; i < cm.options.gutters.length; ++i) {
var g = display.gutters.childNodes[i];
if (g && g.getBoundingClientRect().right >= mX) {
var line = lineAtHeight(cm.view.doc, mY);
var gutter = cm.options.gutters[i];
signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
break;
}
}
return true;
}
function onDragStart(cm, e) {
if (eventInWidget(cm.display, e)) return;
var txt = cm.getSelection();
e.dataTransfer.setData("Text", txt);
// Use dummy image instead of default browsers image.
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
if (e.dataTransfer.setDragImage && !safari) {
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
if (opera) {
img.width = img.height = 1;
cm.display.wrapper.appendChild(img);
// Force a relayout, or Opera won't use our image for some obscure reason
img._top = img.offsetTop;
}
e.dataTransfer.setDragImage(img, 0, 0);
if (opera) img.parentNode.removeChild(img);
}
}
function setScrollTop(cm, val) {
if (Math.abs(cm.view.scrollTop - val) < 2) return;
cm.view.scrollTop = val;
if (!gecko) updateDisplay(cm, [], val);
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
if (gecko) updateDisplay(cm, []);
}
function setScrollLeft(cm, val, isScroller) {
if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
cm.view.scrollLeft = val;
alignHorizontally(cm);
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
}
// Since the delta values reported on mouse wheel events are
// unstandardized between browsers and even browser versions, and
// generally horribly unpredictable, this code starts by measuring
// the scroll effect that the first few mouse wheel events have,
// and, from that, detects the way it can convert deltas to pixel
// offsets afterwards.
//
// The reason we want to know the amount a wheel event will scroll
// is that it gives us a chance to update the display before the
// actual scrolling happens, reducing flickering.
var wheelSamples = 0, wheelPixelsPerUnit = null;
// Fill in a browser-detected starting value on browsers where we
// know one. These don't have to be accurate -- the result of them
// being wrong would just be a slight flicker on the first wheel
// scroll (if it is large enough).
if (ie) wheelPixelsPerUnit = -.53;
else if (gecko) wheelPixelsPerUnit = 15;
else if (chrome) wheelPixelsPerUnit = -.7;
else if (safari) wheelPixelsPerUnit = -1/3;
function onScrollWheel(cm, e) {
var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
else if (dy == null) dy = e.wheelDelta;
// Webkit browsers on OS X abort momentum scrolls when the target
// of the scroll event is removed from the scrollable element.
// This hack (see related code in patchDisplay) makes sure the
// element is kept around.
if (dy && mac && webkit) {
for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
if (cur.lineObj) {
cm.display.currentWheelTarget = cur;
break;
}
}
}
var display = cm.display, scroll = display.scroller;
// On some browsers, horizontal scrolling will cause redraws to
// happen before the gutter has been realigned, causing it to
// wriggle around in a most unseemly way. When we have an
// estimated pixels/delta value, we just handle horizontal
// scrolling entirely here. It'll be slightly off from native, but
// better than glitching out.
if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
if (dy)
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
e_preventDefault(e);
display.wheelStartX = null; // Abort measurement, if in progress
return;
}
if (dy && wheelPixelsPerUnit != null) {
var pixels = dy * wheelPixelsPerUnit;
var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight;
if (pixels < 0) top = Math.max(0, top + pixels - 50);
else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
updateDisplay(cm, [], {top: top, bottom: bot});
}
if (wheelSamples < 20) {
if (display.wheelStartX == null) {
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
display.wheelDX = dx; display.wheelDY = dy;
setTimeout(function() {
if (display.wheelStartX == null) return;
var movedX = scroll.scrollLeft - display.wheelStartX;
var movedY = scroll.scrollTop - display.wheelStartY;
var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
(movedX && display.wheelDX && movedX / display.wheelDX);
display.wheelStartX = display.wheelStartY = null;
if (!sample) return;
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
++wheelSamples;
}, 200);
} else {
display.wheelDX += dx; display.wheelDY += dy;
}
}
}
function doHandleBinding(cm, bound, dropShift) {
if (typeof bound == "string") {
bound = commands[bound];
if (!bound) return false;
}
// Ensure previous input has been read, so that the handler sees a
// consistent view of the document
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
var view = cm.view, prevShift = view.sel.shift;
try {
if (isReadOnly(cm)) view.suppressEdits = true;
if (dropShift) view.sel.shift = false;
bound(cm);
} catch(e) {
if (e != Pass) throw e;
return false;
} finally {
view.sel.shift = prevShift;
view.suppressEdits = false;
}
return true;
}
function allKeyMaps(cm) {
var maps = cm.view.keyMaps.slice(0);
maps.push(cm.options.keyMap);
if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
return maps;
}
var maybeTransition;
function handleKeyBinding(cm, e) {
// Handle auto keymap transitions
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
clearTimeout(maybeTransition);
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
if (getKeyMap(cm.options.keyMap) == startMap)
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
}, 50);
var name = keyNames[e_prop(e, "keyCode")], handled = false;
if (name == null || e.altGraphKey) return false;
if (e_prop(e, "altKey")) name = "Alt-" + name;
if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
var stopped = false;
function stop() { stopped = true; }
var keymaps = allKeyMaps(cm);
if (e_prop(e, "shiftKey")) {
handled = lookupKey("Shift-" + name, keymaps,
function(b) {return doHandleBinding(cm, b, true);}, stop)
|| lookupKey(name, keymaps, function(b) {
if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
}, stop);
} else {
handled = lookupKey(name, keymaps,
function(b) { return doHandleBinding(cm, b); }, stop);
}
if (stopped) handled = false;
if (handled) {
e_preventDefault(e);
restartBlink(cm);
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
}
return handled;
}
function handleCharBinding(cm, e, ch) {
var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
function(b) { return doHandleBinding(cm, b, true); });
if (handled) {
e_preventDefault(e);
restartBlink(cm);
}
return handled;
}
var lastStoppedKey = null;
function onKeyDown(e) {
var cm = this;
if (!cm.view.focused) onFocus(cm);
if (ie && e.keyCode == 27) { e.returnValue = false; }
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
var code = e_prop(e, "keyCode");
// IE does strange things with escape.
cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
// First give onKeyEvent option a chance to handle this.
var handled = handleKeyBinding(cm, e);
if (opera) {
lastStoppedKey = handled ? code : null;
// Opera has no cut event... we try to at least catch the key combo
if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
cm.replaceSelection("");
}
}
function onKeyPress(e) {
var cm = this;
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
if (this.options.electricChars && this.view.mode.electricChars &&
this.options.smartIndent && !isReadOnly(this) &&
this.view.mode.electricChars.indexOf(ch) > -1)
setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
if (handleCharBinding(cm, e, ch)) return;
fastPoll(cm);
}
function onFocus(cm) {
if (cm.options.readOnly == "nocursor") return;
if (!cm.view.focused) {
signal(cm, "focus", cm);
cm.view.focused = true;
if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
cm.display.scroller.className += " CodeMirror-focused";
resetInput(cm, true);
}
slowPoll(cm);
restartBlink(cm);
}
function onBlur(cm) {
if (cm.view.focused) {
signal(cm, "blur", cm);
cm.view.focused = false;
cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
}
clearInterval(cm.display.blinker);
setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
}
var detectingSelectAll;
function onContextMenu(cm, e) {
var display = cm.display;
if (eventInWidget(display, e)) return;
var sel = cm.view.sel;
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
if (!pos || opera) return; // Opera is difficult.
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
operation(cm, setSelection)(cm, pos, pos);
var oldCSS = display.input.style.cssText;
display.inputDiv.style.position = "absolute";
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
focusInput(cm);
resetInput(cm, true);
// Adds "Select all" to context menu in FF
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
function rehide() {
display.inputDiv.style.position = "relative";
display.input.style.cssText = oldCSS;
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
slowPoll(cm);
// Try to detect the user choosing select-all
if (display.input.selectionStart != null) {
clearTimeout(detectingSelectAll);
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
display.prevInput = " ";
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
detectingSelectAll = setTimeout(function poll(){
if (display.prevInput == " " && display.input.selectionStart == 0)
operation(cm, commands.selectAll)(cm);
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
else resetInput(cm);
}, 200);
}
}
if (gecko) {
e_stop(e);
on(window, "mouseup", function mouseup() {
off(window, "mouseup", mouseup);
setTimeout(rehide, 20);
});
} else {
setTimeout(rehide, 50);
}
}
// UPDATING
// Replace the range from from to to by the strings in newText.
// Afterwards, set the selection to selFrom, selTo.
function updateDoc(cm, from, to, newText, selUpdate, origin) {
// Possibly split or suppress the update based on the presence
// of read-only spans in its range.
var split = sawReadOnlySpans &&
removeReadOnlyRanges(cm.view.doc, from, to);
if (split) {
for (var i = split.length - 1; i >= 1; --i)
updateDocInner(cm, split[i].from, split[i].to, [""], origin);
if (split.length)
return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
} else {
return updateDocInner(cm, from, to, newText, selUpdate, origin);
}
}
function updateDocInner(cm, from, to, newText, selUpdate, origin) {
if (cm.view.suppressEdits) return;
var view = cm.view, doc = view.doc, old = [];
doc.iter(from.line, to.line + 1, function(line) {
old.push(newHL(line.text, line.markedSpans));
});
var startSelFrom = view.sel.from, startSelTo = view.sel.to;
var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
if (view.history) addChange(cm, from.line, newText.length, old, origin,
startSelFrom, startSelTo, view.sel.from, view.sel.to);
return retval;
}
function unredoHelper(cm, type) {
var doc = cm.view.doc, hist = cm.view.history;
var set = (type == "undo" ? hist.done : hist.undone).pop();
if (!set) return;
var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
fromAfter: set.fromBefore, toAfter: set.toBefore};
for (var i = set.events.length - 1; i >= 0; i -= 1) {
hist.dirtyCounter += type == "undo" ? -1 : 1;
var change = set.events[i];
var replaced = [], end = change.start + change.added;
doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
anti.events.push({start: change.start, added: change.old.length, old: replaced});
var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
change.old, selPos, type);
}
(type == "undo" ? hist.undone : hist.done).push(anti);
}
function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
var view = cm.view, doc = view.doc, display = cm.display;
if (view.suppressEdits) return;
var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
var recomputeMaxLength = false, checkWidthStart = from.line;
if (!cm.options.lineWrapping) {
checkWidthStart = lineNo(visualLine(doc, firstLine));
doc.iter(checkWidthStart, to.line + 1, function(line) {
if (line == view.maxLine) {
recomputeMaxLength = true;
return true;
}
});
}
var lastHL = lst(lines), th = textHeight(display);
// First adjust the line structure
if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
// This is a whole-line replace. Treated specially to make
// sure line objects move the way they are supposed to.
var added = [];
for (var i = 0, e = lines.length - 1; i < e; ++i)
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
if (nlines) doc.remove(from.line, nlines, cm);
if (added.length) doc.insert(from.line, added);
} else if (firstLine == lastLine) {
if (lines.length == 1) {
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
firstLine.text.slice(to.ch), hlSpans(lines[0]));
} else {
for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
doc.insert(from.line + 1, added);
}
} else if (lines.length == 1) {
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
lastLine.text.slice(to.ch), hlSpans(lines[0]));
doc.remove(from.line + 1, nlines, cm);
} else {
var added = [];
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
for (var i = 1, e = lines.length - 1; i < e; ++i)
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
doc.insert(from.line + 1, added);
}
if (cm.options.lineWrapping) {
var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
doc.iter(from.line, from.line + lines.length, function(line) {
if (line.height == 0) return;
var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
if (guess != line.height) updateLineHeight(line, guess);
});
} else {
doc.iter(checkWidthStart, from.line + lines.length, function(line) {
var len = lineLength(doc, line);
if (len > view.maxLineLength) {
view.maxLine = line;
view.maxLineLength = len;
view.maxLineChanged = true;
recomputeMaxLength = false;
}
});
if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
}
// Adjust frontier, schedule worker
view.frontier = Math.min(view.frontier, from.line);
startWorker(cm, 400);
var lendiff = lines.length - nlines - 1;
// Remember that these lines changed, for updating the display
regChange(cm, from.line, to.line + 1, lendiff);
if (hasHandler(cm, "change")) {
// Normalize lines to contain only strings, since that's what
// the change event handler expects
for (var i = 0; i < lines.length; ++i)
if (typeof lines[i] != "string") lines[i] = lines[i].text;
var changeObj = {from: from, to: to, text: lines, origin: origin};
if (cm.curOp.textChanged) {
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
cur.next = changeObj;
} else cm.curOp.textChanged = changeObj;
}
// Update the selection
var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
if (selUpdate && typeof selUpdate != "string") {
if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
else newSelFrom = newSelTo = selUpdate;
} else if (selUpdate == "end") {
newSelFrom = newSelTo = end;
} else if (selUpdate == "start") {
newSelFrom = newSelTo = from;
} else if (selUpdate == "around") {
newSelFrom = from; newSelTo = end;
} else {
var adjustPos = function(pos) {
if (posLess(pos, from)) return pos;
if (!posLess(to, pos)) return end;
var line = pos.line + lendiff;
var ch = pos.ch;
if (pos.line == to.line)
ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
return {line: line, ch: ch};
};
newSelFrom = adjustPos(view.sel.from);
newSelTo = adjustPos(view.sel.to);
}
setSelection(cm, newSelFrom, newSelTo, null, true);
return end;
}
function replaceRange(cm, code, from, to, origin) {
if (!to) to = from;
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
return updateDoc(cm, from, to, splitLines(code), null, origin);
}
// SELECTION
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
function copyPos(x) {return {line: x.line, ch: x.ch};}
function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
function clipPos(doc, pos) {
if (pos.line < 0) return {line: 0, ch: 0};
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
else if (ch < 0) return {line: pos.line, ch: 0};
else return pos;
}
function isLine(doc, l) {return l >= 0 && l < doc.size;}
// If shift is held, this will move the selection anchor. Otherwise,
// it'll set the whole selection.
function extendSelection(cm, pos, other, bias) {
var sel = cm.view.sel;
if (sel.shift || sel.extend) {
var anchor = sel.anchor;
if (other) {
var posBefore = posLess(pos, anchor);
if (posBefore != posLess(other, anchor)) {
anchor = pos;
pos = other;
} else if (posBefore != posLess(pos, other)) {
pos = other;
}
}
setSelection(cm, anchor, pos, bias);
} else {
setSelection(cm, pos, other || pos, bias);
}
cm.curOp.userSelChange = true;
}
// Update the selection. Last two args are only used by
// updateDoc, since they have to be expressed in the line
// numbers before the update.
function setSelection(cm, anchor, head, bias, checkAtomic) {
cm.view.goalColumn = null;
var sel = cm.view.sel;
// Skip over atomic spans.
if (checkAtomic || !posEq(anchor, sel.anchor))
anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
if (checkAtomic || !posEq(head, sel.head))
head = skipAtomic(cm, head, bias, checkAtomic != "push");
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
sel.anchor = anchor; sel.head = head;
var inv = posLess(head, anchor);
sel.from = inv ? head : anchor;
sel.to = inv ? anchor : head;
cm.curOp.updateInput = true;
cm.curOp.selectionChanged = true;
}
function reCheckSelection(cm) {
setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
}
function skipAtomic(cm, pos, bias, mayClear) {
var doc = cm.view.doc, flipped = false, curPos = pos;
var dir = bias || 1;
cm.view.cantEdit = false;
search: for (;;) {
var line = getLine(doc, curPos.line), toClear;
if (line.markedSpans) {
for (var i = 0; i < line.markedSpans.length; ++i) {
var sp = line.markedSpans[i], m = sp.marker;
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
if (mayClear && m.clearOnEnter) {
(toClear || (toClear = [])).push(m);
continue;
} else if (!m.atomic) continue;
var newPos = m.find()[dir < 0 ? "from" : "to"];
if (posEq(newPos, curPos)) {
newPos.ch += dir;
if (newPos.ch < 0) {
if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
else newPos = null;
} else if (newPos.ch > line.text.length) {
if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
else newPos = null;
}
if (!newPos) {
if (flipped) {
// Driven in a corner -- no valid cursor position found at all
// -- try again *with* clearing, if we didn't already
if (!mayClear) return skipAtomic(cm, pos, bias, true);
// Otherwise, turn off editing until further notice, and return the start of the doc
cm.view.cantEdit = true;
return {line: 0, ch: 0};
}
flipped = true; newPos = pos; dir = -dir;
}
}
curPos = newPos;
continue search;
}
}
if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
}
return curPos;
}
}
// SCROLLING
function scrollCursorIntoView(cm) {
var view = cm.view;
var coords = scrollPosIntoView(cm, view.sel.head);
if (!view.focused) return;
var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
if (coords.top + box.top < 0) doScroll = true;
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
if (doScroll != null && !phantom) {
var hidden = display.cursor.style.display == "none";
if (hidden) {
display.cursor.style.display = "";
display.cursor.style.left = coords.left + "px";
display.cursor.style.top = (coords.top - display.viewOffset) + "px";
}
display.cursor.scrollIntoView(doScroll);
if (hidden) display.cursor.style.display = "none";
}
}
function scrollPosIntoView(cm, pos) {
for (;;) {
var changed = false, coords = cursorCoords(cm, pos);
var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
if (scrollPos.scrollTop != null) {
setScrollTop(cm, scrollPos.scrollTop);
if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
}
if (scrollPos.scrollLeft != null) {
setScrollLeft(cm, scrollPos.scrollLeft);
if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
}
if (!changed) return coords;
}
}
function scrollIntoView(cm, x1, y1, x2, y2) {
var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
}
function calculateScrollPos(cm, x1, y1, x2, y2) {
var display = cm.display, pt = paddingTop(display);
y1 += pt; y2 += pt;
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
var docBottom = cm.view.doc.height + 2 * pt;
var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
var gutterw = display.gutters.offsetWidth;
var atLeft = x1 < gutterw + 10;
if (x1 < screenleft + gutterw || atLeft) {
if (atLeft) x1 = 0;
result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
} else if (x2 > screenw + screenleft - 3) {
result.scrollLeft = x2 + 10 - screenw;
}
return result;
}
// API UTILITIES
function indentLine(cm, n, how, aggressive) {
var doc = cm.view.doc;
if (!how) how = "add";
if (how == "smart") {
if (!cm.view.mode.indent) how = "prev";
else var state = getStateBefore(cm, n);
}
var tabSize = cm.options.tabSize;
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
if (how == "smart") {
indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
if (indentation == Pass) {
if (!aggressive) return;
how = "prev";
}
}
if (how == "prev") {
if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
else indentation = 0;
}
else if (how == "add") indentation = curSpace + cm.options.indentUnit;
else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
indentation = Math.max(0, indentation);
var indentString = "", pos = 0;
if (cm.options.indentWithTabs)
for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
if (pos < indentation) indentString += spaceStr(indentation - pos);
if (indentString != curSpaceString)
replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
line.stateAfter = null;
}
function changeLine(cm, handle, op) {
var no = handle, line = handle, doc = cm.view.doc;
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
else no = lineNo(handle);
if (no == null) return null;
if (op(line, no)) regChange(cm, no, no + 1);
else return null;
return line;
}
function findPosH(cm, dir, unit, visually) {
var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
var lineObj = getLine(doc, line);
function findNextLine() {
var l = line + dir;
if (l < 0 || l == doc.size) return false;
line = l;
return lineObj = getLine(doc, l);
}
function moveOnce(boundToLine) {
var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
if (next == null) {
if (!boundToLine && findNextLine()) {
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
else ch = dir < 0 ? lineObj.text.length : 0;
} else return false;
} else ch = next;
return true;
}
if (unit == "char") moveOnce();
else if (unit == "column") moveOnce(true);
else if (unit == "word") {
var sawWord = false;
for (;;) {
if (dir < 0) if (!moveOnce()) break;
if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
if (dir > 0) if (!moveOnce()) break;
}
}
return skipAtomic(cm, {line: line, ch: ch}, dir, true);
}
function findWordAt(line, pos) {
var start = pos.ch, end = pos.ch;
if (line) {
if (pos.after === false || end == line.length) --start; else ++end;
var startChar = line.charAt(start);
var check = isWordChar(startChar) ? isWordChar :
/\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
while (start > 0 && check(line.charAt(start - 1))) --start;
while (end < line.length && check(line.charAt(end))) ++end;
}
return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
}
function selectLine(cm, line) {
extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
}
// PROTOTYPE
// The publicly visible API. Note that operation(null, f) means
// 'wrap f in an operation, performed on its `this` parameter'
CodeMirror.prototype = {
getValue: function(lineSep) {
var text = [], doc = this.view.doc;
doc.iter(0, doc.size, function(line) { text.push(line.text); });
return text.join(lineSep || "\n");
},
setValue: operation(null, function(code) {
var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
}),
getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
replaceSelection: operation(null, function(code, collapse, origin) {
var sel = this.view.sel;
updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
}),
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
setOption: function(option, value) {
var options = this.options, old = options[option];
if (options[option] == value && option != "mode") return;
options[option] = value;
if (optionHandlers.hasOwnProperty(option))
operation(this, optionHandlers[option])(this, value, old);
},
getOption: function(option) {return this.options[option];},
getMode: function() {return this.view.mode;},
addKeyMap: function(map) {
this.view.keyMaps.push(map);
},
removeKeyMap: function(map) {
var maps = this.view.keyMaps;
for (var i = 0; i < maps.length; ++i)
if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
maps.splice(i, 1);
return true;
}
},
addOverlay: operation(null, function(spec, options) {
var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
if (mode.startState) throw new Error("Overlays may not be stateful.");
this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
this.view.modeGen++;
regChange(this, 0, this.view.doc.size);
}),
removeOverlay: operation(null, function(spec) {
var overlays = this.view.overlays;
for (var i = 0; i < overlays.length; ++i) {
if (overlays[i].modeSpec == spec) {
overlays.splice(i, 1);
this.view.modeGen++;
regChange(this, 0, this.view.doc.size);
return;
}
}
}),
undo: operation(null, function() {unredoHelper(this, "undo");}),
redo: operation(null, function() {unredoHelper(this, "redo");}),
indentLine: operation(null, function(n, dir, aggressive) {
if (typeof dir != "string") {
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
else dir = dir ? "add" : "subtract";
}
if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
}),
indentSelection: operation(null, function(how) {
var sel = this.view.sel;
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
var e = sel.to.line - (sel.to.ch ? 0 : 1);
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
}),
historySize: function() {
var hist = this.view.history;
return {undo: hist.done.length, redo: hist.undone.length};
},
clearHistory: function() {this.view.history = makeHistory();},
markClean: function() {
this.view.history.dirtyCounter = 0;
this.view.history.lastOp = this.view.history.lastOrigin = null;
},
isClean: function () {return this.view.history.dirtyCounter == 0;},
getHistory: function() {
var hist = this.view.history;
function cp(arr) {
for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
var set = arr[i];
nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
fromAfter: set.fromAfter, toAfter: set.toAfter});
for (var j = 0, elt = set.events; j < elt.length; ++j) {
var old = [], cur = elt[j];
nwelt.push({start: cur.start, added: cur.added, old: old});
for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
}
}
return nw;
}
return {done: cp(hist.done), undone: cp(hist.undone)};
},
setHistory: function(histData) {
var hist = this.view.history = makeHistory();
hist.done = histData.done;
hist.undone = histData.undone;
},
// Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion).
getTokenAt: function(pos) {
var doc = this.view.doc;
pos = clipPos(doc, pos);
var state = getStateBefore(this, pos.line), mode = this.view.mode;
var line = getLine(doc, pos.line);
var stream = new StringStream(line.text, this.options.tabSize);
while (stream.pos < pos.ch && !stream.eol()) {
stream.start = stream.pos;
var style = mode.token(stream, state);
}
return {start: stream.start,
end: stream.pos,
string: stream.current(),
className: style || null, // Deprecated, use 'type' instead
type: style || null,
state: state};
},
getStateAfter: function(line) {
var doc = this.view.doc;
line = clipLine(doc, line == null ? doc.size - 1: line);
return getStateBefore(this, line + 1);
},
cursorCoords: function(start, mode) {
var pos, sel = this.view.sel;
if (start == null) pos = sel.head;
else if (typeof start == "object") pos = clipPos(this.view.doc, start);
else pos = start ? sel.from : sel.to;
return cursorCoords(this, pos, mode || "page");
},
charCoords: function(pos, mode) {
return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
},
coordsChar: function(coords) {
var off = this.display.lineSpace.getBoundingClientRect();
return coordsChar(this, coords.left - off.left, coords.top - off.top);
},
defaultTextHeight: function() { return textHeight(this.display); },
markText: operation(null, function(from, to, options) {
return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
options, "range");
}),
setBookmark: operation(null, function(pos, widget) {
pos = clipPos(this.view.doc, pos);
return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
}),
findMarksAt: function(pos) {
var doc = this.view.doc;
pos = clipPos(doc, pos);
var markers = [], spans = getLine(doc, pos.line).markedSpans;
if (spans) for (var i = 0; i < spans.length; ++i) {
var span = spans[i];
if ((span.from == null || span.from <= pos.ch) &&
(span.to == null || span.to >= pos.ch))
markers.push(span.marker);
}
return markers;
},
setGutterMarker: operation(null, function(line, gutterID, value) {
return changeLine(this, line, function(line) {
var markers = line.gutterMarkers || (line.gutterMarkers = {});
markers[gutterID] = value;
if (!value && isEmpty(markers)) line.gutterMarkers = null;
return true;
});
}),
clearGutter: operation(null, function(gutterID) {
var i = 0, cm = this, doc = cm.view.doc;
doc.iter(0, doc.size, function(line) {
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
line.gutterMarkers[gutterID] = null;
regChange(cm, i, i + 1);
if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
}
++i;
});
}),
addLineClass: operation(null, function(handle, where, cls) {
return changeLine(this, handle, function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
if (!line[prop]) line[prop] = cls;
else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
else line[prop] += " " + cls;
return true;
});
}),
removeLineClass: operation(null, function(handle, where, cls) {
return changeLine(this, handle, function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
var cur = line[prop];
if (!cur) return false;
else if (cls == null) line[prop] = null;
else {
var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
if (upd == cur) return false;
line[prop] = upd || null;
}
return true;
});
}),
addLineWidget: operation(null, function(handle, node, options) {
return addLineWidget(this, handle, node, options);
}),
removeLineWidget: function(widget) { widget.clear(); },
lineInfo: function(line) {
if (typeof line == "number") {
if (!isLine(this.view.doc, line)) return null;
var n = line;
line = getLine(this.view.doc, line);
if (!line) return null;
} else {
var n = lineNo(line);
if (n == null) return null;
}
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
widgets: line.widgets};
},
getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
addWidget: function(pos, node, scroll, vert, horiz) {
var display = this.display;
pos = cursorCoords(this, clipPos(this.view.doc, pos));
var top = pos.top, left = pos.left;
node.style.position = "absolute";
display.sizer.appendChild(node);
if (vert == "over") top = pos.top;
else if (vert == "near") {
var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
top = pos.top - node.offsetHeight;
if (left + node.offsetWidth > hspace)
left = hspace - node.offsetWidth;
}
node.style.top = (top + paddingTop(display)) + "px";
node.style.left = node.style.right = "";
if (horiz == "right") {
left = display.sizer.clientWidth - node.offsetWidth;
node.style.right = "0px";
} else {
if (horiz == "left") left = 0;
else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
node.style.left = left + "px";
}
if (scroll)
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
},
lineCount: function() {return this.view.doc.size;},
clipPos: function(pos) {return clipPos(this.view.doc, pos);},
getCursor: function(start) {
var sel = this.view.sel, pos;
if (start == null || start == "head") pos = sel.head;
else if (start == "anchor") pos = sel.anchor;
else if (start == "end" || start === false) pos = sel.to;
else pos = sel.from;
return copyPos(pos);
},
somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
setCursor: operation(null, function(line, ch, extend) {
var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
if (extend) extendSelection(this, pos);
else setSelection(this, pos, pos);
}),
setSelection: operation(null, function(anchor, head) {
var doc = this.view.doc;
setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
}),
extendSelection: operation(null, function(from, to) {
var doc = this.view.doc;
extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
}),
setExtending: function(val) {this.view.sel.extend = val;},
getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
getLineHandle: function(line) {
var doc = this.view.doc;
if (isLine(doc, line)) return getLine(doc, line);
},
getLineNumber: function(line) {return lineNo(line);},
setLine: operation(null, function(line, text) {
if (isLine(this.view.doc, line))
replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
}),
removeLine: operation(null, function(line) {
if (isLine(this.view.doc, line))
replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
}),
replaceRange: operation(null, function(code, from, to) {
var doc = this.view.doc;
from = clipPos(doc, from);
to = to ? clipPos(doc, to) : from;
return replaceRange(this, code, from, to);
}),
getRange: function(from, to, lineSep) {
var doc = this.view.doc;
from = clipPos(doc, from); to = clipPos(doc, to);
var l1 = from.line, l2 = to.line;
if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
var code = [getLine(doc, l1).text.slice(from.ch)];
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
code.push(getLine(doc, l2).text.slice(0, to.ch));
return code.join(lineSep || "\n");
},
triggerOnKeyDown: operation(null, onKeyDown),
execCommand: function(cmd) {return commands[cmd](this);},
// Stuff used by commands, probably not much use to outside code.
moveH: operation(null, function(dir, unit) {
var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
if (sel.shift || sel.extend || posEq(sel.from, sel.to))
pos = findPosH(this, dir, unit, this.options.rtlMoveVisually);
extendSelection(this, pos, pos, dir);
}),
deleteH: operation(null, function(dir, unit) {
var sel = this.view.sel;
if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
this.curOp.userSelChange = true;
}),
moveV: operation(null, function(dir, unit) {
var view = this.view, doc = view.doc, display = this.display;
var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
var x = pos.left, y;
if (view.goalColumn != null) x = view.goalColumn;
if (unit == "page") {
var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
y = pos.top + dir * pageSize;
} else if (unit == "line") {
y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
}
do {
var target = coordsChar(this, x, y);
y += dir * 5;
} while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
extendSelection(this, target, target, dir);
view.goalColumn = x;
}),
toggleOverwrite: function() {
if (this.view.overwrite = !this.view.overwrite)
this.display.cursor.className += " CodeMirror-overwrite";
else
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
},
posFromIndex: function(off) {
var lineNo = 0, ch, doc = this.view.doc;
doc.iter(0, doc.size, function(line) {
var sz = line.text.length + 1;
if (sz > off) { ch = off; return true; }
off -= sz;
++lineNo;
});
return clipPos(doc, {line: lineNo, ch: ch});
},
indexFromPos: function (coords) {
coords = clipPos(this.view.doc, coords);
var index = coords.ch;
this.view.doc.iter(0, coords.line, function (line) {
index += line.text.length + 1;
});
return index;
},
scrollTo: function(x, y) {
if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
updateDisplay(this, []);
},
getScrollInfo: function() {
var scroller = this.display.scroller, co = scrollerCutOff;
return {left: scroller.scrollLeft, top: scroller.scrollTop,
height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
},
scrollIntoView: function(pos) {
if (typeof pos == "number") pos = {line: pos, ch: 0};
if (!pos || pos.line != null) {
pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
scrollPosIntoView(this, pos);
} else {
scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
}
},
setSize: function(width, height) {
function interpret(val) {
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
}
if (width != null) this.display.wrapper.style.width = interpret(width);
if (height != null) this.display.wrapper.style.height = interpret(height);
this.refresh();
},
on: function(type, f) {on(this, type, f);},
off: function(type, f) {off(this, type, f);},
operation: function(f){return operation(this, f)();},
refresh: function() {
clearCaches(this);
var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft;
if (this.display.scroller.scrollHeight > sTop)
this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop;
if (this.display.scroller.scrollWidth > sLeft)
this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft;
updateDisplay(this, true);
},
getInputField: function(){return this.display.input;},
getWrapperElement: function(){return this.display.wrapper;},
getScrollerElement: function(){return this.display.scroller;},
getGutterElement: function(){return this.display.gutters;}
};
// OPTION DEFAULTS
var optionHandlers = CodeMirror.optionHandlers = {};
// The default configuration options.
var defaults = CodeMirror.defaults = {};
function option(name, deflt, handle, notOnInit) {
CodeMirror.defaults[name] = deflt;
if (handle) optionHandlers[name] =
notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
}
var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
// These two are, on init, called from the constructor because they
// have to be initialized before the editor can start at all.
option("value", "", function(cm, val) {cm.setValue(val);}, true);
option("mode", null, loadMode, true);
option("indentUnit", 2, loadMode, true);
option("indentWithTabs", false);
option("smartIndent", true);
option("tabSize", 4, function(cm) {
loadMode(cm);
clearCaches(cm);
updateDisplay(cm, true);
}, true);
option("electricChars", true);
option("rtlMoveVisually", !windows);
option("theme", "default", function(cm) {
themeChanged(cm);
guttersChanged(cm);
}, true);
option("keyMap", "default", keyMapChanged);
option("extraKeys", null);
option("onKeyEvent", null);
option("onDragEvent", null);
option("lineWrapping", false, wrappingChanged, true);
option("gutters", [], function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
}, true);
option("fixedGutter", true, function(cm, val) {
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
cm.refresh();
}, true);
option("lineNumbers", false, function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
}, true);
option("firstLineNumber", 1, guttersChanged, true);
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
option("showCursorWhenSelecting", false, updateSelection, true);
option("readOnly", false, function(cm, val) {
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
else if (!val) resetInput(cm, true);
});
option("dragDrop", true);
option("cursorBlinkRate", 530);
option("cursorHeight", 1);
option("workTime", 100);
option("workDelay", 100);
option("flattenSpans", true);
option("pollInterval", 100);
option("undoDepth", 40);
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
option("tabindex", null, function(cm, val) {
cm.display.input.tabIndex = val || "";
});
option("autofocus", null);
// MODE DEFINITION AND QUERYING
// Known modes, by name and by MIME
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
CodeMirror.defineMode = function(name, mode) {
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
if (arguments.length > 2) {
mode.dependencies = [];
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
}
modes[name] = mode;
};
CodeMirror.defineMIME = function(mime, spec) {
mimeModes[mime] = spec;
};
CodeMirror.resolveMode = function(spec) {
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
spec = mimeModes[spec];
else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
return CodeMirror.resolveMode("application/xml");
if (typeof spec == "string") return {name: spec};
else return spec || {name: "null"};
};
CodeMirror.getMode = function(options, spec) {
spec = CodeMirror.resolveMode(spec);
var mfactory = modes[spec.name];
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
var modeObj = mfactory(options, spec);
if (modeExtensions.hasOwnProperty(spec.name)) {
var exts = modeExtensions[spec.name];
for (var prop in exts) {
if (!exts.hasOwnProperty(prop)) continue;
if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
modeObj[prop] = exts[prop];
}
}
modeObj.name = spec.name;
return modeObj;
};
CodeMirror.defineMode("null", function() {
return {token: function(stream) {stream.skipToEnd();}};
});
CodeMirror.defineMIME("text/plain", "null");
var modeExtensions = CodeMirror.modeExtensions = {};
CodeMirror.extendMode = function(mode, properties) {
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
for (var prop in properties) if (properties.hasOwnProperty(prop))
exts[prop] = properties[prop];
};
// EXTENSIONS
CodeMirror.defineExtension = function(name, func) {
CodeMirror.prototype[name] = func;
};
CodeMirror.defineOption = option;
var initHooks = [];
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
// MODE STATE HANDLING
// Utility functions for working with state. Exported because modes
// sometimes need to do this.
function copyState(mode, state) {
if (state === true) return state;
if (mode.copyState) return mode.copyState(state);
var nstate = {};
for (var n in state) {
var val = state[n];
if (val instanceof Array) val = val.concat([]);
nstate[n] = val;
}
return nstate;
}
CodeMirror.copyState = copyState;
function startState(mode, a1, a2) {
return mode.startState ? mode.startState(a1, a2) : true;
}
CodeMirror.startState = startState;
CodeMirror.innerMode = function(mode, state) {
while (mode.innerMode) {
var info = mode.innerMode(state);
state = info.state;
mode = info.mode;
}
return info || {mode: mode, state: state};
};
// STANDARD COMMANDS
var commands = CodeMirror.commands = {
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
killLine: function(cm) {
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
if (!sel && cm.getLine(from.line).length == from.ch)
cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
},
deleteLine: function(cm) {
var l = cm.getCursor().line;
cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
},
undo: function(cm) {cm.undo();},
redo: function(cm) {cm.redo();},
goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
goLineStart: function(cm) {
cm.extendSelection(lineStart(cm, cm.getCursor().line));
},
goLineStartSmart: function(cm) {
var cur = cm.getCursor(), start = lineStart(cm, cur.line);
var line = cm.getLineHandle(start.line);
var order = getOrder(line);
if (!order || order[0].level == 0) {
var firstNonWS = Math.max(0, line.text.search(/\S/));
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
} else cm.extendSelection(start);
},
goLineEnd: function(cm) {
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
},
goLineUp: function(cm) {cm.moveV(-1, "line");},
goLineDown: function(cm) {cm.moveV(1, "line");},
goPageUp: function(cm) {cm.moveV(-1, "page");},
goPageDown: function(cm) {cm.moveV(1, "page");},
goCharLeft: function(cm) {cm.moveH(-1, "char");},
goCharRight: function(cm) {cm.moveH(1, "char");},
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
goColumnRight: function(cm) {cm.moveH(1, "column");},
goWordLeft: function(cm) {cm.moveH(-1, "word");},
goWordRight: function(cm) {cm.moveH(1, "word");},
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
delCharAfter: function(cm) {cm.deleteH(1, "char");},
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
delWordAfter: function(cm) {cm.deleteH(1, "word");},
indentAuto: function(cm) {cm.indentSelection("smart");},
indentMore: function(cm) {cm.indentSelection("add");},
indentLess: function(cm) {cm.indentSelection("subtract");},
insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
defaultTab: function(cm) {
if (cm.somethingSelected()) cm.indentSelection("add");
else cm.replaceSelection("\t", "end", "input");
},
transposeChars: function(cm) {
var cur = cm.getCursor(), line = cm.getLine(cur.line);
if (cur.ch > 0 && cur.ch < line.length - 1)
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
{line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
},
newlineAndIndent: function(cm) {
operation(cm, function() {
cm.replaceSelection("\n", "end", "input");
cm.indentLine(cm.getCursor().line, null, true);
})();
},
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
};
// STANDARD KEYMAPS
var keyMap = CodeMirror.keyMap = {};
keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
};
// Note that the save and find-related commands aren't defined by
// default. Unknown commands are simply ignored.
keyMap.pcDefault = {
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
"Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
"Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
fallthrough: "basic"
};
keyMap.macDefault = {
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
"Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
"Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
fallthrough: ["basic", "emacsy"]
};
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
keyMap.emacsy = {
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
};
// KEYMAP DISPATCH
function getKeyMap(val) {
if (typeof val == "string") return keyMap[val];
else return val;
}
function lookupKey(name, maps, handle, stop) {
function lookup(map) {
map = getKeyMap(map);
var found = map[name];
if (found === false) {
if (stop) stop();
return true;
}
if (found != null && handle(found)) return true;
if (map.nofallthrough) {
if (stop) stop();
return true;
}
var fallthrough = map.fallthrough;
if (fallthrough == null) return false;
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
return lookup(fallthrough);
for (var i = 0, e = fallthrough.length; i < e; ++i) {
if (lookup(fallthrough[i])) return true;
}
return false;
}
for (var i = 0; i < maps.length; ++i)
if (lookup(maps[i])) return true;
}
function isModifierKey(event) {
var name = keyNames[e_prop(event, "keyCode")];
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
}
CodeMirror.isModifierKey = isModifierKey;
// FROMTEXTAREA
CodeMirror.fromTextArea = function(textarea, options) {
if (!options) options = {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabindex)
options.tabindex = textarea.tabindex;
// Set autofocus to true if this textarea is focused, or if it has
// autofocus and no other element is focused.
if (options.autofocus == null) {
var hasFocus = document.body;
// doc.activeElement occasionally throws on IE
try { hasFocus = document.activeElement; } catch(e) {}
options.autofocus = hasFocus == textarea ||
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
}
function save() {textarea.value = cm.getValue();}
if (textarea.form) {
// Deplorable hack to make the submit method do the right thing.
on(textarea.form, "submit", save);
var form = textarea.form, realSubmit = form.submit;
try {
form.submit = function wrappedSubmit() {
save();
form.submit = realSubmit;
form.submit();
form.submit = wrappedSubmit;
};
} catch(e) {}
}
textarea.style.display = "none";
var cm = CodeMirror(function(node) {
textarea.parentNode.insertBefore(node, textarea.nextSibling);
}, options);
cm.save = save;
cm.getTextArea = function() { return textarea; };
cm.toTextArea = function() {
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (typeof textarea.form.submit == "function")
textarea.form.submit = realSubmit;
}
};
return cm;
};
// STRING STREAM
// Fed to the mode parsers, provides helper functions to make
// parsers more succinct.
// The character stream used by a mode's parser.
function StringStream(string, tabSize) {
this.pos = this.start = 0;
this.string = string;
this.tabSize = tabSize || 8;
}
StringStream.prototype = {
eol: function() {return this.pos >= this.string.length;},
sol: function() {return this.pos == 0;},
peek: function() {return this.string.charAt(this.pos) || undefined;},
next: function() {
if (this.pos < this.string.length)
return this.string.charAt(this.pos++);
},
eat: function(match) {
var ch = this.string.charAt(this.pos);
if (typeof match == "string") var ok = ch == match;
else var ok = ch && (match.test ? match.test(ch) : match(ch));
if (ok) {++this.pos; return ch;}
},
eatWhile: function(match) {
var start = this.pos;
while (this.eat(match)){}
return this.pos > start;
},
eatSpace: function() {
var start = this.pos;
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
return this.pos > start;
},
skipToEnd: function() {this.pos = this.string.length;},
skipTo: function(ch) {
var found = this.string.indexOf(ch, this.pos);
if (found > -1) {this.pos = found; return true;}
},
backUp: function(n) {this.pos -= n;},
column: function() {return countColumn(this.string, this.start, this.tabSize);},
indentation: function() {return countColumn(this.string, null, this.tabSize);},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
if (consume !== false) this.pos += pattern.length;
return true;
}
} else {
var match = this.string.slice(this.pos).match(pattern);
if (match && match.index > 0) return null;
if (match && consume !== false) this.pos += match[0].length;
return match;
}
},
current: function(){return this.string.slice(this.start, this.pos);}
};
CodeMirror.StringStream = StringStream;
// TEXTMARKERS
function TextMarker(cm, type) {
this.lines = [];
this.type = type;
this.cm = cm;
}
CodeMirror.TextMarker = TextMarker;
TextMarker.prototype.clear = function() {
if (this.explicitlyCleared) return;
startOperation(this.cm);
var view = this.cm.view, min = null, max = null;
for (var i = 0; i < this.lines.length; ++i) {
var line = this.lines[i];
var span = getMarkedSpanFor(line.markedSpans, this);
if (span.to != null) max = lineNo(line);
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
if (span.from != null)
min = lineNo(line);
else if (this.collapsed && !lineIsHidden(line))
updateLineHeight(line, textHeight(this.cm.display));
}
if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual);
if (len > view.maxLineLength) {
view.maxLine = visual;
view.maxLineLength = len;
view.maxLineChanged = true;
}
}
if (min != null) regChange(this.cm, min, max + 1);
this.lines.length = 0;
this.explicitlyCleared = true;
if (this.collapsed && this.cm.view.cantEdit) {
this.cm.view.cantEdit = false;
reCheckSelection(this.cm);
}
endOperation(this.cm);
signalLater(this.cm, this, "clear");
};
TextMarker.prototype.find = function() {
var from, to;
for (var i = 0; i < this.lines.length; ++i) {
var line = this.lines[i];
var span = getMarkedSpanFor(line.markedSpans, this);
if (span.from != null || span.to != null) {
var found = lineNo(line);
if (span.from != null) from = {line: found, ch: span.from};
if (span.to != null) to = {line: found, ch: span.to};
}
}
if (this.type == "bookmark") return from;
return from && {from: from, to: to};
};
TextMarker.prototype.getOptions = function(copyWidget) {
var repl = this.replacedWith;
return {className: this.className,
inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
atomic: this.atomic,
collapsed: this.collapsed,
clearOnEnter: this.clearOnEnter,
replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
readOnly: this.readOnly,
startStyle: this.startStyle, endStyle: this.endStyle};
};
function markText(cm, from, to, options, type) {
var doc = cm.view.doc;
var marker = new TextMarker(cm, type);
if (type == "range" && !posLess(from, to)) return marker;
if (options) for (var opt in options) if (options.hasOwnProperty(opt))
marker[opt] = options[opt];
if (marker.replacedWith) {
marker.collapsed = true;
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
}
if (marker.collapsed) sawCollapsedSpans = true;
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
doc.iter(curLine, to.line + 1, function(line) {
if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine)
cm.curOp.updateMaxLine = true;
var span = {from: null, to: null, marker: marker};
size += line.text.length;
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
if (marker.collapsed) {
if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
else updateLineHeight(line, 0);
}
addMarkedSpan(line, span);
++curLine;
});
if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
if (lineIsHidden(line)) updateLineHeight(line, 0);
});
if (marker.readOnly) {
sawReadOnlySpans = true;
if (cm.view.history.done.length || cm.view.history.undone.length)
cm.clearHistory();
}
if (marker.collapsed) {
if (collapsedAtStart != collapsedAtEnd)
throw new Error("Inserting collapsed marker overlapping an existing one");
marker.size = size;
marker.atomic = true;
}
if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
regChange(cm, from.line, to.line + 1);
if (marker.atomic) reCheckSelection(cm);
return marker;
}
// TEXTMARKER SPANS
function getMarkedSpanFor(spans, marker) {
if (spans) for (var i = 0; i < spans.length; ++i) {
var span = spans[i];
if (span.marker == marker) return span;
}
}
function removeMarkedSpan(spans, span) {
for (var r, i = 0; i < spans.length; ++i)
if (spans[i] != span) (r || (r = [])).push(spans[i]);
return r;
}
function addMarkedSpan(line, span) {
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
span.marker.lines.push(line);
}
function markedSpansBefore(old, startCh) {
if (old) for (var i = 0, nw; i < old.length; ++i) {
var span = old[i], marker = span.marker;
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
(nw || (nw = [])).push({from: span.from,
to: endsAfter ? null : span.to,
marker: marker});
}
}
return nw;
}
function markedSpansAfter(old, startCh, endCh) {
if (old) for (var i = 0, nw; i < old.length; ++i) {
var span = old[i], marker = span.marker;
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
to: span.to == null ? null : span.to - endCh,
marker: marker});
}
}
return nw;
}
function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
if (!oldFirst && !oldLast) return newText;
// Get the spans that 'stick out' on both sides
var first = markedSpansBefore(oldFirst, startCh);
var last = markedSpansAfter(oldLast, startCh, endCh);
// Next, merge those two ends
var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
if (first) {
// Fix up .to properties of first
for (var i = 0; i < first.length; ++i) {
var span = first[i];
if (span.to == null) {
var found = getMarkedSpanFor(last, span.marker);
if (!found) span.to = startCh;
else if (sameLine) span.to = found.to == null ? null : found.to + offset;
}
}
}
if (last) {
// Fix up .from in last (or move them into first in case of sameLine)
for (var i = 0; i < last.length; ++i) {
var span = last[i];
if (span.to != null) span.to += offset;
if (span.from == null) {
var found = getMarkedSpanFor(first, span.marker);
if (!found) {
span.from = offset;
if (sameLine) (first || (first = [])).push(span);
}
} else {
span.from += offset;
if (sameLine) (first || (first = [])).push(span);
}
}
}
var newMarkers = [newHL(newText[0], first)];
if (!sameLine) {
// Fill gap with whole-line-spans
var gap = newText.length - 2, gapMarkers;
if (gap > 0 && first)
for (var i = 0; i < first.length; ++i)
if (first[i].to == null)
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
for (var i = 0; i < gap; ++i)
newMarkers.push(newHL(newText[i+1], gapMarkers));
newMarkers.push(newHL(lst(newText), last));
}
return newMarkers;
}
function removeReadOnlyRanges(doc, from, to) {
var markers = null;
doc.iter(from.line, to.line + 1, function(line) {
if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
var mark = line.markedSpans[i].marker;
if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
(markers || (markers = [])).push(mark);
}
});
if (!markers) return null;
var parts = [{from: from, to: to}];
for (var i = 0; i < markers.length; ++i) {
var m = markers[i].find();
for (var j = 0; j < parts.length; ++j) {
var p = parts[j];
if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
var newParts = [j, 1];
if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
parts.splice.apply(parts, newParts);
j += newParts.length - 1;
}
}
return parts;
}
function collapsedSpanAt(line, ch) {
var sps = sawCollapsedSpans && line.markedSpans, found;
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
sp = sps[i];
if (!sp.marker.collapsed) continue;
if ((sp.from == null || sp.from < ch) &&
(sp.to == null || sp.to > ch) &&
(!found || found.width < sp.marker.width))
found = sp.marker;
}
return found;
}
function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
function visualLine(doc, line) {
var merged;
while (merged = collapsedSpanAtStart(line))
line = getLine(doc, merged.find().from.line);
return line;
}
function lineIsHidden(line) {
var sps = sawCollapsedSpans && line.markedSpans;
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
sp = sps[i];
if (!sp.marker.collapsed) continue;
if (sp.from == null) return true;
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
return true;
}
}
function lineIsHiddenInner(line, span) {
if (span.to == null) {
var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line);
return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
}
if (span.marker.inclusiveRight && span.to == line.text.length)
return true;
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
sp = line.markedSpans[i];
if (sp.marker.collapsed && sp.from == span.to &&
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
lineIsHiddenInner(line, sp)) return true;
}
}
// hl stands for history-line, a data structure that can be either a
// string (line without markers) or a {text, markedSpans} object.
function hlText(val) { return typeof val == "string" ? val : val.text; }
function hlSpans(val) {
if (typeof val == "string") return null;
var spans = val.markedSpans, out = null;
for (var i = 0; i < spans.length; ++i) {
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
else if (out) out.push(spans[i]);
}
return !out ? spans : out.length ? out : null;
}
function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
function detachMarkedSpans(line) {
var spans = line.markedSpans;
if (!spans) return;
for (var i = 0; i < spans.length; ++i) {
var lines = spans[i].marker.lines;
var ix = indexOf(lines, line);
lines.splice(ix, 1);
}
line.markedSpans = null;
}
function attachMarkedSpans(line, spans) {
if (!spans) return;
for (var i = 0; i < spans.length; ++i)
spans[i].marker.lines.push(line);
line.markedSpans = spans;
}
// LINE WIDGETS
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
for (var opt in options) if (options.hasOwnProperty(opt))
this[opt] = options[opt];
this.cm = cm;
this.node = node;
};
function widgetOperation(f) {
return function() {
startOperation(this.cm);
try {var result = f.apply(this, arguments);}
finally {endOperation(this.cm);}
return result;
};
}
LineWidget.prototype.clear = widgetOperation(function() {
var ws = this.line.widgets, no = lineNo(this.line);
if (no == null || !ws) return;
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
regChange(this.cm, no, no + 1);
});
LineWidget.prototype.changed = widgetOperation(function() {
var oldH = this.height;
this.height = null;
var diff = widgetHeight(this) - oldH;
if (!diff) return;
updateLineHeight(this.line, this.line.height + diff);
var no = lineNo(this.line);
regChange(this.cm, no, no + 1);
});
function widgetHeight(widget) {
if (widget.height != null) return widget.height;
if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
return widget.height = widget.node.offsetHeight;
}
function addLineWidget(cm, handle, node, options) {
var widget = new LineWidget(cm, node, options);
if (widget.noHScroll) cm.display.alignWidgets = true;
changeLine(cm, handle, function(line) {
(line.widgets || (line.widgets = [])).push(widget);
widget.line = line;
if (!lineIsHidden(line) || widget.showIfHidden) {
var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
updateLineHeight(line, line.height + widgetHeight(widget));
if (aboveVisible)
setTimeout(function() {cm.display.scroller.scrollTop += widget.height;});
}
return true;
});
return widget;
}
// LINE DATA STRUCTURE
// Line objects. These hold state related to a line, including
// highlighting info (the styles array).
function makeLine(text, markedSpans, height) {
var line = {text: text, height: height};
attachMarkedSpans(line, markedSpans);
if (lineIsHidden(line)) line.height = 0;
return line;
}
function updateLine(cm, line, text, markedSpans) {
line.text = text;
if (line.stateAfter) line.stateAfter = null;
if (line.styles) line.styles = null;
if (line.order != null) line.order = null;
detachMarkedSpans(line);
attachMarkedSpans(line, markedSpans);
if (lineIsHidden(line)) line.height = 0;
else if (!line.height) line.height = textHeight(cm.display);
signalLater(cm, line, "change");
}
function cleanUpLine(line) {
line.parent = null;
detachMarkedSpans(line);
}
// Run the given mode's parser over a line, update the styles
// array, which contains alternating fragments of text and CSS
// classes.
function runMode(cm, text, mode, state, f) {
var flattenSpans = cm.options.flattenSpans;
var curText = "", curStyle = null;
var stream = new StringStream(text, cm.options.tabSize);
if (text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
var style = mode.token(stream, state);
if (stream.pos > 5000) {
flattenSpans = false;
// Webkit seems to refuse to render text nodes longer than 57444 characters
stream.pos = Math.min(text.length, stream.start + 50000);
style = null;
}
var substr = stream.current();
stream.start = stream.pos;
if (!flattenSpans || curStyle != style) {
if (curText) f(curText, curStyle);
curText = substr; curStyle = style;
} else curText = curText + substr;
}
if (curText) f(curText, curStyle);
}
function highlightLine(cm, line, state) {
// A styles array always starts with a number identifying the
// mode/overlays that it is based on (for easy invalidation).
var st = [cm.view.modeGen];
// Compute the base array of styles
runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);});
// Run overlays, adjust style array.
for (var o = 0; o < cm.view.overlays.length; ++o) {
var overlay = cm.view.overlays[o], i = 1;
runMode(cm, line.text, overlay.mode, true, function(txt, style) {
var start = i, len = txt.length;
// Ensure there's a token end at the current position, and that i points at it
while (len) {
var cur = st[i], len_ = cur.length;
if (len_ <= len) {
len -= len_;
} else {
st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
len = 0;
}
i += 2;
}
if (!style) return;
if (overlay.opaque) {
st.splice(start, i - start, txt, style);
i = start + 2;
} else {
for (; start < i; start += 2) {
var cur = st[start+1];
st[start+1] = cur ? cur + " " + style : style;
}
}
});
}
return st;
}
function getLineStyles(cm, line) {
if (!line.styles || line.styles[0] != cm.view.modeGen)
line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
return line.styles;
}
// Lightweight form of highlight -- proceed over this line and
// update state, but don't save a style array.
function processLine(cm, line, state) {
var mode = cm.view.mode;
var stream = new StringStream(line.text, cm.options.tabSize);
if (line.text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol() && stream.pos <= 5000) {
mode.token(stream, state);
stream.start = stream.pos;
}
}
var styleToClassCache = {};
function styleToClass(style) {
if (!style) return null;
return styleToClassCache[style] ||
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
}
function lineContent(cm, realLine, measure) {
var merged, line = realLine, lineBefore, sawBefore, simple = true;
while (merged = collapsedSpanAtStart(line)) {
simple = false;
line = getLine(cm.view.doc, merged.find().from.line);
if (!lineBefore) lineBefore = line;
}
var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
measure: null, addedOne: false, cm: cm};
if (line.textClass) builder.pre.className = line.textClass;
do {
builder.measure = line == realLine && measure;
builder.pos = 0;
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
if (measure && sawBefore && line != realLine && !builder.addedOne) {
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
builder.addedOne = true;
}
var next = insertLineContent(line, builder, getLineStyles(cm, line));
sawBefore = line == lineBefore;
if (next) {
line = getLine(cm.view.doc, next.to.line);
simple = false;
}
} while (next);
if (measure && !builder.addedOne)
measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
if (!builder.pre.firstChild && !lineIsHidden(realLine))
builder.pre.appendChild(document.createTextNode("\u00a0"));
return builder.pre;
}
var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
function buildToken(builder, text, style, startStyle, endStyle) {
if (!text) return;
if (!tokenSpecialChars.test(text)) {
builder.col += text.length;
var content = document.createTextNode(text);
} else {
var content = document.createDocumentFragment(), pos = 0;
while (true) {
tokenSpecialChars.lastIndex = pos;
var m = tokenSpecialChars.exec(text);
var skipped = m ? m.index - pos : text.length - pos;
if (skipped) {
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
builder.col += skipped;
}
if (!m) break;
pos += skipped + 1;
if (m[0] == "\t") {
var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
builder.col += tabWidth;
} else {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
content.appendChild(token);
builder.col += 1;
}
}
}
if (style || startStyle || endStyle || builder.measure) {
var fullStyle = style || "";
if (startStyle) fullStyle += startStyle;
if (endStyle) fullStyle += endStyle;
return builder.pre.appendChild(elt("span", [content], fullStyle));
}
builder.pre.appendChild(content);
}
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
for (var i = 0; i < text.length; ++i) {
if (i && i < text.length &&
builder.cm.options.lineWrapping &&
spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
builder.pre.appendChild(elt("wbr"));
builder.measure[builder.pos++] =
buildToken(builder, text.charAt(i), style,
i == 0 && startStyle, i == text.length - 1 && endStyle);
}
if (text.length) builder.addedOne = true;
}
function buildCollapsedSpan(builder, size, widget) {
if (widget) {
if (!builder.display) widget = widget.cloneNode(true);
builder.pre.appendChild(widget);
if (builder.measure && size) {
builder.measure[builder.pos] = widget;
builder.addedOne = true;
}
}
builder.pos += size;
}
// Outputs a number of spans to make up a line, taking highlighting
// and marked text into account.
function insertLineContent(line, builder, styles) {
var spans = line.markedSpans;
if (!spans) {
for (var i = 1; i < styles.length; i+=2)
builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
return;
}
var allText = line.text, len = allText.length;
var pos = 0, i = 1, text = "", style;
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
for (;;) {
if (nextChange == pos) { // Update current marker set
spanStyle = spanEndStyle = spanStartStyle = "";
collapsed = null; nextChange = Infinity;
var foundBookmark = null;
for (var j = 0; j < spans.length; ++j) {
var sp = spans[j], m = sp.marker;
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
if (m.className) spanStyle += " " + m.className;
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
collapsed = sp;
} else if (sp.from > pos && nextChange > sp.from) {
nextChange = sp.from;
}
if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
foundBookmark = m.replacedWith;
}
if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
collapsed.from != null && collapsed.marker.replacedWith);
if (collapsed.to == null) return collapsed.marker.find();
}
if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
}
if (pos >= len) break;
var upto = Math.min(len, nextChange);
while (true) {
if (text) {
var end = pos + text.length;
if (!collapsed) {
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
builder.addToken(builder, tokenText, style + spanStyle,
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
}
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
pos = end;
spanStartStyle = "";
}
text = styles[i++]; style = styleToClass(styles[i++]);
}
}
}
// DOCUMENT DATA STRUCTURE
function LeafChunk(lines) {
this.lines = lines;
this.parent = null;
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
lines[i].parent = this;
height += lines[i].height;
}
this.height = height;
}
LeafChunk.prototype = {
chunkSize: function() { return this.lines.length; },
remove: function(at, n, cm) {
for (var i = at, e = at + n; i < e; ++i) {
var line = this.lines[i];
this.height -= line.height;
cleanUpLine(line);
signalLater(cm, line, "delete");
}
this.lines.splice(at, n);
},
collapse: function(lines) {
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
},
insertHeight: function(at, lines, height) {
this.height += height;
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
},
iterN: function(at, n, op) {
for (var e = at + n; at < e; ++at)
if (op(this.lines[at])) return true;
}
};
function BranchChunk(children) {
this.children = children;
var size = 0, height = 0;
for (var i = 0, e = children.length; i < e; ++i) {
var ch = children[i];
size += ch.chunkSize(); height += ch.height;
ch.parent = this;
}
this.size = size;
this.height = height;
this.parent = null;
}
BranchChunk.prototype = {
chunkSize: function() { return this.size; },
remove: function(at, n, callbacks) {
this.size -= n;
for (var i = 0; i < this.children.length; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at < sz) {
var rm = Math.min(n, sz - at), oldHeight = child.height;
child.remove(at, rm, callbacks);
this.height -= oldHeight - child.height;
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
if ((n -= rm) == 0) break;
at = 0;
} else at -= sz;
}
if (this.size - n < 25) {
var lines = [];
this.collapse(lines);
this.children = [new LeafChunk(lines)];
this.children[0].parent = this;
}
},
collapse: function(lines) {
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
},
insert: function(at, lines) {
var height = 0;
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
this.insertHeight(at, lines, height);
},
insertHeight: function(at, lines, height) {
this.size += lines.length;
this.height += height;
for (var i = 0, e = this.children.length; i < e; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at <= sz) {
child.insertHeight(at, lines, height);
if (child.lines && child.lines.length > 50) {
while (child.lines.length > 50) {
var spilled = child.lines.splice(child.lines.length - 25, 25);
var newleaf = new LeafChunk(spilled);
child.height -= newleaf.height;
this.children.splice(i + 1, 0, newleaf);
newleaf.parent = this;
}
this.maybeSpill();
}
break;
}
at -= sz;
}
},
maybeSpill: function() {
if (this.children.length <= 10) return;
var me = this;
do {
var spilled = me.children.splice(me.children.length - 5, 5);
var sibling = new BranchChunk(spilled);
if (!me.parent) { // Become the parent node
var copy = new BranchChunk(me.children);
copy.parent = me;
me.children = [copy, sibling];
me = copy;
} else {
me.size -= sibling.size;
me.height -= sibling.height;
var myIndex = indexOf(me.parent.children, me);
me.parent.children.splice(myIndex + 1, 0, sibling);
}
sibling.parent = me.parent;
} while (me.children.length > 10);
me.parent.maybeSpill();
},
iter: function(from, to, op) { this.iterN(from, to - from, op); },
iterN: function(at, n, op) {
for (var i = 0, e = this.children.length; i < e; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at < sz) {
var used = Math.min(n, sz - at);
if (child.iterN(at, used, op)) return true;
if ((n -= used) == 0) break;
at = 0;
} else at -= sz;
}
}
};
// LINE UTILITIES
function getLine(chunk, n) {
while (!chunk.lines) {
for (var i = 0;; ++i) {
var child = chunk.children[i], sz = child.chunkSize();
if (n < sz) { chunk = child; break; }
n -= sz;
}
}
return chunk.lines[n];
}
function updateLineHeight(line, height) {
var diff = height - line.height;
for (var n = line; n; n = n.parent) n.height += diff;
}
function lineNo(line) {
if (line.parent == null) return null;
var cur = line.parent, no = indexOf(cur.lines, line);
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
for (var i = 0;; ++i) {
if (chunk.children[i] == cur) break;
no += chunk.children[i].chunkSize();
}
}
return no;
}
function lineDoc(line) {
for (var d = line.parent; d.parent; d = d.parent) {}
return d;
}
function lineAtHeight(chunk, h) {
var n = 0;
outer: do {
for (var i = 0, e = chunk.children.length; i < e; ++i) {
var child = chunk.children[i], ch = child.height;
if (h < ch) { chunk = child; continue outer; }
h -= ch;
n += child.chunkSize();
}
return n;
} while (!chunk.lines);
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
var line = chunk.lines[i], lh = line.height;
if (h < lh) break;
h -= lh;
}
return n + i;
}
function heightAtLine(cm, lineObj) {
lineObj = visualLine(cm.view.doc, lineObj);
var h = 0, chunk = lineObj.parent;
for (var i = 0; i < chunk.lines.length; ++i) {
var line = chunk.lines[i];
if (line == lineObj) break;
else h += line.height;
}
for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
for (var i = 0; i < p.children.length; ++i) {
var cur = p.children[i];
if (cur == chunk) break;
else h += cur.height;
}
}
return h;
}
function getOrder(line) {
var order = line.order;
if (order == null) order = line.order = bidiOrdering(line.text);
return order;
}
// HISTORY
function makeHistory() {
return {
// Arrays of history events. Doing something adds an event to
// done and clears undo. Undoing moves events from done to
// undone, redoing moves them in the other direction.
done: [], undone: [],
// Used to track when changes can be merged into a single undo
// event
lastTime: 0, lastOp: null, lastOrigin: null,
// Used by the isClean() method
dirtyCounter: 0
};
}
function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
var history = cm.view.history;
history.undone.length = 0;
var time = +new Date, cur = lst(history.done);
if (cur &&
(history.lastOp == cm.curOp.id ||
history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
history.lastTime > time - 600)) {
// Merge this change into the last event
var last = lst(cur.events);
if (last.start > start + old.length || last.start + last.added < start) {
// Doesn't intersect with last sub-event, add new sub-event
cur.events.push({start: start, added: added, old: old});
} else {
// Patch up the last sub-event
var startBefore = Math.max(0, last.start - start),
endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
if (startBefore) last.start = start;
last.added += added - (old.length - startBefore - endAfter);
}
cur.fromAfter = fromAfter; cur.toAfter = toAfter;
} else {
// Can not be merged, start a new event.
cur = {events: [{start: start, added: added, old: old}],
fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
history.done.push(cur);
while (history.done.length > cm.options.undoDepth)
history.done.shift();
if (history.dirtyCounter < 0)
// The user has made a change after undoing past the last clean state.
// We can never get back to a clean state now until markClean() is called.
history.dirtyCounter = NaN;
else
history.dirtyCounter++;
}
history.lastTime = time;
history.lastOp = cm.curOp.id;
history.lastOrigin = origin;
}
// EVENT OPERATORS
function stopMethod() {e_stop(this);}
// Ensure an event has a stop method.
function addStop(event) {
if (!event.stop) event.stop = stopMethod;
return event;
}
function e_preventDefault(e) {
if (e.preventDefault) e.preventDefault();
else e.returnValue = false;
}
function e_stopPropagation(e) {
if (e.stopPropagation) e.stopPropagation();
else e.cancelBubble = true;
}
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
CodeMirror.e_stop = e_stop;
CodeMirror.e_preventDefault = e_preventDefault;
CodeMirror.e_stopPropagation = e_stopPropagation;
function e_target(e) {return e.target || e.srcElement;}
function e_button(e) {
var b = e.which;
if (b == null) {
if (e.button & 1) b = 1;
else if (e.button & 2) b = 3;
else if (e.button & 4) b = 2;
}
if (mac && e.ctrlKey && b == 1) b = 3;
return b;
}
// Allow 3rd-party code to override event properties by adding an override
// object to an event object.
function e_prop(e, prop) {
var overridden = e.override && e.override.hasOwnProperty(prop);
return overridden ? e.override[prop] : e[prop];
}
// EVENT HANDLING
function on(emitter, type, f) {
if (emitter.addEventListener)
emitter.addEventListener(type, f, false);
else if (emitter.attachEvent)
emitter.attachEvent("on" + type, f);
else {
var map = emitter._handlers || (emitter._handlers = {});
var arr = map[type] || (map[type] = []);
arr.push(f);
}
}
function off(emitter, type, f) {
if (emitter.removeEventListener)
emitter.removeEventListener(type, f, false);
else if (emitter.detachEvent)
emitter.detachEvent("on" + type, f);
else {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
for (var i = 0; i < arr.length; ++i)
if (arr[i] == f) { arr.splice(i, 1); break; }
}
}
function signal(emitter, type /*, values...*/) {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
var args = Array.prototype.slice.call(arguments, 2);
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
}
function signalLater(cm, emitter, type /*, values...*/) {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
function bnd(f) {return function(){f.apply(null, args);};};
for (var i = 0; i < arr.length; ++i)
if (flist) flist.push(bnd(arr[i]));
else arr[i].apply(null, args);
}
function hasHandler(emitter, type) {
var arr = emitter._handlers && emitter._handlers[type];
return arr && arr.length > 0;
}
CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
// MISC UTILITIES
// Number of pixels added to scroller and sizer to hide scrollbar
var scrollerCutOff = 30;
// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
function Delayed() {this.id = null;}
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
function countColumn(string, end, tabSize) {
if (end == null) {
end = string.search(/[^\s\u00a0]/);
if (end == -1) end = string.length;
}
for (var i = 0, n = 0; i < end; ++i) {
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
else ++n;
}
return n;
}
CodeMirror.countColumn = countColumn;
var spaceStrs = [""];
function spaceStr(n) {
while (spaceStrs.length <= n)
spaceStrs.push(lst(spaceStrs) + " ");
return spaceStrs[n];
}
function lst(arr) { return arr[arr.length-1]; }
function selectInput(node) {
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
node.selectionStart = 0;
node.selectionEnd = node.value.length;
} else node.select();
}
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
function emptyArray(size) {
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
return a;
}
function bind(f) {
var args = Array.prototype.slice.call(arguments, 1);
return function(){return f.apply(null, args);};
}
var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
function isWordChar(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
}
function isEmpty(obj) {
var c = 0;
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
return !c;
}
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
// DOM UTILITIES
function elt(tag, content, className, style) {
var e = document.createElement(tag);
if (className) e.className = className;
if (style) e.style.cssText = style;
if (typeof content == "string") setTextContent(e, content);
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
return e;
}
function removeChildren(e) {
// IE will break all parent-child relations in subnodes when setting innerHTML
if (!ie) e.innerHTML = "";
else while (e.firstChild) e.removeChild(e.firstChild);
return e;
}
function removeChildrenAndAdd(parent, e) {
return removeChildren(parent).appendChild(e);
}
function setTextContent(e, str) {
if (ie_lt9) {
e.innerHTML = "";
e.appendChild(document.createTextNode(str));
} else e.textContent = str;
}
// FEATURE DETECTION
// Detect drag-and-drop
var dragAndDrop = function() {
// There is *some* kind of drag-and-drop support in IE6-8, but I
// couldn't get it to work yet.
if (ie_lt9) return false;
var div = elt('div');
return "draggable" in div || "dragDrop" in div;
}();
// For a reason I have yet to figure out, some browsers disallow
// word wrapping between certain characters *only* if a new inline
// element is started between them. This makes it hard to reliably
// measure the position of things, since that requires inserting an
// extra span. This terribly fragile set of regexps matches the
// character combinations that suffer from this phenomenon on the
// various browsers.
var spanAffectsWrapping = /^$/; // Won't match any two-character string
if (gecko) spanAffectsWrapping = /$'/;
else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
var knownScrollbarWidth;
function scrollbarWidth(measure) {
if (knownScrollbarWidth != null) return knownScrollbarWidth;
var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
removeChildrenAndAdd(measure, test);
if (test.offsetWidth)
knownScrollbarWidth = test.offsetHeight - test.clientHeight;
return knownScrollbarWidth || 0;
}
var zwspSupported;
function zeroWidthElement(measure) {
if (zwspSupported == null) {
var test = elt("span", "\u200b");
removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
if (measure.firstChild.offsetHeight != 0)
zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
}
if (zwspSupported) return elt("span", "\u200b");
else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
}
// See if "".split is the broken IE version, if so, provide an
// alternative way to split lines.
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
var pos = 0, result = [], l = string.length;
while (pos <= l) {
var nl = string.indexOf("\n", pos);
if (nl == -1) nl = string.length;
var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
var rt = line.indexOf("\r");
if (rt != -1) {
result.push(line.slice(0, rt));
pos += rt + 1;
} else {
result.push(line);
pos = nl + 1;
}
}
return result;
} : function(string){return string.split(/\r\n?|\n/);};
CodeMirror.splitLines = splitLines;
var hasSelection = window.getSelection ? function(te) {
try { return te.selectionStart != te.selectionEnd; }
catch(e) { return false; }
} : function(te) {
try {var range = te.ownerDocument.selection.createRange();}
catch(e) {}
if (!range || range.parentElement() != te) return false;
return range.compareEndPoints("StartToEnd", range) != 0;
};
var hasCopyEvent = (function() {
var e = elt("div");
if ("oncopy" in e) return true;
e.setAttribute("oncopy", "return;");
return typeof e.oncopy == 'function';
})();
// KEY NAMING
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
CodeMirror.keyNames = keyNames;
(function() {
// Number keys
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
// Alphabetic keys
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
// Function keys
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
})();
// BIDI HELPERS
function iterateBidiSections(order, from, to, f) {
if (!order) return f(from, to, "ltr");
for (var i = 0; i < order.length; ++i) {
var part = order[i];
if (part.from < to && part.to > from || from == to && part.to == from)
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
}
}
function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
function lineRight(line) {
var order = getOrder(line);
if (!order) return line.text.length;
return bidiRight(lst(order));
}
function lineStart(cm, lineN) {
var line = getLine(cm.view.doc, lineN);
var visual = visualLine(cm.view.doc, line);
if (visual != line) lineN = lineNo(visual);
var order = getOrder(visual);
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
return {line: lineN, ch: ch};
}
function lineEnd(cm, lineNo) {
var merged, line;
while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
lineNo = merged.find().to.line;
var order = getOrder(line);
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
return {line: lineNo, ch: ch};
}
// This is somewhat involved. It is needed in order to move
// 'visually' through bi-directional text -- i.e., pressing left
// should make the cursor go left, even when in RTL text. The
// tricky part is the 'jumps', where RTL and LTR text touch each
// other. This often requires the cursor offset to move more than
// one unit, in order to visually move one unit.
function moveVisually(line, start, dir, byUnit) {
var bidi = getOrder(line);
if (!bidi) return moveLogically(line, start, dir, byUnit);
var moveOneUnit = byUnit ? function(pos, dir) {
do pos += dir;
while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
return pos;
} : function(pos, dir) { return pos + dir; };
var linedir = bidi[0].level;
for (var i = 0; i < bidi.length; ++i) {
var part = bidi[i], sticky = part.level % 2 == linedir;
if ((part.from < start && part.to > start) ||
(sticky && (part.from == start || part.to == start))) break;
}
var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
while (target != null) {
if (part.level % 2 == linedir) {
if (target < part.from || target > part.to) {
part = bidi[i += dir];
target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
} else break;
} else {
if (target == bidiLeft(part)) {
part = bidi[--i];
target = part && bidiRight(part);
} else if (target == bidiRight(part)) {
part = bidi[++i];
target = part && bidiLeft(part);
} else break;
}
}
return target < 0 || target > line.text.length ? null : target;
}
function moveLogically(line, start, dir, byUnit) {
var target = start + dir;
if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
return target < 0 || target > line.text.length ? null : target;
}
// Bidirectional ordering algorithm
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
// that this (partially) implements.
// One-char codes used for character types:
// L (L): Left-to-Right
// R (R): Right-to-Left
// r (AL): Right-to-Left Arabic
// 1 (EN): European Number
// + (ES): European Number Separator
// % (ET): European Number Terminator
// n (AN): Arabic Number
// , (CS): Common Number Separator
// m (NSM): Non-Spacing Mark
// b (BN): Boundary Neutral
// s (B): Paragraph Separator
// t (S): Segment Separator
// w (WS): Whitespace
// N (ON): Other Neutrals
// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
var bidiOrdering = (function() {
// Character types for codepoints 0 to 0xff
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
// Character types for codepoints 0x600 to 0x6ff
var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
function charType(code) {
if (code <= 0xff) return lowTypes.charAt(code);
else if (0x590 <= code && code <= 0x5f4) return "R";
else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
else if (0x700 <= code && code <= 0x8ac) return "r";
else return "L";
}
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
// Browsers seem to always treat the boundaries of block elements as being L.
var outerType = "L";
return function charOrdering(str) {
if (!bidiRE.test(str)) return false;
var len = str.length, types = [];
for (var i = 0, type; i < len; ++i)
types.push(type = charType(str.charCodeAt(i)));
// W1. Examine each non-spacing mark (NSM) in the level run, and
// change the type of the NSM to the type of the previous
// character. If the NSM is at the start of the level run, it will
// get the type of sor.
for (var i = 0, prev = outerType; i < len; ++i) {
var type = types[i];
if (type == "m") types[i] = prev;
else prev = type;
}
// W2. Search backwards from each instance of a European number
// until the first strong type (R, L, AL, or sor) is found. If an
// AL is found, change the type of the European number to Arabic
// number.
// W3. Change all ALs to R.
for (var i = 0, cur = outerType; i < len; ++i) {
var type = types[i];
if (type == "1" && cur == "r") types[i] = "n";
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
}
// W4. A single European separator between two European numbers
// changes to a European number. A single common separator between
// two numbers of the same type changes to that type.
for (var i = 1, prev = types[0]; i < len - 1; ++i) {
var type = types[i];
if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
else if (type == "," && prev == types[i+1] &&
(prev == "1" || prev == "n")) types[i] = prev;
prev = type;
}
// W5. A sequence of European terminators adjacent to European
// numbers changes to all European numbers.
// W6. Otherwise, separators and terminators change to Other
// Neutral.
for (var i = 0; i < len; ++i) {
var type = types[i];
if (type == ",") types[i] = "N";
else if (type == "%") {
for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
for (var j = i; j < end; ++j) types[j] = replace;
i = end - 1;
}
}
// W7. Search backwards from each instance of a European number
// until the first strong type (R, L, or sor) is found. If an L is
// found, then change the type of the European number to L.
for (var i = 0, cur = outerType; i < len; ++i) {
var type = types[i];
if (cur == "L" && type == "1") types[i] = "L";
else if (isStrong.test(type)) cur = type;
}
// N1. A sequence of neutrals takes the direction of the
// surrounding strong text if the text on both sides has the same
// direction. European and Arabic numbers act as if they were R in
// terms of their influence on neutrals. Start-of-level-run (sor)
// and end-of-level-run (eor) are used at level run boundaries.
// N2. Any remaining neutrals take the embedding direction.
for (var i = 0; i < len; ++i) {
if (isNeutral.test(types[i])) {
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
var before = (i ? types[i-1] : outerType) == "L";
var after = (end < len - 1 ? types[end] : outerType) == "L";
var replace = before || after ? "L" : "R";
for (var j = i; j < end; ++j) types[j] = replace;
i = end - 1;
}
}
// Here we depart from the documented algorithm, in order to avoid
// building up an actual levels array. Since there are only three
// levels (0, 1, 2) in an implementation that doesn't take
// explicit embedding into account, we can build up the order on
// the fly, without following the level-based algorithm.
var order = [], m;
for (var i = 0; i < len;) {
if (countsAsLeft.test(types[i])) {
var start = i;
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
order.push({from: start, to: i, level: 0});
} else {
var pos = i, at = order.length;
for (++i; i < len && types[i] != "L"; ++i) {}
for (var j = pos; j < i;) {
if (countsAsNum.test(types[j])) {
if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
var nstart = j;
for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
order.splice(at, 0, {from: nstart, to: j, level: 2});
pos = j;
} else ++j;
}
if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
}
}
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
order[0].from = m[0].length;
order.unshift({from: 0, to: m[0].length, level: 0});
}
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
lst(order).to -= m[0].length;
order.push({from: len - m[0].length, to: len, level: 0});
}
if (order[0].level != lst(order).level)
order.push({from: len, to: len, level: order[0].level});
return order;
};
})();
// THE END
CodeMirror.version = "3.02";
return CodeMirror;
})();
CodeMirror.defineMode("css", function(config) {
var indentUnit = config.indentUnit, type;
var atMediaTypes = keySet([
"all", "aural", "braille", "handheld", "print", "projection", "screen",
"tty", "tv", "embossed"
]);
var atMediaFeatures = keySet([
"width", "min-width", "max-width", "height", "min-height", "max-height",
"device-width", "min-device-width", "max-device-width", "device-height",
"min-device-height", "max-device-height", "aspect-ratio",
"min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
"min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
"max-color", "color-index", "min-color-index", "max-color-index",
"monochrome", "min-monochrome", "max-monochrome", "resolution",
"min-resolution", "max-resolution", "scan", "grid"
]);
var propertyKeywords = keySet([
"align-content", "align-items", "align-self", "alignment-adjust",
"alignment-baseline", "anchor-point", "animation", "animation-delay",
"animation-direction", "animation-duration", "animation-iteration-count",
"animation-name", "animation-play-state", "animation-timing-function",
"appearance", "azimuth", "backface-visibility", "background",
"background-attachment", "background-clip", "background-color",
"background-image", "background-origin", "background-position",
"background-repeat", "background-size", "baseline-shift", "binding",
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
"bookmark-target", "border", "border-bottom", "border-bottom-color",
"border-bottom-left-radius", "border-bottom-right-radius",
"border-bottom-style", "border-bottom-width", "border-collapse",
"border-color", "border-image", "border-image-outset",
"border-image-repeat", "border-image-slice", "border-image-source",
"border-image-width", "border-left", "border-left-color",
"border-left-style", "border-left-width", "border-radius", "border-right",
"border-right-color", "border-right-style", "border-right-width",
"border-spacing", "border-style", "border-top", "border-top-color",
"border-top-left-radius", "border-top-right-radius", "border-top-style",
"border-top-width", "border-width", "bottom", "box-decoration-break",
"box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
"caption-side", "clear", "clip", "color", "color-profile", "column-count",
"column-fill", "column-gap", "column-rule", "column-rule-color",
"column-rule-style", "column-rule-width", "column-span", "column-width",
"columns", "content", "counter-increment", "counter-reset", "crop", "cue",
"cue-after", "cue-before", "cursor", "direction", "display",
"dominant-baseline", "drop-initial-after-adjust",
"drop-initial-after-align", "drop-initial-before-adjust",
"drop-initial-before-align", "drop-initial-size", "drop-initial-value",
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
"float", "float-offset", "font", "font-feature-settings", "font-family",
"font-kerning", "font-language-override", "font-size", "font-size-adjust",
"font-stretch", "font-style", "font-synthesis", "font-variant",
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
"font-weight", "grid-cell", "grid-column", "grid-column-align",
"grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
"grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
"grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
"icon", "image-orientation", "image-rendering", "image-resolution",
"inline-box-align", "justify-content", "left", "letter-spacing",
"line-break", "line-height", "line-stacking", "line-stacking-ruby",
"line-stacking-shift", "line-stacking-strategy", "list-style",
"list-style-image", "list-style-position", "list-style-type", "margin",
"margin-bottom", "margin-left", "margin-right", "margin-top",
"marker-offset", "marks", "marquee-direction", "marquee-loop",
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
"nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
"outline-color", "outline-offset", "outline-style", "outline-width",
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
"page", "page-break-after", "page-break-before", "page-break-inside",
"page-policy", "pause", "pause-after", "pause-before", "perspective",
"perspective-origin", "pitch", "pitch-range", "play-during", "position",
"presentation-level", "punctuation-trim", "quotes", "rendering-intent",
"resize", "rest", "rest-after", "rest-before", "richness", "right",
"rotation", "rotation-point", "ruby-align", "ruby-overhang",
"ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
"tab-size", "table-layout", "target", "target-name", "target-new",
"target-position", "text-align", "text-align-last", "text-decoration",
"text-decoration-color", "text-decoration-line", "text-decoration-skip",
"text-decoration-style", "text-emphasis", "text-emphasis-color",
"text-emphasis-position", "text-emphasis-style", "text-height",
"text-indent", "text-justify", "text-outline", "text-shadow",
"text-space-collapse", "text-transform", "text-underline-position",
"text-wrap", "top", "transform", "transform-origin", "transform-style",
"transition", "transition-delay", "transition-duration",
"transition-property", "transition-timing-function", "unicode-bidi",
"vertical-align", "visibility", "voice-balance", "voice-duration",
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
"word-spacing", "word-wrap", "z-index"
]);
var colorKeywords = keySet([
"black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
"green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
]);
var valueKeywords = keySet([
"above", "absolute", "activeborder", "activecaption", "afar",
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
"backwards", "baseline", "below", "bidi-override", "binary", "bengali",
"blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
"both", "bottom", "break-all", "break-word", "button", "button-bevel",
"buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
"cell", "center", "checkbox", "circle", "cjk-earthly-branch",
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
"col-resize", "collapse", "compact", "condensed", "contain", "content",
"content-box", "context-menu", "continuous", "copy", "cover", "crop",
"cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
"decimal-leading-zero", "default", "default-button", "destination-atop",
"destination-in", "destination-out", "destination-over", "devanagari",
"disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
"double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
"element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
"ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
"ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
"ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
"ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
"inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
"italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
"landscape", "lao", "large", "larger", "left", "level", "lighter",
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
"lower-roman", "lowercase", "ltr", "malayalam", "match",
"media-controls-background", "media-current-time-display",
"media-fullscreen-button", "media-mute-button", "media-play-button",
"media-return-to-realtime-button", "media-rewind-button",
"media-seek-back-button", "media-seek-forward-button", "media-slider",
"media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
"menu", "menulist", "menulist-button", "menulist-text",
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
"mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
"narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
"ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
"outside", "overlay", "overline", "padding", "padding-box", "painted",
"paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
"pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
"radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
"repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
"ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
"s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
"searchfield-cancel-button", "searchfield-decoration",
"searchfield-results-button", "searchfield-results-decoration",
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
"single", "skip-white-space", "slide", "slider-horizontal",
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
"small", "small-caps", "small-caption", "smaller", "solid", "somali",
"source-atop", "source-in", "source-out", "source-over", "space", "square",
"square-button", "start", "static", "status-bar", "stretch", "stroke",
"sub", "subpixel-antialiased", "super", "sw-resize", "table",
"table-caption", "table-cell", "table-column", "table-column-group",
"table-footer-group", "table-header-group", "table-row", "table-row-group",
"telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
"transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
"vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
"visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
"xx-large", "xx-small", "yellow"
]);
function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
function ret(style, tp) {type = tp; return style;}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
else if (ch == "/" && stream.eat("*")) {
state.tokenize = tokenCComment;
return tokenCComment(stream, state);
}
else if (ch == "<" && stream.eat("!")) {
state.tokenize = tokenSGMLComment;
return tokenSGMLComment(stream, state);
}
else if (ch == "=") ret(null, "compare");
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
else if (ch == "#") {
stream.eatWhile(/[\w\\\-]/);
return ret("atom", "hash");
}
else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
}
else if (ch === "-") {
if (/\d/.test(stream.peek())) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
} else if (stream.match(/^[^-]+-/)) {
return ret("meta", "meta");
}
}
else if (/[,+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
return ret("qualifier", "qualifier");
}
else if (ch == ":") {
return ret("operator", ch);
}
else if (/[;{}\[\]\(\)]/.test(ch)) {
return ret(null, ch);
}
else if (ch == "u" && stream.match("rl(")) {
stream.backUp(1);
state.tokenize = tokenParenthesized;
return ret("property", "variable");
}
else {
stream.eatWhile(/[\w\\\-]/);
return ret("property", "variable");
}
}
function tokenCComment(stream, state) {
var maybeEnd = false, ch;
while ((ch = stream.next()) != null) {
if (maybeEnd && ch == "/") {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
function tokenSGMLComment(stream, state) {
var dashes = 0, ch;
while ((ch = stream.next()) != null) {
if (dashes >= 2 && ch == ">") {
state.tokenize = tokenBase;
break;
}
dashes = (ch == "-") ? dashes + 1 : 0;
}
return ret("comment", "comment");
}
function tokenString(quote, nonInclusive) {
return function(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped) {
if (nonInclusive) stream.backUp(1);
state.tokenize = tokenBase;
}
return ret("string", "string");
};
}
function tokenParenthesized(stream, state) {
stream.next(); // Must be '('
if (!stream.match(/\s*[\"\']/, false))
state.tokenize = tokenString(")", true);
else
state.tokenize = tokenBase;
return ret(null, "(");
}
return {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
stack: []};
},
token: function(stream, state) {
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
//
// rule** or **ruleset:
// A selector + braces combo, or an at-rule.
//
// declaration block:
// A sequence of declarations.
//
// declaration:
// A property + colon + value combo.
//
// property value:
// The entire value of a property.
//
// component value:
// A single piece of a property value. Like the 5px in
// text-shadow: 0 0 5px blue;. Can also refer to things that are
// multiple terms, like the 1-4 terms that make up the background-size
// portion of the background shorthand.
//
// term:
// The basic unit of author-facing CSS, like a single number (5),
// dimension (5px), string ("foo"), or function. Officially defined
// by the CSS 2.1 grammar (look for the 'term' production)
//
//
// simple selector:
// A single atomic selector, like a type selector, an attr selector, a
// class selector, etc.
//
// compound selector:
// One or more simple selectors without a combinator. div.example is
// compound, div > .example is not.
//
// complex selector:
// One or more compound selectors chained with combinators.
//
// combinator:
// The parts of selectors that express relationships. There are four
// currently - the space (descendant combinator), the greater-than
// bracket (child combinator), the plus sign (next sibling combinator),
// and the tilda (following sibling combinator).
//
// sequence of selectors:
// One or more of the named type of selector chained with commas.
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
// Changing style returned based on context
var context = state.stack[state.stack.length-1];
if (style == "property") {
if (context == "propertyValue"){
if (valueKeywords[stream.current()]) {
style = "string-2";
} else if (colorKeywords[stream.current()]) {
style = "keyword";
} else {
style = "variable-2";
}
} else if (context == "rule") {
if (!propertyKeywords[stream.current()]) {
style += " error";
}
} else if (!context || context == "@media{") {
style = "tag";
} else if (context == "@media") {
if (atMediaTypes[stream.current()]) {
style = "attribute"; // Known attribute
} else if (/^(only|not)$/i.test(stream.current())) {
style = "keyword";
} else if (stream.current().toLowerCase() == "and") {
style = "error"; // "and" is only allowed in @mediaType
} else if (atMediaFeatures[stream.current()]) {
style = "error"; // Known property, should be in @mediaType(
} else {
// Unknown, expecting keyword or attribute, assuming attribute
style = "attribute error";
}
} else if (context == "@mediaType") {
if (atMediaTypes[stream.current()]) {
style = "attribute";
} else if (stream.current().toLowerCase() == "and") {
style = "operator";
} else if (/^(only|not)$/i.test(stream.current())) {
style = "error"; // Only allowed in @media
} else if (atMediaFeatures[stream.current()]) {
style = "error"; // Known property, should be in parentheses
} else {
// Unknown attribute or property, but expecting property (preceded
// by "and"). Should be in parentheses
style = "error";
}
} else if (context == "@mediaType(") {
if (propertyKeywords[stream.current()]) {
// do nothing, remains "property"
} else if (atMediaTypes[stream.current()]) {
style = "error"; // Known property, should be in parentheses
} else if (stream.current().toLowerCase() == "and") {
style = "operator";
} else if (/^(only|not)$/i.test(stream.current())) {
style = "error"; // Only allowed in @media
} else {
style += " error";
}
} else {
style = "error";
}
} else if (style == "atom") {
if(!context || context == "@media{") {
style = "builtin";
} else if (context == "propertyValue") {
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
style += " error";
}
} else {
style = "error";
}
} else if (context == "@media" && type == "{") {
style = "error";
}
// Push/pop context stack
if (type == "{") {
if (context == "@media" || context == "@mediaType") {
state.stack.pop();
state.stack[state.stack.length-1] = "@media{";
}
else state.stack.push("rule");
}
else if (type == "}") {
state.stack.pop();
if (context == "propertyValue") state.stack.pop();
}
else if (type == "@media") state.stack.push("@media");
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
state.stack.push("@mediaType");
else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
else if (context == "@mediaType(" && type == ")") state.stack.pop();
else if (context == "rule" && type == ":") state.stack.push("propertyValue");
else if (context == "propertyValue" && type == ";") state.stack.pop();
return style;
},
indent: function(state, textAfter) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
return state.baseIndent + n * indentUnit;
},
electricChars: "}"
};
});
CodeMirror.defineMIME("text/css", "css");
function blockAdminCSS() {
this.loadCreationMode = function () {
//ui update
$("#panelcss").on("keyup.creation change.creation",".liveconfig", function(event){
var nbstyle = document.getElementById("current_stylesheet_nb").value;
var nbrule = document.getElementById("current_stylesheet_nb_rule").value;
var stylesh = ParsimonyAdmin.currentDocument.styleSheets[nbstyle].cssRules[nbrule];
if(typeof stylesh != "undefined") var rules = stylesh.style.cssText + this.getAttribute("name") + ": " + this.value + ";";
else rules = this.getAttribute("name") + ": " + this.value + ";";
blockAdminCSS.setCss(nbstyle, nbrule, document.getElementById("current_selector_update").value + "{" + rules + "}");
ParsimonyAdmin.$currentDocument.find(".parsimonyDND").parsimonyDND("updatePosition");
})
.on('click.creation',".explorer",function(event){
window.callbackExplorerID = $(this).attr('rel');
window.callbackExplorer = function (file){
$("#" + window.callbackExplorerID).val("url( " + file + ")");
$("#" + window.callbackExplorerID).trigger('keyup');
}
ParsimonyAdmin.displayConfBox(BASE_PATH + "admin/explorer","Explorer");
});
$("#right_sidebar").on('click.creation',".cssPickerBTN", function(e){
e.preventDefault();
e.stopPropagation();
$("#threed").show();
function destroyCSSpicker(){
$('#container',ParsimonyAdmin.currentBody).off(".csspicker");
$("#rotatex,#rotatey").val(0);
$("#rotatez").val(300);
$(".cssPickerBTN").removeClass("active");
}
if($(this).hasClass("active")){
destroyCSSpicker();
return false;
}
ParsimonyAdmin.closeParsiadminMenu();
$('#container',ParsimonyAdmin.currentBody).on('mouseover.csspicker',"*", function(event) {
event.stopPropagation();
$(".cssPicker",ParsimonyAdmin.currentBody).removeClass("cssPicker");
this.classList.add("cssPicker");
});
$(".cssPickerBTN").addClass("active");
$('#container',ParsimonyAdmin.currentBody).on('click.csspicker',"*",function(e){
e.preventDefault();
e.stopPropagation();
$("#threed").hide();
ParsimonyAdmin.$currentBody.css('-webkit-transform','initial').removeClass("threed");
$(".cssPicker").removeClass("cssPicker");
$(this).addClass("cssPicker");
blockAdminCSS.getCSSForCSSpicker();
var title = CSSTHEMEPATH;
if(this.id.length > 0 && $(".selectorcss[selector='#" + this.id + "']", $("#changecsscode")).length == 0) blockAdminCSS.addNewSelectorCSS( title, "#" + this.id)
var forbidClasses = ",selection-block,block,container,selection-container,";
$.each(this.classList, function(index, value) {
if($(".selectorcss[selector='." + value + "']").length == 0 && forbidClasses.indexOf("," + value+ ",") == "-1" && value != "parsieditinline"){ blockAdminCSS.addNewSelectorCSS( title, "." + value);}
});
var good = false;
var selectProp = "";
if(this.id == ""){
if(this.getAttribute('class') != undefined && this.getAttribute('class') != "") selectProp = ("." + this.getAttribute("class").replace(" ","."));
$(this).parentsUntil("body").each(function(){
if(!good){
var selectid = "";
var selectclass = "";
if(this.getAttribute('id') != undefined) selectid = "#" + this.getAttribute('id');
else{
if(this.getAttribute('class') != undefined && this.getAttribute('class') != "") selectclass = "." + this.getAttribute("class").replace(" ",".");
}
selectProp = selectid + selectclass + " " + selectProp;
if(selectid != "") good = true;
}
});
selectProp = selectProp.replace(".cssPicker","").replace(".clearboth","").replace(".parsieditinline","").replace(/\s\s+/g," ");
if($(".selectorcss[selector='" + selectProp + "']", $("#changecsscode")).length == 0) blockAdminCSS.addNewSelectorCSS( title, selectProp);
}
destroyCSSpicker();
return false;
});
});
blockAdminCSS.iframeStyleSheet = ParsimonyAdmin.currentDocument.styleSheets[ParsimonyAdmin.currentDocument.styleSheets.length-1];
/* Shortcut : Save on CTRL+S */
$(document).on("keydown.creation", function(e) {
if (e.keyCode == 83 && e.ctrlKey) {
e.preventDefault();
$("#savemycss").trigger("click");
}
});
}
this.unloadCreationMode = function(){
$("#panelcss").off('.creation');
$('.parsimonyDND',ParsimonyAdmin.currentDocument).parsimonyDND('destroy');
$("#colorjack_square").hide();
}
}
blockAdminCSS.csseditors = [];
blockAdminCSS.updateCSSUI = function (cssprop) {
$("#current_selector_update,#current_selector_update_prev").val(cssprop.selector);
$("#changecsspath").val(cssprop.filePath);
$("#changecssform input,select").val('');
$.each(cssprop.values, function(i,item){
$("#panelcss [css=" + i + "]").val(item);
});
}
blockAdminCSS.setCss = function (nbstyle, nbrule, rule) {
if(nbrule == null){
nbRule = ParsimonyAdmin.currentDocument.styleSheets[nbstyle].cssRules.length;
if(nbRule > 0) nbrule = nbRule - 1;
}
if(typeof ParsimonyAdmin.currentDocument.styleSheets[nbstyle].cssRules[nbrule] != "undefined") ParsimonyAdmin.currentDocument.styleSheets[nbstyle].deleteRule(nbrule);
ParsimonyAdmin.currentDocument.styleSheets[nbstyle].insertRule(rule,nbrule);
}
blockAdminCSS.displayCSSConf = function (filePath,selector) {
blockAdminCSS.openCSSForm();
ParsimonyAdmin.postData(BASE_PATH + "admin/getCSSSelectorRules" ,{
TOKEN:TOKEN,
selector: selector,
filePath: filePath
} ,function(data){
$("#css_panel input[type!=\"hidden\"]").val("");
blockAdminCSS.updateCSSUI($.parseJSON(data));
});
var selectorPrev = document.getElementById("current_selector_update_prev").value;
if(selectorPrev.length > 0){
var nbstyle = document.getElementById("current_stylesheet_nb").value;
var nbrule = document.getElementById("current_stylesheet_nb_rule").value;
blockAdminCSS.setCss(nbstyle, nbrule, selectorPrev + "{" + (document.getElementById("current_stylesheet_rules").value || " ") + "}");
}
document.getElementById("typeofinput").value = "form";
document.getElementById("current_stylesheet_rules").value = "";
document.getElementById("current_stylesheet_nb_rule").value = "";
var styleSheets = ParsimonyAdmin.currentDocument.styleSheets;
for (var i = 0; i < styleSheets.length; i++){
if(styleSheets[i].href != null && !!styleSheets[i].href && styleSheets[i].href.match(new RegExp(filePath))){
document.getElementById("current_stylesheet_nb").value = i;
$.each(styleSheets[i].cssRules, function(nbrule) {
if(this.selectorText == selector){
document.getElementById("current_stylesheet_nb_rule").value = nbrule;
document.getElementById("current_stylesheet_rules").value = styleSheets[i].cssRules[nbrule].style.cssText;
}
});
if(document.getElementById("current_stylesheet_nb_rule").value.length == 0){
var nbRule = ParsimonyAdmin.currentDocument.styleSheets[i].cssRules.length;
if(nbRule > 0) nbrule = ParsimonyAdmin.currentDocument.styleSheets[i].cssRules.length - 1;
document.getElementById("current_stylesheet_nb_rule").value = nbRule;
}
}
}
if($(selector,ParsimonyAdmin.currentBody).length == 1){
$(selector,ParsimonyAdmin.currentBody).parsimonyDND('destroy').parsimonyDND({
stopResizable : function(event, ui) {
$("#form_css input[css=width]").val((ui.width() != 'auto' ? ui.width() : '') + "px");
$("#form_css input[css=height]").val((ui.height() != 'auto' ? ui.height() : '') + "px");
$("#form_css input[css=left]").val((ui.css('left') != 'auto' ? ui.css('left') : ''));
$("#form_css input[css=top]").val((ui.css('top') != 'auto' ? ui.css('top') : ''));
},
stopDraggable: function(event, ui) {
$("#form_css input[css=left]").val((ui.css('left') != 'auto' ? ui.css('left') : ''));
$("#form_css input[css=top]").val((ui.css('top') != 'auto' ? ui.css('top') : ''));
}
});
}
}
blockAdminCSS.CSSeditor = function (id) {
editor = CodeMirror(function(node) {
document.getElementById(id).parentNode.insertBefore(node, document.getElementById(id).nextSibling);
}, {
gutters: ["guttercss"],
mode:"text/css",
value: document.getElementById(id).value,
lineNumbers: false,
autoClearEmptyLines:true
});
editor.id = id;
editor.on("change", function(c, change) {
var textarea = document.getElementById(c.id);
var nbstyle = textarea.getAttribute('data-nbstyle');
var nbrule = textarea.getAttribute('data-nbrule');
var selector = decodeURIComponent(textarea.getAttribute('data-selector'));
var code = selector + '{';
var cptL = c.lineCount();
for(var i = 0;i < cptL; i++){
if(c.lineInfo(i).textClass != "barre") code += c.getLine(i);
}
blockAdminCSS.setCss(nbstyle, nbrule, code + "}");
textarea.value = c.getValue();
});
editor.on("gutterClick", function(c, n) {
var info = c.lineInfo(n);
if (info.textClass == "barre"){
c.removeLineClass(n, "text", "barre");
c.setGutterMarker(n, "guttercss", null);
}else{
var elmt = document.createElement("span");
elmt.classList.add("activebtn");
elmt.innerHTML = "×";
c.setGutterMarker(n, "guttercss", elmt);
c.addLineClass(n, "text", "barre");
}
c._handlers['change'][0](c);
});
blockAdminCSS.csseditors.push(editor);
}
blockAdminCSS.getCSSForCSSpicker = function () {
var matchesSelector = (document.documentElement.webkitMatchesSelector || document.documentElement.mozMatchesSelector || document.documentElement.oMatchesSelector || document.documentElement.matchesSelector);
var json = '[';
this.openCSSCode();
var elmt = $('.cssPicker',ParsimonyAdmin.currentBody).removeClass('cssPicker').get(0);
var styleSheets = ParsimonyAdmin.currentDocument.styleSheets;
for (var i = 0; i < styleSheets.length; i++){
if(styleSheets[i].cssRules !== null && styleSheets[i].href != null && !!styleSheets[i].href && styleSheets[i].href.indexOf("iframe.css") == "-1" && styleSheets[i].href.indexOf("/" + window.location.host + BASE_PATH + "lib") == "-1"){
for(j=0; j < styleSheets[i].cssRules.length;j++) {
var rule = styleSheets[i].cssRules[j];
if(matchesSelector.call(elmt,rule.selectorText)){
var url = styleSheets[i].href.replace("http://" + window.location.host,"").substring(BASE_PATH.length);
blockAdminCSS.addSelectorCSS(url, rule.selectorText, rule.style.cssText.replace(/;[^a-zA-Z\-]+/gm, ";\n"), i , j);
json += '{"nbstyle":"' + i + '","nbrule":"' + j + '","url":"' + url + '","selector":"' + rule.selectorText + '"},';
}
}
}
}
if(json.length > 1){
json = json.substring(0, json.length - 1) + ']';
$.post(BASE_PATH + "admin/getCSSSelectorsRules", {
json: json
},function(data) {
$.each(data, function(i,item) {
var id = 'idcss' + item.nbstyle + item.nbrule;
document.getElementById(id).value = item.cssText;
});
$.each(blockAdminCSS.csseditors,function(i, el){
el.setValue(document.getElementById(el.id).value);
});
});
}
}
blockAdminCSS.addNewSelectorCSS = function (path, selector) {
var code = '';
var nbstyle = '';
var nbrule = '';
var styleSheets = ParsimonyAdmin.currentDocument.styleSheets;
for (var i = 0; i < styleSheets.length; i++){
if(styleSheets[i].href != null && !!styleSheets[i].href && styleSheets[i].href.match(new RegExp(path))){
nbstyle = i;
$.each(styleSheets[i].cssRules, function(indexRule) {
if(this.selectorText == selector){
nbrule = indexRule;
code = styleSheets[i].cssRules[nbrule].style.cssText;
}
});
if(nbrule.length == 0){
nbrule = ParsimonyAdmin.currentDocument.styleSheets[i].cssRules.length;
}
}
}
this.addSelectorCSS( path, selector, code, nbstyle, nbrule);
this.setCss(nbstyle, nbrule, selector + "{" + code + "}");
}
blockAdminCSS.addSelectorCSS = function (url, selector, styleCSS, nbstyle, nbrule) {
var id = 'idcss' + nbstyle + nbrule;
var code = '<div class="selectorcss" title="' + url + '" selector="' + selector + '"><div class="selectorTitle"><b>' + selector + '</b> <small>in ' + url.replace(/^.*[\/\\]/g, '') + '</small></div><div class="gotoform" onclick="blockAdminCSS.displayCSSConf(\'' + url + '\',\'' + selector + '\')"> '+ t('Visual') +' </div></div>'
+ '<input type="hidden" name="selectors[' + id + '][file]" value="' + url + '"><input type="hidden" name="selectors[' + id + '][selector]" value="' + encodeURIComponent(selector) + '">'
+ '<textarea class="csscode" id="' + id + '" name="selectors[' + id + '][code]" data-nbstyle="' + nbstyle + '" data-nbrule="' + nbrule + '" data-selector="' + encodeURIComponent(selector) + '">' + styleCSS.replace(/;/,";\n").replace("\n\n","\n") + '</textarea>';
$("#changecsscode").prepend(code);
this.CSSeditor(id);
}
blockAdminCSS.openCSSForm = function () {
$("#right_sidebar .active").removeClass("active");
$(".panelcss").addClass("active");
$("#panelcss").removeClass("CSSCode CSSSearch").addClass("CSSForm").show();
document.getElementById("typeofinput").value = "form";
}
blockAdminCSS.openCSSCode = function () {
$("#right_sidebar .active").removeClass("active");
$(".panelcss").addClass("active");
$("#panelcss").removeClass("CSSForm CSSSearch").addClass("CSSCode");
document.getElementById("typeofinput").value = "code";
$("#changecsscode").empty();
$.each(blockAdminCSS.csseditors,function(i, el){
blockAdminCSS.csseditors.splice(i,i+1);
});
}
ParsimonyAdmin.setPlugin(new blockAdminCSS());
/**
* Parsimony
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to contact@parsimony-cms.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Parsimony to newer
* versions in the future. If you wish to customize Parsimony for your
* needs please refer to http://www.parsimony.mobi for more information.
*
* @authors Julien Gras et Benoît Lorillot
* @copyright Julien Gras et Benoît Lorillot
* @version Release: 1.0
* @category cms.js
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
var onorientationchange = function(){
$("body").removeClass("landscape portrait");
$("body").addClass(Math.abs(window.orientation == 90)? "landscape" : "portrait");
}
function loadBlock(id, params, callback){
if(!params) params = {};
if(!callback) window['callback'] = '';
else window['callback'] = callback;
$.get(window.location.href.toLocaleString(), params, function(data) {
$('#' + id).html($("<div>").append(data).find("#" + id).html());
if(typeof window['callback'] == 'function') window['callback'].call();
});
}
var $lang = new Array;
function t(val){
if($lang[val]){
return $lang[val];
}else{
return val;
}
}
$(document).ready(function(){
//autocomplete
$("select.autocomplete").each(function(){
$(this).attr('type','text');
var obj = $("option", this);
var arr = $.makeArray(obj);
$(this).autocomplete({
source: arr
});
});
//datepicker
/*$(function() {
$( ".datepicker" ).datepicker($.datepicker.regional[ "fr" ]);
});*/
// Device orientation
if(typeof window.orientation != "undefined"){
$("body").addClass(Math.abs(window.orientation == 90)? "landscape" : "portrait");
window.onorientationchange = onorientationchange;
}
});
$(document).ready(function(){
$(document).on('click','a', function(e){
if($(this).attr("href").substring(0,1) != '#' && $(this).attr("href").substring(0,7) != 'http://'){
e.preventDefault();
loadPage($(this).attr('href'));
}
});
if((window.history && history.pushState)) history.replaceState({url:document.location.href}, document.title, document.location.href);
});
function loadPage(url, isHistory){
$("#content").removeClass("flip");
$.get(url + "?nostructure=yes", function(data) {
/*$("#content").fadeOut("speed",function(){
$(this).html(data).fadeIn("speed")
});*/
$("#content").html(data).addClass("flip");
if(typeof isHistory == "undefined"){
var hist = new Object() ;
hist.url = url;
if((window.history && history.pushState)) history.pushState(hist, "", url);
}
});
}
window.onpopstate = function( event ){
var data = event.state;
if(data && data.url){
loadPage( data.url, false );
}
}/*
* Tooltip - jQuery Plugin
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to contact@parsimony-cms.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Parsimony to newer
* versions in the future. If you wish to customize Parsimony for your
* needs please refer to http://www.parsimony.mobi for more information.
*
* @authors Julien Gras et Benoît Lorillot
* @copyright Julien Gras et Benoît Lorillot
* @version Release: 1.0
* @category Tooltip - jQuery Plugin
* Requires: jQuery v1.4.2+
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
(function( $ ){
var methods = {
init : function( options ) {
params = $.extend( {
position: '',
triangleWidth: 7
}, options);
var tooltip = $("#parsimonyTooltip");
if(tooltip.length ==0){
$('<div id="parsimonyTooltip"><div class="tri"></div><div class="parsimonyTooltipContent"></div></div>').appendTo("body");
tooltip = $("#parsimonyTooltip");
}
var tooltipTriangle = $(".tri", tooltip);
var tooltipContent = $(".parsimonyTooltipContent", tooltip);
/* On mouse enter on the object */
$(this.context).on("mouseenter.parsimonyTooltip", this.selector, function(e){
var $this = $(this);
/* Get position of tooltip */
var position = $(this).data("pos");
/* Get offset position of tooltip */
var off = $this.offset();
/* get content to display */
if($(this).data("tooltip").substring(0, 1) == "#") var content = $($(this).data("tooltip")).html();
else var content = $(this).data("tooltip");
/* Set data to display in tooltip */
$(".parsimonyTooltipContent",tooltip).html(content);
var left, top = '';
off = $this.offset();
/* If no position set */
if(typeof position == 'undefined'){
if(params.position == ''){
/* If no position set on data attributes */
if(!$(this).data("pos")){
/* If left space lower than right */
var first = 'w';
if( off.left <= $(window).width() - off.left + $this.width()){
first = 'e';
}
/* If top space lower than bottom */
var second = 's';
if( off.top <= $(window).height() - off.top + $this.height()){
second = 'n';
}
/* Set position of tooltip */
$(this).data("pos",first + second);
}
}else{
$(this).data("pos",params.position);
}
position = $(this).data("pos");
}
/* Calculate pos of tooltip */
switch(position.substring(0,1)){
case 'n':
tooltipContent.css("margin","0 0 " + params.triangleWidth + "px 0");
if(position.length==1) {
left = off.left + ($this.outerWidth()/2) - (tooltip.outerWidth()/2);
tooltipTriangle.attr('style','').css("left",(tooltip.outerWidth()/2 - params.triangleWidth) + "px");
}
top = off.top - tooltip.outerHeight();
break;
case 's':
tooltipContent.css("margin","" + params.triangleWidth + "px 0 0 0");
if(position.length==1) {
left = off.left + ($this.outerWidth()/2) - (tooltip.outerWidth()/2);
tooltipTriangle.attr('style','').css("left",(tooltip.outerWidth()/2 - params.triangleWidth) + "px");
}
top = off.top + $this.outerHeight();
break;
case 'e':
tooltipContent.css("margin","0 0 0 " + params.triangleWidth + "px");
left = off.left + $this.outerWidth();
if(position.length==1) {
top = (off.top + $this.outerHeight()/2) - tooltip.outerHeight()/2;
tooltipTriangle.attr('style','').css("top",(tooltip.outerHeight()/2 - params.triangleWidth) + "px");
}
break;
case 'w':
tooltipContent.css("margin","0 " + params.triangleWidth + "px 0 0");
left = off.left - tooltip.outerWidth();
if(position.length==1) {
top = (off.top + $this.outerHeight()/2) - tooltip.outerHeight()/2;
tooltipTriangle.attr('style','').css("top",(tooltip.outerHeight()/2 - params.triangleWidth) + "px");
}
break;
}
if(position.length==2){
switch(position.substring(1)){
case 'e':
left = off.left + $this.outerWidth() - tooltip.outerWidth();
tooltipTriangle.attr('style','').css("right",params.triangleWidth + "px");
break;
case 'w':
left = off.left;
tooltipTriangle.attr('style','').css("left",params.triangleWidth + "px");
break;
case 'n':
top = off.top;
tooltipTriangle.attr('style','').css("top",params.triangleWidth + "px");
break;
case 's':
top = off.top + $this.outerHeight() - tooltip.outerHeight();
tooltipTriangle.attr('style','').css("bottom",params.triangleWidth + "px");
break;
}
}
/* Set class to the triangle of the tooltip */
var classes = 'tri-' + position;
if(position.length==2)classes += ' tri-' + position.substring(0,1);
tooltipTriangle.attr('class','tri ' + classes);
/* Set triangle style */
tooltipTriangle.css("border-width", params.triangleWidth + "px");
/* Display tooltip */
tooltip.stop().css({
left:left,
top:top,
opacity:1,
display:"block"
});
});
/* On mouse leave the object */
$(this.context).on("mouseleave.parsimonyTooltip", this.selector,function(e){
/* We hide the tooltip */
tooltip.fadeOut("speed");
});
},
destroy : function( ) {
return this.each(function(){
$(this).off('.parsimonyTooltip');
})
}
};
$.fn.parsimonyTooltip = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.parsimonyTooltip' );
}
};
})( jQuery );/**
* Parsimony
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to contact@parsimony-cms.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Parsimony to newer
* versions in the future. If you wish to customize Parsimony for your
* needs please refer to http://www.parsimony.mobi for more information.
*
* @authors Julien Gras et Benoît Lorillot
* @copyright Julien Gras et Benoît Lorillot
* @version Release: 1.0
* @category Parsimony
* @package admin
* Requires: jQuery
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
var ParsimonyAdmin = {
isInit : false,
currentWindow : "",
currentDocument : "",
currentBody : "",
currentMode : "",
inProgress : "",
typeProgress : "",
wysiwyg : "",
unsavedChanges : false,
plugins: [],
setPlugin : function(plugin){
this.plugins.push(plugin);
},
pluginDispatch : function(methodName){
/* Call this method for all plugins */
for (var i=0; i < this.plugins.length; i++) {
var plugin = this.plugins[i];
eval("if(typeof plugin." + methodName + " != 'undefined') plugin." + methodName + "();");
}
},
initBefore : function(){
$("#ajaxhack").on("load",function() {
var elmt = $(this).contents().find('body').text();
if(elmt != "") ParsimonyAdmin.execResult(elmt); /* Firefox fix */
});
$("#dialog-id").keyup(function(){
this.value = this.value.toLowerCase().replace(/[^a-z_]+/,"");
});
$("#conf_box").on('click','#conf_box_wpopup', function(e){
var action = $("#conf_box_form input[name=action]").val();
$("#conf_box_form").attr('target','conf_box_content_popup' + action);
ParsimonyAdmin.closeConfBox();
window.open ($("#conf_box_content_iframe").attr('src'),'conf_box_content_popup' + action,"width=" + $("#conf_box_content_iframe").width() + ",height=" + $("#conf_box_content_iframe").height());
$("#conf_box_form").trigger("submit").attr('target','conf_box_content_iframe');
});
this.pluginDispatch("initBefore");
},
initIframe : function(){
ParsimonyAdmin.iframe = document.getElementById("parsiframe");
ParsimonyAdmin.$iframe = $(ParsimonyAdmin.iframe);
ParsimonyAdmin.currentWindow = ParsimonyAdmin.iframe.contentWindow;
ParsimonyAdmin.currentDocument = ParsimonyAdmin.currentWindow.document;
ParsimonyAdmin.$currentDocument = $(ParsimonyAdmin.currentDocument);
ParsimonyAdmin.currentBody = ParsimonyAdmin.currentDocument.body;
ParsimonyAdmin.$currentBody = $(ParsimonyAdmin.currentBody);
ParsimonyAdmin.inProgress = "container";
ParsimonyAdmin.updateUI();
ParsimonyAdmin.changeDeviceUpdate();
/* Add Iframe style */
var iframeStyle = document.createElement("link");
iframeStyle.setAttribute("rel", "stylesheet");
iframeStyle.setAttribute("type", "text/css");
iframeStyle.setAttribute("href", BASE_PATH + "admin/iframe.css");
ParsimonyAdmin.currentBody.insertBefore(iframeStyle, ParsimonyAdmin.currentBody.firstChild);
/* Init mode */
var initialMode = ParsimonyAdmin.getCookie("mode");
if(initialMode == 'edit'){
$("#editMode").trigger('click');
}else if(initialMode == 'preview'){
$("#previewMode").trigger('click');
}else{
$("#creationMode").trigger('click');
}
//override jQuery ready function to exec them with ajax portions
setTimeout('$.fn.ready = function(a) {ParsimonyAdmin.currentWindow.eval(" exec = " + a.toString()+";exec.call(window)");}',4000);
//document.getElementById("parsiframe").contentWindow.$.fn.ready = function(a) {a.call(document.getElementById("parsiframe").contentWindow);}
this.pluginDispatch("initIframe");
}
,
loadCreationMode : function(){
ParsimonyAdmin.unloadCreationMode();
ParsimonyAdmin.$currentBody.on('click.creation','.traduction', function(e){
e.trad = true;
ParsimonyAdmin.closeParsiadminMenu();
ParsimonyAdmin.addTitleParsiadminMenu(t('Translation'));
ParsimonyAdmin.addOptionParsiadminMenu('<span class="ui-icon ui-icon-pencil floatleft"></span><a href="#" class="action" rel="getViewTranslation" params="key=' + $(this).data("key") + '" title="'+ t('Translation') +'">'+ t('Translate') +'</a>');
})
.on('click.creation','a', function(e){
e.link = true;
e.preventDefault();
if(e.trad != true) ParsimonyAdmin.closeParsiadminMenu();
ParsimonyAdmin.addTitleParsiadminMenu("Link");
ParsimonyAdmin.addOptionParsiadminMenu('<a href="#" onclick="ParsimonyAdmin.goToPage(\'' + $.trim($(this).text().replace("'","\\'")) + '\',\'' + $(this).attr('href') + '\');return false;"><span class="ui-icon ui-icon-extlink floatleft"></span>'+ t('Go to the link') +'</a>');
});
$(document).on("keypress.creation",'#dialog-id',function(e){
var code = e.keyCode || e.which;
if(code == 13) {
$("#dialog-ok").trigger("click");
}
});
$(".parsieditinline",ParsimonyAdmin.currentBody).removeClass('usereditinline').attr("contenteditable", "false");
this.pluginDispatch("loadCreationMode");
},
unloadCreationMode : function(){
$(".selection-block",ParsimonyAdmin.currentBody).removeClass("selection-block");
$(".selection-container",ParsimonyAdmin.currentBody).removeClass("selection-container");
ParsimonyAdmin.closeParsiadminMenu();
ParsimonyAdmin.$currentBody.off('.creation');
this.pluginDispatch("unloadCreationMode");
},
loadEditMode : function(){
$(".parsieditinline",ParsimonyAdmin.currentBody).addClass('usereditinline').attr("contenteditable", "true");
/* Active edit behavior on WYSIWYG blocks */
$(".wysiwyg",ParsimonyAdmin.currentBody).addClass('activeEdit').attr("contenteditable", "true")
/* Shortcut : Save on CTRL+S */
.on("keydown.edit", function(e) {
if (e.keyCode == 83 && e.ctrlKey) {
e.preventDefault();
ParsimonyAdmin.haveToSave = true;
}
});
/* Init WYSIWYG editor */
if(typeof ParsimonyAdmin.wysiwyg == "string"){
ParsimonyAdmin.wysiwyg = new wysiwyg();
ParsimonyAdmin.wysiwyg.init(".wysiwyg",["bold","underline","italic","justifyLeft","justifyCenter","justifyRight","justifyFull","strikeThrough","subscript","superscript","orderedList","unOrderedList","outdent","indent","removeFormat","createLink","unlink","formatBlock","fontName","fontSize","foreColor","hiliteColor","insertImage"], document, ParsimonyAdmin.currentDocument);
}
$(".HTML5editorToolbar").hide();
/* Manage clicks on <a> in edit mode */
ParsimonyAdmin.$currentDocument.on('click.edit','a', function(e){
e.preventDefault();
/*if($(this).attr("href").substring(0,1) != '#' && $(this).attr("href").substring(0,7) != 'http://' && $(".usereditinline",this).length == 0){
ParsimonyAdmin.goToPage( $.trim($(this).text().replace("'","\\'")) , $(this).attr('href') );
}*/
})
/* Hide WYSIWYG editor if focused element isn't a WYSIWYG block */
.on('click.edit','.block',function(e){
if(!$(this).hasClass("wysiwyg")) $(".HTML5editorToolbar").hide();
else $(".HTML5editorToolbar").show();
});
/* Manage undo/redo on save toolbar */
$("#toolbarEditMode").on('click.edit',".toolbarEditModeCommands",function(e){
ParsimonyAdmin.currentDocument.execCommand(this.dataset.command, false, null);
})
/* Save all WYSISYG blocks or contenteditable fields */
.on('click.edit',"#toolbarEditModeSave",function(e){
$(this).trigger("focus");
/* We collect fresh data for WYSIWYG blocks */
var changes = {};
$(".wysiwyg.activeEdit",ParsimonyAdmin.currentBody).each(function(){
if(this.dataset.modified) {
var module = ParsimonyAdmin.currentWindow.THEMEMODULE;
var theme = ParsimonyAdmin.currentWindow.THEME;
var idPage = '';
if(ParsimonyAdmin.whereIAm(this.id) == 'page'){
theme = '';
module = ParsimonyAdmin.currentWindow.MODULE;
idPage = $(".container_page",ParsimonyAdmin.currentBody).data('page');
}
changes[this.id] = {idPage:idPage,theme:theme,module:module,html:this.innerHTML};
}
});
/* We collect fresh data for contenteditable fields */
$(".usereditinline",ParsimonyAdmin.currentBody).each(function(){
if(this.dataset.modified) {
changes[this.dataset.property+this.dataset.id] = {module:this.dataset.module,entity:this.dataset.entity,fieldName:this.dataset.property,id:this.dataset.id,html:this.innerHTML};
}
});
/* We send fresh data to the serve to save it */
$.post(BASE_PATH + 'admin/saveWYSIWYGS',{changes:JSON.stringify(changes)},function(data){
ParsimonyAdmin.unsavedChanges = false
$("#toolbarEditMode").slideUp();
$(".wysiwyg.activeEdit, .usereditinline").attr("data-modified","0");
ParsimonyAdmin.execResult(data);
});
});
/* Manage save toolbar : show/hide for WYSISYG blocks or contenteditable fields */
$(ParsimonyAdmin.currentBody).on('keyup.edit',".wysiwyg.activeEdit, .usereditinline",function(e){
var undo = ParsimonyAdmin.currentDocument.queryCommandEnabled("undo");
var redo = ParsimonyAdmin.currentDocument.queryCommandEnabled("redo");
if(undo || redo){
$("#toolbarEditMode").slideDown();
if(undo) document.getElementById("toolbarEditModeUndo").style.display = "inline-block";
else document.getElementById("toolbarEditModeUndo").style.display = "none";
if(redo) document.getElementById("toolbarEditModeRedo").style.display = "inline-block";
else document.getElementById("toolbarEditModeRedo").style.display = "none";
}else{
$("#toolbarEditMode").slideUp();
}
if(ParsimonyAdmin.haveToSave){
ParsimonyAdmin.haveToSave = false;
$("#toolbarEditModeSave").trigger("click");
}
ParsimonyAdmin.unsavedChanges = true;
});
this.pluginDispatch("loadEditMode");
},
unloadEditMode : function(){
//if(typeof ParsimonyAdmin.wysiwyg == "object") ParsimonyAdmin.wysiwyg.disable();
delete ParsimonyAdmin.wysiwyg;
ParsimonyAdmin.wysiwyg = "";
ParsimonyAdmin.$currentDocument.add(ParsimonyAdmin.currentBody).off('.edit');
$(".wysiwyg.activeEdit",ParsimonyAdmin.currentBody).off();
$(".parsieditinline",ParsimonyAdmin.currentBody).removeClass('usereditinline').attr("contenteditable", "false");
$(".wysiwyg",ParsimonyAdmin.currentBody).removeClass('activeEdit').attr("contenteditable", "false");
$(".HTML5editorToolbar").hide();
$("#toolbarEditMode").hide();
this.pluginDispatch("unloadEditMode");
},
loadPreviewMode : function(){
ParsimonyAdmin.$currentBody.on('click.preview','a', function(e){
if($(this).attr("href").substring(0,1) != '#' && $(this).attr("href").substring(0,7) != 'http://'){
e.preventDefault();
ParsimonyAdmin.goToPage( $.trim($(this).text().replace("'","\\'")) , $(this).attr('href') );
}
});
ParsimonyAdmin.closeConfBox();
this.pluginDispatch("loadPreviewMode");
},
unloadPreviewMode : function(){
ParsimonyAdmin.$currentBody.off('.preview');
this.pluginDispatch("unloadPreviewMode");
},
init : function(){
ParsimonyAdmin.isInit = true;
$(document).add('#config_tree_selector').on('click',".action", function(e){
var parentId = '';
var inProgress = $("#treedom_" + ParsimonyAdmin.inProgress);
if(inProgress.length > 0){
if(inProgress.parent().closest(".container").attr("id") == "treedom_content") parentId = inProgress.parent().closest("#treedom_content").data('page');
else parentId = inProgress.parent().closest(".container").attr('id').replace("treedom_","");
}
ParsimonyAdmin.displayConfBox(BASE_PATH + "admin/action",($(this).data('title') || $(this).attr('title') || $(this).data('tooltip')),"TOKEN=" + TOKEN + "&idBlock=" + ParsimonyAdmin.inProgress + "&parentBlock=" + parentId + "&typeProgress=" + ParsimonyAdmin.typeProgress + "&action=" + $(this).attr('rel') +"&IDPage=" + $(".container_page",ParsimonyAdmin.currentBody).data('page') +"&" + $(this).attr('params'));
e.preventDefault();
}).on('click','#menu a',function(e){
ParsimonyAdmin.closeParsiadminMenu();
$('.cssPicker',ParsimonyAdmin.currentDocument).removeClass('cssPicker');
});
$("#menu").on("mouseenter",".CSSProps a",function(){
$("#" + ParsimonyAdmin.inProgress + " " + this.dataset.css,ParsimonyAdmin.currentDocument).addClass('cssPicker');
}).on("mouseout",".CSSProps a",function(){
$('.cssPicker',ParsimonyAdmin.currentDocument).removeClass('cssPicker');
});
/* Init tooltip */
$(".tooltip").parsimonyTooltip({
triangleWidth:5
});
var timer = setInterval(function resizeIframe() {
if(document.getElementById("changeres").value == "max"){
var height = ParsimonyAdmin.currentBody.getBoundingClientRect().bottom;
if(screen.height > height) height = screen.height - 35;
if(ParsimonyAdmin.iframe.style.height != height + "px"){
ParsimonyAdmin.iframe.style.height = height + "px";
document.getElementById("overlays").style.height = height + "px";
}
}
}, 1000);
/* Shortcut : Save on CTRL+S */
document.addEventListener("keydown", function(e) {
if (e.keyCode == 83 && e.ctrlKey) {
e.preventDefault();
$("form",$('#conf_box_content_iframe').contents().find("body")).trigger("submit");
}
}, false);
$(window).bind("beforeunload",function(event) {
if(ParsimonyAdmin.unsavedChanges) return t("You have unsaved changes");
});
ParsimonyAdmin.hideOverlay();
ParsimonyAdmin.removeEmptyTextNodes(document.body);
this.pluginDispatch("init");
}
,
goToPage : function (pageTitle,pageUrl, isHistory){
ParsimonyAdmin.unloadCreationMode();
ParsimonyAdmin.unloadEditMode();
ParsimonyAdmin.unloadPreviewMode();
if(pageUrl.substring(0,BASE_PATH.length) != BASE_PATH && pageUrl.substring(0,7) != "http://") pageUrl = BASE_PATH + pageUrl;
pageUrl = $.trim(pageUrl);
if(pageUrl.indexOf('?') > -1 && pageUrl.indexOf('?parsiframe=ok') == -1) pageUrl += '&parsiframe=ok';
else pageUrl += '?parsiframe=ok';
ParsimonyAdmin.currentDocument.title = pageTitle;
$('#parsiframe').attr('src', pageUrl);
return false;
},
execResult : function (obj){
if (obj.notification == null)
var obj = jQuery.parseJSON(obj);
if(obj.eval != null) eval(obj.eval);
var headParsiFrame = ParsimonyAdmin.$iframe.contents().find("head");
if(obj.jsFiles){
obj.jsFiles = jQuery.parseJSON(obj.jsFiles);
$.each(obj.jsFiles, function(index, url) {
if (!$("script[scr='" + url + "']",headParsiFrame).length) {
ParsimonyAdmin.$currentBody.append('<script type="text/javascript" src="' + url + '"></script>');
}
});
}
if(obj.CSSFiles){
obj.CSSFiles = jQuery.parseJSON(obj.CSSFiles);
$.each(obj.CSSFiles, function(index, url) {
if (!$('link[href="' + url + '"]',headParsiFrame).length) {
ParsimonyAdmin.$currentBody.append('<link rel="stylesheet" type="text/css" href="' + url + '">');
}
});
}
if (obj.notification)
ParsimonyAdmin.notify(obj.notification,obj.notificationType);
},
postData : function (url,params,callBack){
$.post(url,params,function(data){
callBack(data);
});
},
destroyBlock : function (){
if(ParsimonyAdmin.inProgress != "container"){
if(confirm(t('Do you really want to remove the block ') + ParsimonyAdmin.inProgress + ' ?')==true){
ParsimonyAdmin.returnToShelter();
if($("#treedom_" + ParsimonyAdmin.inProgress).parent().closest(".container").attr("id")=="treedom_content") var parentId = $("#treedom_" + ParsimonyAdmin.inProgress).parent().closest("#treedom_content").data('page');
else var parentId = $("#treedom_" + ParsimonyAdmin.inProgress).parent().closest(".container").attr('id').replace("treedom_","");
ParsimonyAdmin.postData(BASE_PATH + "admin/removeBlock",{
TOKEN: TOKEN ,
idBlock:ParsimonyAdmin.inProgress,
parentBlock: parentId ,
typeProgress:ParsimonyAdmin.typeProgress,
IDPage:($(".container_page",ParsimonyAdmin.currentBody).data('page') || $(".sublist.selected").attr("id").replace("page_",""))
} ,function(data){
ParsimonyAdmin.execResult(data);
ParsimonyAdmin.returnToShelter();
ParsimonyAdmin.updateUI();
});
}
}
},
addBlock : function (idBlock, contentBlock, idBlockAfter){
if($( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).parent().hasClass("container")){
$( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).after(contentBlock);
ParsimonyAdmin.returnToShelter();
}else {
var block = $( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).closest(".container");
ParsimonyAdmin.returnToShelter();
$(".dropInContainer:first",block).remove();
if(block.get(0).id == 'container' && block.children(".block").length==1) block.prepend(contentBlock);
else block.append(contentBlock);
}
$("#" + idBlock,ParsimonyAdmin.currentBody ).trigger("click");
var offset = $("#" + idBlock,ParsimonyAdmin.currentBody ).offset();
ParsimonyAdmin.openParsiadminMenu(offset.left + 30,offset.top + 30);
},
moveMyBlock : function (idBlock, idBlockAfter){
if($( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).parent().hasClass("container")){
$( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).after( $("#" + idBlock,ParsimonyAdmin.currentBody) );
ParsimonyAdmin.returnToShelter();
}else {
var block = $( "#" + idBlockAfter ,ParsimonyAdmin.currentBody).parent().parent().parent();
ParsimonyAdmin.returnToShelter();
block.html($("#" + idBlock,ParsimonyAdmin.currentBody ) );
}
},
selectBlock : function (idBlock){
var blockTreeObj = document.getElementById("treedom_" + idBlock);
var block = ParsimonyAdmin.currentDocument.getElementById(idBlock);
var oldSelection = ParsimonyAdmin.currentDocument.querySelector(".selection-block");
var oldSelectionTree = document.querySelector(".currentDOM");
var config_tree_selector = document.getElementById("config_tree_selector");
oldSelection && oldSelection.classList.remove("selection-block");
oldSelectionTree && oldSelectionTree.classList.remove("currentDOM");
ParsimonyAdmin.inProgress = idBlock;
ParsimonyAdmin.typeProgress = ParsimonyAdmin.whereIAm(ParsimonyAdmin.inProgress);
block && block.classList.add("selection-block");
if(blockTreeObj) blockTreeObj.classList.add("currentDOM");
if(idBlock == "container" || (block && block.classList.contains("container_page"))) config_tree_selector.classList.add("restrict");
else document.getElementById("config_tree_selector").classList.remove("restrict");
config_tree_selector.style.display = "block";
if(blockTreeObj) blockTreeObj.insertBefore(config_tree_selector, blockTreeObj.firstChild);
},
whereIAm : function (idBlock){
var where = "theme";
var elmt = ParsimonyAdmin.currentDocument.getElementById(idBlock);
if(elmt){
if(elmt.compareDocumentPosition(ParsimonyAdmin.currentDocument.getElementById("content")) == 10){
where = "page";
}
}else{
if(idBlock == 'dropInTree') var obj = document.getElementById("dropInTree");
else var obj = document.getElementById("treedom_" + idBlock);
if(obj.compareDocumentPosition(document.getElementById("treedom_content")) == 10){
where = "page";
}
}
return where;
},
showOverlay : function (opacity){
if(typeof opacity== "undefined") opacity=1;
$( "#conf_box_overlay").css('opacity',opacity).show();
},
hideOverlay : function (){
document.getElementById("conf_box_overlay").style.display = "none";
},
displayConfBox : function (url,title,params,modal){
$("#conf_box_load").show();
$("#conf_box,#conf_box_content" ).removeAttr("style");
$("#conf_box").css("visibility","hidden").show();
if(typeof modal == "undefined" || modal == true) ParsimonyAdmin.showOverlay();
ParsimonyAdmin.setConfBoxTitle(title);
if(url.substring(0,1) != "#"){
ParsimonyAdmin.returnToShelter();
$("#conf_box_form").attr("action",url).empty();
if(typeof params != "undefined"){
var vars = params.split(/&/);
for (var i=0; i< vars.length; i++) {
var myvar = vars[i].split(/=/);
$("#conf_box_form").append('<input type="hidden" name="' + myvar[0] + '" value="' + myvar[1] + '">');
}
}
$("#conf_box_form").append('<input type="hidden" name="popup" value="yes">').trigger("submit");
$("#conf_box_content_iframe").show();
$("#conf_box_content_inline").hide();
}else{
$("#shelter").append($("#conf_box_content_inline").html());
$("#conf_box_content_inline").show().append($(url));
$("#conf_box_content_iframe").hide();
$(url).show();
document.getElementById("conf_box").style.visibility = "visible";
}
},
closeConfBox : function (){
$("#conf_box").hide();
ParsimonyAdmin.hideOverlay();
$("#conf_box_title").empty();
$("#conf_box_content_iframe").attr("src","about:blank");
},
resizeConfBox : function(){
var iframe = document.getElementById("conf_box_content_iframe");
iframe.removeAttribute("style");
var doc = iframe.contentDocument;
document.getElementById("conf_box_load").style.display = "none";
if(doc.location.href != "about:blank"){
var elmt = $(".adminzone",doc)[0] || $("body",doc)[0] ;
var height = $(".adminzonefooter",doc).length > 0 ? ( elmt.scrollHeight + 40) : elmt.scrollHeight ;
iframe.style.cssText = "width:" + elmt.scrollWidth + "px;height:" + height + "px";
}
document.getElementById("conf_box").style.visibility = "visible";
},
setConfBoxTitle : function (title){
$("#conf_box_title").html(title);
},
returnToShelter : function () {
$("#dropInPage",ParsimonyAdmin.currentBody).prependTo($("#shelter"));
$("#dropInTree").prependTo($("#shelter"));
},
changeDevice : function (device) {
ParsimonyAdmin.setCookie("device",device,999);
THEMETYPE = device;
$('#changeres').val('');// to change res.
ParsimonyAdmin.changeDeviceUpdate(device);
$("#info_themetype").text(device);
ParsimonyAdmin.$iframe.attr("src", ParsimonyAdmin.$iframe.attr("src"));
ParsimonyAdmin.loadBlock('panelblocks');
},
changeDeviceUpdate : function () {
var select = '';
var nb = 0;
var changeres = $('#changeres');
$.each($.parseJSON(resultions[THEMETYPE]), function(i,item){
if(changeres[0].value == "" && nb == 0) changeres.val(i).trigger('change');
select += '<li><a href="#" onclick="$(\'#changeres\').val(\'' + i + '\').trigger(\'change\');">' + item + ' (' + i + ')</a></li>';
nb++;
});
$("#currentRes").text(changeres[0].value);
$('#listres').html(select);$('#currentRes').css("position","relative");
},
changeLocale : function (locale) {
ParsimonyAdmin.setCookie("locale",locale,999);
window.location.reload();
},
setCookie : function (name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
},
getCookie: function (name){
var i, x, y, cookies = document.cookie.split(";");
for (i=0; i < cookies.length;i++){
x = cookies[i].substr(0,cookies[i].indexOf("="));
y = cookies[i].substr(cookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g,"");
if (x==name) return unescape(y);
}
},
notify : function (message,type) {
$("#notify").appendTo("body").attr("class","").addClass(type).html(message).fadeIn("normal").delay(4000).fadeOut("slow");
},
openParsiadminMenu : function (x,y) {
var off = ParsimonyAdmin.$iframe.offset();
$("#menu").appendTo("body").css({
"top":(y + off.top),
"left":(x + off.left)
});
},
closeParsiadminMenu : function () {
$("#menu").appendTo($("#shelter")).find(".options").empty();
},
addTitleParsiadminMenu : function (title) {
$("#menu .options").append('<h5>' + title + '</h5>');
},
addOptionParsiadminMenu : function (option) {
$("#menu .options").append('<div class="option">' + option + '</div>');
},
/*reloadIframe : function (){
$.get("index.html?parsiframe=ok",
function(data){
ParsimonyAdmin.$iframe.contents().find("html").replaceWith(data);
});
},*/
updateUI : function (tree){
$(".dropInContainer",ParsimonyAdmin.currentBody).remove();
if(tree!=false) {
$("#config_tree_selector").hide().prependTo("#right_sidebar");
ParsimonyAdmin.loadBlock('tree');
ParsimonyAdmin.loadBlock('tree',{}, function(){
if(ParsimonyAdmin.inProgress != "container") $("#treedom_" + ParsimonyAdmin.inProgress).trigger("click");
});
}
$(".container",ParsimonyAdmin.currentBody).each(function(){
if($(this).find('.block:not("#content")').length==0) {
$(this).prepend('<div class="dropInContainer"><div class="dropInContainerChild">Id #' + $(this).get(0).id + ". " + t("Drop the blocks in this space") + '</div></div>');
}else $(".dropInContainerChild:first",this).remove();
});
},
setCreationMode : function (){
$('.sidebar,.panelblocks,.creation').show();
$(".panelblocks").removeClass("active");
$(".panelmodules").addClass("active");
ParsimonyAdmin.setMode("creation");
},
setEditMode : function (){
$('#right_sidebar,.panelblocks,.creation').hide();
$('#left_sidebar').show();
$(".panelmodules").addClass("active");
ParsimonyAdmin.setMode("edit");
},
setPreviewMode : function (){
$('.sidebar').hide();
ParsimonyAdmin.setMode("preview");
},
setMode : function (mode){
$("body").removeClass("previewMode modeMode creationMode").addClass(mode + "Mode");
$(".switchMode").removeClass("selected");
$("#" + mode + "Mode").addClass("selected");
/* Unload current mode if exists */
if(ParsimonyAdmin.currentMode.length > 0){
var captitalizeOldMode = ParsimonyAdmin.currentMode[0].toUpperCase() + ParsimonyAdmin.currentMode.substring(1);
ParsimonyAdmin["unload" + captitalizeOldMode + "Mode"]();
}
ParsimonyAdmin.currentMode = mode;
var captitalizeNewMode = mode[0].toUpperCase() + mode.substring(1);
/* Load new mode */
ParsimonyAdmin["load" + captitalizeNewMode + "Mode"]();
ParsimonyAdmin.setCookie("mode",mode,999);
},
loadBlock: function(id, params, func){
if(!params) params = {};
$.get(window.location.href.toLocaleString(),params , function(data) {
$('#' + id).html($("<div>").append(data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")).find("#" + id).html());
},func);
//$('#' + id).load(window.location.toLocaleString() + " #" + id + " > div");
},
removeEmptyTextNodes: function(elem){
var children = elem.childNodes;
var child;
var len = children.length;
var whitespace = /^\s*$/;
for(var i = 0; i < len; i++){
child = children[i];
if(child.nodeType == 3){
if(whitespace.test(child.nodeValue)){
elem.removeChild(child);
i--;
len--;
}
}else if(child.nodeType == 1){
this.removeEmptyTextNodes(child);
}
}
}
}
var $lang = new Array;
function t(val){
if($lang[val]){
return $lang[val];
}else{
return val;
}
}
function block_calendar() {
block.call(this);
this.name = "calendar";
this.stylableElements = {
"calendar":".calendar",
"calendar inner":".calendarInner",
"a day":".day",
"a link day":".day a",
"a week":".week",
"month title":".monthTitle",
"week title":".weekTitle",
"days which has posts":".day.hasposts",
"days link which has post":".day.hasposts a",
"days which are in this month":".day.thisMonth",
"days which aren't in this month":".day.out",
"calendar navigation":".calendarNav",
"calendar navigation links":".calendarNav a",
"link previous month":".prevMonth",
"link next month":".nextMonth"
}
}function block_categories() {
block.call(this);
this.name = "categories";
this.stylableElements = {
"categories list":"ul",
"categories items":"li",
"links":"a"
}
}function block_connexion() {
block.call(this);
this.name = "connexion";
this.stylableElements = {
"My account title":"h3",
"user info box":".userInfo",
"logout link":".logout",
"form":"form",
"labels":"label",
"login part":".connectLogin",
"login input":".connectLogin input",
"login label":".connectLogin label",
"password part":".connectPassword",
"password input":".connectPassword input",
"password label":".connectPassword label",
"button part":".connectSubmit",
"submit button":".connectSubmit input",
"error message":".error"
}
}function block_contactform() {
block.call(this);
this.name = "contactform";
this.stylableElements = {
"form":"form",
"form parts":"form div",
"labels":"label",
"inputs":"input",
"textareas":"textarea",
"selects":"select",
"submit button":".submit",
"notification":".notify",
"positive notification":".positive",
"negative notification":".negative"
}
}function block_formadd() {
block.call(this);
this.name = "formadd";
this.stylableElements = {
"form":"form",
"form parts":"form div",
"labels":"label",
"inputs":"input",
"textareas":"textarea",
"selects":"select",
"submit button":".submit",
"notification":".notify",
"positive notification":".positive",
"negative notification":".negative"
}
}function block_gallery() {
block.call(this);
this.name = "gallery";
this.stylableElements = {
"slider":".slider",
"sldies container":".slides_container",
"each slide":".slide",
"slide links":".slide a",
"images":".slide img",
"captions container":".caption",
"caption":".caption p",
"previous button":".prev",
"next button":".next",
"pagination container":".paginationSlides",
"pagination buttons":".paginationSlides li",
"pagination links":".paginationSlides a",
"pagination current slide":".paginationSlides li.current a"}
}function block_menu() {
block.call(this);
this.name = "menu";
this.stylableElements = {"Menu container":".parsimenu",
"Menu Items":".parsimenu li",
"Menu Links Items":".parsimenu a",
"Menu Active Items":".parsimenu .current a",
"Menu Items Hover":".parsimenu li:hover > a",
"Sub Menu Links Hover":".parsimenu ul a:hover",
"Sub Menu":".parsimenu ul",
"Sub Menu Items":".parsimenu ul li",
"Sub Menu links":".parsimenu ul a",
"Sub Sub Menu Items":".parsimenu ul ul"}
}function block_query() {
block.call(this);
this.name = "query";
this.stylableElements = {
"each line":".itemscope",
"each property":".itemprop",
"pagination links":".pagination a",
"active pagination links":".pagination a.active",
"pagination links hover":".pagination a:hover",
"no results message":".noResults"
}
}function block_recentposts() {
block.call(this);
this.name = "recentposts";
this.stylableElements = {
"posts list":"ul",
"posts items":"li",
"links":"a"
}
}function block_tags() {
block.call(this);
this.name = "tags";
this.stylableElements = {
"tags list":"ul",
"tags items":"li",
"links":"a",
"x small tags":".xsmall",
"small tags":".small",
"medium tags":".medium",
"large tags":".large",
"x large tags":".xlarge"
}
}/*
DHTML Color Picker : v1.1 : 2010/12/28
---------------------------------------
http://www.colorjack.com/software/dhtml+color+picker.html
Native support: Firefox 2+, Safari 3+, Opera 9+, Google Chrome, IE9+
ChromeFrame supprt: IE7+
*/
if(window.Color == undefined) Color = {};
Color.Picker = function (props) {
/// loading properties
if (typeof(props) == "undefined") props = {};
this.callback = props.callback; // bind custom function
this.hue = props.hue || 0; // 0-360
this.sat = props.sat || 0; // 0-100
this.val = props.val || 100; // 0-100
this.element = props.element || document.body;
this.size = 165; // size of colorpicker
this.margin = 10; // margins on colorpicker
this.offset = this.margin / 2;
this.hueWidth = 30;
/// creating colorpicker (header)
var plugin = document.createElement("div");
plugin.id = "colorjack_square";
plugin.style.cssText = "height: " + (this.size + this.margin * 2) + "px";
// shows current selected color as the background of this box
var hexBox = document.createElement("div");
hexBox.className = "hexBox";
plugin.appendChild(hexBox);
// shows current selected color as HEX string
var hexString = document.createElement("div");
hexString.className = "hexString";
plugin.appendChild(hexString);
// close the plugin
var hexClose = document.createElement("div");
hexClose.className = "hexClose";
hexClose.textContent = "X";
hexClose.onclick = function () { // close colorpicker
plugin.style.display = (plugin.style.display == "none") ? "block" : "none";
};
plugin.appendChild(hexClose);
plugin.appendChild(document.createElement("br"));
/// creating media-resources
var arrows = document.createElement("canvas");
arrows.width = 40;
arrows.height = 5;
(function () { // creating arrows
var ctx = arrows.getContext("2d");
var width = 3;
var height = 5;
var size = 9;
var top = -size / 4;
var left = 1;
for (var n = 0; n < 20; n++) { // multiply anti-aliasing
ctx.beginPath();
ctx.fillStyle = "#FFF";
ctx.moveTo(left + size / 4, size / 2 + top);
ctx.lineTo(left, size / 4 + top);
ctx.lineTo(left, size / 4 * 3 + top);
ctx.fill();
}
ctx.translate(width, height);
ctx.rotate(180 * Math.PI / 180); // rotate arrows
ctx.drawImage(arrows, -29, 0);
ctx.translate(-width, -height);
})();
var circle = document.createElement("canvas");
circle.width = 10;
circle.height = 10;
(function () { // creating circle-selection
var ctx = circle.getContext("2d");
ctx.lineWidth = 1;
ctx.beginPath();
var x = circle.width / 2;
var y = circle.width / 2;
ctx.arc(x, y, 4.5, 0, Math.PI * 2, true);
ctx.strokeStyle = '#000';
ctx.stroke();
ctx.beginPath();
ctx.arc(x, y, 3.5, 0, Math.PI * 2, true);
ctx.strokeStyle = '#FFF';
ctx.stroke();
})();
/// creating colorpicker sliders
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.cssText = "position: absolute; top: 19px; left: " + (this.offset) + "px;";
canvas.width = this.size + this.hueWidth + this.margin;
canvas.height = this.size + this.margin;
plugin.appendChild(canvas);
plugin.onmousemove =
plugin.onmousedown = function (e) {
var down = (e.type == "mousedown");
var offset = that.margin / 2;
var abs = abPos(canvas);
var x0 = (e.pageX - abs.x) - offset;
var y0 = (e.pageY - abs.y) - offset;
var x = clamp(x0, 0, canvas.width);
var y = clamp(y0, 0, that.size);
if (e.target.className == "hexString") {
plugin.style.cursor = "text";
return; // allow selection of HEX
} else if (x != x0 || y != y0) { // move colorpicker
plugin.style.cursor = "move";
if (down) dragElement({
type: "difference",
event: e,
element: plugin,
callback: function (coords, state) {
plugin.style.left = coords.x + "px";
plugin.style.top = coords.y + "px";
}
});
} else if (x <= that.size) { // saturation-value selection
plugin.style.cursor = "crosshair";
if (down) dragElement({
type: "relative",
event: e,
element: canvas,
callback: function (coords, state) {
var x = clamp(coords.x - that.offset, 0, that.size);
var y = clamp(coords.y - that.offset, 0, that.size);
that.sat = x / that.size * 100; // scale saturation
that.val = 100 - (y / that.size * 100); // scale value
that.drawSample();
}
});
} else if (x > that.size + that.margin && x <= that.size + that.hueWidth) { // hue selection
plugin.style.cursor = "crosshair";
if (down) dragElement({
type: "relative",
event: e,
element: canvas,
callback: function (coords, state) {
var y = clamp(coords.y - that.offset, 0, that.size);
that.hue = Math.min(1, y / that.size) * 360;
that.drawSample();
}
});
} else { // margin between hue/saturation-value
plugin.style.cursor = "default";
}
return false; // prevent selection
};
// appending to element
this.element.appendChild(plugin);
/// helper functions
var that = this;
this.el = plugin;
this.drawSample = function () {
// clearing canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
that.drawSquare();
that.drawHue();
// retrieving hex-code
var hex = Color.HSV_HEX({
H: that.hue,
S: that.sat,
V: that.val
});
// display hex string
hexString.textContent = hex.toUpperCase();
// display background color
hexBox.style.backgroundColor = "#" + hex;
// arrow-selection
var y = (that.hue / 362) * that.size - 2;
ctx.drawImage(arrows, that.size + that.offset + 4, Math.round(y) + that.offset);
// circle-selection
var x = that.sat / 100 * that.size;
var y = (1 - (that.val / 100)) * that.size;
x = x - circle.width / 2;
y = y - circle.height / 2;
ctx.drawImage(circle, Math.round(x) + that.offset, Math.round(y) + that.offset);
// run custom code
if (that.callback) that.callback(hex);
};
this.drawSquare = function () {
// retrieving hex-code
var hex = Color.HSV_HEX({
H: that.hue,
S: 100,
V: 100
});
var offset = that.offset;
var size = that.size;
// drawing color
ctx.fillStyle = "#" + hex;
ctx.fillRect(offset, offset, size, size);
// overlaying saturation
var gradient = ctx.createLinearGradient(offset, offset, size + offset, 0);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(1, "rgba(255, 255, 255, 0)");
ctx.fillStyle = gradient;
ctx.fillRect(offset, offset, size, size);
// overlaying value
var gradient = ctx.createLinearGradient(offset, offset, 0, size + offset);
gradient.addColorStop(0, "rgba(0, 0, 0, 0)");
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
ctx.fillStyle = gradient;
ctx.fillRect(offset, offset, size, size);
// drawing outer bounds
ctx.strokeStyle = "rgba(255,255,255,0.15)";
ctx.strokeRect(offset+0.5, offset+0.5, size-1, size-1);
};
this.drawHue = function () {
// drawing hue selector
var left = that.size + that.margin + that.offset;
var gradient = ctx.createLinearGradient(0, 0, 0, that.size);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
ctx.fillStyle = gradient;
ctx.fillRect(left, that.offset, 20, that.size);
// drawing outer bounds
ctx.strokeStyle = "rgba(255,255,255,0.2)";
ctx.strokeRect(left + 0.5, that.offset + 0.5, 19, that.size-1);
};
this.destory = function () {
document.body.removeChild(plugin);
for (var key in that) delete that[key];
};
// drawing color selection
this.drawSample();
return this;
};
/* GLOBALS LIBRARY */
var dragElement = function(props) {
function mouseMove(e, state) {
if (typeof(state) == "undefined") state = "move";
var coord = XY(e);
switch (props.type) {
case "difference":
props.callback({
x: coord.x + oX - eX,
y: coord.y + oY - eY
}, state);
break;
case "relative":
props.callback({
x: coord.x - oX,
y: coord.y - oY
}, state);
break;
default: // "absolute"
props.callback({
x: coord.x,
y: coord.y
}, state);
break;
}
};
function mouseUp(e) {
window.removeEventListener("mousemove", mouseMove, false);
window.removeEventListener("mouseup", mouseUp, false);
mouseMove(e, "up");
};
// current element position
var el = props.element;
var origin = abPos(el);
var oX = origin.x;
var oY = origin.y;
// current mouse position
var e = props.event;
var coord = XY(e);
var eX = coord.x;
var eY = coord.y;
// events
window.addEventListener("mousemove", mouseMove, false);
window.addEventListener("mouseup", mouseUp, false);
mouseMove(e, "down"); // run mouse-down
};
var clamp = function(n, min, max) {
return (n < min) ? min : ((n > max) ? max : n);
};
var XY = window.ActiveXObject ? // fix XY to work in various browsers
function(event) {
return {
x: event.clientX + document.documentElement.scrollLeft,
y: event.clientY + document.documentElement.scrollTop
};
} : function(event) {
return {
x: event.pageX,
y: event.pageY
};
};
var abPos = function(o) {
o = typeof(o) == 'object' ? o : $(o);
var offset = { x: 0, y: 0 };
while(o != null) {
offset.x += o.offsetLeft;
offset.y += o.offsetTop;
o = o.offsetParent;
};
return offset;
};
/* COLOR LIBRARY */
Color.HEX_STRING = function (o) {
var z = o.toString(16);
var n = z.length;
while (n < 6) {
z = '0' + z;
n ++;
}
return z;
};
Color.RGB_HEX = function (o) {
return o.R << 16 | o.G << 8 | o.B;
};
Color.HSV_RGB = function (o) {
var H = o.H / 360,
S = o.S / 100,
V = o.V / 100,
R, G, B;
var A, B, C, D;
if (S == 0) {
R = G = B = Math.round(V * 255);
} else {
if (H >= 1) H = 0;
H = 6 * H;
D = H - Math.floor(H);
A = Math.round(255 * V * (1 - S));
B = Math.round(255 * V * (1 - (S * D)));
C = Math.round(255 * V * (1 - (S * (1 - D))));
V = Math.round(255 * V);
switch (Math.floor(H)) {
case 0:
R = V;
G = C;
B = A;
break;
case 1:
R = B;
G = V;
B = A;
break;
case 2:
R = A;
G = V;
B = C;
break;
case 3:
R = A;
G = B;
B = V;
break;
case 4:
R = C;
G = A;
B = V;
break;
case 5:
R = V;
G = A;
B = B;
break;
}
}
return {
R: R,
G: G,
B: B
};
};
Color.HSV_HEX = function (o) {
return Color.HEX_STRING(Color.RGB_HEX(Color.HSV_RGB(o)));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment