2 'libs/jquery-1.9.1.min',
4 'modules/documentCanvas/canvas/documentElement'
5 ], function($, _, documentElement) {
9 var Canvas = function(wlxml) {
10 this.loadWlxml(wlxml);
13 $.extend(Canvas.prototype, {
15 loadWlxml: function(wlxml) {
16 var d = wlxml ? $($.trim(wlxml)) : null;
18 var wrapper = $('<div>');
21 wrapper.find('*').replaceWith(function() {
22 var currentTag = $(this);
23 if(currentTag.attr('wlxml-tag'))
25 var toret = $('<div>')
26 .attr('wlxml-tag', currentTag.prop('tagName').toLowerCase());
27 //toret.attr('id', 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {var r = Math.random()*16|0,v=c=='x'?r:r&0x3|0x8;return v.toString(16);}));
28 for(var i = 0; i < this.attributes.length; i++) {
29 var attr = this.attributes.item(i);
30 var value = attr.name === 'class' ? attr.value.replace(/\./g, '-') : attr.value;
31 toret.attr('wlxml-' + attr.name, value);
33 toret.append(currentTag.contents());
37 wrapper.find(':not(iframe)').addBack().contents()
38 .filter(function() {return this.nodeType === Node.TEXT_NODE})
43 // TODO: use DocumentElement API
44 var spanParent = el.parent().attr('wlxml-tag') === 'span',
45 spanBefore = el.prev().length > 0 && $(el.prev()[0]).attr('wlxml-tag') === 'span',
46 spanAfter = el.next().length > 0 && $(el.next()[0]).attr('wlxml-tag') === 'span';
48 if(spanParent || spanBefore || spanAfter) {
49 var startSpace = /\s/g.test(this.data.substr(0,1));
50 var endSpace = /\s/g.test(this.data.substr(-1)) && this.data.length > 1;
51 var trimmed = $.trim(this.data);
52 this.data = (startSpace && (spanParent || spanBefore) ? ' ' : '')
54 + (endSpace && (spanParent || spanAfter) ? ' ' : '');
57 var oldLength = this.data.length;
58 this.data = $.trim(this.data);
59 if(this.data.length === 0 && oldLength > 0 && el.parent().contents().length === 1)
61 if(this.data.length === 0) {
63 return true; // continue
67 var wrapper = documentElement.DocumentTextElement.create({text: this.data});
68 $(this).before(wrapper.dom());
72 this.d = wrapper.children(0);
82 return documentElement.DocumentNodeElement.fromHTMLElement(this.d.get(0), this); //{wlxmlTag: this.d.prop('tagName')};
85 wrapText: function(params) {
86 params = _.extend({textNodeIdx: 0}, params);
87 if(typeof params.textNodeIdx === 'number')
88 params.textNodeIdx = [params.textNodeIdx];
90 var childrenInside = params.inside.children(),
91 idx1 = Math.min.apply(Math, params.textNodeIdx),
92 idx2 = Math.max.apply(Math, params.textNodeIdx),
93 textNode1 = childrenInside[idx1],
94 textNode2 = childrenInside[idx2],
95 sameNode = textNode1.sameNode(textNode2),
96 prefixOutside = textNode1.getText().substr(0, params.offsetStart),
97 prefixInside = textNode1.getText().substr(params.offsetStart),
98 suffixInside = textNode2.getText().substr(0, params.offsetEnd),
99 suffixOutside = textNode2.getText().substr(params.offsetEnd)
102 var wrapperElement = documentElement.DocumentNodeElement.create({tag: params._with.tag, klass: params._with.klass});
103 textNode1.after(wrapperElement);
106 if(prefixOutside.length > 0)
107 wrapperElement.before({text:prefixOutside});
109 var core = textNode1.getText().substr(params.offsetStart, params.offsetEnd - params.offsetStart);
110 wrapperElement.append({text: core});
113 if(prefixInside.length > 0)
114 wrapperElement.append({text: prefixInside});
115 for(var i = idx1 + 1; i < idx2; i++) {
116 wrapperElement.append(childrenInside[i]);
118 if(suffixInside.length > 0)
119 wrapperElement.append({text: suffixInside});
121 if(suffixOutside.length > 0)
122 wrapperElement.after({text: suffixOutside});
123 return wrapperElement;
125 getDocumentElement: function(from) {
126 if(from instanceof HTMLElement || from instanceof Text) {
127 return documentElement.DocumentElement.fromHTMLElement(from, this);
130 getCursor: function() {
131 return new Cursor(this);
138 $.extend(Canvas.prototype.list, {
139 create: function(params) {
140 if(!(params.element1.parent().sameNode(params.element2.parent())))
143 var parent = params.element1.parent();
145 if(parent.childIndex(params.element1) > parent.childIndex(params.element2)) {
146 var tmp = params.element1;
147 params.element1 = params.element2;
148 params.element2 = tmp;
151 var elementsToWrap = [];
153 var place = 'before';
155 parent.children().some(function(element) {
157 if(element.sameNode(params.element1))
159 if(place === 'inside') {
160 if(element instanceof documentElement.DocumentTextElement) {
161 element = element.wrapWithNodeElement({tag: 'div', klass: 'list.item'});
162 if(element.children()[0].sameNode(params.element1))
163 params.element1 = element;
165 element.setWlxmlClass('item');
166 elementsToWrap.push(element);
168 if(_e.sameNode(params.element2))
172 var listElement = documentElement.DocumentNodeElement.create({tag: 'div', klass: 'list-items' + (params.type === 'enum' ? '-enum' : '')});
175 if(parent.is('list')) {
176 listElement.wrapWithNodeElement({tag: 'div', klass: 'item'});
177 toret = listElement.parent();
182 params.element1.before(toret);
184 elementsToWrap.forEach(function(element) {
186 listElement.append(element);
189 extractItems: function(params) {
190 params = _.extend({merge: true}, params);
191 var list = params.element1.parent();
192 if(!list.is('list') || !(list.sameNode(params.element2.parent())))
195 var idx1 = list.childIndex(params.element1),
196 idx2 = list.childIndex(params.element2),
199 succeedingItems = [],
200 items = list.children(),
201 listIsNested = list.parent().getWlxmlClass() === 'item',
205 var tmp = idx1; idx1 = idx2; idx2 = tmp;
208 items.forEach(function(item, idx) {
210 precedingItems.push(item);
211 else if(idx >= idx1 && idx <= idx2) {
212 extractedItems.push(item);
215 succeedingItems.push(item);
219 var reference = listIsNested ? list.parent() : list;
220 if(succeedingItems.length === 0) {
221 var reference_orig = reference;
222 extractedItems.forEach(function(item) {
223 reference.after(item);
226 item.setWlxmlClass(null);
228 if(precedingItems.length === 0)
229 reference_orig.detach();
230 } else if(precedingItems.length === 0) {
231 extractedItems.forEach(function(item) {
232 reference.before(item);
234 item.setWlxmlClass(null);
237 extractedItems.forEach(function(item) {
238 reference.after(item);
240 item.setWlxmlClass(null);
243 var secondList = documentElement.DocumentNodeElement.create({tag: 'div', klass:'list-items'}, this),
247 toAdd = secondList.wrapWithNodeElement({tag: 'div', klass:'item'});
249 succeedingItems.forEach(function(item) {
250 secondList.append(item);
253 reference.after(toAdd);
255 if(!params.merge && listIsNested) {
256 return this.extractItems({element1: extractedItems[0], element2: extractedItems[extractedItems.length-1]});
260 areItemsOfTheSameList: function(params) {
261 var e1 = params.element1,
262 e2 = params.element2;
263 return e1.parent().sameNode(e2.parent())
264 && e1.parent().is('list');
269 var Cursor = function(canvas) {
270 this.canvas = canvas;
273 $.extend(Cursor.prototype, {
274 isSelecting: function() {
275 var selection = window.getSelection();
276 return !selection.isCollapsed;
278 isSelectingWithinElement: function() {
279 return this.isSelecting() && this.getSelectionStart().element.sameNode(this.getSelectionEnd().element);
281 isSelectingSiblings: function() {
282 return this.isSelecting() && this.getSelectionStart().element.parent().sameNode(this.getSelectionEnd().element.parent());
284 getPosition: function() {
285 return this.getSelectionAnchor();
287 getSelectionStart: function() {
288 return this.getSelectionBoundry('start');
290 getSelectionEnd: function() {
291 return this.getSelectionBoundry('end');
293 getSelectionAnchor: function() {
294 return this.getSelectionBoundry('anchor');
296 getSelectionBoundry: function(which) {
297 var selection = window.getSelection(),
298 anchorElement = this.canvas.getDocumentElement(selection.anchorNode),
299 focusElement = this.canvas.getDocumentElement(selection.focusNode);
301 if(which === 'anchor') {
303 element: anchorElement,
304 offset: selection.anchorOffset
311 if(anchorElement.parent().sameNode(focusElement.parent())) {
312 var parent = anchorElement.parent(),
313 anchorFirst = parent.childIndex(anchorElement) < parent.childIndex(focusElement);
315 if(which === 'start') {
316 element = anchorElement;
317 offset = selection.anchorOffset;
319 else if(which === 'end') {
320 element = focusElement,
321 offset = selection.focusOffset;
324 if(which === 'start') {
325 element = focusElement,
326 offset = selection.focusOffset
328 else if(which === 'end') {
329 element = anchorElement;
330 offset = selection.anchorOffset;
334 // TODO: Handle order
335 if(which === 'start') {
336 element = anchorElement;
337 offset = selection.anchorOffset
339 element = focusElement;
340 offset = selection.focusOffset
352 fromXML: function(xml) {
353 return new Canvas(xml);