| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * WARNING: this file is supposed to be cross-browser. All browser-specific features |
| * should be wrapped with a function call and factored out in the "browser_dependent.js" |
| * file. Please, keep it this way. |
| * |
| */ |
| |
| // -------------------------- global variables ------------------------------ |
| |
| var iframe; |
| var editor; |
| var editor_window; |
| var path; |
| var formatblock; |
| var alternatives; |
| var alternativesTarget; |
| var class_selector; |
| var block_selector; |
| var lastOrigin; |
| var image_inputs; |
| var image_controls; |
| |
| var previousKey; |
| var modified = false; |
| var initialized = false; |
| |
| var imagePrefix = "image"; |
| var imageCounter = 0; |
| var imageData = new Array(); |
| |
| var sourceMode = false; |
| |
| // ----------------------- UI-modifying functions --------------------------- |
| |
| function start(e) { |
| |
| if (initialized) { |
| alert("already initialized"); |
| return; |
| } |
| |
| // First of all, setup the editing canvas |
| try { |
| iframe = document.getElementById('edit'); |
| editor_window = iframe.contentWindow; |
| editor = editor_window.document; |
| editor.designMode = "On"; |
| addEvent(editor,"click",click,true); |
| addEvent(editor,"keydown",keypress,true); |
| try { |
| editor.execCommand("useCSS", false, true); |
| } catch(e) {} |
| } catch (e) { |
| alert("I'm sorry, but Linotype doesn't work on this browser: " + e.name + " " + e.message); |
| return; |
| } |
| |
| // If we get here, the browser supports "designMode" so we |
| // instrument the active parts of the page |
| path = document.getElementById('editpath').firstChild; |
| formatblock = document.getElementById('formatblock'); |
| formatblock.onchange = formatblockChange; |
| alternatives = document.getElementById('alternatives'); |
| block_selector = document.getElementById('block_selector'); |
| class_selector = document.getElementById('class_selector'); |
| image_inputs = document.getElementById('image_inputs'); |
| image_controls = document.getElementById('image_controls'); |
| document.getElementById("wysiwyg-checkbox").checked = true; |
| |
| // Then we instrument the toolbar <div> and turn them into buttons |
| divs = document.getElementsByTagName('div'); |
| for (var i = 0; i < divs.length; i++) { |
| var nameclass = getClass(divs[i]); |
| if (nameclass == "imagebutton") { |
| divs[i].onmousedown = buttonDown; |
| divs[i].onmouseup = buttonUp; |
| divs[i].onmouseover = buttonOver; |
| divs[i].onmouseout = buttonOut; |
| divs[i].onclick = buttonClick; |
| } |
| } |
| |
| // Then we instrument the images that might be already contained |
| // in the editing canvas. |
| instrumentImages(editor); |
| |
| // set the initialization flag to avoid onload event loops |
| initialized = true; |
| |
| // and here we go! |
| window.status = "Welcome to Linotype"; |
| } |
| |
| function stop(e) { |
| |
| /* |
| * FIXME (SM): the onload() event isn't cancellable. This means that we can't |
| * prevent people from unloading the page and loose their changes. In IE |
| * there is a non-standard way to do this which is hacky at hell but does |
| * the job. Is there an equivalent thing for moz? |
| */ |
| |
| if (modified) { |
| // if ( window.confirm("You havn't saved. You changes will be lost.\nDo you want to continue?") ) { |
| // return; |
| // } else { |
| // e.stopPropagation(); |
| // e.preventDefault(); |
| // e.returnValue = false; |
| // return false; |
| // } |
| } |
| } |
| |
| function updateUI(force) { |
| |
| var target = getOrigin(); |
| |
| if (target != lastOrigin || force) { |
| lastOrigin = target; |
| |
| if (!sourceMode) { |
| path.nodeValue = getPath(target); |
| } else { |
| path.nodeValue = "..."; |
| } |
| |
| if (target && (target.nodeType == NodeType.TEXT_NODE)) { |
| target = target.parentNode; |
| } |
| |
| var format = getBlockFormat(target); |
| if (format != -1) { |
| formatblock.selectedIndex = format; |
| block_selector.style.visibility = "visible"; |
| } else { |
| block_selector.style.visibility = "hidden"; |
| } |
| |
| var options = getAlternatives(target); |
| if (options) { |
| var parent = alternatives.parentNode; |
| parent.removeChild(alternatives); |
| alternatives = document.createElement('select'); |
| var type = getClass(target); |
| for (i = 0; i < options.length; i++) { |
| var option = document.createElement('option'); |
| option.setAttribute("value", options[i]); |
| if (type == options[i]) { |
| option.setAttribute("selected", "true"); |
| } |
| option.appendChild(document.createTextNode(options[i])); |
| alternatives.appendChild(option); |
| } |
| alternatives.onchange = alternativesChange; |
| parent.appendChild(alternatives); |
| alternativesTarget = target; |
| class_selector.style.visibility = "visible"; |
| } else { |
| class_selector.style.visibility = "hidden"; |
| } |
| } |
| |
| return target; |
| } |
| |
| function wysiwyg(enabled) { |
| if (enabled) { |
| var text = serializeChildren(editor.body); |
| var source = editor.createTextNode(text); |
| editor.body.innerHTML = ""; |
| editor.body.appendChild(source); |
| setClass(editor.body, "source"); |
| sourceMode = true; |
| updateUI(true); |
| } else { |
| var source = getContentSource(); |
| editor.body.innerHTML = source; |
| setClass(editor.body,"body"); |
| reinstrumentImages(editor); |
| sourceMode = false; |
| } |
| } |
| |
| function loadContent(content) { |
| editor.body.innerHTML = content; |
| } |
| |
| function addImage() { |
| editor.execCommand("insertimage", false, "template.jpg"); |
| var img = getOrigin(); |
| var input = instrumentImg(img, true); |
| activateImgInstrumentation(input, img); |
| } |
| |
| function instrumentImages(edit) { |
| var imgs = detach(editor.getElementsByTagName('img')); |
| for (var i = 0; i < imgs.length; i++) { |
| instrumentImg(imgs[i], false); |
| } |
| } |
| |
| function reinstrumentImages(editor) { |
| var imgs = detach(editor.getElementsByTagName('img')); |
| for (var i = 0; i < imgs.length; i++) { |
| reinstrumentImg(imgs[i]); |
| } |
| } |
| |
| function instrumentImg(img, template) { |
| var id = imageCounter++; |
| var imgID = imagePrefix + "-" + id; |
| var src = img.getAttribute("src"); |
| |
| img.setAttribute("id", imgID); |
| img.setAttribute("ihref", src); |
| setTemplate(img, template); |
| |
| addEvent(img.parentNode,"DOMNodeRemoved",imageRemoved); |
| |
| imageData[imgID] = [src, src, template]; |
| |
| var input = document.createElement("input"); |
| input.setAttribute("type", "file"); |
| input.setAttribute("id", imgID + "-input"); |
| input.setAttribute("size", "1"); |
| setClass(input, "image_browser"); |
| addEvent(input,"click", inputChange); |
| addEvent(input,"change", inputChange); |
| input.style.position = "absolute"; |
| input.style.visiblility = "hidden"; |
| |
| image_inputs.appendChild(input); |
| |
| return input; |
| } |
| |
| function reinstrumentImg(img, id) { |
| if (!id) { |
| var src = img.getAttribute("src"); |
| var id = src.substring(src.lastIndexOf('/'), src.lastIndexOf('.')); |
| } |
| |
| var data = imageData[id]; |
| |
| img.setAttribute("id",id); |
| img.setAttribute("src",data[0]); |
| img.setAttribute("ihref",data[1]); |
| setTemplate(img, data[2]); |
| } |
| |
| function activateImgInstrumentation(input,img) { |
| input.style.left = (iframe.offsetLeft + img.x + 5) + "px"; |
| input.style.top = (iframe.offsetTop + img.y + 5) + "px"; |
| input.style.visibility = "visible"; |
| } |
| |
| function deactivateImgInstrumentation(input) { |
| input.style.visibility = "hidden"; |
| input.name = ""; |
| } |
| |
| function block() { |
| editor.execCommand("formatblock", false, getCommandFormat("P")); |
| } |
| |
| function unblock() { |
| var block = getParentBlock(getOrigin()); |
| var parent = block.parentNode; |
| var children = block.childNodes; |
| for (var i = children.length - 1; i >= 0; i--) { |
| parent.insertBefore(children.item(i),block); |
| } |
| parent.removeChild(block); |
| } |
| |
| // --------------------- Content-related functions ------------------- |
| |
| function getInnerHTML() { |
| return editor.body.innerHTML; |
| } |
| |
| function getContent() { |
| if (sourceMode) wysiwyg(false); |
| var content = '<html xmlns="http://www.w3.org/1999/xhtml"><body>'; |
| content += serializeChildren(editor.body); |
| content += '</body></html>'; |
| return content; |
| } |
| |
| // ----------------------- Event functions --------------------------- |
| |
| function keypress(e) { |
| var key, ch = 0 |
| modified = true; |
| if (!e) e = edit.event; |
| key = e.keyCode; |
| |
| //var target = getPreviousMeaningfulNode(updateUI()); |
| |
| //window.status = "[" + key + "," + e.shiftKey + "," + e.ctrlKey + "," + e.altKey + "," + e.metaKey + "]"; |
| |
| try { |
| // Disable "Back" / "Forward" buttons (Alt|Apple+LeftArrow / Alt|Apple+RightArrow) |
| if (e.altKey || e.metaKey) { |
| if (key > 0) { |
| switch (key) { |
| // FIXME(SM): is seems that these events aren't canceled by |
| // the event cancellation model in Mozilla and Firebird |
| // but if we trigger an alert() box they are stopped |
| // it's hacky, I know, but I can't find a better way and |
| // it is vital that I stop these since it's muscle memory |
| // for MacOS users to use Apple+arrows to move to star/end |
| // of line and if done here we loose all the content without |
| // a warning!! |
| case 37: // left arrow |
| alert("blocking Alt|Apple+LeftArrow"); |
| e.stopPropagation(); |
| e.preventDefault(); |
| e.returnValue = false; |
| return false; |
| case 39: // right arrow |
| alert("blocking Alt|Apple+RightArrow"); |
| e.stopPropagation(); |
| e.preventDefault(); |
| e.returnValue = false; |
| return false; |
| } |
| } |
| |
| // FIXME(SM): Here is the place to look for advanced keyboard interaction with the |
| // schema, for example when doing two returns in a particular location |
| // will generate a particular element, but since this is a hell of |
| // state control and I'm a lazy ass, we'll do it in the future ;-) |
| |
| //} else if (key == 13) { |
| // if (previousKey == 13) { |
| // editor.execCommand("insertparagraph", false, null); |
| // } else if (target.parentNode.nodeName == "H1") { |
| // //editor.execCommand("insertparagraph", false, null); |
| // } |
| |
| } |
| } catch (e) { } |
| |
| previousKey = key; |
| } |
| |
| function click() { |
| updateUI(); |
| previousKey = null; |
| } |
| |
| function formatblockChange() { |
| var selection = formatblock.selectedIndex; |
| var format = formatblock.options[selection].value; |
| var commandFormat = getCommandFormat(format); |
| editor.execCommand("formatblock", false, commandFormat); |
| updateUI(true); |
| editor_window.focus(); |
| modified = true; |
| } |
| |
| function alternativesChange() { |
| var selection = alternatives.selectedIndex; |
| var selectionClass = alternatives.options[selection].value; |
| setClass(alternativesTarget,selectionClass); |
| } |
| |
| function buttonClick() { |
| if (this.id == "createlink") { |
| var href = prompt("Enter a URL:", ""); |
| editor.execCommand(this.id, false, href); |
| } else if (this.id == "insertimage") { |
| addImage(); |
| } else if (this.id == "block") { |
| block(); |
| } else if (this.id == "unblock") { |
| unblock(); |
| } else { |
| editor.execCommand(this.id, false, null); |
| } |
| updateUI(true); |
| editor_window.focus(); |
| modified = true; |
| } |
| |
| function buttonDown() { |
| this.firstChild.style.left = "1px"; |
| this.firstChild.style.top = "1px"; |
| this.style.border="inset 1px"; |
| } |
| |
| function buttonUp() { |
| this.firstChild.style.left = "0px"; |
| this.firstChild.style.top = "0px"; |
| this.style.border="outset 1px"; |
| } |
| |
| function buttonOver() { |
| this.style.border="outset 1px"; |
| } |
| |
| function buttonOut() { |
| this.style.border="solid 1px #eee"; |
| } |
| |
| function imageRemoved(e) { |
| var target = getTarget(e,this); |
| if (target) { |
| var id = target.getAttribute("id"); |
| var input = document.getElementById(id + "-input"); |
| if (input) deactivateImgInstrumentation(input); |
| } |
| } |
| |
| function inputChange(e) { |
| var input = getTarget(e,this); |
| var inputID = input.getAttribute("id"); |
| var imgID = inputID.substring(0, inputID.indexOf("-input")); |
| var img = editor.getElementById(imgID); |
| if (input.value != "") { |
| var src = "file:///" + input.value; |
| var href = imgID + input.value.substring(input.value.lastIndexOf('.')); |
| var newImg = document.createElement("img"); |
| img.parentNode.replaceChild(newImg, img); |
| imageData[imgID] = [src, href, false]; |
| reinstrumentImg(newImg, imgID); |
| deactivateImgInstrumentation(input); |
| input.setAttribute("name", "save:" + href); |
| } else { |
| img.parentNode.removeChild(img); |
| // this will cause a "node removed" event that will cause |
| // the imageRemoved() function to be called, cleaning up |
| // the instrumented input field |
| } |
| } |
| |
| // ------------------------------ end of file -------------------------- |