1 /*! 2 * This file is part of Aloha Editor Project http://aloha-editor.org 3 * Copyright © 2010-2011 Gentics Software GmbH, aloha@gentics.com 4 * Contributors http://aloha-editor.org/contribution.php 5 * Licensed unter the terms of http://www.aloha-editor.org/license.html 6 *//* 7 * Aloha Editor is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU Affero General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version.* 11 * 12 * Aloha Editor is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 define( 22 [ 'aloha/core', 'aloha/selection', 'aloha/jquery', 'aloha/console' ], 23 function( Aloha, Selection, jQuery, console ) { 24 "use strict"; 25 26 var 27 // $ = jQuery, 28 // Aloha = window.Aloha, 29 // console = window.console, 30 XMLSerializer = window.XMLSerializer; 31 32 /** 33 * jQuery between Extension 34 * 35 * insert either html code, a dom object OR a jQuery object inside of an existing text node. 36 * if the chained jQuery object is not a text node, nothing will happen. 37 * 38 * @param content HTML Code, DOM object or jQuery object to be inserted 39 * @param offset character offset from the start where the content should be inserted 40 */ 41 jQuery.fn.between = function(content, offset) { 42 var 43 offSize, 44 fullText; 45 46 if (this[0].nodeType !== 3) { 47 // we are not in a text node, just insert the element at the corresponding position 48 offSize = this.children().size(); 49 if (offset > offSize) { 50 offset = offSize; 51 } 52 if (offset <= 0) { 53 this.prepend(content); 54 } else { 55 this.children().eq(offset -1).after(content); 56 } 57 } else { 58 // we are in a text node so we have to split it at the correct position 59 if (offset <= 0) { 60 this.before(content); 61 } else if (offset >= this[0].length) { 62 this.after(content); 63 } else { 64 fullText = this[0].data; 65 this[0].data = fullText.substring(0, offset); 66 this.after(fullText.substring(offset, fullText.length)); 67 this.after(content); 68 } 69 } 70 }; 71 72 /** 73 * Make the object contenteditable. Care about browser version (name of contenteditable attribute depends on it) 74 */ 75 jQuery.fn.contentEditable = function( b ) { 76 // ie does not understand contenteditable but contentEditable 77 // contentEditable is not xhtml compatible. 78 var $el = jQuery(this); 79 var ce = 'contenteditable'; 80 81 // Check 82 if (jQuery.browser.msie && parseInt(jQuery.browser.version,10) == 7 ) { 83 ce = 'contentEditable'; 84 } 85 86 if (typeof b === 'undefined' ) { 87 88 // For chrome use this specific attribute. The old ce will only 89 // return 'inherit' for nested elements of a contenteditable. 90 // The isContentEditable is a w3c standard compliant property which works in IE7,8,FF36+, Chrome 12+ 91 if (typeof $el[0] === 'undefined' ) { 92 console.error('The jquery object did not contain any valid elements.'); 93 return undefined; 94 } 95 if (typeof $el[0].isContentEditable === 'undefined') { 96 console.warn('Could not determine whether the is editable or not. I assume it is.'); 97 return true; 98 } else { 99 return $el[0].isContentEditable; 100 } 101 } else if (b === '') { 102 $el.removeAttr(ce); 103 } else { 104 if (b && b !== 'false') { 105 b = 'true'; 106 } else { 107 b = 'false'; 108 } 109 $el.attr(ce, b); 110 } 111 112 return $el; 113 }; 114 115 /** 116 * jQuery Aloha Plugin 117 * 118 * turn all dom elements to continous text 119 * @return jQuery object for the matched elements 120 * @api 121 */ 122 jQuery.fn.aloha = function() { 123 var $this = jQuery( this ); 124 125 Aloha.bind( 'aloha-ready', function() { 126 $this.each( function() { 127 // create a new aloha editable object for each passed object 128 129 if ( !Aloha.isEditable( this ) ) { 130 new Aloha.Editable( jQuery( this ) ); 131 } 132 133 }); 134 }); 135 136 // Chain 137 return $this; 138 }; 139 140 /** 141 * jQuery destroy elements as editable 142 * 143 * destroy all mached elements editable capabilities 144 * @return jQuery object for the matched elements 145 * @api 146 */ 147 jQuery.fn.mahalo = function() { 148 return this.each(function() { 149 if (Aloha.isEditable(this)) { 150 Aloha.getEditableById(jQuery(this).attr('id')).destroy(); 151 } 152 }); 153 }; 154 155 /** 156 * jQuery Extension 157 * new Event which is triggered whenever a selection (length >= 0) is made in 158 * an Aloha Editable element 159 */ 160 jQuery.fn.contentEditableSelectionChange = function(callback) { 161 var that = this; 162 163 // update selection when keys are pressed 164 this.keyup(function(event){ 165 var rangeObject = Selection.getRangeObject(); 166 callback(event); 167 }); 168 169 // update selection on doubleclick (especially important for the first automatic selection, when the Editable is not active yet, but is at the same time activated as the selection occurs 170 this.dblclick(function(event) { 171 callback(event); 172 }); 173 174 // update selection when text is selected 175 this.mousedown(function(event){ 176 // remember that a selection was started 177 that.selectionStarted = true; 178 }); 179 jQuery(document).mouseup(function(event) { 180 Selection.eventOriginalTarget = that; 181 if (that.selectionStarted) { 182 callback(event); 183 } 184 Selection.eventOriginalTarget = false; 185 that.selectionStarted = false; 186 }); 187 188 return this; 189 }; 190 191 /** 192 * Fetch the outerHTML of an Element 193 * @version 1.0.0 194 * @date February 01, 2011 195 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle} 196 * @author Benjamin Arthur Lupton {@link http://balupton.com} 197 * @copyright 2011 Benjamin Arthur Lupton {@link http://balupton.com} 198 * @license MIT License {@link http://creativecommons.org/licenses/MIT/} 199 * @return {String} outerHtml 200 */ 201 jQuery.fn.outerHtml = jQuery.fn.outerHtml || function(){ 202 var 203 $el = jQuery(this), 204 el = $el.get(0); 205 if (typeof el.outerHTML != 'undefined') { 206 return el.outerHTML; 207 } else { 208 try { 209 // Gecko-based browsers, Safari, Opera. 210 return (new XMLSerializer()).serializeToString(el); 211 } catch (e) { 212 try { 213 // Internet Explorer. 214 return el.xml; 215 } catch (e) {} 216 } 217 } 218 219 }; 220 221 222 jQuery.fn.zap = function () { 223 return this.each(function(){ jQuery(this.childNodes).insertBefore(this); }).remove(); 224 }; 225 226 jQuery.fn.textNodes = function(excludeBreaks, includeEmptyTextNodes) { 227 var 228 ret = [], 229 doSomething = function(el){ 230 if ( 231 (el.nodeType === 3 && jQuery.trim(el.data) && !includeEmptyTextNodes) || 232 (el.nodeType === 3 && includeEmptyTextNodes) || 233 (el.nodeName =="BR" && !excludeBreaks)) { 234 ret.push(el); 235 } else { 236 for (var i=0, childLength = el.childNodes.length; i < childLength; ++i) { 237 doSomething(el.childNodes[i]); 238 } 239 } 240 }; 241 242 doSomething(this[0]); 243 244 return jQuery(ret); 245 }; 246 247 /** 248 * extendObjects is like jQuery.extend, but it does not extend arrays 249 */ 250 jQuery.extendObjects = jQuery.fn.extendObjects = function() { 251 var options, name, src, copy, copyIsArray, clone, 252 target = arguments[0] || {}, 253 i = 1, 254 length = arguments.length, 255 deep = false; 256 257 // Handle a deep copy situation 258 if ( typeof target === "boolean" ) { 259 deep = target; 260 target = arguments[1] || {}; 261 // skip the boolean and the target 262 i = 2; 263 } 264 265 // Handle case when target is a string or something (possible in deep copy) 266 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 267 target = {}; 268 } 269 270 // extend jQuery itself if only one argument is passed 271 if ( length === i ) { 272 target = this; 273 --i; 274 } 275 276 for ( ; i < length; i++ ) { 277 // Only deal with non-null/undefined values 278 if ( (options = arguments[ i ]) != null ) { 279 // Extend the base object 280 for ( name in options ) { 281 src = target[ name ]; 282 copy = options[ name ]; 283 284 // Prevent never-ending loop 285 if ( target === copy ) { 286 continue; 287 } 288 289 // Recurse if we're merging plain objects or arrays 290 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 291 if ( copyIsArray ) { 292 copyIsArray = false; 293 clone = src && jQuery.isArray(src) ? src : []; 294 295 } else { 296 clone = src && jQuery.isPlainObject(src) ? src : {}; 297 } 298 299 // Never move original objects, clone them 300 if (jQuery.isArray(copy)) { 301 // don't extend arrays 302 target[ name ] = copy; 303 } else { 304 target[ name ] = jQuery.extendObjects( deep, clone, copy ); 305 } 306 307 // Don't bring in undefined values 308 } else if ( copy !== undefined ) { 309 target[ name ] = copy; 310 } 311 } 312 } 313 } 314 315 // Return the modified object 316 return target; 317 }; 318 319 jQuery.isBoolean = function(b) { 320 return b === true || b === false; 321 }, 322 323 jQuery.isNumeric = function(o) { 324 return ! isNaN (o-0); 325 } 326 }); 327