body {
margin: 0;
- font: 10pt Helvetica, Verdana, sans-serif;
+ font: 12px Helvetica, Verdana, sans-serif;
overflow: hidden;
background: #AAA;
}
top: 2.4em; left: 0px; right: 0px; bottom: 0px;
overflow: auto;
background-color: white;
- padding: 0.2em 1em;
}
/*
/* ========== */
#panels {
- position: absolute;
- bottom: 0px; left: 0px; right: 0px; top: 0px;
+ height: 100%;
+ width: 100%;
}
.panel-wrap {
margin: 0.5em;
}
-.CodeMirror-line-numbers
-{
- text-align: right;
- padding-top: 0.4em;
- padding-right: 2px;
- width: 28px;
- font-size: 10pt;
- background: black;
- color: white;
-}
\ No newline at end of file
+/* ======= */
+/* = New = */
+/* ======= */
+#splitview {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+}
+
+.splitview-splitbar {
+ width: 5px;
+ border-left: 1px solid #999;
+ border-right: 1px solid #999;
+ height: 100%;
+ background-color: #CCC;
+ z-index: 100;
+}
+
+.splitview-overlay {
+ z-index: 90;
+ background: #FFF;
+ opacity: 0.5;
+}
+
+.panel-container {
+ height: 100%;
+ position: relative;
+}
+
+.content-view {
+ position: absolute;
+ top: 20px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: none;
+}
+
+.panel-container select {
+ z-index: 1100;
+}
+
+.xmlview {
+ height: 100%;
+}
+
+.view-overlay {
+ z-index: 1000;
+ background: #FFF;
+ opacity: 0.8;
+ text-align: center;
+ text-valign: center;
+}
+
+.view-overlay p {
+ display: block;
+ position: relative;
+ top: auto;
+ bottom: auto;
+ height: 40px;
+}
+
+.buttontoolbarview {
+ display: block;
+ background-color: #CCC;
+}
+
+.buttontoolbarview a {
+ color: #000;
+ text-decoration: none;
+}
--- /dev/null
+/*global Class*/
+var editor;
+var panel_hooks;
+
+
+(function(){
+ // Classes
+ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+ this.Class = function(){};
+ Class.extend = function(prop) {
+ var _super = this.prototype;
+ initializing = true;
+ var prototype = new this();
+ initializing = false;
+ for (var name in prop) {
+ prototype[name] = typeof prop[name] == "function" &&
+ typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+ (function(name, fn){
+ return function() {
+ var tmp = this._super;
+ this._super = _super[name];
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+ return ret;
+ };
+ })(name, prop[name]) :
+ prop[name];
+ }
+ function Class() {
+ if ( !initializing && this.init )
+ this.init.apply(this, arguments);
+ }
+ Class.prototype = prototype;
+ Class.constructor = Class;
+ Class.extend = arguments.callee;
+ return Class;
+ };
+
+ // Templates
+ var cache = {};
+
+ this.render_template = function render_template(str, data){
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ var fn = !/^[\d\s-_]/.test(str) ?
+ cache[str] = cache[str] ||
+ render_template(document.getElementById(str).innerHTML) :
+
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\t\n]/g, " ")
+ .split("<%").join("\t")
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)%>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("%>").join("p.push('")
+ .split("\r").join("\\'")
+ + "');}return p.join('');");
+
+ // Provide some basic currying to the user
+ return data ? fn( data ) : fn;
+ };
+})();
+
+
+(function() {
+ var slice = Array.prototype.slice;
+
+ function update(array, args) {
+ var arrayLength = array.length, length = args.length;
+ while (length--) array[arrayLength + length] = args[length];
+ return array;
+ };
+
+ function merge(array, args) {
+ array = slice.call(array, 0);
+ return update(array, args);
+ };
+
+ Function.prototype.bind = function(context) {
+ if (arguments.length < 2 && typeof arguments[0] === 'undefined') {
+ return this;
+ }
+ var __method = this;
+ var args = slice.call(arguments, 1);
+ return function() {
+ var a = merge(args, arguments);
+ return __method.apply(context, a);
+ }
+ }
+
+})();
+
+
+var Editor = Editor || {};
+
+// Obiekt implementujący wzorzec KVC/KVO
+Editor.Object = Class.extend({
+ _className: 'Editor.Object',
+ _observers: {},
+ _guid: null,
+
+ init: function() {
+ this._observers = {};
+ },
+
+ description: function() {
+ return this._className + '(guid = ' + this.guid() + ')';
+ },
+
+ addObserver: function(observer, property, callback) {
+ // console.log('Add observer', observer.description(), 'to', this.description(), '[', property, ']');
+ if (!this._observers[property]) {
+ this._observers[property] = {}
+ }
+ this._observers[property][observer.guid()] = callback;
+ return this;
+ },
+
+ removeObserver: function(observer, property) {
+ if (!property) {
+ for (var property in this._observers) {
+ this.removeObserver(observer, property)
+ }
+ } else {
+ // console.log('Remove observer', observer.description(), 'from', this.description(), '[', property, ']');
+ delete this._observers[property][observer.guid()];
+ }
+ return this;
+ },
+
+ notifyObservers: function(property) {
+ var currentValue = this[property];
+ for (var guid in this._observers[property]) {
+ // console.log(this._observers[property][guid]);
+ // console.log('Notifying', guid, 'of', this.description(), '[', property, ']');
+ this._observers[property][guid](property, currentValue, this);
+ }
+ return this;
+ },
+
+ guid: function() {
+ if (!this._guid) {
+ this._guid = ('editor-' + Editor.Object._lastGuid++);
+ }
+ return this._guid;
+ },
+
+ get: function(property) {
+ return this[property];
+ },
+
+ set: function(property, value) {
+ if (this[property] != value) {
+ this[property] = value;
+ this.notifyObservers(property);
+ }
+ return this;
+ },
+
+ dispose: function() {
+ delete this._observers;
+ }
+});
+
+Editor.Object._lastGuid = 0;
+
+
+var panels = [];
};
Editor.prototype.loadPanelOptions = function() {
- var self = this;
- var totalWidth = 0;
-
- $('.panel-wrap', self.rootDiv).each(function(index) {
- var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
- if ($(this).hasClass('last-panel')) {
- $(this).css({
- left: totalWidth,
- right: 0
- });
- } else {
- $(this).css({
- left: totalWidth,
- width: panelWidth
- });
- totalWidth += panelWidth;
- }
- $.log('panel:', this, $(this).css('left'));
- $('.panel-toolbar option', this).each(function() {
- if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
- $(this).parent('select').val($(this).attr('value'));
- }
- });
- });
+ // var self = this;
+ // var totalWidth = 0;
+ //
+ // $('.panel-wrap', self.rootDiv).each(function(index) {
+ // var panelWidth = self.fileOptions.panels[index].ratio * self.rootDiv.width();
+ // if ($(this).hasClass('last-panel')) {
+ // $(this).css({
+ // left: totalWidth,
+ // right: 0
+ // });
+ // } else {
+ // $(this).css({
+ // left: totalWidth,
+ // width: panelWidth
+ // });
+ // totalWidth += panelWidth;
+ // }
+ // $.log('panel:', this, $(this).css('left'));
+ // $('.panel-toolbar option', this).each(function() {
+ // if ($(this).attr('p:panel-name') == self.fileOptions.panels[index].name) {
+ // $(this).parent('select').val($(this).attr('value'));
+ // }
+ // });
+ // });
};
Editor.prototype.savePanelOptions = function() {
* UI related Editor methods\r
*/\r
Editor.prototype.setupUI = function() {\r
- // set up the UI visually and attach callbacks\r
+// // set up the UI visually and attach callbacks\r
var self = this;\r
-\r
- var resize_start = function(event, mydata) {\r
- $(document).bind('mousemove', mydata, resize_changed).\r
- bind('mouseup', mydata, resize_stop);\r
-\r
- $('.panel-overlay', mydata.root).css('display', 'block');\r
- return false;\r
- }\r
- var resize_changed = function(event) {\r
- var old_width = parseInt(event.data.overlay.css('width'));\r
- var delta = event.pageX + event.data.hotspot_x - old_width;\r
-\r
- if(old_width + delta < 12) delta = 12 - old_width;\r
- if(old_width + delta > $(window).width()) \r
- delta = $(window).width() - old_width;\r
- \r
- event.data.overlay.css({\r
- 'width': old_width + delta\r
- });\r
-\r
- if(event.data.overlay.next) {\r
- var left = parseInt(event.data.overlay.next.css('left'));\r
- event.data.overlay.next.css('left', left+delta);\r
- }\r
-\r
- return false;\r
- };\r
-\r
- var resize_stop = function(event) {\r
- $(document).unbind('mousemove', resize_changed).unbind('mouseup', resize_stop);\r
- // $('.panel-content', event.data.root).css('display', 'block');\r
- var overlays = $('.panel-content-overlay', event.data.root);\r
- $('.panel-content-overlay', event.data.root).each(function(i) {\r
- if( $(this).data('panel').hasClass('last-panel') )\r
- $(this).data('panel').css({\r
- 'left': $(this).css('left'),\r
- 'right': $(this).css('right')\r
- });\r
- else\r
- $(this).data('panel').css({\r
- 'left': $(this).css('left'),\r
- 'width': $(this).css('width')\r
- });\r
- });\r
- $('.panel-overlay', event.data.root).css('display', 'none');\r
- $(event.data.root).trigger('stopResize');\r
- };\r
-\r
- /*\r
- * Prepare panels (overlays & stuff)\r
- */\r
- /* create an overlay */\r
- var panel_root = self.rootDiv;\r
- var overlay_root = $("<div class='panel-overlay'></div>");\r
- panel_root.append(overlay_root);\r
-\r
- var prev = null;\r
-\r
- $('*.panel-wrap', panel_root).each( function()\r
- {\r
- var panel = $(this);\r
- var handle = $('.panel-slider', panel);\r
- var overlay = $("<div class='panel-content-overlay panel-wrap'> </div>");\r
- overlay_root.append(overlay);\r
- overlay.data('panel', panel);\r
- overlay.data('next', null);\r
-\r
- if (prev) prev.next = overlay;\r
-\r
- if( panel.hasClass('last-panel') )\r
- {\r
- overlay.css({\r
- 'left': panel.css('left'),\r
- 'right': panel.css('right')\r
- });\r
- }\r
- else {\r
- overlay.css({\r
- 'left': panel.css('left'),\r
- 'width': panel.css('width')\r
- });\r
- // $.log('Has handle: ' + panel.attr('id'));\r
- overlay.append(handle.clone());\r
- /* attach the trigger */\r
- handle.mousedown(function(event) {\r
- var touch_data = {\r
- root: panel_root,\r
- overlay: overlay,\r
- hotspot_x: event.pageX - handle.position().left\r
- };\r
-\r
- $(this).trigger('hpanel:panel-resize-start', touch_data);\r
- return false;\r
- });\r
- $('.panel-content', panel).css('right',\r
- (handle.outerWidth() || 10) + 'px');\r
- $('.panel-content-overlay', panel).css('right',\r
- (handle.outerWidth() || 10) + 'px');\r
- };\r
-\r
- prev = overlay;\r
- });\r
-\r
- panel_root.bind('hpanel:panel-resize-start', resize_start);\r
- self.rootDiv.bind('stopResize', function() {\r
- self.savePanelOptions(); \r
- });\r
- \r
+// \r
+// var resize_start = function(event, mydata) {\r
+// $(document).bind('mousemove', mydata, resize_changed).\r
+// bind('mouseup', mydata, resize_stop);\r
+// \r
+// $('.panel-overlay', mydata.root).css('display', 'block');\r
+// return false;\r
+// }\r
+// var resize_changed = function(event) {\r
+// var old_width = parseInt(event.data.overlay.css('width'));\r
+// var delta = event.pageX + event.data.hotspot_x - old_width;\r
+// \r
+// if(old_width + delta < 12) delta = 12 - old_width;\r
+// if(old_width + delta > $(window).width()) \r
+// delta = $(window).width() - old_width;\r
+// \r
+// event.data.overlay.css({\r
+// 'width': old_width + delta\r
+// });\r
+// \r
+// if(event.data.overlay.next) {\r
+// var left = parseInt(event.data.overlay.next.css('left'));\r
+// event.data.overlay.next.css('left', left+delta);\r
+// }\r
+// \r
+// return false;\r
+// };\r
+// \r
+// var resize_stop = function(event) {\r
+// $(document).unbind('mousemove', resize_changed).unbind('mouseup', resize_stop);\r
+// // $('.panel-content', event.data.root).css('display', 'block');\r
+// var overlays = $('.panel-content-overlay', event.data.root);\r
+// $('.panel-content-overlay', event.data.root).each(function(i) {\r
+// if( $(this).data('panel').hasClass('last-panel') )\r
+// $(this).data('panel').css({\r
+// 'left': $(this).css('left'),\r
+// 'right': $(this).css('right')\r
+// });\r
+// else\r
+// $(this).data('panel').css({\r
+// 'left': $(this).css('left'),\r
+// 'width': $(this).css('width')\r
+// });\r
+// });\r
+// $('.panel-overlay', event.data.root).css('display', 'none');\r
+// $(event.data.root).trigger('stopResize');\r
+// };\r
+// \r
+// /*\r
+// * Prepare panels (overlays & stuff)\r
+// */\r
+// /* create an overlay */\r
+// var panel_root = self.rootDiv;\r
+// var overlay_root = $("<div class='panel-overlay'></div>");\r
+// panel_root.append(overlay_root);\r
+// \r
+// var prev = null;\r
+// \r
+// $('*.panel-wrap', panel_root).each( function()\r
+// {\r
+// var panel = $(this);\r
+// var handle = $('.panel-slider', panel);\r
+// var overlay = $("<div class='panel-content-overlay panel-wrap'> </div>");\r
+// overlay_root.append(overlay);\r
+// overlay.data('panel', panel);\r
+// overlay.data('next', null);\r
+// \r
+// if (prev) prev.next = overlay;\r
+// \r
+// if( panel.hasClass('last-panel') )\r
+// {\r
+// overlay.css({\r
+// 'left': panel.css('left'),\r
+// 'right': panel.css('right')\r
+// });\r
+// }\r
+// else {\r
+// overlay.css({\r
+// 'left': panel.css('left'),\r
+// 'width': panel.css('width')\r
+// });\r
+// // $.log('Has handle: ' + panel.attr('id'));\r
+// overlay.append(handle.clone());\r
+// /* attach the trigger */\r
+// handle.mousedown(function(event) {\r
+// var touch_data = {\r
+// root: panel_root,\r
+// overlay: overlay,\r
+// hotspot_x: event.pageX - handle.position().left\r
+// };\r
+// \r
+// $(this).trigger('hpanel:panel-resize-start', touch_data);\r
+// return false;\r
+// });\r
+// $('.panel-content', panel).css('right',\r
+// (handle.outerWidth() || 10) + 'px');\r
+// $('.panel-content-overlay', panel).css('right',\r
+// (handle.outerWidth() || 10) + 'px');\r
+// };\r
+// \r
+// prev = overlay;\r
+// });\r
+// \r
+// panel_root.bind('hpanel:panel-resize-start', resize_start);\r
+// self.rootDiv.bind('stopResize', function() {\r
+// self.savePanelOptions(); \r
+// });\r
+// \r
/*\r
* Connect panel actions\r
*/\r
onShow: $.fbind(self, self.loadSplitDialog)\r
}).\r
jqmAddClose('button.dialog-close-button');\r
-\r
-// $('#split-dialog'). \r
}\r
\r
Editor.prototype.loadRelatedIssues = function(hash)\r
function wrapLineNumberDiv(place) {
return function(node) {
-
var container = document.createElement("DIV"),
nums = document.createElement("DIV"),
scroller = document.createElement("DIV");
-
+ container.style.position = "relative";
nums.style.position = "absolute";
- nums.style.height = "100%";
+ nums.style.height = "100%";
if (nums.style.setExpression) {
try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
}
nums.style.top = "0px";
- nums.style.overflow = "hidden";
-
- container.style.position = "absolute";
- container.style.left = "0px";
- container.style.right = "0px";
- container.style.bottom = "0px";
- container.style.top = "0px";
-
- node.style.position = "absolute";
- node.style.top = "0px";
- node.style.right = "0px";
- node.style.bottom = "0px";
- node.style.left = "16px"
-
+ nums.style.overflow = "hidden";
place(container);
- container.appendChild(node);
+ container.appendChild(node);
container.appendChild(nums);
scroller.className = "CodeMirror-line-numbers";
nums.appendChild(scroller);
function applyLineNumbers(frame) {
var win = frame.contentWindow, doc = win.document,
- nums = frame.parentNode.nextSibling, scroller = nums.firstChild;
+ nums = frame.nextSibling, scroller = nums.firstChild;
var nextNum = 1, barWidth = null;
function sizeBar() {
- if (!frame.offsetWidth || !win.Editor) {
- for (var cur = frame; cur.parentNode; cur = cur.parentNode) {
- if (cur != document) {
- clearInterval(sizeInterval);
- return;
- }
- }
+ for (var root = frame; root.parentNode; root = root.parentNode);
+ if (root != document || !win.Editor) {
+ clearInterval(sizeInterval);
+ return;
}
if (nums.offsetWidth != barWidth) {
barWidth = nums.offsetWidth;
- // nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
+ nums.style.left = "-" + (frame.parentNode.style.marginLeft = barWidth + "px");
}
}
function update() {
frame.frameBorder = 0;
frame.src = "javascript:false;";
frame.style.border = "0";
- frame.style.width = "100%";
- frame.style.height = "100%";
+ frame.style.width = options.width;
+ frame.style.height = options.height;
// display: block occasionally suppresses some Firefox bugs, so we
// always add it, redundant as it sounds.
frame.style.display = "block";
var node = place;
place = function(n){node.appendChild(n);};
}
-
- var iframe_container = document.createElement("DIV");
- iframe_container.appendChild(frame);
-
- var content_wrapper = document.createElement("DIV");
- content_wrapper.appendChild(iframe_container);
- content_wrapper.style.position = 'relative';
- content_wrapper.className = 'CodeMirror-content-wrapper';
- content_wrapper.style.width = options.width;
- content_wrapper.style.height = options.height;
-
- iframe_container.style.position = 'absolute';
- iframe_container.style.top = '0px';
- iframe_container.style.right = '0px';
- iframe_container.style.bottom = '0px';
- iframe_container.style.left = '0px';
-
- if (options.lineNumbers) {
- iframe_container.style.left = '28px';
-
- var nums = document.createElement("DIV"),
- scroller = document.createElement("DIV");
-
- nums.style.position = "absolute";
- nums.style.height = "100%";
-
- nums.style.top = "0px";
- nums.style.left = "0px";
- nums.style.overflow = "hidden";
-
- scroller.className = "CodeMirror-line-numbers";
- nums.appendChild(scroller);
- content_wrapper.appendChild(nums);
-
- iframe_container.style.right = nums.width;
- }
- place(content_wrapper);
+ if (options.lineNumbers) place = wrapLineNumberDiv(place);
+ place(frame);
// Link back to this object, so that the editor can fetch options
// and add a reference to itself.
getCode: function() {return this.editor.getCode();},
setCode: function(code) {this.editor.importCode(code);},
- selection: function() {return this.editor.selectedText();},
+ selection: function() {this.focusIfIE(); return this.editor.selectedText();},
reindent: function() {this.editor.reindent();},
- reindentSelection: function() {this.editor.reindentSelection(null);},
+ reindentSelection: function() {this.focusIfIE(); this.editor.reindentSelection(null);},
+ focusIfIE: function() {
+ // in IE, a lot of selection-related functionality only works when the frame is focused
+ if (this.win.select.ie_selection) this.focus();
+ },
focus: function() {
this.win.focus();
if (this.editor.selectionSnapshot) // IE hack
setParser: function(name) {this.editor.setParser(name);},
- cursorPosition: function(start) {
- if (this.win.select.ie_selection) this.focus();
- return this.editor.cursorPosition(start);
- },
+ cursorPosition: function(start) {this.focusIfIE(); return this.editor.cursorPosition(start);},
firstLine: function() {return this.editor.firstLine();},
lastLine: function() {return this.editor.lastLine();},
nextLine: function(line) {return this.editor.nextLine(line);},
if (text.length) leaving = false;
result.push(node);
}
- else if (node.nodeName == "BR" && node.childNodes.length == 0) {
+ else if (isBR(node) && node.childNodes.length == 0) {
leaving = true;
result.push(node);
}
else {
forEach(node.childNodes, simplifyNode);
- if (!leaving && newlineElements.hasOwnProperty(node.nodeName)) {
+ if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
leaving = true;
if (!atEnd || !top)
result.push(doc.createElement("BR"));
nodeQueue.push(node);
return yield(node.currentText, c);
}
- else if (node.nodeName == "BR") {
+ else if (isBR(node)) {
nodeQueue.push(node);
return yield("\n", c);
}
// Determine the text size of a processed node.
function nodeSize(node) {
- if (node.nodeName == "BR")
- return 1;
- else
- return node.currentText.length;
+ return isBR(node) ? 1 : node.currentText.length;
}
// Search backwards through the top-level nodes until the next BR or
// the start of the frame.
function startOfLine(node) {
- while (node && node.nodeName != "BR") node = node.previousSibling;
+ while (node && !isBR(node)) node = node.previousSibling;
return node;
}
function endOfLine(node, container) {
if (!node) node = container.firstChild;
- else if (node.nodeName == "BR") node = node.nextSibling;
+ else if (isBR(node)) node = node.nextSibling;
- while (node && node.nodeName != "BR") node = node.nextSibling;
+ while (node && !isBR(node)) node = node.nextSibling;
return node;
}
function time() {return new Date().getTime();}
- // Replace all DOM nodes in the current selection with new ones.
- // Needed to prevent issues in IE where the old DOM nodes can be
- // pasted back into the document, still holding their old undo
- // information.
- function scrubPasted(container, start, start2) {
- var end = select.selectionTopNode(container, true),
- doc = container.ownerDocument;
- if (start != null && start.parentNode != container) start = start2;
- if (start === false) start = null;
- if (start == end || !end || !container.firstChild) return;
-
- var clear = traverseDOM(start ? start.nextSibling : container.firstChild);
- while (end.parentNode == container) try{clear.next();}catch(e){break;}
- forEach(clear.nodes, function(node) {
- var newNode = node.nodeName == "BR" ? doc.createElement("BR") : makePartSpan(node.currentText, doc);
- container.replaceChild(newNode, node);
- });
- }
-
// Client interface for searching the content of the editor. Create
// these by calling CodeMirror.getSearchCursor. To use, call
// findNext on the resulting object -- this returns a boolean
this.dirty = [];
if (options.content)
this.importCode(options.content);
- else // FF acts weird when the editable document is completely empty
- container.appendChild(this.doc.createElement("BR"));
if (!options.readOnly) {
if (options.continuousScanning !== false) {
function cursorActivity() {self.cursorActivity(false);}
addEventHandler(document.body, "mouseup", cursorActivity);
+ addEventHandler(document.body, "cut", cursorActivity);
+
addEventHandler(document.body, "paste", function(event) {
cursorActivity();
- if (internetExplorer) {
- var text = null;
- try {text = window.clipboardData.getData("Text");}catch(e){}
- if (text != null) {
- self.replaceSelection(text);
- event.stop();
- }
- else {
- var start = select.selectionTopNode(self.container, true),
- start2 = start && start.previousSibling;
- setTimeout(function(){scrubPasted(self.container, start, start2);}, 0);
- }
+ var text = null;
+ try {
+ var clipboardData = event.clipboardData || window.clipboardData;
+ if (clipboardData) text = clipboardData.getData('Text');
+ }
+ catch(e) {}
+ if (text !== null) {
+ self.replaceSelection(text);
+ event.stop();
}
});
- addEventHandler(document.body, "cut", cursorActivity);
+
+ addEventHandler(document.body, "beforepaste", method(this, "reroutePasteEvent"));
if (this.options.autoMatchParens)
addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
this.checkLine(line);
var accum = [];
for (line = line ? line.nextSibling : this.container.firstChild;
- line && line.nodeName != "BR"; line = line.nextSibling)
+ line && !isBR(line); line = line.nextSibling)
accum.push(nodeText(line));
return cleanText(accum.join(""));
},
before = cur;
break;
}
- var text = (cur.innerText || cur.textContent || cur.nodeValue || "");
+ var text = nodeText(cur);
if (text.length > position) {
before = cur.nextSibling;
content = text.slice(0, position) + content + text.slice(position);
// Replace the selection with another piece of text.
replaceSelection: function(text) {
this.history.commit();
+
var start = select.cursorPos(this.container, true),
end = select.cursorPos(this.container, false);
if (!start || !end) return;
end = this.replaceRange(start, end, text);
- select.setCursorPos(this.container, start, end);
+ select.setCursorPos(this.container, end);
+ webkitLastLineHack(this.container);
+ },
+
+ reroutePasteEvent: function() {
+ if (this.capturingPaste || window.opera) return;
+ this.capturingPaste = true;
+ var te = parent.document.createElement("TEXTAREA");
+ te.style.position = "absolute";
+ te.style.left = "-500px";
+ te.style.width = "10px";
+ te.style.top = nodeTop(frameElement) + "px";
+ parent.document.body.appendChild(te);
+ parent.focus();
+ te.focus();
+
+ var self = this;
+ this.parent.setTimeout(function() {
+ self.capturingPaste = false;
+ self.win.focus();
+ if (self.selectionSnapshot) // IE hack
+ self.win.select.selectCoords(self.win, self.selectionSnapshot);
+ var text = te.value;
+ if (text) self.replaceSelection(text);
+ removeElement(te);
+ }, 10);
},
replaceRange: function(from, to, text) {
// Intercept enter and tab, and assign their new functions.
keyDown: function(event) {
if (this.frozen == "leave") this.frozen = null;
- if (this.frozen && (!this.keyFilter || this.keyFilter(event))) {
+ if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) {
event.stop();
this.frozen(event);
return;
if (this.options.autoMatchParens)
this.scheduleParenBlink();
- // The variouschecks for !altKey are there because AltGr sets both
+ // The various checks for !altKey are there because AltGr sets both
// ctrlKey and altKey to true, and should not be recognised as
// Control.
if (code == 13) { // enter
// keydown event does not prevent the associated keypress event
// from happening, so we have to cancel enter and tab again
// here.
- if ((this.frozen && (!this.keyFilter || this.keyFilter(event))) ||
+ if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) ||
event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
(event.keyCode == 32 && event.shiftKey && this.options.tabMode == "default"))
event.stop();
else if (electric && electric.indexOf(event.character) != -1)
this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
+ else if ((event.character == "v" || event.character == "V")
+ && (event.ctrlKey || event.metaKey) && !event.altKey) // ctrl-V
+ this.reroutePasteEvent();
},
// Mark the node at the cursor dirty when a non-safe key is
home: function() {
var cur = select.selectionTopNode(this.container, true), start = cur;
- if (cur === false || !(!cur || cur.isPart || cur.nodeName == "BR") || !this.container.firstChild)
+ if (cur === false || !(!cur || cur.isPart || isBR(cur)) || !this.container.firstChild)
return false;
- while (cur && cur.nodeName != "BR") cur = cur.previousSibling;
+ while (cur && !isBR(cur)) cur = cur.previousSibling;
var next = cur ? cur.nextSibling : this.container.firstChild;
if (next && next != start && next.isPart && hasClass(next, "whitespace"))
select.focusAfterNode(next, this.container);
function tryFindMatch() {
var stack = [], ch, ok = true;;
for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
- if (runner.className == className && runner.nodeName == "SPAN" && (ch = paren(runner))) {
+ if (runner.className == className && isSpan(runner) && (ch = paren(runner))) {
if (forward(ch) == dir)
stack.push(ch);
else if (!stack.length)
ok = false;
if (!stack.length) break;
}
- else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") {
+ else if (runner.dirty || !isSpan(runner) && !isBR(runner)) {
return {node: runner, status: "dirty"};
}
}
// selection.
indentRegion: function(start, end, direction) {
var current = (start = startOfLine(start)), before = start && startOfLine(start.previousSibling);
- if (end.nodeName != "BR") end = endOfLine(end, this.container);
+ if (!isBR(end)) end = endOfLine(end, this.container);
do {
var next = endOfLine(current, this.container);
if (!this.options.readOnly) select.markSelection(this.win);
var start, endTime = force ? null : time() + this.options.passTime;
- while (time() < endTime && (start = this.getDirtyNode())) {
+ while ((time() < endTime || force) && (start = this.getDirtyNode())) {
var result = this.highlight(start, endTime);
if (result && result.node && result.dirty)
this.addDirtyNode(result.node);
// Backtrack to the first node before from that has a partial
// parse stored.
while (from && (!from.parserFromHere || from.dirty)) {
- if (maxBacktrack != null && from.nodeName == "BR" && (--maxBacktrack) < 0)
+ if (maxBacktrack != null && isBR(from) && (--maxBacktrack) < 0)
return false;
from = from.previousSibling;
}
function maybeTouch(node) {
if (node) {
- if (lineDirty || node.nextSibling != node.oldNextSibling)
+ var old = node.oldNextSibling;
+ if (lineDirty || old === undefined || node.nextSibling != old)
self.history.touch(node);
node.oldNextSibling = node.nextSibling;
}
else {
- if (lineDirty || self.container.firstChild != self.container.oldFirstChild)
- self.history.touch(node);
+ var old = self.container.oldFirstChild;
+ if (lineDirty || old === undefined || self.container.firstChild != old)
+ self.history.touch(null);
self.container.oldFirstChild = self.container.firstChild;
}
}
// Allow empty nodes when they are alone on a line, needed
// for the FF cursor bug workaround (see select.js,
// insertNewlineAtCursor).
- while (part && part.nodeName == "SPAN" && part.currentText == "") {
+ while (part && isSpan(part) && part.currentText == "") {
var old = part;
this.remove();
part = this.get();
if (token.value == "\n"){
// The idea of the two streams actually staying synchronized
// is such a long shot that we explicitly check.
- if (part.nodeName != "BR")
+ if (!isBR(part))
throw "Parser out of sync. Expected BR.";
if (part.dirty || !part.indentation) lineDirty = true;
parts.next();
}
else {
- if (part.nodeName != "SPAN")
+ if (!isSpan(part))
throw "Parser out of sync. Expected SPAN.";
if (part.dirty)
lineDirty = true;
while (pos && pos.offsetParent) {
y += pos.offsetTop;
// Don't count X offset for <br> nodes
- if (pos.nodeName != "BR")
+ if (!isBR(pos))
x += pos.offsetLeft;
pos = pos.offsetParent;
}
// Move the start of a range to the start of a node,
// compensating for the fact that you can't call
// moveToElementText with text nodes.
- function moveToNodeStart(range, node) {
+ function moveToNodeStart(range, node) {
if (node.nodeType == 3) {
var count = 0, cur = node.previousSibling;
while (cur && cur.nodeType == 3) {
}
if (cur) {
try{range.moveToElementText(cur);}
- catch(e){alert(cur + " " + cur.nodeType + " " + (cur && cur.outerHTML));}
+ catch(e){return false;}
range.collapse(false);
}
else range.moveToElementText(node.parentNode);
if (count) range.move("character", count);
}
- else try{range.moveToElementText(node);} catch(e) {};
+ else {
+ try{range.moveToElementText(node);}
+ catch(e){return false;}
+ }
+ return true;
}
// Do a binary search through the container object, comparing
// the start of each node to the selection
- var start = 0, end = container.childNodes.length;
- while (start != end) {
+ var start = 0, end = container.childNodes.length - 1;
+ while (start < end) {
var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
if (!node) return false; // Don't ask. IE6 manages this sometimes.
- moveToNodeStart(range2, node);
+ if (!moveToNodeStart(range2, node)) return false;
if (range.compareEndPoints("StartToStart", range2) == 1)
start = middle;
else
if (!selection) return null;
var topNode = select.selectionTopNode(container, start);
- while (topNode && topNode.nodeName != "BR")
+ while (topNode && !isBR(topNode))
topNode = topNode.previousSibling;
var range = selection.createRange(), range2 = range.duplicate();
// ancestors with a suitable offset. This goes down the DOM tree
// until a 'leaf' is reached (or is it *up* the DOM tree?).
function normalize(point){
- while (point.node.nodeType != 3 && point.node.nodeName != "BR") {
+ while (point.node.nodeType != 3 && !isBR(point.node)) {
var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
point.offset = 0;
while (!newNode && point.node.parentNode) {
};
select.selectMarked = function () {
- if (!currentSelection || !currentSelection.changed) return;
- var win = currentSelection.window, range = win.document.createRange();
+ var cs = currentSelection;
+ if (!(cs && (cs.changed || (webkit && cs.start.node == cs.end.node)))) return;
+ var win = cs.window, range = win.document.createRange();
function setPoint(point, which) {
if (point.node) {
}
}
- setPoint(currentSelection.end, "End");
- setPoint(currentSelection.start, "Start");
+ setPoint(cs.end, "End");
+ setPoint(cs.start, "Start");
selectRange(range, win);
};
var offset = start ? range.startOffset : range.endOffset;
// Work around (yet another) bug in Opera's selection model.
if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
- container.childNodes[range.startOffset] && container.childNodes[range.startOffset].nodeName == "BR")
+ container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
offset--;
// For text nodes, we look at the node itself if the cursor is
// Occasionally, browsers will return the HTML node as
// selection. If the offset is 0, we take the start of the frame
// ('after null'), otherwise, we take the last node.
- else if (node.nodeName == "HTML") {
+ else if (node.nodeName.toUpperCase() == "HTML") {
return (offset == 1 ? null : container.lastChild);
}
// If the given node is our 'container', we just look up the
if (!range) return;
var topNode = select.selectionTopNode(container, start);
- while (topNode && topNode.nodeName != "BR")
+ while (topNode && !isBR(topNode))
topNode = topNode.previousSibling;
range = range.cloneRange();
* An easy mistake to make is to let a StopIteration exception finish
* the token stream while there are still characters pending in the
* string stream (hitting the end of the buffer while parsing a
- * token). To make it easier to detect such errors, the strings throw
- * an exception when this happens.
+ * token). To make it easier to detect such errors, the stringstreams
+ * throw an exception when this happens.
*/
-// Make a string stream out of an iterator that returns strings. This
-// is applied to the result of traverseDOM (see codemirror.js), and
-// the resulting stream is fed to the parser.
+// Make a stringstream stream out of an iterator that returns strings.
+// This is applied to the result of traverseDOM (see codemirror.js),
+// and the resulting stream is fed to the parser.
window.stringStream = function(source){
// String that's currently being iterated over.
var current = "";
// Commit unless there are pending dirty nodes.
tryCommit: function() {
if (!window.History) return; // Stop when frame has been unloaded
- if (this.editor.highlightDirty()) this.commit();
+ if (this.editor.highlightDirty()) this.commit(true);
else this.scheduleCommit();
},
function buildLine(node) {
var text = [];
for (var cur = node ? node.nextSibling : self.container.firstChild;
- cur && cur.nodeName != "BR"; cur = cur.nextSibling)
+ cur && !isBR(cur); cur = cur.nextSibling)
if (cur.currentText) text.push(cur.currentText);
return {from: node, to: cur, text: cleanText(text.join(""))};
}
// Get the BR element after/before the given node.
function nextBR(node, dir) {
var link = dir + "Sibling", search = node[link];
- while (search && search.nodeName != "BR")
+ while (search && !isBR(search))
search = search[link];
return search;
}
function nodeText(node) {
return node.textContent || node.innerText || node.nodeValue || "";
}
+
+function nodeTop(node) {
+ var top = 0;
+ while (node.offsetParent) {
+ top += node.offsetTop;
+ node = node.offsetParent;
+ }
+ return top;
+}
+
+function isBR(node) {
+ var nn = node.nodeName;
+ return nn == "BR" || nn == "br";
+}
+function isSpan(node) {
+ var nn = node.nodeName;
+ return nn == "SPAN" || nn == "span";
+}
--- /dev/null
+/*globals Editor fileId SplitView PanelContainerView EditorView*/
+var documentsUrl = '/api/documents/';
+
+
+Editor.Model = Editor.Object.extend({
+ synced: false,
+ data: null
+});
+
+
+Editor.ToolbarButtonsModel = Editor.Model.extend({
+ _className: 'Editor.ToolbarButtonsModel',
+ serverURL: '/api/toolbar/buttons',
+ buttons: {},
+
+ init: function() {
+ this._super();
+ },
+
+ load: function() {
+ if (!this.get('buttons').length) {
+ $.ajax({
+ url: this.serverURL,
+ dataType: 'json',
+ success: this.loadSucceeded.bind(this)
+ });
+ }
+ },
+
+ loadSucceeded: function(data) {
+ this.set('buttons', data);
+ }
+});
+
+
+// Stany modelu:
+//
+// empty -> loading -> synced -> unsynced -> loading
+// \
+// -> dirty -> updating -> updated -> synced
+//
+Editor.XMLModel = Editor.Model.extend({
+ _className: 'Editor.XMLModel',
+ serverURL: null,
+ data: '',
+ state: 'empty',
+
+ init: function(serverURL) {
+ this._super();
+ this.set('state', 'empty');
+ this.serverURL = serverURL;
+ this.toolbarButtonsModel = new Editor.ToolbarButtonsModel();
+ this.addObserver(this, 'data', this.dataChanged.bind(this));
+ },
+
+ load: function() {
+ if (this.get('state') == 'empty') {
+ this.set('state', 'loading');
+ $.ajax({
+ url: this.serverURL,
+ dataType: 'text',
+ success: this.loadingSucceeded.bind(this)
+ });
+ return true;
+ }
+ return false;
+ },
+
+ update: function(message) {
+ if (this.get('state') == 'dirty') {
+ this.set('state', 'updating');
+
+ var payload = {
+ contents: this.get('data')
+ };
+ if (message) {
+ payload.message = message;
+ }
+
+ $.ajax({
+ url: this.serverURL,
+ type: 'put',
+ dataType: 'json',
+ data: payload,
+ success: this.updatingSucceeded.bind(this),
+ error: this.updatingFailed.bind(this)
+ });
+ return true;
+ }
+ return false;
+ },
+
+ updatingSucceeded: function() {
+ if (this.get('state') != 'updating') {
+ alert('erroneous state:', this.get('state'));
+ }
+ this.set('state', 'updated');
+ },
+
+ updatingFailed: function() {
+ if (this.get('state') != 'updating') {
+ alert('erroneous state:', this.get('state'));
+ }
+ this.set('state', 'dirty');
+ },
+
+ set: function(property, value) {
+ if (property == 'state') {
+ console.log(this.description(), ':', property, '=', value);
+ }
+ return this._super(property, value);
+ },
+
+ dataChanged: function(property, value) {
+ if (this.get('state') == 'synced') {
+ this.set('state', 'dirty');
+ }
+ },
+
+ loadingSucceeded: function(data) {
+ if (this.get('state') != 'loading') {
+ alert('erroneous state:', this.get('state'));
+ }
+ this.set('data', data);
+ this.set('state', 'synced');
+ },
+
+ dispose: function() {
+ this.removeObserver(this);
+ this._super();
+ }
+});
+
+
+Editor.HTMLModel = Editor.Model.extend({
+ _className: 'Editor.HTMLModel',
+ serverURL: null,
+ data: '',
+ state: 'empty',
+
+ init: function(serverURL) {
+ this._super();
+ this.set('state', 'empty');
+ this.serverURL = serverURL;
+ },
+
+ load: function() {
+ if (this.get('state') == 'empty') {
+ this.set('state', 'loading');
+ $.ajax({
+ url: this.serverURL,
+ dataType: 'text',
+ success: this.loadingSucceeded.bind(this)
+ });
+ }
+ },
+
+ loadingSucceeded: function(data) {
+ if (this.get('state') != 'loading') {
+ alert('erroneous state:', this.get('state'));
+ }
+ this.set('data', data);
+ this.set('state', 'synced');
+ },
+
+ set: function(property, value) {
+ if (property == 'state') {
+ console.log(this.description(), ':', property, '=', value);
+ }
+ return this._super(property, value);
+ }
+});
+
+
+Editor.DocumentModel = Editor.Model.extend({
+ _className: 'Editor.DocumentModel',
+ data: null, // name, text_url, latest_rev, latest_shared_rev, parts_url, dc_url, size
+ contentModels: {},
+ state: 'empty',
+
+ init: function() {
+ this._super();
+ this.set('state', 'empty');
+ this.load();
+ },
+
+ load: function() {
+ if (this.get('state') == 'empty') {
+ this.set('state', 'loading');
+ $.ajax({
+ cache: false,
+ url: documentsUrl + fileId,
+ dataType: 'json',
+ success: this.successfulLoad.bind(this)
+ });
+ }
+ },
+
+ successfulLoad: function(data) {
+ this.set('data', data);
+ this.set('state', 'synced');
+ this.contentModels = {
+ 'xml': new Editor.XMLModel(data.text_url),
+ 'html': new Editor.HTMLModel(data.html_url)
+ };
+ for (var key in this.contentModels) {
+ this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
+ }
+ },
+
+ contentModelStateChanged: function(property, value, contentModel) {
+ if (value == 'dirty') {
+ for (var key in this.contentModels) {
+ if (this.contentModels[key].guid() != contentModel.guid()) {
+ // console.log(this.contentModels[key].description(), 'frozen');
+ this.contentModels[key].set('state', 'unsynced');
+ }
+ }
+ }
+ },
+
+ quickSave: function(message) {
+ for (var key in this.contentModels) {
+ if (this.contentModels[key].get('state') == 'dirty') {
+ this.contentModels[key].update(message);
+ break;
+ }
+ }
+ }
+});
+
+
+var leftPanelView, rightPanelContainer, doc;
+
+$(function() {
+ doc = new Editor.DocumentModel();
+ var editor = new EditorView('#body-wrap', doc);
+ editor.freeze();
+ var splitView = new SplitView('#splitview', doc);
+ leftPanelView = new PanelContainerView('#left-panel-container', doc);
+ rightPanelContainer = new PanelContainerView('#right-panel-container', doc);
+});
--- /dev/null
+/*globals View render_template scriptletCenter*/
+var ButtonToolbarView = View.extend({
+ _className: 'ButtonToolbarView',
+ template: null,
+ buttons: null,
+
+ init: function(element, model, parent, template) {
+ this._super(element, model, null);
+ this.parent = parent;
+ this.template = 'button-toolbar-view-template';
+
+ this.model.addObserver(this, 'buttons', this.modelButtonsChanged.bind(this));
+ this.buttons = this.model.get('buttons');
+ this.model.load();
+ this.render();
+ },
+
+ modelButtonsChanged: function(property, value) {
+ this.set('buttons', value);
+ this.render();
+ },
+
+ render: function() {
+ $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
+ $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
+ var self = this;
+
+ this.element.html(render_template(this.template, this));
+
+ $('.buttontoolbarview-tab', this.element).bind('click.buttontoolbarview', function() {
+ var groupIndex = $(this).attr('ui:groupindex');
+ $('.buttontoolbarview-group', self.element).each(function() {
+ if ($(this).attr('ui:groupindex') == groupIndex) {
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ });
+ $(self.element).trigger('resize');
+ });
+
+ $('.buttontoolbarview-button', this.element).bind('click.buttontoolbarview', function() {
+ var groupIndex = parseInt($(this).attr('ui:groupindex'), 10);
+ var buttonIndex = parseInt($(this).attr('ui:buttonindex'), 10);
+ var button = self.get('buttons')[groupIndex].buttons[buttonIndex];
+ var scriptletId = button.scriptlet_id;
+ var params = eval('(' + button.params + ')'); // To nie powinno być potrzebne
+ console.log('Executing', scriptletId, 'with params', params);
+ scriptletCenter[scriptletId](self.parent, params);
+ });
+
+ $(this.element).trigger('resize');
+ },
+
+ dispose: function() {
+ $('.buttontoolbarview-tab', this.element).unbind('click.buttontoolbarview');
+ $('.buttontoolbarview-button', this.element).unbind('click.buttontoolbarview');
+ this._super();
+ }
+});
+
--- /dev/null
+/*global View render_template panels */
+var EditorView = View.extend({
+ _className: 'EditorView',
+ element: null,
+ model: null,
+ template: null,
+
+ init: function(element, model, template) {
+ this._super(element, model, template);
+ this.model.load();
+
+ $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this));
+ $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this));
+ $('#action-update', this.element).bind('click.editorview', this.update.bind(this));
+ this.freeze('Ładowanie');
+ },
+
+ quickSave: function(event) {
+ console.log('quickSave');
+ this.model.quickSave();
+ },
+
+ commit: function(event) {
+ console.log('commit');
+ },
+
+ update: function(event) {
+ console.log('update');
+ },
+
+ dispose: function() {
+ $('#action-quick-save', this.element).unbind('click.editorview');
+ $('#action-commit', this.element).unbind('click.editorview');
+ $('#action-update', this.element).unbind('click.editorview');
+ this._super();
+ }
+});
--- /dev/null
+/*global View render_template panels */
+var HTMLView = View.extend({
+ _className: 'HTMLView',
+ element: null,
+ model: null,
+ template: 'html-view-template',
+
+ init: function(element, model, parent, template) {
+ this._super(element, model, template);
+ this.parent = parent;
+
+ this.model
+ .addObserver(this, 'data', this.modelDataChanged.bind(this))
+ .addObserver(this, 'state', this.modelStateChanged.bind(this));
+
+ $('.htmlview', this.element).html(this.model.get('data'));
+ this.modelStateChanged('state', this.model.get('state'));
+ this.model.load();
+ },
+
+ modelDataChanged: function(property, value) {
+ $('.htmlview', this.element).html(value);
+ },
+
+ modelStateChanged: function(property, value) {
+ if (value == 'synced' || value == 'dirty') {
+ this.parent.unfreeze();
+ } else if (value == 'unsynced') {
+ this.parent.freeze('Niezsynchronizowany...');
+ } else if (value == 'loading') {
+ this.parent.freeze('Ładowanie...');
+ } else if (value == 'saving') {
+ this.parent.freeze('Zapisywanie...');
+ }
+ },
+
+ dispose: function() {
+ this.model.removeObserver(this);
+ this._super();
+ }
+});
+
+// Register view
+panels['html'] = HTMLView;
\ No newline at end of file
--- /dev/null
+/*globals View render_template panels*/
+
+var PanelContainerView = View.extend({
+ _className: 'PanelContainerView',
+ element: null,
+ model: null,
+ template: 'panel-container-view-template',
+ contentView: null,
+
+ init: function(element, model, template) {
+ this._super(element, model, template);
+
+ $('select', this.element.get(0)).bind('change.panel-container-view', this.selectChanged.bind(this));
+ },
+
+ selectChanged: function(event) {
+ var value = $('select', this.element.get(0)).val();
+ var klass = panels[value];
+ if (this.contentView) {
+ this.contentView.dispose();
+ this.contentView = null;
+ }
+ this.contentView = new klass($('.content-view',
+ this.element.get(0)), this.model.contentModels[value], this);
+ },
+
+ dispose: function() {
+ $('select', this.element.get(0)).unbind('change.panel-container-view');
+ this._super();
+ }
+});
+
--- /dev/null
+/*globals View*/
+
+// Split view inspired by jQuery Splitter Plugin http://methvin.com/splitter/
+var SplitView = View.extend({
+ _className: 'SplitView',
+ splitbarClass: 'splitview-splitbar',
+ activeClass: 'splitview-active',
+ overlayClass: 'splitview-overlay',
+ element: null,
+ model: null,
+ zombie: null,
+ leftViewOffset: 0,
+
+ // Cache
+ _splitbarWidth: 0,
+
+ init: function(element, model) {
+ this._super(element, model, null);
+ this.element.css('position', 'relative');
+ this._resizingSubviews = false;
+
+ this.views = $(">*", this.element[0]).css({
+ position: 'absolute', // positioned inside splitter container
+ 'z-index': 1, // splitbar is positioned above
+ '-moz-outline-style': 'none', // don't show dotted outline
+ overflow: 'auto'
+ });
+
+ this.leftView = $(this.views[0]);
+ this.rightView = $(this.views[1]);
+ this.splitbar = $(this.views[2] || '<div></div>')
+ .insertAfter(this.leftView)
+ .css({
+ position: 'absolute',
+ 'user-select': 'none',
+ '-webkit-user-select': 'none',
+ '-khtml-user-select': 'none',
+ '-moz-user-select': 'none',
+ 'z-index': 100
+ })
+ .attr('unselectable', 'on')
+ .addClass(this.splitbarClass)
+ .bind('mousedown.splitview', this.beginResize.bind(this));
+
+ this._splitbarWidth = this.splitbar.outerWidth();
+
+ // Solomon's algorithm ;-)
+ this.resplit(this.element.width() / 2);
+ },
+
+ beginResize: function(event) {
+ this.zombie = this.zombie || this.splitbar.clone(false).insertAfter(this.leftView);
+ this.overlay = this.overlay || $('<div></div>').addClass(this.overlayClass).css({
+ position: 'absolute',
+ width: this.element.width(),
+ height: this.element.height(),
+ top: this.element.position().top,
+ left: this.element.position().left
+ }).appendTo(this.element);
+ this.views.css("-webkit-user-select", "none"); // Safari selects A/B text on a move
+ this.splitbar.addClass(this.activeClass);
+ this.leftViewOffset = this.leftView[0].offsetWidth - event.pageX;
+
+ $(document)
+ .bind('mousemove.splitview', this.resizeChanged.bind(this))
+ .bind('mouseup.splitview', this.endResize.bind(this));
+ },
+
+ resizeChanged: function(event) {
+ var newPosition = event.pageX + this.leftViewOffset;
+ newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+ this.splitbar.css('left', newPosition);
+ },
+
+ endResize: function(event) {
+ var newPosition = event.pageX + this.leftViewOffset;
+ this.zombie.remove();
+ this.zombie = null;
+ this.overlay.remove();
+ this.overlay = null;
+ this.resplit(newPosition);
+
+ $(document)
+ .unbind('mousemove.splitview')
+ .unbind('mouseup.splitview');
+ },
+
+ resized: function(event) {
+ if (!this._resizingSubviews) {
+ this.resplit(Math.min(this.leftView.width(), this.element.width() - this._splitbarWidth));
+ }
+ },
+
+ resplit: function(newPosition) {
+ newPosition = Math.max(0, Math.min(newPosition, this.element.width() - this._splitbarWidth));
+ this.splitbar.css('left', newPosition);
+ this.leftView.css({
+ left: 0,
+ width: newPosition
+ });
+ this.rightView.css({
+ left: newPosition + this._splitbarWidth,
+ width: this.element.width() - newPosition - this._splitbarWidth
+ });
+ if (!$.browser.msie) {
+ this._resizingSubviews = true;
+ $(window).trigger('resize');
+ this._resizingSubviews = false;
+ }
+ },
+
+ dispose: function() {
+ this.splitter.unbind('mousedown.splitview');
+ this._super();
+ }
+});
+
--- /dev/null
+/*globals Editor render_template*/
+var View = Editor.Object.extend({
+ _className: 'View',
+ element: null,
+ model: null,
+ template: null,
+ overlayClass: 'view-overlay',
+ overlay: null,
+
+ init: function(element, model, template) {
+ this.element = $(element);
+ this.model = model;
+ this.template = template || this.template;
+
+ if (this.template) {
+ this.element.html(render_template(this.template, this));
+ }
+
+ this._resizeHandler = this.resized.bind(this);
+ $(window).bind('resize', this._resizeHandler);
+ $(this.element).bind('resize', this._resizeHandler);
+ },
+
+ frozen: function() {
+ return !!this.overlay;
+ },
+
+ freeze: function(message) {
+ this.overlay = this.overlay
+ || $('<div><div>' + message + '</div></div>')
+ .addClass(this.overlayClass)
+ .css({
+ position: 'absolute',
+ width: this.element.width(),
+ height: this.element.height(),
+ top: this.element.position().top,
+ left: this.element.position().left,
+ 'user-select': 'none',
+ '-webkit-user-select': 'none',
+ '-khtml-user-select': 'none',
+ '-moz-user-select': 'none',
+ overflow: 'hidden'
+ })
+ .attr('unselectable', 'on')
+ .appendTo(this.element.parent());
+
+ this.overlay.children('div').css({
+ position: 'relative',
+ top: this.overlay.height() / 2 - 20
+ });
+ },
+
+ unfreeze: function() {
+ if (this.frozen()) {
+ this.overlay.remove();
+ this.overlay = null;
+ }
+ },
+
+ resized: function(event) {
+ if (this.frozen()) {
+ this.overlay.css({
+ position: 'absolute',
+ width: this.element.width(),
+ height: this.element.height(),
+ top: this.element.position().top,
+ left: this.element.position().left
+ }).children('div').css({
+ position: 'relative',
+ top: this.overlay.height() / 2 - 20
+ });
+ }
+ },
+
+ dispose: function() {
+ $(window).unbind('resize', this._resizeHandler);
+ $(this.element).unbind('resize', this._resizeHandler);
+ this.unfreeze();
+ this.element.contents().remove();
+ }
+});
--- /dev/null
+/*global View CodeMirror ButtonToolbarView render_template panels */
+var XMLView = View.extend({
+ _className: 'XMLView',
+ element: null,
+ model: null,
+ template: 'xml-view-template',
+ editor: null,
+ buttonToolbar: null,
+
+ init: function(element, model, parent, template) {
+ this._super(element, model, template);
+ this.parent = parent;
+ this.buttonToolbar = new ButtonToolbarView(
+ $('.xmlview-toolbar', this.element),
+ this.model.toolbarButtonsModel, parent);
+
+ $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this));
+
+ this.parent.freeze('Ładowanie edytora...');
+ this.editor = new CodeMirror($('.xmlview', this.element).get(0), {
+ parserfile: 'parsexml.js',
+ path: "/static/js/lib/codemirror/",
+ stylesheet: "/static/css/xmlcolors.css",
+ parserConfig: {useHTMLKludges: false},
+ textWrapping: false,
+ tabMode: 'spaces',
+ indentUnit: 0,
+ onChange: this.editorDataChanged.bind(this),
+ initCallback: this.editorDidLoad.bind(this)
+ });
+ },
+
+ resized: function(event) {
+ var height = this.element.height() - $('.xmlview-toolbar', this.element).outerHeight();
+ $('.xmlview', this.element).height(height);
+ },
+
+ editorDidLoad: function(editor) {
+ $(editor.frame).css({width: '100%', height: '100%'});
+ this.model
+ .addObserver(this, 'data', this.modelDataChanged.bind(this))
+ .addObserver(this, 'state', this.modelStateChanged.bind(this))
+ .load();
+
+ this.parent.unfreeze();
+
+ this.editor.setCode(this.model.get('data'));
+ this.modelStateChanged('state', this.model.get('state'));
+
+ // editor.grabKeys(
+ // $.fbind(self, self.hotkeyPressed),
+ // $.fbind(self, self.isHotkey)
+ // );
+ },
+
+ editorDataChanged: function() {
+ this.model.set('data', this.editor.getCode());
+ },
+
+ modelDataChanged: function(property, value) {
+ console.log('modelDataChanged');
+ if (this.editor.getCode() != value) {
+ this.editor.setCode(value);
+ }
+ },
+
+ modelStateChanged: function(property, value) {
+ if (value == 'synced' || value == 'dirty') {
+ this.parent.unfreeze();
+ } else if (value == 'unsynced') {
+ this.parent.freeze('Niezsynchronizowany...');
+ } else if (value == 'loading') {
+ this.parent.freeze('Ładowanie...');
+ } else if (value == 'saving') {
+ this.parent.freeze('Zapisywanie...');
+ }
+ },
+
+ dispose: function() {
+ this.model.removeObserver(this);
+ $(this.editor.frame).remove();
+ this._super();
+ }
+});
+
+// Register view
+panels['xml'] = XMLView;
{% endblock %}
</head>
<body id="{% block bodyid %}base{% endblock %}">
+ <div id="body-wrap">
<div id="header">
<span id="breadcrumbs">{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a>{% endblock breadcrumbs %}</span>
<span id="header-right-toolbar">
<div id="content">{% block maincontent %} {% endblock %}</div>
{% block extrabody %}{% endblock %}
+ </div>
</body>
</html>
{% extends "base.html" %}
{% block extrahead %}
+ <link rel="stylesheet" href="{{STATIC_URL}}css/html.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" charset="utf-8">
var fileId = '{{ fileid }}';
</script>
- <link rel="stylesheet" href="{{STATIC_URL}}css/toolbar.css" type="text/css" />
- <link rel="stylesheet" href="{{STATIC_URL}}css/jquery.modal.css" type="text/css" />
- <script src="{{STATIC_URL}}js/lib/jquery.lazyload.js" type="text/javascript" charset="utf-8"></script>
<script src="{{STATIC_URL}}js/lib/codemirror/codemirror.js" type="text/javascript" charset="utf-8"></script>
- <script src="{{STATIC_URL}}js/lib/jquery.wtooltip.js" type="text/javascript" charset="utf-8"></script>
- <script src="{{STATIC_URL}}js/lib/jquery.json.js" type="text/javascript" charset="utf-8"></script>
- <script src="{{STATIC_URL}}js/lib/jquery.cookie.js" type="text/javascript" charset="utf-8"></script>
- <script src="{{STATIC_URL}}js/lib/jquery.modal.js" type="text/javascript" charset="utf-8"></script>
-
- <script src="{{STATIC_URL}}js/editor.js" type="text/javascript" charset="utf-8"></script>
- <script src="{{STATIC_URL}}js/editor.ui.js" type="text/javascript" charset="utf-8"></script>
+
+ {# Scriptlets #}
+ <script src="http://localhost:8000/api/toolbar/scriptlets" type="text/javascript" charset="utf-8"></script>
+
+ {# App and views #}
+ <script src="{{STATIC_URL}}js/app.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/view.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/editor.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/button_toolbar.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/split.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/xml.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/html.js" type="text/javascript" charset="utf-8"></script>
+ <script src="{{STATIC_URL}}js/views/panel_container.js" type="text/javascript" charset="utf-8"></script>
+
+ <script src="{{STATIC_URL}}js/models.js" type="text/javascript" charset="utf-8"></script>
+
+ {# JavaScript templates #}
+ <script type="text/html" charset="utf-8" id="panel-container-view-template">
+ <select>
+ <% for (panel in panels) { %>
+ <option value="<%= panel %>"><%= panel %></option>
+ <% }; %>
+ </select>
+ <div class="content-view"></div>
+ </script>
+
+ <script type="text/html" charset="utf-8" id="xml-view-template">
+ <div class="xmlview-toolbar"></div>
+ <div class="xmlview">
+
+ </div>
+ </script>
+
+ <script type="text/html" charset="utf-8" id="html-view-template">
+ <div class="htmlview">
+ </div>
+ </script>
+
+ <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
+ <div class="buttontoolbarview">
+ <div class="buttontoolbarview-tabs">
+ <% for (var i=0; i < buttons.length; i++) { %>
+ <a href="#" class="buttontoolbarview-tab" ui:groupindex="<%= i %>"><%= buttons[i].name %></a>
+ <% }; %>
+ </div>
+ <div class="buttontoolbarview-groups">
+ <% for (var i=0; i < buttons.length; i++) { %>
+ <div class="buttontoolbarview-group" ui:groupIndex="<%= i %>" style="display: none">
+ <% for (var j=0; j < buttons[i].buttons.length; j++) { %>
+ <% if (buttons[i].buttons[j].scriptlet_id) { %>
+ <a href="#" class="buttontoolbarview-button" ui:groupindex="<%= i %>" ui:buttonindex="<%= j %>">
+ <%= buttons[i].buttons[j].label %>
+ </a>
+ <% } %>
+ <% } %>
+ </div>
+ <% }; %>
+ </div>
+ </div>
+ </script>
{% endblock extrahead %}
-{% block extrabody %}
- <script type="text/javascript" charset="utf-8">
- $(function() {
- {% for scriptlet in scriptlets %}
- editor.registerScriptlet("{{scriptlet.name}}",
- function(editor, panel, params) {
- {{ scriptlet.code|safe }}
- });
- {% endfor %}
- });
- </script>
-{% endblock extrabody %}
-
-{% load explorer_tags %}
-
-{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> > {{ fileid|bookname }}{% endblock breadcrumbs %}
-
-{% block header-toolbar %}
-<button type="button" class="requires-save toolbar-button" id="toolbar-button-commit">Merge</button>
-<button type="button" class="requires-save toolbar-button" id="toolbar-button-update"
- ui:ajax-action="{% url file_update fileid %}">Update</button>
-<button type="button" class="provides-save toolbar-button" id="toolbar-button-save" disabled="disabled">Commit</button>
-<button type="button" class="provides-save toolbar-button" id="toolbar-button-quick-save" disabled="disabled">Quick Save</button>
-{% endblock %}
+{% block breadcrumbs %}<a href="{% url file_list %}">Platforma Redakcyjna</a> > {{ fileid }}{% endblock breadcrumbs %}
{% block message-box %}
<div class="msg-info" id="save-waiting"><p>Zapisuję dane na serwerze.</p><p class="data"></p></div>
{% endblock %}
-{% block maincontent %}
- <div id="panels">
- {% for n in panel_list %}
- <div class="panel-wrap{% if forloop.last %} last-panel{% endif %}" id="panel-{{n}}">
- <div class="panel-toolbar">
- <p><label>{{n|capfirst}} panel:
- <select name="select-{{n}}-panel" id="panel-{{n}}-select">
- {% for panel_type in availble_panels %}
- <option value="{% url panel_view path=fileid,name=panel_type.id %}" p:panel-name="{{ panel_type.id }}">{{panel_type.display_name}}</option>
- {% endfor %}
- </select>
- </label>
+{% block header-toolbar %}
+ <button id="action-update">Update</button> <button id="action-commit">Commit</button> <button id="action-quick-save">Quick Save</button>
+{% endblock %}
- <span
- <span class="toolbar-buttons-container panel-toolbar-extra">
- </span>
- </span>
- <!-- rethink the refresh button - it doesn't work very well -->
- <!-- <button type="button" class="refresh-button">Odśwież</button> -->
- {# <a href="{% url print_xml fileid %}" target="_new">Wydruk</a> #}
- <strong class="change-notification" style="display: none">Widok nieaktualny!</strong>
- </p>
- </div>
- <div id="panel-{{n}}-content" class="panel-content"></div>
- <button type="button" class="panel-slider"></button>
- </div>
- {% endfor %}
- </div>
+{% block maincontent %}
+ <div id="splitview">
+ <div id="left-panel-container" class='panel-container'></div>
+ <div id="right-panel-container" class='panel-container'></div>
+ </div>
- <div id="commit-dialog" class="jqmWindow">
- <form action="{% url file_commit fileid %}" method="POST">
- <label for="message">Commit message:</label>
- <textarea cols="60" rows="10" name="message" id="commit-dialog-message"></textarea>
- <p id="commit-dialog-error-empty-message">Wiadomość nie może być pusta.</p>
- <fieldset id="commit-dialog-related-issues"
- ui:ajax-src="http://localhost:3000/publications/issues/{{fileid}}">
- <legend>Related issues</legend>
- <div class="loading-box" style="display: none;">
- <p>Loading related issues...</p>
- </div>
- <div class="container-box">No related issues.</div>
- </fieldset>
- <p>
- <input type="button" value="Save" id="commit-dialog-save-button" />
- <input type="reset" value="Cancel" id="commit-dialog-cancel-button" />
- </p>
- </form>
- </div>
- <div id="split-dialog" class="jqmWindow">
- <div class="container-box"> </div>
- <div class="loading-box" style="display: none;">
- <p>Loading dialog contents...</p>
- <!-- <p><button type="button" class="dialog-close-button">Close</button></p> -->
- </div>
- <div class="fatal-error-box" style="display: none;">
- <p>Server error, while loading dialog :(</p>
- <p><button type="button" class="dialog-close-button">Close</button></p>
- </div>
- </div>
-{% endblock maincontent %}
+ {# <div id="commit-dialog" class="jqmWindow"> #}
+ {# <form action="{% url file_commit fileid %}" method="POST"> #}
+ {# <label for="message">Commit message:</label> #}
+ {# <textarea cols="60" rows="10" name="message" id="commit-dialog-message"></textarea> #}
+ {# <p id="commit-dialog-error-empty-message">Wiadomość nie może być pusta.</p> #}
+ {# <fieldset id="commit-dialog-related-issues" #}
+ {# ui:ajax-src="http://localhost:3000/publications/issues/{{fileid}}"> #}
+ {# <legend>Related issues</legend> #}
+ {# <div class="loading-box" style="display: none;"> #}
+ {# <p>Loading related issues...</p> #}
+ {# </div> #}
+ {# <div class="container-box">No related issues.</div> #}
+ {# </fieldset> #}
+ {# <p> #}
+ {# <input type="button" value="Save" id="commit-dialog-save-button" /> #}
+ {# <input type="reset" value="Cancel" id="commit-dialog-cancel-button" /> #}
+ {# </p> #}
+ {# </form> #}
+ {# </div> #}
+ {# #}
+ {# <div id="split-dialog" class="jqmWindow"> #}
+ {# <div class="container-box"> </div> #}
+ {# <div class="loading-box" style="display: none;"> #}
+ {# <p>Loading dialog contents...</p> #}
+ {# <!-- <p><button type="button" class="dialog-close-button">Close</button></p> --> #}
+ {# </div> #}
+ {# <div class="fatal-error-box" style="display: none;"> #}
+ {# <p>Server error, while loading dialog :(</p> #}
+ {# <p><button type="button" class="dialog-close-button">Close</button></p> #}
+ {# </div> #}
+ {# </div> #}
+{% endblock maincontent %}