var Xonomy={ lang: "", //"en"|"de"|fr"| ... mode: "nerd", //"nerd"|"laic" }; Xonomy.setMode=function(mode) { if(mode=="nerd" || mode=="laic") Xonomy.mode=mode; if(mode=="nerd") $(".xonomy").removeClass("laic").addClass("nerd"); if(mode=="laic") $(".xonomy").removeClass("nerd").addClass("laic"); } Xonomy.jsEscape=function(str) { return String(str) .replace(/\"/g, '\\\"') .replace(/\'/g, '\\\'') }; Xonomy.xmlEscape=function(str, jsEscape) { if(jsEscape) str=Xonomy.jsEscape(str); return String(str) .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') .replace(//g, '>'); }; Xonomy.xmlUnscape=function(value){ return String(value) .replace(/"/g, '"') .replace(/'/g, "'") .replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&'); }; Xonomy.isNamespaceDeclaration=function(attributeName) { //Tells you whether an attribute name is a namespace declaration. var ret=false; if(attributeName=="xmlns") ret=true; if(attributeName.length>=6 && attributeName.substring(0, 6)=="xmlns:") ret=true; return ret; }; Xonomy.namespaces={}; //eg. "xmlns:mbm": "http://lexonista.com" Xonomy.xml2js=function(xml, jsParent) { if(typeof(xml)=="string") xml=$.parseXML(xml); if(xml.documentElement) xml=xml.documentElement; var js=new Xonomy.surrogate(jsParent); js.type="element"; js.name=xml.nodeName; js.htmlID=""; js.attributes=[]; for(var i=0; i0) { var hasText=false; for(var i=0; i"; } else { xml+="/>"; } return xml; } }; Xonomy.enrichElement=function(jsElement) { jsElement.hasAttribute=function(name) { var ret=false; for(var i=0; i .children > .element").each(function () { //determine whether each child element of hasText element should have empty text nodes on either side if($(this).prev().length == 0 || !$(this).prev().hasClass("textnode")) { $(this).before(Xonomy.renderText({ type: "text", value: "" })); } if($(this).next().length == 0 || !$(this).next().hasClass("textnode")) { $(this).after(Xonomy.renderText({ type: "text", value: "" })); } }); var merged=false; while(!merged) { //merge adjacent text nodes merged=true; var textnodes=$(".xonomy .textnode").toArray(); for(var i=0; i0) { //is it after an attribute it cannot be after? then move it up until it's not! var $this=$(this); var ok; do { ok=true; for(var ii=0; ii0 ) { $this.prev().before($this); ok=false; } } } while(!ok) } if(mustBeAfter.length>0) { //is it before an attribute it cannot be before? then move it down until it's not! var $this=$(this); var ok; do { ok=true; for(var ii=0; ii0 ) { $this.next().after($this); ok=false; } } } while(!ok) } }); $(".xonomy .attributes").each(function(){ //determine whether each attribute list has any shy attributes: if($(this).children(".shy").toArray().length==0) { $(this.parentNode).children(".rollouter").hide().removeClass("rolledout"); $(this).removeClass("rolledout").css("display", ""); } else { $(this.parentNode).children(".rollouter").show(); } }); $(".xonomy .element").each(function(){ //refresh display names, display values and captions: var elSpec=Xonomy.docSpec.elements[this.getAttribute("data-name")]; if(elSpec.displayName) $(this).children(".tag").children(".name").html(Xonomy.textByLang(elSpec.displayName(Xonomy.harvestElement(this)))); if(elSpec.caption) { var jsEl=Xonomy.harvestElement(this); $(this).children(".inlinecaption").html(Xonomy.textByLang(elSpec.caption(jsEl))); } if(elSpec.displayValue) { var jsEl=Xonomy.harvestElement(this); if(!jsEl.hasElements()) $(this).children(".children").html( Xonomy.textByLang(Xonomy.renderDisplayText(jsEl.getText(), elSpec.displayValue(jsEl))) ); } $(this).children(".tag.opening").children(".attributes").children(".attribute").each(function(){ var atSpec=elSpec.attributes[this.getAttribute("data-name")]; if(atSpec.displayName) $(this).children(".name").html(Xonomy.textByLang(atSpec.displayName(Xonomy.harvestAttribute(this)))); if(atSpec.displayValue) $(this).children(".value").html(Xonomy.textByLang(atSpec.displayValue(Xonomy.harvestAttribute(this)))); if(atSpec.caption) $(this).children(".inlinecaption").html(" "+Xonomy.textByLang(atSpec.caption(Xonomy.harvestAttribute(this)))+" "); }); }); }; Xonomy.harvestCache={}; Xonomy.harvest=function() { //harvests the contents of an editor //Returns xml-as-string. var rootElement=$(".xonomy .element").first().toArray()[0]; var js=Xonomy.harvestElement(rootElement); for(var key in Xonomy.namespaces) { if(!js.hasAttribute(key)) js.attributes.push({ type: "attribute", name: key, value: Xonomy.namespaces[key], parent: js }); } return Xonomy.js2xml(js); } Xonomy.harvestElement=function(htmlElement, jsParent) { var htmlID=htmlElement.id; if(!Xonomy.harvestCache[htmlID]) { var js=new Xonomy.surrogate(jsParent); js.type="element"; js.name=htmlElement.getAttribute("data-name"); js.htmlID=htmlElement.id; js.attributes=[]; var htmlAttributes=$(htmlElement).find(".tag.opening > .attributes").toArray()[0]; for(var i=0; i"; laybyHtml+=" "; laybyHtml+=" "; laybyHtml+="
"; laybyHtml+="
"+Xonomy.textByLang(docSpec.laybyMessage)+"
"; laybyHtml+=""; $(laybyHtml).appendTo($(editor)); } if(docSpec.allowModeSwitching){ $("
").appendTo($(editor)).on("click", function(e){ if(Xonomy.mode=="nerd") { Xonomy.setMode("laic"); } else { Xonomy.setMode("nerd"); } if(docSpec.onModeSwitch) docSpec.onModeSwitch(Xonomy.mode); }); } //Make sure the "click off" handler is attached: $(document.body).off("click", Xonomy.clickoff); $(document.body).on("click", Xonomy.clickoff); //Make sure the "drag end" handler is attached: $(document.body).off("dragend", Xonomy.dragend); $(document.body).on("dragend", Xonomy.dragend); Xonomy.refresh(); Xonomy.validate(); }; Xonomy.renderElement=function(element) { var htmlID=Xonomy.nextID(); Xonomy.verifyDocSpecElement(element.name); var spec=Xonomy.docSpec.elements[element.name]; var classNames="element"; if(spec.canDropTo && spec.canDropTo.length>0) classNames+=" draggable"; var hasText = spec.hasText(element); if(hasText) classNames+=" hasText"; if(spec.inlineMenu && spec.inlineMenu.length>0) classNames+=" hasInlineMenu"; if(spec.oneliner(element)) classNames+=" oneliner"; if(!spec.collapsible(element)) { classNames+=" uncollapsible"; } else { if(spec.collapsed(element) && element.children.length>0) classNames+=" collapsed"; } if(spec.isInvisible && spec.isInvisible(element)) { classNames+=" invisible"; } if(spec.isReadOnly && spec.isReadOnly(element)) { readonly=true; classNames+=" readonly"; } if(spec.menu.length>0) classNames+=" hasMenu"; //not always accurate: whether an element has a menu is actually determined at runtime var displayName=element.name; if(spec.displayName) displayName=Xonomy.textByLang(spec.displayName(element)); var title=""; if(spec.title) title=Xonomy.textByLang(spec.title(element)); var html=""; html+='
'; html+=''; html+=''; html+=''; html+=''; html+=''; html+='<'; html+=''; html+=''+displayName+''; html+=''; for(var i=0; i'; html+='/'; html+='>'; html+=''; if(spec.caption && !spec.oneliner(element)) html+=""+Xonomy.textByLang(spec.caption(element))+""; html+='···'; html+='
'; if(spec.displayValue && !element.hasElements()) { html+=Xonomy.renderDisplayText(element.getText(), spec.displayValue(element)); } else { var prevChildType=""; if(hasText && (element.children.length==0 || element.children[0].type=="element")) { html+=Xonomy.renderText({type: "text", value: ""}); //if inline layout, insert empty text node between two elements } for(var i=0; i1 && element.children[element.children.length-1].type=="element") { html+=Xonomy.renderText({type: "text", value: ""}); //if inline layout, insert empty text node between two elements } } html+='
'; html+=''; html+='<'; html+='/'; html+=''+displayName+''; html+='>'; html+=''; if(spec.caption && spec.oneliner(element)) html+=""+Xonomy.textByLang(spec.caption(element))+""; html+='
'; element.htmlID = htmlID; return html; }; Xonomy.renderAttribute=function(attribute, optionalParentName) { var htmlID=Xonomy.nextID(); classNames="attribute"; var readonly=false; var displayName=attribute.name; var displayValue=Xonomy.xmlEscape(attribute.value); var caption=""; var title=""; if(optionalParentName) { var spec=Xonomy.docSpec.elements[optionalParentName].attributes[attribute.name]; if(spec) { if(spec.displayName) displayName=Xonomy.textByLang(spec.displayName(attribute)); if(spec.displayValue) displayValue=Xonomy.textByLang(spec.displayValue(attribute)); if(spec.title) title=Xonomy.textByLang(spec.title(attribute)); if(spec.caption) caption=Xonomy.textByLang(spec.caption(attribute)); if(spec.isReadOnly && spec.isReadOnly(attribute)) { readonly=true; classNames+=" readonly"; } if(spec.isInvisible && spec.isInvisible(attribute)) { classNames+=" invisible"; } if(spec.shy && spec.shy(attribute)) { classNames+=" shy"; } } } var html=""; html+=''; html+=' '; var onclick=''; if(!readonly) onclick=' onclick="Xonomy.click(\''+htmlID+'\', \'attributeName\')"'; html+=''; html+=''+displayName+''; html+='='; var onclick=''; if(!readonly) onclick=' onclick="Xonomy.click(\''+htmlID+'\', \'attributeValue\')"'; html+=''; html+='"'; html+=''+displayValue+''; html+='"'; html+=''; if(caption) html+=""+caption+""; html+=''; attribute.htmlID = htmlID; return html; }; Xonomy.renderText=function(text) { var htmlID=Xonomy.nextID(); var classNames="textnode focusable"; if($.trim(text.value)=="") classNames+=" whitespace"; if(text.value=="") classNames+=" empty"; var html=""; html+='
'; html+=''; var txt=Xonomy.chewText(text.value); html+=''+txt+''; html+='
'; text.htmlID = htmlID; return html; } Xonomy.renderDisplayText=function(text, displayText) { var htmlID=Xonomy.nextID(); var classNames="textnode"; if($.trim(displayText)=="") classNames+=" whitespace"; if(displayText=="") classNames+=" empty"; var html=""; html+='
'; html+=''; html+=''+Xonomy.textByLang(displayText)+''; html+='
'; text.htmlID = htmlID; return html; } Xonomy.chewText=function(txt) { var ret=""; ret+=""; //start word for(var i=0; i"+t+""; if(txt[i]==" ") ret+=""; //start word } ret+=""; //end word return ret; }; Xonomy.charClick=function(c) { Xonomy.clickoff(); var isReadOnly=( $(c).closest(".readonly").toArray().length>0 ); if(!isReadOnly) { Xonomy.notclick=true; if( $(".xonomy .char.on").toArray().length==1 && //if there is precisely one previously selected character $(".xonomy .char.on").closest(".element").is($(c).closest(".element")) //and if it has the same parent element as this character ) { var $element=$(".xonomy .char.on").closest(".element"); var chars=$element.find(".char").toArray(); var iFrom=$.inArray($(".xonomy .char.on").toArray()[0], chars); var iTill=$.inArray(c, chars); if(iFrom>iTill) {var temp=iFrom; iFrom=iTill; iTill=temp;} for(var i=0; i=iFrom && i<=iTill) $(chars[i]).addClass("on"); } //Save for later the info Xonomy needs to know what to wrap: Xonomy.textFromID=$(chars[iFrom]).closest(".textnode").attr("id"); Xonomy.textTillID=$(chars[iTill]).closest(".textnode").attr("id"); Xonomy.textFromIndex=$.inArray(chars[iFrom], $("#"+Xonomy.textFromID).find(".char").toArray()); Xonomy.textTillIndex=$.inArray(chars[iTill], $("#"+Xonomy.textTillID).find(".char").toArray()); //Show inline menu etc: var htmlID=$element.attr("id"); var content=Xonomy.inlineMenu(htmlID); //compose bubble content if(content!="" && content!="") { document.body.appendChild(Xonomy.makeBubble(content)); //create bubble Xonomy.showBubble($("#"+htmlID+" .char.on").last()); //anchor bubble to highlighted chars } Xonomy.clearChars=true; } else { $(".xonomy .char.on").removeClass("on"); $(c).addClass("on"); Xonomy.setFocus(c.id, "char"); } } }; Xonomy.wrap=function(htmlID, param) { Xonomy.clickoff(); Xonomy.destroyBubble(); var xml=param.template; var ph=param.placeholder; var jsElement=Xonomy.harvestElement(document.getElementById(htmlID)); if(Xonomy.textFromID==Xonomy.textTillID) { //abc --> abc var jsOld=Xonomy.harvestText(document.getElementById(Xonomy.textFromID)); var txtOpen=jsOld.value.substring(0, Xonomy.textFromIndex); var txtMiddle=jsOld.value.substring(Xonomy.textFromIndex, Xonomy.textTillIndex+1); var txtClose=jsOld.value.substring(Xonomy.textTillIndex+1); xml=xml.replace(ph, Xonomy.xmlEscape(txtMiddle)); var html=""; html+=Xonomy.renderText({type: "text", value: txtOpen}); var js=Xonomy.xml2js(xml, jsElement); html+=Xonomy.renderElement(js); var newID=js.htmlID; html+=Xonomy.renderText({type: "text", value: txtClose}); $("#"+Xonomy.textFromID).replaceWith(html); window.setTimeout(function(){ Xonomy.setFocus(newID, "openingTagName"); }, 100); } else { //ab<...>cd --> ab<...>cd var jsOldOpen=Xonomy.harvestText(document.getElementById(Xonomy.textFromID)); var jsOldClose=Xonomy.harvestText(document.getElementById(Xonomy.textTillID)); var txtOpen=jsOldOpen.value.substring(0, Xonomy.textFromIndex); var txtMiddleOpen=jsOldOpen.value.substring(Xonomy.textFromIndex); var txtMiddleClose=jsOldClose.value.substring(0, Xonomy.textTillIndex+1); var txtClose=jsOldClose.value.substring(Xonomy.textTillIndex+1); xml=xml.replace(ph, Xonomy.xmlEscape(txtMiddleOpen)+ph); $("#"+Xonomy.textFromID).nextUntil("#"+Xonomy.textTillID).each(function(){ if($(this).hasClass("element")) xml=xml.replace(ph, Xonomy.js2xml(Xonomy.harvestElement(this))+ph); else if($(this).hasClass("textnode")) xml=xml.replace(ph, Xonomy.js2xml(Xonomy.harvestText(this))+ph); }); xml=xml.replace(ph, Xonomy.xmlEscape(txtMiddleClose)); $("#"+Xonomy.textFromID).nextUntil("#"+Xonomy.textTillID).remove(); $("#"+Xonomy.textTillID).remove(); var html=""; html+=Xonomy.renderText({type: "text", value: txtOpen}); var js=Xonomy.xml2js(xml, jsElement); html+=Xonomy.renderElement(js); var newID=js.htmlID; html+=Xonomy.renderText({type: "text", value: txtClose}); $("#"+Xonomy.textFromID).replaceWith(html); window.setTimeout(function(){ Xonomy.setFocus(newID, "openingTagName"); }, 100); } Xonomy.changed(); }; Xonomy.unwrap=function(htmlID, param) { var parentID=$("#"+htmlID)[0].parentNode.parentNode.id; Xonomy.clickoff(); $("#"+htmlID).replaceWith($("#"+htmlID+" > .children > *")); Xonomy.changed(); window.setTimeout(function(){ Xonomy.setFocus(parentID, "openingTagName"); }, 100); }; Xonomy.plusminus=function(htmlID, forceExpand) { var $element=$("#"+htmlID); var $children=$element.children(".children"); if($element.hasClass("collapsed")) { $children.hide(); $element.removeClass("collapsed"); if($element.hasClass("oneliner")) $children.fadeIn("fast"); else $children.slideDown("fast"); } else if(!forceExpand) { Xonomy.updateCollapsoid(htmlID); if($element.hasClass("oneliner")) $children.fadeOut("fast", function(){ $element.addClass("collapsed"); }); else $children.slideUp("fast", function(){ $element.addClass("collapsed"); }); } window.setTimeout(function(){ if($("#"+Xonomy.currentHtmlId+" .opening:visible").length>0) { Xonomy.setFocus(Xonomy.currentHtmlId, "openingTagName"); } else { Xonomy.setFocus(Xonomy.currentHtmlId, "childrenCollapsed"); } }, 300); }; Xonomy.updateCollapsoid=function(htmlID) { var $element=$("#"+htmlID); var whisper=""; var elementName=$element.data("name"); var spec=Xonomy.docSpec.elements[elementName]; if(spec.collapsoid) { whisper=spec.collapsoid(Xonomy.harvestElement($element.toArray()[0])); } else { var abbreviated=false; $element.find(".textnode").each(function(){ var txt=Xonomy.harvestText(this).value; for(var i=0; i0 ); if(!isReadOnly && (what=="openingTagName" || what=="closingTagName") ) { $("#"+htmlID).addClass("current"); //make the element current var content=Xonomy.elementMenu(htmlID); //compose bubble content if(content!="" && content!="") { document.body.appendChild(Xonomy.makeBubble(content)); //create bubble if(what=="openingTagName") Xonomy.showBubble($("#"+htmlID+" > .tag.opening > .name")); //anchor bubble to opening tag if(what=="closingTagName") Xonomy.showBubble($("#"+htmlID+" > .tag.closing > .name")); //anchor bubble to closing tag } var surrogateElem = Xonomy.harvestElement(document.getElementById(htmlID)); $("#"+htmlID).trigger("xonomy-click-element", [surrogateElem]); } if(!isReadOnly && what=="attributeName") { $("#"+htmlID).addClass("current"); //make the attribute current var content=Xonomy.attributeMenu(htmlID); //compose bubble content if(content!="" && content!="") { document.body.appendChild(Xonomy.makeBubble(content)); //create bubble Xonomy.showBubble($("#"+htmlID+" > .name")); //anchor bubble to attribute name } var surrogateAttr = Xonomy.harvestAttribute(document.getElementById(htmlID)); $("#"+htmlID).trigger("xonomy-click-attribute", [surrogateAttr]); } if(!isReadOnly && what=="attributeValue") { $("#"+htmlID+" > .valueContainer").addClass("current"); //make attribute value current var name=$("#"+htmlID).attr("data-name"); //obtain attribute's name var value=$("#"+htmlID).attr("data-value"); //obtain current value var elName=$("#"+htmlID).closest(".element").attr("data-name"); Xonomy.verifyDocSpecAttribute(elName, name); var spec=Xonomy.docSpec.elements[elName].attributes[name]; var content=spec.asker(value, spec.askerParameter, Xonomy.harvestAttribute(document.getElementById(htmlID))); //compose bubble content if(content!="" && content!="") { document.body.appendChild(Xonomy.makeBubble(content)); //create bubble Xonomy.showBubble($("#"+htmlID+" > .valueContainer > .value")); //anchor bubble to value Xonomy.answer=function(val) { var obj=document.getElementById(htmlID); var html=Xonomy.renderAttribute({type: "attribute", name: name, value: val}, elName); $(obj).replaceWith(html); Xonomy.changed(); window.setTimeout(function(){Xonomy.clickoff(); Xonomy.setFocus($(html).prop("id"), what)}, 100); }; } } if(!isReadOnly && what=="text") { $("#"+htmlID).addClass("current"); var value=$("#"+htmlID).attr("data-value"); //obtain current value var elName=$("#"+htmlID).closest(".element").attr("data-name"); var spec=Xonomy.docSpec.elements[elName]; if (typeof(spec.asker) != "function") { var content=Xonomy.askLongString(value, null, Xonomy.harvestElement($("#"+htmlID).closest(".element").toArray()[0])); //compose bubble content } else { var content=spec.asker(value, spec.askerParameter, Xonomy.harvestElement($("#"+htmlID).closest(".element").toArray()[0])); //use specified asker } if(content!="" && content!="") { document.body.appendChild(Xonomy.makeBubble(content)); //create bubble Xonomy.showBubble($("#"+htmlID+" > .value")); //anchor bubble to value Xonomy.answer=function(val) { var obj=document.getElementById(htmlID); var jsText = {type: "text", value: val}; var html=Xonomy.renderText(jsText); $(obj).replaceWith(html); Xonomy.changed(Xonomy.harvestText(document.getElementById(jsText.htmlID))); window.setTimeout(function(){Xonomy.clickoff(); Xonomy.setFocus($(html).prop("id"), what)}, 100); }; } } if(what=="warner") { //$("#"+htmlID).addClass("current"); var content=""; //compose bubble content for(var iWarning=0; iWarning"; } } document.body.appendChild(Xonomy.makeBubble(content)); //create bubble Xonomy.showBubble($("#"+htmlID+" .warner .inside").first()); //anchor bubble to warner } if(what=="rollouter" && $("#"+htmlID+" > .tag.opening > .attributes").children(".shy").toArray().length>0) { if( $("#"+htmlID).children(".tag.opening").children(".rollouter").hasClass("rolledout") ) { $("#"+htmlID).children(".tag.opening").children(".rollouter").removeClass("rolledout"); $("#"+htmlID).children(".tag.opening").children(".attributes").slideUp("fast", function(){ $(this).removeClass("rolledout").css("display", ""); }) } else { $("#"+htmlID).children(".tag.opening").children(".rollouter").addClass("rolledout"); $("#"+htmlID).children(".tag.opening").children(".attributes").addClass("rolledout").hide().slideDown("fast"); } window.setTimeout(function(){Xonomy.setFocus(htmlID, "rollouter")}, 100); } Xonomy.notclick=true; } }; Xonomy.notclick=false; //should the latest click-off event be ignored? Xonomy.clearChars=false; //if true, un-highlight any highlighted characters at the next click-off event Xonomy.clickoff=function() { //event handler for the document-wide click-off event. if(!Xonomy.notclick) { Xonomy.currentHtmlId=null; Xonomy.currentFocus=null; Xonomy.destroyBubble(); $(".xonomy .current").removeClass("current"); $(".xonomy .focused").removeClass("focused"); if(Xonomy.clearChars) { $(".xonomy .char.on").removeClass("on"); Xonomy.clearChars=false; } } Xonomy.notclick=false; }; Xonomy.destroyBubble=function() { if(document.getElementById("xonomyBubble")) { var bubble=document.getElementById("xonomyBubble"); $(bubble).find(":focus").blur(); bubble.parentNode.removeChild(bubble); if(Xonomy.keyboardEventCatcher) Xonomy.keyboardEventCatcher.focus(); } }; Xonomy.makeBubble=function(content) { Xonomy.destroyBubble(); var bubble=document.createElement("div"); bubble.id="xonomyBubble"; bubble.className=Xonomy.mode; bubble.innerHTML="
" +"
"+content+"
" +"
"; return bubble; }; Xonomy.showBubble=function($anchor) { var $bubble=$("#xonomyBubble"); var offset=$anchor.offset(); var screenWidth = $("body").width(); var screenHeight = $(document).height(); var bubbleHeight = $bubble.outerHeight(); var width = $anchor.width(); if (width > 40) width = 40; var height = $anchor.height(); if (height > 25) height = 25; if (Xonomy.mode == "laic") { width = width - 25; height = height + 10; } function verticalPlacement() { var top = ""; var bottom = ""; if (offset.top + height + bubbleHeight <= screenHeight) { // enough space - open down top = (offset.top + height) + "px"; } else if (screenHeight - offset.top + 5 + bubbleHeight > 0) { // 5px above for some padding. Anchor using bottom so animation opens upwards. bottom = (screenHeight - offset.top + 5) + "px"; } else { // neither downwards nor upwards is enough space => center the bubble top = (screenHeight - bubbleHeight)/2 + "px"; } return { top: top, bottom: bottom }; } var placement = verticalPlacement(); if(offset.left"; html+=" "; html+=""; return html; }; Xonomy.askLongString=function(defaultString, askerParameter, jsMe) { var width=($("body").width()*.5)-75 var html=""; html+="
"; html+=""; html+="
"; html+="
"; return html; }; Xonomy.askPicklist=function(defaultString, picklist, jsMe) { var html=""; html+=Xonomy.pickerMenu(picklist, defaultString); return html; }; Xonomy.askOpenPicklist=function(defaultString, picklist) { var isInPicklist=false; var html=""; html+=Xonomy.pickerMenu(picklist, defaultString); html+="
"; html+=""; html+=" "; html+="
"; return html; }; Xonomy.askRemote=function(defaultString, param, jsMe) { var html=""; if(param.searchUrl || param.createUrl) { html+="
"; html+=""; if(param.searchUrl) html+=" "; if(param.createUrl) html+=" "; html+="
"; } html+=Xonomy.wyc(param.url, function(picklist){ var items=[]; if(param.add) for(var i=0; i"; var alone=true; html+="\""; if(item.displayValue) { html+=Xonomy.textByLang(item.displayValue); alone=false; } else { html+=Xonomy.xmlEscape(item.value); if(item.value) alone=false; } html+="\""; if(item.caption!="") html+=" "+Xonomy.xmlEscape(Xonomy.textByLang(item.caption))+""; html+=""; } html+=""; return html; }; Xonomy.wycLastID=0; Xonomy.wycCache={}; Xonomy.wyc=function(url, callback){ //a "when-you-can" function for delayed rendering: gets json from url, passes it to callback, and delayed-returns html-as-string from callback Xonomy.wycLastID++; var wycID="xonomy_wyc_"+Xonomy.wycLastID; if(Xonomy.wycCache[url]) return callback(Xonomy.wycCache[url]); $.ajax({url: url, dataType: "json", method: "POST"}).done(function(data){ $("#"+wycID).replaceWith(callback(data)); Xonomy.wycCache[url]=data; }); return ""; }; Xonomy.toggleSubmenu=function(menuItem){ var $menuItem=$(menuItem); if($menuItem.hasClass("expanded")){ $menuItem.find(".submenu").first().slideUp("fast", function(){$menuItem.removeClass("expanded");}); } else { $menuItem.find(".submenu").first().slideDown("fast", function(){$menuItem.addClass("expanded");}); }; } Xonomy.internalMenu=function(htmlID, items, harvest, getter, indices) { indices = indices || []; var fragments = items.map(function (item, i) { Xonomy.verifyDocSpecMenuItem(item); var jsMe=harvest(document.getElementById(htmlID)); var includeIt=!item.hideIf(jsMe); var html=""; if(includeIt) { indices.push(i); var icon=""; if(item.icon) icon=" "; var key=""; if(item.keyTrigger && item.keyCaption) key=""+Xonomy.textByLang(item.keyCaption)+""; if (item.menu) { var internalHtml=Xonomy.internalMenu(htmlID, item.menu, harvest, getter, indices); if(internalHtml!="") { html+=""; } } else { html+=""; } indices.pop(); } return html; }); var cls = !indices.length ? 'menu' : 'submenu'; return fragments.length ? "
"+fragments.join("")+"
" : ""; }; Xonomy.attributeMenu=function(htmlID) { var name=$("#"+htmlID).attr("data-name"); //obtain attribute's name var elName=$("#"+htmlID).closest(".element").attr("data-name"); //obtain element's name Xonomy.verifyDocSpecAttribute(elName, name); var spec=Xonomy.docSpec.elements[elName].attributes[name]; function getter(indices) { return 'Xonomy.docSpec.elements["'+elName+'"].attributes["'+name+'"].menu['+indices.join('].menu[')+']'; } return Xonomy.internalMenu(htmlID, spec.menu, Xonomy.harvestAttribute, getter); }; Xonomy.elementMenu=function(htmlID) { var elName=$("#"+htmlID).attr("data-name"); //obtain element's name var spec=Xonomy.docSpec.elements[elName]; function getter(indices) { return 'Xonomy.docSpec.elements["'+elName+'"].menu['+indices.join('].menu[')+']'; } return Xonomy.internalMenu(htmlID, spec.menu, Xonomy.harvestElement, getter); }; Xonomy.inlineMenu=function(htmlID) { var elName=$("#"+htmlID).attr("data-name"); //obtain element's name var spec=Xonomy.docSpec.elements[elName]; function getter(indices) { return 'Xonomy.docSpec.elements["'+elName+'"].inlineMenu['+indices.join('].menu[')+']'; } return Xonomy.internalMenu(htmlID, spec.inlineMenu, Xonomy.harvestElement, getter); }; Xonomy.callMenuFunction=function(menuItem, htmlID) { menuItem.action(htmlID, menuItem.actionParameter); }; Xonomy.formatCaption=function(caption) { caption=caption.replace(/\<(\/?)([^\>\/]+)(\/?)\>/g, "<$1$2$3>"); caption=caption.replace(/\@"([^\"]+)"/g, "\"$1\""); caption=caption.replace(/\@([^ =]+)=""/g, "$1=\"\""); caption=caption.replace(/\@([^ =]+)="([^\"]+)"/g, "$1=\"$2\""); caption=caption.replace(/\@([^ =]+)/g, "$1"); return caption; }; Xonomy.deleteAttribute=function(htmlID, parameter) { Xonomy.clickoff(); var obj=document.getElementById(htmlID); var parentID=obj.parentNode.parentNode.parentNode.id; obj.parentNode.removeChild(obj); Xonomy.changed(); window.setTimeout(function(){ Xonomy.setFocus(parentID, "openingTagName"); }, 100); }; Xonomy.deleteElement=function(htmlID, parameter) { Xonomy.clickoff(); var obj=document.getElementById(htmlID); var parentID=obj.parentNode.parentNode.id; $(obj).fadeOut(function(){ var parentNode=obj.parentNode; parentNode.removeChild(obj); Xonomy.changed(); if($(parentNode).closest(".layby").length==0) { window.setTimeout(function(){ Xonomy.setFocus(parentID, "openingTagName"); }, 100); } }); }; Xonomy.newAttribute=function(htmlID, parameter) { Xonomy.clickoff(); var $element=$("#"+htmlID); var html=Xonomy.renderAttribute({type: "attribute", name: parameter.name, value: parameter.value}, $element.data("name")); $("#"+htmlID+" > .tag.opening > .attributes").append(html); Xonomy.changed(); //if the attribute we have just added is shy, force rollout: if($("#"+htmlID+" > .tag.opening > .attributes").children("[data-name='"+parameter.name+"'].shy").toArray().length>0) { if( !$("#"+htmlID).children(".tag.opening").children(".rollouter").hasClass("rolledout") ) { $("#"+htmlID).children(".tag.opening").children(".rollouter").addClass("rolledout"); $("#"+htmlID).children(".tag.opening").children(".attributes").addClass("rolledout").hide().slideDown("fast"); } } if(parameter.value=="") Xonomy.click($(html).prop("id"), "attributeValue"); else Xonomy.focus($(html).prop("id"), "attributeValue"); }; Xonomy.newElementChild=function(htmlID, parameter) { Xonomy.clickoff(); var jsElement=Xonomy.harvestElement(document.getElementById(htmlID)); var html=Xonomy.renderElement(Xonomy.xml2js(parameter, jsElement)); var $html=$(html).hide(); $("#"+htmlID+" > .children").append($html); Xonomy.plusminus(htmlID, true); Xonomy.elementReorder($html.attr("id")); Xonomy.changed(); $html.fadeIn(); window.setTimeout(function(){ Xonomy.setFocus($html.prop("id"), "openingTagName"); }, 100); }; Xonomy.elementReorder=function(htmlID){ var that=document.getElementById(htmlID); var elSpec=Xonomy.docSpec.elements[that.getAttribute("data-name")]; if(elSpec.mustBeBefore) { //is it after an element it cannot be after? then move it up until it's not! var $this=$(that); var jsElement=Xonomy.harvestElement(that); var mustBeBefore=elSpec.mustBeBefore(jsElement); var ok; do { ok=true; for(var ii=0; ii0 ) { $this.prev().before($this); ok=false; } } } while(!ok) } if(elSpec.mustBeAfter) { //is it before an element it cannot be before? then move it down until it's not! var $this=$(that); var jsElement=Xonomy.harvestElement(that); var mustBeAfter=elSpec.mustBeAfter(jsElement); var ok; do { ok=true; for(var ii=0; ii0 ) { $this.next().after($this); ok=false; } } } while(!ok) } }; Xonomy.newElementBefore=function(htmlID, parameter) { Xonomy.clickoff(); var jsElement=Xonomy.harvestElement(document.getElementById(htmlID)); var html=Xonomy.renderElement(Xonomy.xml2js(parameter, jsElement.parent())); var $html=$(html).hide(); $("#"+htmlID).before($html); Xonomy.elementReorder($html.prop("id")); Xonomy.changed(); $html.fadeIn(); window.setTimeout(function(){ Xonomy.setFocus($html.prop("id"), "openingTagName"); }, 100); }; Xonomy.newElementAfter=function(htmlID, parameter) { Xonomy.clickoff(); var jsElement=Xonomy.harvestElement(document.getElementById(htmlID)); var html=Xonomy.renderElement(Xonomy.xml2js(parameter, jsElement.parent())); var $html=$(html).hide(); $("#"+htmlID).after($html); Xonomy.elementReorder($html.prop("id")); Xonomy.changed(); $html.fadeIn(); window.setTimeout(function(){ Xonomy.setFocus($html.prop("id"), "openingTagName"); }, 100); }; Xonomy.replace=function(htmlID, jsNode) { var what=Xonomy.currentFocus; Xonomy.clickoff(); var html=""; if(jsNode.type=="element") html=Xonomy.renderElement(jsNode); if(jsNode.type=="attribute") html=Xonomy.renderAttribute(jsNode); if(jsNode.type=="text") html=Xonomy.renderText(jsNode); $("#"+htmlID).replaceWith(html); Xonomy.changed(); window.setTimeout(function(){ Xonomy.setFocus($(html).prop("id"), what); }, 100); }; Xonomy.editRaw=function(htmlID, parameter) { var div=document.getElementById(htmlID); var jsElement=Xonomy.harvestElement(div); if(parameter.fromJs) var txt=parameter.fromJs( jsElement ); else if(parameter.fromXml) var txt=parameter.fromXml( Xonomy.js2xml(jsElement) ); else var txt=Xonomy.js2xml(jsElement); document.body.appendChild(Xonomy.makeBubble(Xonomy.askLongString(txt))); //create bubble Xonomy.showBubble($(div)); //anchor bubble to element Xonomy.answer=function(val) { var jsNewElement; if(parameter.toJs) jsNewElement=parameter.toJs(val, jsElement); else if(parameter.toXml) jsNewElement=Xonomy.xml2js(parameter.toXml(val, jsElement), jsElement.parent()); else jsNewElement=Xonomy.xml2js(val, jsElement.parent()); var obj=document.getElementById(htmlID); var html=Xonomy.renderElement(jsNewElement); $(obj).replaceWith(html); Xonomy.clickoff(); Xonomy.changed(); window.setTimeout(function(){ Xonomy.setFocus($(html).prop("id"), "openingTagName"); }, 100); }; }; Xonomy.duplicateElement=function(htmlID) { Xonomy.clickoff(); var html=document.getElementById(htmlID).outerHTML.replace(/ id=['"]/g, function(x){return x+"d_"}); var $html=$(html).hide(); $("#"+htmlID).after($html); Xonomy.changed(); $html.fadeIn(); window.setTimeout(function(){ Xonomy.setFocus($html.prop("id"), "openingTagName"); }, 100); }; Xonomy.moveElementUp=function(htmlID){ Xonomy.clickoff(); var $me=$("#"+htmlID); if($me.closest(".layby > .content").length==0) { Xonomy.insertDropTargets(htmlID); var $droppers=$(".xonomy .elementDropper").add($me); var i=$droppers.index($me[0])-1; if(i>=0) { $($droppers[i]).replaceWith($me); Xonomy.changed(); $me.hide().fadeIn(); } Xonomy.dragend(); } window.setTimeout(function(){ Xonomy.setFocus(htmlID, "openingTagName"); }, 100); }; Xonomy.moveElementDown=function(htmlID){ Xonomy.clickoff(); var $me=$("#"+htmlID); if($me.closest(".layby > .content").length==0) { Xonomy.insertDropTargets(htmlID); var $droppers=$(".xonomy .elementDropper").add($me); var i=$droppers.index($me[0])+1; if(i<$droppers.length) { $($droppers[i]).replaceWith($me); Xonomy.changed(); $me.hide().fadeIn(); } Xonomy.dragend(); } window.setTimeout(function(){ Xonomy.setFocus(htmlID, "openingTagName"); }, 100); }; Xonomy.canMoveElementUp=function(htmlID){ var ret=false; var $me=$("#"+htmlID); if($me.closest(".layby > .content").length==0) { Xonomy.insertDropTargets(htmlID); var $droppers=$(".xonomy .elementDropper").add($me); var i=$droppers.index($me[0])-1; if(i>=0) ret=true; Xonomy.dragend(); } return ret; }; Xonomy.canMoveElementDown=function(htmlID){ var ret=false; var $me=$("#"+htmlID); if($me.closest(".layby > .content").length==0) { Xonomy.insertDropTargets(htmlID); var $droppers=$(".xonomy .elementDropper").add($me); var i=$droppers.index($me[0])+1; if(i<$droppers.length) ret=true; Xonomy.dragend(); } return ret; }; Xonomy.mergeWithPrevious=function(htmlID, parameter){ var domDead=document.getElementById(htmlID); var elDead=Xonomy.harvestElement(domDead); var elLive=elDead.getPrecedingSibling(); Xonomy.mergeElements(elDead, elLive); }; Xonomy.mergeWithNext=function(htmlID, parameter){ var domDead=document.getElementById(htmlID); var elDead=Xonomy.harvestElement(domDead); var elLive=elDead.getFollowingSibling(); Xonomy.mergeElements(elDead, elLive); }; Xonomy.mergeElements=function(elDead, elLive){ Xonomy.clickoff(); var domDead=document.getElementById(elDead.htmlID); if(elLive && elLive.type=="element") { for(var i=0; i
") $(".xonomy .children:visible > .element").before("
") $(".xonomy .children:visible > .text").before("
") $(".xonomy .dragging .children:visible > .elementDropper").remove(); //remove drop targets fom inside the element being dragged $(".xonomy .dragging").prev(".elementDropper").remove(); //remove drop targets from immediately before the element being dragged $(".xonomy .dragging").next(".elementDropper").remove(); //remove drop targets from immediately after the element being dragged $(".xonomy .children:visible > .element.readonly .elementDropper").remove(); //remove drop targets from inside read-only elements var harvestCache={}; var harvestElement=function(div){ var htmlID=$(div).prop("id"); if(!harvestCache[htmlID]) harvestCache[htmlID]=Xonomy.harvestElement(div); return harvestCache[htmlID]; }; if(elSpec.localDropOnly(harvestElement($element.toArray()[0]))) { if(elSpec.canDropTo) { //remove the drop target from elements that are not the dragged element's parent var droppers=$(".xonomy .elementDropper").toArray(); for(var i=0; i0 ) { dropper.parentNode.removeChild(dropper); } } } } if(elSpec.mustBeAfter) { //remove the drop target from before elements it cannot be before var jsElement=harvestElement($element.toArray()[0]); var droppers=$(".xonomy .elementDropper").toArray(); for(var i=0; i0 ) { dropper.parentNode.removeChild(dropper); } } } } }; Xonomy.draggingID=null; //what are we dragging? Xonomy.drag=function(ev) { //called when dragging starts // Wrapping all the code into a timeout handler is a workaround for a Chrome browser bug // (if the DOM is manipulated in the 'dragStart' event then 'dragEnd' event is sometimes fired immediately) // // for more details @see: // http://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately ev.dataTransfer.effectAllowed="move"; //only allow moving (and not eg. copying] var htmlID=ev.target.parentNode.parentNode.id; ev.dataTransfer.setData("text", htmlID); setTimeout(function() { Xonomy.clickoff(); Xonomy.insertDropTargets(htmlID); Xonomy.draggingID=htmlID; Xonomy.refresh(); }, 10); }; Xonomy.dragOver=function(ev) { ev.preventDefault(); ev.dataTransfer.dropEffect="move"; //only allow moving (and not eg. copying] if($(ev.currentTarget).hasClass("layby")){ $(ev.currentTarget).addClass("activeDropper"); } else { $(ev.target.parentNode).addClass("activeDropper"); } }; Xonomy.dragOut=function(ev) { ev.preventDefault(); if($(ev.currentTarget).hasClass("layby")){ $(ev.currentTarget).removeClass("activeDropper"); } else { $(".xonomy .activeDropper").removeClass("activeDropper"); } }; Xonomy.drop=function(ev) { ev.preventDefault(); var node=document.getElementById(Xonomy.draggingID); //the thing we are moving if($(ev.currentTarget).hasClass("layby")) { $(node).hide(); $(".xonomy .layby > .content").append(node); $(node).fadeIn(function(){ Xonomy.changed(); }); } else { $(node).hide(); $(ev.target.parentNode).replaceWith(node); $(node).fadeIn(function(){ Xonomy.changed(); }); } Xonomy.openCloseLayby(); Xonomy.recomputeLayby(); }; Xonomy.dragend=function(ev) { $(".xonomy .attributeDropper").remove(); $(".xonomy .elementDropper").remove(); $(".xonomy .dragging").removeClass("dragging"); Xonomy.refresh(); $(".xonomy .layby").removeClass("activeDropper"); }; Xonomy.openCloseLayby=function(){ //open the layby if it's full, close it if it's empty if($(".xonomy .layby > .content > *").length>0){ $(".xonomy .layby").removeClass("closed").addClass("open"); } else { $(".xonomy .layby").removeClass("open").addClass("closed"); } }; Xonomy.openLayby=function(){ $(".xonomy .layby").removeClass("closed").addClass("open"); }; Xonomy.closeLayby=function(){ window.setTimeout(function(){ $(".xonomy .layby").removeClass("open").addClass("closed"); }, 10); }; Xonomy.emptyLayby=function(){ $(".xonomy .layby .content").html(""); $(".xonomy .layby").removeClass("nonempty").addClass("empty"); }; Xonomy.recomputeLayby=function(){ if($(".xonomy .layby > .content > *").length>0){ $(".xonomy .layby").removeClass("empty").addClass("nonempty"); } else { $(".xonomy .layby").removeClass("nonempty").addClass("empty"); } } Xonomy.changed=function(jsElement) { //called when the document changes Xonomy.harvestCache={}; Xonomy.refresh(); Xonomy.validate(); Xonomy.docSpec.onchange(jsElement); //report that the document has changed }; Xonomy.validate=function() { var js=Xonomy.harvestElement($(".xonomy .element").toArray()[0], null); $(".xonomy .invalid").removeClass("invalid"); Xonomy.warnings=[]; Xonomy.docSpec.validate(js); //validate the document for(var iWarning=0; iWarning-1 && $("input:focus, select:focus, textarea:focus").length==0) e.preventDefault(); }); //prevent default browser scrolling on arrow keys Xonomy.keyboardEventCatcher=$keyboardEventCatcher; Xonomy.scrollableContainer=$scrollableContainer; }; Xonomy.setFocus=function(htmlID, what){ if(Xonomy.keyNav) { $(".xonomy .current").removeClass("current"); $(".xonomy .focused").removeClass("focused"); if(what=="attributeValue") $("#"+htmlID+" > .valueContainer").addClass("current").addClass("focused"); else $("#"+htmlID).addClass("current").addClass("focused"); Xonomy.currentHtmlId=htmlID; Xonomy.currentFocus=what; if(Xonomy.currentFocus=="openingTagName") $("#"+htmlID+" > .tag.opening").first().addClass("focused"); if(Xonomy.currentFocus=="closingTagName") $("#"+htmlID+" > .tag.closing").last().addClass("focused"); if(Xonomy.currentFocus=="childrenCollapsed") $("#"+htmlID+" > .childrenCollapsed").last().addClass("focused"); if(Xonomy.currentFocus=="rollouter") $("#"+htmlID+" > .tag.opening > .rollouter").last().addClass("focused"); } }; Xonomy.key=function(event){ if(!Xonomy.notKeyUp) { if(!event.shiftKey && !$("#xonomyBubble").length>0 ) { if(event.which==27) { //escape key event.preventDefault(); event.stopImmediatePropagation(); Xonomy.destroyBubble(); } else if(event.which==13){ //enter key event.preventDefault(); event.stopImmediatePropagation(); if(Xonomy.currentFocus=="childrenCollapsed") Xonomy.plusminus(Xonomy.currentHtmlId, true); if(Xonomy.currentFocus=="char") { Xonomy.charClick($("#"+Xonomy.currentHtmlId)[0]); } else { Xonomy.click(Xonomy.currentHtmlId, Xonomy.currentFocus); Xonomy.clickoff(); } } else if((event.ctrlKey || event.metaKey) && event.which==40) { //down key with Ctrl or Cmd (Mac OS) event.preventDefault(); event.stopImmediatePropagation(); Xonomy.scrollableContainer.scrollTop( Xonomy.scrollableContainer.scrollTop()+60 ); } else if((event.ctrlKey || event.metaKey) && event.which==38) { //up key with Ctrl or Cmd (Mac OS) event.preventDefault(); event.stopImmediatePropagation(); Xonomy.scrollableContainer.scrollTop( Xonomy.scrollableContainer.scrollTop()-60 ); } else if((event.ctrlKey || event.metaKey) && [37, 39].indexOf(event.which)>-1) { //arrow keys with Ctrl or Cmd (Mac OS) event.preventDefault(); event.stopImmediatePropagation(); var $el=$("#"+Xonomy.currentHtmlId); if($el.hasClass("element") && !$el.hasClass("uncollapsible")){ if(event.which==39 && $el.hasClass("collapsed")) { //expand it! Xonomy.plusminus(Xonomy.currentHtmlId); } if(event.which==37 && !$el.hasClass("collapsed")) { //collapse it! Xonomy.plusminus(Xonomy.currentHtmlId); } } } else if([37, 38, 39, 40].indexOf(event.which)>-1 && !event.altKey) { //arrow keys event.preventDefault(); event.stopImmediatePropagation(); if(!Xonomy.currentHtmlId) { //nothing is current yet Xonomy.setFocus($(".xonomy .element").first().prop("id"), "openingTagName"); } else if($(".xonomy .focused").length==0) { //something is current but nothing is focused yet Xonomy.setFocus(Xonomy.currentHtmlId, Xonomy.currentFocus); } else { //something is current, do arrow action if(event.which==40) Xonomy.goDown(); //down key if(event.which==38) Xonomy.goUp(); //up key if(event.which==39) Xonomy.goRight(); //right key if(event.which==37) Xonomy.goLeft(); //left key } } } else if(!$("#xonomyBubble").length>0) { Xonomy.keyboardMenu(event); } } Xonomy.notKeyUp=false; }; Xonomy.keyboardMenu=function(event){ var $obj=$("#"+Xonomy.currentHtmlId); var jsMe=null; var menu=null; if($obj.hasClass("element")){ jsMe=Xonomy.harvestElement($obj[0]); var elName=$obj.attr("data-name"); menu=Xonomy.docSpec.elements[elName].menu; } else if($obj.hasClass("attribute")) { jsMe=Xonomy.harvestAttribute($obj[0]); var atName=$obj.attr("data-name"); var elName=$obj.closest(".element").attr("data-name"); menu=Xonomy.docSpec.elements[elName].attributes[atName].menu; } if(menu){ var findMenuItem=function(menu){ var ret=null; for(var i=0; i0) { var $next=$candidates.eq( $candidates.index($me[0])-1 ); if($next.hasClass("opening")) Xonomy.setFocus($next.closest(".element").prop("id"), "openingTagName"); if($next.hasClass("closing")) Xonomy.setFocus($next.closest(".element").prop("id"), "closingTagName"); if($next.hasClass("textnode")) Xonomy.setFocus($next.prop("id"), "text"); } } }; Xonomy.goRight=function(){ var $el=$("#"+Xonomy.currentHtmlId); var $me=$el; if(Xonomy.currentFocus=="openingTagName") var $me=$el.find(".tag.opening").first(); if(Xonomy.currentFocus=="closingTagName") var $me=$el.find(".tag.closing").last(); if(Xonomy.currentFocus=="attributeName") var $me=$el.find(".attributeName").first(); if(Xonomy.currentFocus=="attributeValue") var $me=$el.find(".attributeValue").first(); if(Xonomy.currentFocus=="childrenCollapsed") var $me=$el.find(".childrenCollapsed").first(); if(Xonomy.currentFocus=="rollouter") var $me=$el.find(".rollouter").first(); var $candidates=$(".xonomy .focusable:visible"); $candidates=$candidates.not(".char").add(".hasInlineMenu > .children > .textnode .char:visible"); var $next=$candidates.eq( $candidates.index($me[0])+1 ); if($next.hasClass("attributeName")) Xonomy.setFocus($next.closest(".attribute").prop("id"), "attributeName"); if($next.hasClass("attributeValue")) Xonomy.setFocus($next.closest(".attribute").prop("id"), "attributeValue"); if($next.hasClass("opening")) Xonomy.setFocus($next.closest(".element").prop("id"), "openingTagName"); if($next.hasClass("closing")) Xonomy.setFocus($next.closest(".element").prop("id"), "closingTagName"); if($next.hasClass("textnode")) Xonomy.setFocus($next.prop("id"), "text"); if($next.hasClass("childrenCollapsed")) Xonomy.setFocus($next.closest(".element").prop("id"), "childrenCollapsed"); if($next.hasClass("rollouter")) Xonomy.setFocus($next.closest(".element").prop("id"), "rollouter"); if($next.hasClass("char")) Xonomy.setFocus($next.prop("id"), "char"); }; Xonomy.goLeft=function(){ var $el=$("#"+Xonomy.currentHtmlId); var $me=$el; if(Xonomy.currentFocus=="openingTagName") var $me=$el.find(".tag.opening").first(); if(Xonomy.currentFocus=="closingTagName") var $me=$el.find(".tag.closing").last(); if(Xonomy.currentFocus=="attributeName") var $me=$el.find(".attributeName").first(); if(Xonomy.currentFocus=="attributeValue") var $me=$el.find(".attributeValue").first(); if(Xonomy.currentFocus=="childrenCollapsed") var $me=$el.find(".childrenCollapsed").first(); if(Xonomy.currentFocus=="rollouter") var $me=$el.find(".rollouter").first(); var $candidates=$(".xonomy .focusable:visible"); $candidates=$candidates.not(".char").add(".hasInlineMenu > .children > .textnode .char:visible"); var $next=$candidates.eq( $candidates.index($me[0])-1 ); if($next.hasClass("attributeName")) Xonomy.setFocus($next.closest(".attribute").prop("id"), "attributeName"); if($next.hasClass("attributeValue")) Xonomy.setFocus($next.closest(".attribute").prop("id"), "attributeValue"); if($next.hasClass("opening")) Xonomy.setFocus($next.closest(".element").prop("id"), "openingTagName"); if($next.hasClass("closing")) Xonomy.setFocus($next.closest(".element").prop("id"), "closingTagName"); if($next.hasClass("textnode")) Xonomy.setFocus($next.prop("id"), "text"); if($next.hasClass("childrenCollapsed")) Xonomy.setFocus($next.closest(".element").prop("id"), "childrenCollapsed"); if($next.hasClass("rollouter")) Xonomy.setFocus($next.closest(".element").prop("id"), "rollouter"); if($next.hasClass("char")) Xonomy.setFocus($next.prop("id"), "char"); };