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

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

Some major improvements including the functionality to select arbitrary files to modify

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