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

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

Make sure empty elements can't be added

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