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

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

Various fixes and resizing will now only be done if it is necessary

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