require('selection'); require('mustache'); jQuery.fn.wysiwyg = function(template, rootSelector) { var editor; this.each(function() { editor = new Editor(this, template, rootSelector); }); return editor; }; var Editor = function(textarea, template, rootSelector) { // Find the element we should make editable. Defaults to the BODY element // once we've rendered the template. this.rootSelector = rootSelector || 'body'; this.textarea = $(textarea); this.container = $('
'); var toolbarItems = [ {id: 'bold', alt: 'Make text bold'}, {id: 'italic', alt: 'Make text italic'}, {id: 'hyperlink', alt: 'Insert a link'}, {id: 'unordered_list', alt: 'Insert a bullet list'}, {id: 'ordered_list', alt: 'Insert a numbered list'}, // {id: 'gallery', alt: 'Embed a gallery'}, {id: 'image', alt: 'Embed an image'} ]; this.toolbar = new Editor.Toolbar(this, toolbarItems); this.container.append(this.toolbar.container); this.iframe = $('').hide(); this.container.height(this.textarea.height() + 6.0); this.textarea.height(this.textarea.height() + 5.0); this.container.append(this.iframe); this.startLoading(); this.textarea.after(this.container); this.container.append(this.textarea); var editor = this; $.get('/grandstand/templates/' + template, function(response) { editor.template = response; editor.initialize(); }); }; Editor.prototype = { focus: function(selector, offset) { var selection = this.selection(); selection.select(selector || ':block', offset || 0); }, initialize: function() { var editor = this; try { this.window = this.iframe[0].contentWindow; this.document = this.window.document; } catch(exception) { return setTimeout(function(){ editor.initialize(); }, 10); } this.write(this.textarea.val()); this.body = this.document.body; // Make sure all data is saved, no matter what. this.textarea.parents('form').submit(function() { editor.save(); }); this.textarea.hide(); this.toolbar.container.show(); this.iframe.height(this.textarea.height() - this.toolbar.container.height() - 4); this.iframe.show(); this.root = $(this.document).find(this.rootSelector); this.root.css('height', '100%'); this.root.find('img').live('click', function(event) { event.preventDefault(); event.stopPropagation(); var img = $(this); var ids = this.src.match(/\d\d\d\d\d\d/ig); var url = '/grandstand/galleries/' + ids[0].replace(/^0+/, '') + '/images/' + ids[1].replace(/^0+/, ''); url += '?align=' + this.className + '&size=' + this.src.substring(this.src.lastIndexOf(ids[1]) + 7, this.src.lastIndexOf('.')); Dialog.show(url, { style: 'medium', submit: function(event) { event.preventDefault(); // Grab the right form, thanks a lot IE... var form = $(event.target); if (!form.is('form')) { form = form.parents('form'); } img.removeClass().addClass(form.find('input[name=align]:checked').val()); img.load(function() { img.attr('height', img.height()); img.attr('width', img.width()); }); img.attr('src', form.find('input[name=size]:checked').val()); img.removeAttr('height'); img.removeAttr('width'); Dialog.hide(); } }); }); // $([this.document, document]).keyup(function(event) { // editor.selection().normalize(); // }); var pasting, selection; this.root.keyup(function(event) { selection = editor.selection(); if (jQuery.browser.mozilla && editor.root.find('p').length === 0) { // Ensure Firefox is using paragraphs and not line breaks when I // insert content if (editor.root.text().replace(/\s+/ig, '') == '') { editor.root.html(''); selection.select('p'); } else { selection.selectAll(); selection.wrap('p'); selection.select('p', 1); } } clearTimeout(editor.timeout); editor.timeout = setTimeout(function() { editor.save(); }, 100); if (selection.wrappedIn('strong')) { editor.toolbar.on('bold'); } else { editor.toolbar.off('bold'); } if (selection.wrappedIn('em')) { editor.toolbar.on('italic'); } else { editor.toolbar.off('italic'); } }).bind('paste', function(event) { var clipboard; if (event.originalEvent.clipboardData) { clipboard = event.originalEvent.clipboardData; } else if (typeof(Components) != 'undefined' && Components.interfaces.nsIClipboard) { // return true; // // TODO: Handle Firefox's ABYSMAL onPaste support with signed JavaScript or some equally pointless bullshit. // Hey, Firefox! If someone *pastes*, let me see what they *pasted*, you fucking bastard! // // var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard); // // if (!clip) return false; // // var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable); // // if (!trans) return false; // trans.addDataFlavor("text/unicode"); // // clip.getData(trans, clip.kGlobalClipboard); // // var str = new Object(); // var strLength = new Object(); // trans.getTransferData("text/unicode", str, strLength); } else if (window.clipboardData) { clipboard = window.clipboardData; } if (clipboard) { selection = editor.selection(); var text = clipboard.getData('text/plain').toString().split(/(\n|\r){2,}/igm); if (text.length > 0) { selection.replace(text.shift().clean()); $.each(text.reverse(), function() { selection.endBlock().after('' + this.clean() + '
'); }); return false; } } }); this.root.attr('contentEditable', 'true').css('outline', '0'); // this.document.body.contentEditable = 'true'; this.focus(); this.stopLoading(); }, save: function() { var html = this.root.html(); // Clean that HTML good html = html.replace(/