source: main/trunk/greenstone3/web/interfaces/default/js/visual-xml-editor.js@ 27015

Last change on this file since 27015 was 27015, checked in by sjm84, 11 years ago

Adding a recycle bin icon that changes if there has been an element removed

File size: 29.3 KB
Line 
1// ********************************************************************** //
2// Visual XML Editor //
3// This class represents an editor that allows you to modify XML visually //
4// ********************************************************************** //
5
6function visualXMLEditor(xmlString)
7{
8 var thisEditor = this;
9
10 var _globalID = 0;
11
12 var _xml;
13
14 var _mainDiv = $("<div>", {"id":"veMainDiv"});
15 var _toolboxDiv = $("<div>", {"id":"veToolboxDiv"});
16 var _editorContainer = $("<div>", {"id":"veEditorContainer"});
17 var _editorDiv = $("<div>", {"id":"veEditorDiv"});
18 var _infoDiv = $("<div>", {"id":"veInfoDiv"});
19 var _rootElement;
20 var _selectedElement;
21
22 var _overTrash = false;
23
24 var _validDropSpot = false;
25 var _validDropType;
26 var _validDropElem;
27
28 var _origDDParent;
29 var _origDDPosition;
30
31 var _editingNodes = new Array();
32
33 var _overList = new Array();
34 _overList.freeSpaces = new Array();
35
36 var _transactions = new Array();
37
38 var _childRestrictions =
39 {
40 gsf:
41 {
42 "choose-metadata":["gsf:metadata", "gsf:default"],
43 "metadata":[]
44 }
45 };
46
47 this.getXML = function()
48 {
49 return _xml;
50 }
51
52 this.undo = function()
53 {
54 if(_transactions.length > 0)
55 {
56 var t = _transactions.pop();
57 //Undo an added element
58 if(t.type == "addElem")
59 {
60 $(t.vElem.data("parentVEElement").getXMLNode()).remove();
61 t.vElem.remove();
62 resizeAll();
63 }
64 //Undo a removed or moved element
65 else if(t.type == "remMvElem")
66 {
67 var parent = t.vElemParent;
68 var pos = t.vElemPos;
69 var elem = t.vElem;
70
71 elem.detach();
72 if(pos == 0)
73 {
74 parent.prepend(elem);
75 $(parent.parent().data("parentVEElement").getXMLNode()).prepend(elem.data("parentVEElement").getXMLNode());
76 }
77 else if(pos == parent.children(".veElement").length)
78 {
79 $(parent.children(".veElement").eq(pos - 1).data("parentVEElement").getXMLNode()).after(elem.data("parentVEElement").getXMLNode());
80 parent.children(".veElement").eq(pos - 1).after(elem);
81 }
82 else
83 {
84 $(parent.children(".veElement").eq(pos).data("parentVEElement").getXMLNode()).before(elem.data("parentVEElement").getXMLNode());
85 parent.children(".veElement").eq(pos).before(elem);
86 }
87 resizeAll();
88
89 //Check if we need to change the recycle bin icon
90 var found = false;
91 for(var i = 0; i < _transactions.length; i++)
92 {
93 if(_transactions[i].type == "remMvElem"){found = true; break;}
94 }
95
96 if(!found)
97 {
98 $("#veTrash").children("img").attr("src", gs.imageURLs.trashEmpty);
99 }
100 }
101 //Undo an added attribute
102 else if(t.type == "addAttr")
103 {
104 if(t.row)
105 {
106 t.row.remove();
107 }
108 }
109 //Undo a removed or edited attribute
110 else if(t.type == "editAttr")
111 {
112 t.elem.removeAttribute(t.newName);
113 t.elem.setAttribute(t.name, t.value);
114 if(t.row)
115 {
116 t.row.children("td").eq(0).text(t.name);
117 t.row.children("td").eq(1).text(t.value);
118 }
119 }
120 //Undo a removed or edited attribute
121 else if(t.type == "remAttr")
122 {
123 t.elem.setAttribute(t.name, t.value);
124 if(t.rowParent)
125 {
126 t.rowParent.append(t.row);
127 }
128 }
129 //Undo edited text
130 else if(t.type == "editText")
131 {
132 t.elem.nodeValue = t.value;
133 if(t.vElem)
134 {
135 t.vElem.text(t.value);
136 }
137 }
138 }
139 }
140
141 var checkRestricted = function(child, parent)
142 {
143 var pFullNodename = parent.tagName;
144 var cFullNodename = child.tagName;
145 var pNamespace;
146 var pNodeName;
147 if(pFullNodename.indexOf(":") == -1)
148 {
149 pNamespace = "no namespace";
150 pNodeName = pFullNodename;
151 }
152 else
153 {
154 pNamespace = pFullNodename.substring(0, pFullNodename.indexOf(":"));
155 pNodeName = pFullNodename.substring(pFullNodename.indexOf(":") + 1);
156 }
157
158 var namespaceList = _childRestrictions[pNamespace];
159 if(namespaceList)
160 {
161 var childList = namespaceList[pNodeName];
162 if(childList)
163 {
164 for(var i = 0; i < childList.length; i++)
165 {
166 if(childList[i] == cFullNodename)
167 {
168 return true;
169 }
170 }
171 return false;
172 }
173 }
174
175 return true;
176 }
177
178 var placeTrashBin = function()
179 {
180 var binImage = $("<img src=\"" + gs.imageURLs.trashEmpty + "\"/>");
181 var bin = $("<div id=\"veTrash\">");
182 bin.append(binImage);
183 bin.addClass("ui-state-default");
184 bin.addClass("ui-corner-all");
185 bin.droppable(
186 {
187 "over":function()
188 {
189 _overTrash = true;
190 },
191 "out":function()
192 {
193 _overTrash = false;
194 }
195 });
196 _editorContainer.append(bin);
197 }
198
199 var populateToolbar = function()
200 {
201 var elemList =
202 {
203 html:["a", "br", "div", "li", "link", "p", "script", "span", "table", "td", "tr", "ul"],
204 xsl:
205 [
206 "apply-imports", "apply-templates", "attribute", "attribute-set", "call-template",
207 "choose", "copy", "copy-of", "decimal-format", "element",
208 "fallback", "for-each", "if", "import", "include",
209 "key", "message", "namespace-alias", "number", "otherwise",
210 "output", "param", "preserve-space", "processing-instruction", "sort",
211 "strip-space", "stylesheet", "template", "text", "transform",
212 "value-of", "variable", "when", "with-param"
213 ],
214 gsf:
215 [
216 "cgi-param", "choose-metadata", "collectionText", "displayItem", "displayText",
217 "equivlinkgs3", "foreach-metadata", "icon", "if-metadata-exists", "image",
218 "interfaceText", "link", "meta-value", "metadata", "script",
219 "style", "switch", "template", "text", "variable"
220 ]
221 };
222
223 var tabHolder = $("<ul>");
224 _toolboxDiv.append(tabHolder);
225
226 for(var key in elemList)
227 {
228 var currentList = elemList[key];
229
230 var tab = $("<li>");
231 var tabLink = $("<a>", {"href":"#ve" + key});
232 tabLink.css({"font-size":"0.9em", "padding":"5px"});
233 tabLink.text(key);
234 tab.append(tabLink);
235 tabHolder.append(tab);
236
237 var tabDiv = $("<div>", {"id":"ve" + key});
238 for(var j = 0; j < currentList.length; j++)
239 {
240 var elemName = currentList[j];
241
242 var ns = (key == "html") ? "" : (key + ":");
243
244 var newElem = _xml.createElement(ns + elemName);
245 var veElement = new VEElement(newElem);
246 var veDiv = veElement.getDiv();
247 veDiv.css("float", "none");
248 veDiv.data("toolbar", true);
249 tabDiv.append(veDiv);
250 }
251
252 _toolboxDiv.append(tabDiv);
253 }
254
255 var otherTab = $("<li>");
256 var otherTabLink = $("<a>", {"href":"#veother"});
257 otherTabLink.css({"font-size":"0.9em", "padding":"5px"});
258 otherTabLink.text("other");
259 otherTab.append(otherTabLink);
260 tabHolder.append(otherTab);
261
262 var otherTabDiv = $("<div>", {"id":"veother"});
263 var textNode = _xml.createTextNode("text");
264 var textVEElement = new VEElement(textNode);
265 var textDiv = textVEElement.getDiv();
266 textDiv.css("float", "none");
267 textDiv.data("toolbar", true);
268 otherTabDiv.append(textDiv);
269
270 var customInput = $("<input type=\"text\">");
271 var customElemHolder = $("<div>");
272 var customCreateButton = $("<button>Create element</button>");
273 customCreateButton.click(function()
274 {
275 var elemName = customInput.val();
276 if(elemName.length)
277 {
278 var elem = _xml.createElement(elemName);
279 var veElement = new VEElement(elem);
280 var customElemDiv = veElement.getDiv();
281 customElemDiv.css("float", "none");
282 customElemDiv.data("toolbar", true);
283 customElemHolder.empty();
284 customElemHolder.append(customElemDiv);
285 }
286 });
287 otherTabDiv.append(customInput);
288 otherTabDiv.append(customCreateButton);
289 otherTabDiv.append(customElemHolder);
290
291 _toolboxDiv.append(otherTabDiv);
292
293 _toolboxDiv.tabs();
294 }
295
296 var constructDivsRecursive = function(currentDiv, currentParent, level)
297 {
298 if(!level)
299 {
300 level = 1;
301 }
302
303 var container = $("<div>");
304 container.addClass("veContainerElement");
305 currentDiv.append(container);
306
307 var allowedList = new Array();
308 var counter = currentParent.firstChild;
309 while(counter)
310 {
311 if(counter.nodeType == 1)
312 {
313 allowedList.push(counter)
314 }
315 else if(counter.nodeType == 3 && counter.nodeValue.search(/\S/) != -1)
316 {
317 allowedList.push(counter)
318 }
319 counter = counter.nextSibling;
320 }
321
322 var width = 100 / allowedList.length;
323 for(var i = 0; i < allowedList.length; i++)
324 {
325 var currentElement = allowedList[i];
326
327 var veElement = new VEElement(currentElement);
328 var elementDiv = veElement.getDiv();
329 veElement.setWidth(width);
330
331 if(!_rootElement)
332 {
333 _rootElement = elementDiv;
334 }
335
336 container.append(elementDiv);
337 if(currentElement.firstChild)
338 {
339 constructDivsRecursive(elementDiv, currentElement, level + 1);
340 }
341
342 currentElement = currentElement.nextSibling;
343 }
344
345 container.append($("<div>", {"style":"clear:both;"}));
346 }
347
348 this.selectRootElement = function()
349 {
350 var height = _editorDiv.height() + 10;
351 if(height < 300){height = 300;}
352
353 _editorContainer.css("height", height + "px");
354 _infoDiv.css("height", height + "px");
355 _rootElement.trigger("click");
356 }
357
358 this.getMainDiv = function()
359 {
360 return _mainDiv;
361 }
362
363 this.savePendingEdits = function()
364 {
365 while(_editingNodes && _editingNodes.length > 0)
366 {
367 var attr = _editingNodes.pop();
368 attr.saveEdits();
369 }
370 }
371
372 var addToOverList = function(veElement)
373 {
374 for(var i = 0; i < _overList.length; i++)
375 {
376 if(!_overList[i])
377 {
378 continue;
379 }
380
381 if(_overList[i].getID() == veElement.getID())
382 {
383 return false;
384 }
385 }
386
387 if(_overList.freeSpaces.length > 0)
388 {
389 _overList[_overList.freeSpaces.pop()] = veElement;
390 }
391 else
392 {
393 _overList.push(veElement);
394 }
395 }
396
397 var removeFromOverList = function(veElement)
398 {
399 for(var i = 0; i < _overList.length; i++)
400 {
401 if(!_overList[i])
402 {
403 continue;
404 }
405
406 if(_overList[i].getID() == veElement.getID())
407 {
408 delete _overList[i];
409 _overList.freeSpaces.push(i);
410 }
411 }
412 }
413
414 var getDeepestOverElement = function()
415 {
416 if(_overList.length == 0)
417 {
418 return null;
419 }
420
421 var deepestVal = 0;
422 var deepestElem = _overList[0];
423
424 for(var i = 0; i < _overList.length; i++)
425 {
426 if(!_overList[i])
427 {
428 continue;
429 }
430
431 var depth = _overList[i].getDiv().parents(".veElement").length;
432 depth = (depth) ? depth : 0;
433
434 if (depth > deepestVal)
435 {
436 deepestVal = depth;
437 deepestElem = _overList[i];
438 }
439 }
440
441 return deepestElem;
442 }
443
444 var resizeAll = function()
445 {
446 var filterFunction = function()
447 {
448 var toolbarStatus = $(this).data("toolbar");
449 var beingDraggedStatus = $(this).data("dragging");
450
451 if(beingDraggedStatus || !toolbarStatus)
452 {
453 return true;
454 }
455
456 return false;
457 }
458
459 var allElems = $(".veElement").filter(filterFunction).each(function()
460 {
461 if($(this).data("helper")){return;}
462
463 var size = $(this).data("expanded");
464 if(size == "small")
465 {
466 var width = (20 / ($(this).siblings(".veElement").filter(function(){return !($(this).data("helper"))}).length - 1));
467 $(this).css("width", width + "%");
468 }
469 else if(size == "normal")
470 {
471 var width = (100 / ($(this).siblings(".veElement").filter(function(){return !($(this).data("helper"))}).length + 1));
472 $(this).css("width", width + "%");
473 }
474 else if(size == "expanded")
475 {
476 $(this).css("width", "80%");
477 }
478 });
479 }
480
481 var initVXE = function()
482 {
483 try
484 {
485 _xml = $.parseXML('<testContainer xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:java="http://xml.apache.org/xslt/java" xmlns:util="xalan://org.greenstone.gsdl3.util.XSLTUtil" xmlns:gslib="http://www.greenstone.org/skinning" xmlns:gsf="http://www.greenstone.org/greenstone3/schema/ConfigFormat">' + xmlString + "</testContainer>");
486 constructDivsRecursive(_editorDiv, _xml.firstChild);
487 }
488 catch(error)
489 {
490 console.log(error);
491 return null;
492 }
493
494 populateToolbar();
495 placeTrashBin();
496
497 _editorContainer.append(_editorDiv);
498 _mainDiv.append(_toolboxDiv);
499 _mainDiv.append(_editorContainer);
500 _mainDiv.append($("<div>", {"id":"veSpacerDiv"}));
501 _mainDiv.append(_infoDiv);
502 _mainDiv.append($("<div>", {"style":"clear:both;"}));
503 }
504
505 // *********************************************************************** //
506 // Visual Editor Text //
507 // This inner class represents a single xml text node in the visual editor //
508 // *********************************************************************** //
509
510 var VEText = function(node)
511 {
512 var _thisNode = this;
513 var _xmlNode = node;
514
515 var _textEditor = $("<div>");
516 var textTitle = $("<div>Text:</div>");
517 var _nodeText = $("<div>");
518 _nodeText.text(_xmlNode.nodeValue);
519
520 _textEditor.append(textTitle);
521 _textEditor.append(_nodeText);
522
523 var editButton = $("<button>edit text</button>");
524 editButton.click(function()
525 {
526 if(editButton.text() == "edit text")
527 {
528 _thisNode.editMode();
529 }
530 else
531 {
532 _thisNode.saveEdits();
533 }
534 });
535 _textEditor.append(editButton);
536
537 this.editMode = function()
538 {
539 _editingNodes.push(_thisNode);
540 _nodeText.data("prevTextValue", _nodeText.text());
541 var textArea = $("<textarea>");
542 textArea.val(_nodeText.text());
543 _nodeText.text("");
544 _nodeText.append(textArea);
545 editButton.text("done");
546 }
547
548 this.saveEdits = function()
549 {
550 for(var i = 0; i < _editingNodes.length; i++)
551 {
552 if(_editingNodes[i] == _thisNode)
553 {
554 _editingNodes.splice(i, 1);
555 break;
556 }
557 }
558
559 _transactions.push({type:"editText", elem:_xmlNode, vElem: _nodeText, value:_nodeText.data("prevTextValue")});
560 var textArea = _nodeText.find("textarea");
561 var newValue = textArea.val();
562 _xmlNode.nodeValue = newValue;
563 _nodeText.empty();
564 _nodeText.text(newValue);
565 editButton.text("edit text");
566 }
567
568 this.getDiv = function()
569 {
570 return _textEditor;
571 }
572 }
573
574 // *********************************************************************** //
575 // Visual Editor Attribute //
576 // This inner class represents a single xml attribute in the visual editor //
577 // *********************************************************************** //
578
579 var VEAttribute = function(attrElem, xmlElem, name, value)
580 {
581 var _name;
582 if(name)
583 {
584 _name = name;
585 }
586 else if(attrElem && attrElem.name)
587 {
588 _name = attrElem.name;
589 }
590
591 var _value;
592 if(value)
593 {
594 _value = value;
595 }
596 else if(attrElem && attrElem.value)
597 {
598 _value = attrElem.value;
599 }
600 var _xmlElem = xmlElem;
601 var _row;
602
603 var _thisAttr = this;
604
605 this.getName = function()
606 {
607 return _name;
608 }
609
610 this.getValue = function()
611 {
612 return _value;
613 }
614
615 var createNameCell = function()
616 {
617 var cell = $("<td>", {"class":"veNameCell"});
618 cell.text(_name);
619 return cell;
620 }
621
622 var createValueCell = function()
623 {
624 var cell = $("<td>", {"class":"veValueCell"});
625 cell.text(_value);
626 return cell;
627 }
628
629 var createEditCell = function()
630 {
631 var cell = $("<td>", {"class":"veEditCell"});
632 var link = $("<a href=\"javascript:;\">edit</a>");
633
634 link.click(function()
635 {
636 _thisAttr.editMode();
637 });
638 cell.append(link);
639 return cell;
640 }
641
642 var createDeleteCell = function()
643 {
644 var cell = $("<td>", {"class":"veDeleteCell"});
645 var link = $("<a href=\"javascript:;\">delete</a>");
646 link.click(function()
647 {
648 _transactions.push({type:"remAttr", elem:_xmlElem, row:_row, rowParent:_row.parent(), name:_name, value:_value});
649 _xmlElem.removeAttribute(_name);
650 _row.detach();
651 });
652 cell.append(link);
653 return cell;
654 }
655
656 this.getAsTableRow = function()
657 {
658 var tableRow = $("<tr>");
659
660 var attributeName = createNameCell();
661 tableRow.append(attributeName);
662
663 var attributeValue = createValueCell();
664 tableRow.append(attributeValue);
665
666 var editCell = createEditCell()
667 tableRow.append(editCell);
668
669 var deleteCell = createDeleteCell();
670 tableRow.append(deleteCell);
671
672 _row = tableRow;
673
674 return tableRow;
675 }
676
677 this.editMode = function()
678 {
679 _editingNodes.push(_thisAttr);
680
681 var nameCell = _row.children("td").eq(0);
682 var valueCell = _row.children("td").eq(1);
683 var editLink = _row.children("td").eq(2).find("a");
684
685 editLink.text("done");
686 editLink.off("click");
687 editLink.click(function()
688 {
689 _thisAttr.saveEdits();
690 });
691
692 var nameInput = $("<input type=\"text\">");
693 nameInput.width(nameCell.width() - 5);
694 nameInput.val(_name);
695
696 var valueInput = $("<input type=\"text\">");
697 valueInput.width(valueCell.width() - 5);
698 valueInput.val(_value);
699
700 nameCell.text("");
701 valueCell.text("");
702
703 nameCell.append(nameInput);
704 valueCell.append(valueInput);
705
706 nameInput.focus();
707 }
708
709 this.saveEdits = function()
710 {
711 for(var i = 0; i < _editingNodes.length; i++)
712 {
713 if(_editingNodes[i] == _thisAttr)
714 {
715 _editingNodes.splice(i, 1);
716 break;
717 }
718 }
719
720 var nameCell = _row.children("td").eq(0);
721 var valueCell = _row.children("td").eq(1);
722 var editLink = _row.children("td").eq(2).find("a");
723
724 editLink.text("edit");
725 editLink.off("click");
726 editLink.click(function()
727 {
728 _thisAttr.editMode();
729 });
730
731 var nameInput = nameCell.children("input");
732 var valueInput = valueCell.children("input");
733
734 nameInput.blur();
735 valueInput.blur();
736
737 var name = nameInput.val();
738 var value = valueInput.val();
739
740 nameCell.empty();
741 nameCell.text(name);
742
743 valueCell.empty();
744 valueCell.text(value);
745
746 if(nameCell.data("prevName") != "")
747 {
748 _xmlElem.removeAttribute(_name);
749 }
750 _xmlElem.setAttribute(name, value);
751
752 _transactions.push({type:"editAttr", elem:_xmlElem, row:_row, newName:name, name:_name, value:_value});
753
754 _name = name;
755 _value = value;
756 }
757 }
758
759 // ********************************************************************************** //
760 // Visual Editor Element //
761 // This inner class represents a single xml element or text node in the visual editor //
762 // ********************************************************************************** //
763 var VEElement = function(xml)
764 {
765 var _div = $("<div>");
766 var _xmlNode = xml;
767 var _id = _globalID++;
768
769 _div.data("parentVEElement", this);
770 _div.data("expanded", "normal");
771
772 var makeDraggable = function()
773 {
774 _div.draggable(
775 {
776 "revert":"true",
777 "helper":function()
778 {
779 //Make sure the cursor is put in the centre of the div
780 var height = _div.children(".veTitleElement").height();
781 _div.draggable("option", "cursorAt", {top:(height / 2), left:(_div.width() / 2)});
782
783 var tempVEE = new VEElement(_xmlNode);
784 var tempDiv = tempVEE.getDiv();
785 tempDiv.css("border", "1px solid orangered");
786 tempDiv.css("background", "orange");
787 tempDiv.width(_div.width());
788 tempDiv.data("helper", true);
789 return tempDiv;
790 },
791 "cursor":"move",
792 "appendTo":_mainDiv,
793 "start":function(event, ui)
794 {
795 _overTrash = false;
796 _origDDParent = _div.parent();
797 _origDDPosition = _div.index();
798
799 _div.siblings(".veElement").filter(function(){return !($(this).data("helper"))}).data("expanded", "normal");
800 _div.css("border", "1px solid orangered");
801 _div.data("prevBackground", _div.css("background"));
802 _div.css("background", "orange");
803 _div.css("float", "left");
804
805 _div.data("dragging", true);
806 if(_div.data("toolbar"))
807 {
808 var cloneElem = new VEElement(_xmlNode.cloneNode(true));
809 var cloneDiv = cloneElem.getDiv();
810 cloneDiv.css("float", "none");
811 cloneDiv.data("toolbar", true);
812 _div.before(cloneDiv);
813 }
814 _div.detach();
815
816 resizeAll();
817 },
818 "drag":function(event, ui)
819 {
820 var foundDefined = false;
821 for(var i = 0; i < _overList.length; i++)
822 {
823 if(!(_overList[i] === "undefined"))
824 {
825 foundDefined = true;
826 }
827 }
828
829 _validDropSpot = false;
830 if(foundDefined)
831 {
832 var overElement = getDeepestOverElement();
833 if(overElement && overElement.getXMLNode().nodeType != 3 && checkRestricted(_xmlNode, overElement.getXMLNode()))
834 {
835 _validDropSpot = true;
836 var overDiv = overElement.getDiv();
837
838 var overLeft = overDiv.offset().left;
839 var helperLeft = ui.helper.offset().left;
840 var helperMiddle = helperLeft + (ui.helper.width() / 2);
841
842 var overContainers = overDiv.children(".veContainerElement");
843 if(!overContainers.length)
844 {
845 overDiv.append($("<div>", {"class":"veContainerElement"}));
846 overContainers = overDiv.children(".veContainerElement");
847 }
848 var overChildren = overContainers.children(".veElement").filter(function(){return !($(this).data("helper")) && !(_div.data("parentVEElement").getID() == $(this).data("parentVEElement").getID())});
849 var overChildrenLength = overChildren.length + 1;
850
851 if(!overChildren.length)
852 {
853 _validDropElem = overDiv;
854 _validDropType = "into";
855 overContainers.append(_div);
856 }
857 else
858 {
859 var posPercent = (helperMiddle - overLeft) / overDiv.width();
860 if(posPercent < 0)
861 {
862 posPercent = 0;
863 }
864 else if(posPercent > 1)
865 {
866 posPercent = 1;
867 }
868 var pos = Math.floor(overChildrenLength * posPercent);
869
870 if(pos < overChildrenLength - 1)
871 {
872 _validDropElem = overChildren.eq(pos);
873 _validDropType = "before";
874 overChildren.eq(pos).before(_div);
875 }
876 else
877 {
878 _validDropElem = overChildren.eq(pos - 1);
879 //Necessary to fix a rare bug that causes pos to be off by one
880 if(!_validDropElem.length)
881 {
882 _validDropElem = overChildren.eq(pos - 2);
883 }
884 _validDropType = "after";
885 overChildren.eq(pos - 1).after(_div);
886 }
887 }
888
889 overChildren.data("expanded", "normal");
890 _div.data("expanded", "normal");
891
892 resizeAll();
893 }
894 }
895 },
896 "stop":function(event)
897 {
898 var transactionType = (_div.data("toolbar")) ? "addElem" : "remMvElem";
899 _div.data("dragging", false);
900 _div.data("toolbar", false);
901
902 _div.css("border", "1px dashed black");
903 _div.css("background", _div.data("prevBackground"));
904
905 //If the element was not dropped in a valid place then put it back
906 if(!_validDropSpot)
907 {
908 _div.detach();
909 if(_origDDPosition == 0)
910 {
911 _origDDParent.prepend(_div);
912 }
913 else if(_origDDPosition == _origDDParent.children(".veElement").length)
914 {
915 _origDDParent.children(".veElement").eq(_origDDPosition - 1).after(_div);
916 }
917 else
918 {
919 _origDDParent.children(".veElement").eq(_origDDPosition).before(_div);
920 }
921
922 if(_overTrash)
923 {
924 _div.data("parentVEElement").remove();
925 return;
926 }
927
928 resizeAll();
929 }
930 //Otherwise modify the XML
931 else
932 {
933 var xmlNode = _validDropElem.data("parentVEElement").getXMLNode();
934 if(_validDropType == "before")
935 {
936 $(xmlNode).before(_xmlNode);
937 }
938 else if (_validDropType == "after")
939 {
940 $(xmlNode).after(_xmlNode);
941 }
942 else if (_validDropType == "into")
943 {
944 $(xmlNode).append(_xmlNode);
945 }
946 _transactions.push({type:transactionType, vElemParent:_origDDParent, vElemPos:_origDDPosition, vElem:_div});
947 }
948
949 _overList = new Array();
950 _overList.freeSpaces = new Array();
951 }
952 });
953
954 _div.droppable(
955 {
956 "over":function(event, ui)
957 {
958 addToOverList($(this).data("parentVEElement"));
959 event.stopPropagation();
960 },
961 "out":function(event)
962 {
963 removeFromOverList($(this).data("parentVEElement"));
964 event.stopPropagation();
965 }
966 });
967 }
968
969 this.getDiv = function()
970 {
971 return _div;
972 }
973
974 this.getXMLNode = function()
975 {
976 return _xmlNode;
977 }
978
979 this.getID = function()
980 {
981 return _id;
982 }
983
984 var createTableHeader = function()
985 {
986 var tableHeader = $("<tr>");
987 tableHeader.html("<td class=\"veNameCell\">Name</td><td class=\"veValueCell\">Value</td>");
988 return tableHeader;
989 }
990
991 this.populateInformationDiv = function()
992 {
993 thisEditor.savePendingEdits();
994 _infoDiv.empty();
995
996 var nameElement = $("<p>");
997 if(_xmlNode.nodeType == 1)
998 {
999 nameElement.text("Name: " + _xmlNode.nodeName);
1000 }
1001 else
1002 {
1003 nameElement.text("[text node]");
1004 }
1005 _infoDiv.append(nameElement);
1006
1007 if(_xmlNode.nodeType == 1)
1008 {
1009 var attributeTableTitle = $("<p>Attributes:<p/>");
1010 var attributeTable = $("<table>");
1011 attributeTable.addClass("veAttributeTableContainer");
1012
1013 attributeTable.append(createTableHeader());
1014
1015 $(_xmlNode.attributes).each(function()
1016 {
1017 var veAttribute = new VEAttribute(this, _xmlNode);
1018 attributeTable.append(veAttribute.getAsTableRow());
1019 });
1020
1021 _infoDiv.append(attributeTableTitle);
1022 _infoDiv.append(attributeTable);
1023
1024 var addButton = $("<button>Add attribute</button>");
1025 addButton.click(function()
1026 {
1027 var newAtt = new VEAttribute(null, _xmlNode, "", "");
1028 var row = newAtt.getAsTableRow();
1029 attributeTable.append(row);
1030 newAtt.editMode();
1031 _transactions.push({type:"addAttr", row:row})
1032 });
1033 _infoDiv.append(addButton);
1034 }
1035
1036 if(_xmlNode.nodeType == 3)
1037 {
1038 var textNode = new VEText(_xmlNode);
1039 _infoDiv.append(textNode.getDiv());
1040 }
1041 }
1042
1043 this.remove = function()
1044 {
1045 var divParent = _div.parents(".veElement");
1046 _transactions.push({type:"remMvElem", vElemParent:_div.parent(), vElemPos:_div.index(), vElem:_div});
1047 _div.data("expanded", "normal");
1048 $(_xmlNode).remove();
1049 _div.detach();
1050 _infoDiv.empty();
1051
1052 if(divParent.length)
1053 {
1054 divParent.first().trigger("click");
1055 }
1056
1057 $("#veTrash").children("img").attr("src", gs.imageURLs.trashFull);
1058 }
1059
1060 var addMouseEvents = function()
1061 {
1062 _div.mouseover(function(event)
1063 {
1064 event.stopPropagation();
1065 _div.css("border", "1px solid orange");
1066 var titleString = " ";
1067 if(_xmlNode.nodeType == 1)
1068 {
1069 for(var i = 0; i < _xmlNode.attributes.length; i++)
1070 {
1071 var current = _xmlNode.attributes[i];
1072 var name = current.name;
1073 var value = current.value;
1074
1075 titleString += name + "=\"" + value + "\" ";
1076 }
1077 }
1078 else if(_xmlNode.nodeType == 3)
1079 {
1080 titleString = _xmlNode.nodeValue;
1081 }
1082 _div.attr("title", titleString);
1083 });
1084 _div.mouseout(function(event)
1085 {
1086 _div.css("border", "1px dashed black");
1087 event.stopPropagation();
1088 });
1089 _div.click(function(event)
1090 {
1091 if(_selectedElement)
1092 {
1093 _selectedElement.css("border", _selectedElement.prevBorder);
1094 }
1095 _selectedElement = _div;
1096 _div.prevBorder = _div.css("border");
1097 _div.css("border", "red solid 1px");
1098
1099 _div.data("parentVEElement").focus();
1100 _div.data("parentVEElement").populateInformationDiv();
1101
1102 event.stopPropagation();
1103 });
1104 }
1105
1106 this.expand = function()
1107 {
1108 var siblings = _div.siblings(".veElement");
1109 if(!(_div.data("expanded") == "expanded") && siblings.length && siblings.length > 0)
1110 {
1111 var sibWidth = 20 / siblings.length;
1112 siblings.each(function()
1113 {
1114 $(this).animate({width:sibWidth + "%"}, 900);
1115 $(this).data("expanded", "small");
1116 });
1117
1118 _div.animate({width:"80%"}, 1000);
1119 _div.data("expanded", "expanded");
1120 }
1121 }
1122
1123 this.evenlyDistributeChildren = function()
1124 {
1125 var children = _div.find(".veElement")
1126 .each(function()
1127 {
1128 $(this).data("expanded", "normal");
1129 var length = $(this).siblings(".veElement").filter(function(){return !($(this).data("helper"))}).length + 1;
1130 $(this).css("width", (100 / length) + "%");//$(this).animate({"width":(100 / length) + "%"}, 900);
1131 });
1132 }
1133
1134 this.focus = function()
1135 {
1136 _div.data("parentVEElement").expand();
1137
1138 var parents = _div.parents(".veElement");
1139 parents.each(function()
1140 {
1141 $(this).data("parentVEElement").expand();
1142 });
1143
1144 _div.data("parentVEElement").evenlyDistributeChildren();
1145 }
1146
1147 this.setWidth = function(width)
1148 {
1149 _div.css("width", width + "%");
1150 }
1151
1152 //Visual Editor Element constructor
1153 var initVEE = function()
1154 {
1155 _div.addClass("veElement");
1156 makeDraggable();
1157
1158 var titleText;
1159 if(_xmlNode.nodeType == 3 && _xmlNode.nodeValue.search(/\S/) != -1)
1160 {
1161 _div.addClass("veTextElement");
1162 titleText = "[text]";
1163 }
1164 else if (_xmlNode.nodeType == 1)
1165 {
1166 if(_xmlNode.tagName.search(/^xsl/) != -1)
1167 {
1168 _div.addClass("veXSLElement");
1169 }
1170 else if(_xmlNode.tagName.search(/^gsf/) != -1)
1171 {
1172 _div.addClass("veGSFElement");
1173 }
1174 else if(_xmlNode.tagName.search(/^gslib/) != -1)
1175 {
1176 _div.addClass("veGSLIBElement");
1177 }
1178 else
1179 {
1180 _div.addClass("veHTMLElement");
1181 }
1182 titleText = _xmlNode.tagName;
1183 }
1184
1185 addMouseEvents();
1186
1187 _div.append("<div class=\"veTitleElement\">" + titleText + "</div>");
1188 }
1189
1190 initVEE();
1191 }
1192
1193 //Call the constructor
1194 initVXE();
1195}
Note: See TracBrowser for help on using the repository browser.