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

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

Various fixes and improvements. Also trialling a different look for elements in the visual editor

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