blob: 4e603df84543831f5b7c77a7fb128c71aec16a8b [file] [log] [blame]
/*
* 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 --------------------------