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.warn('The jquery object did not contain any valid elements.'); // die silent 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 if ( !Aloha.isEditable( this ) ) { 129 new Aloha.Editable( jQuery( this ) ); 130 } 131 }); 132 }); 133 134 // Chain 135 return $this; 136 }; 137 138 /** 139 * jQuery destroy elements as editable 140 * 141 * destroy all mached elements editable capabilities 142 * @return jQuery object for the matched elements 143 * @api 144 */ 145 jQuery.fn.mahalo = function() { 146 return this.each(function() { 147 if (Aloha.isEditable(this)) { 148 Aloha.getEditableById(jQuery(this).attr('id')).destroy(); 149 } 150 }); 151 }; 152 153 /** 154 * jQuery Extension 155 * new Event which is triggered whenever a selection (length >= 0) is made in 156 * an Aloha Editable element 157 */ 158 jQuery.fn.contentEditableSelectionChange = function(callback) { 159 var that = this; 160 161 // update selection when keys are pressed 162 this.keyup(function(event){ 163 var rangeObject = Selection.getRangeObject(); 164 callback(event); 165 }); 166 167 // 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 168 this.dblclick(function(event) { 169 callback(event); 170 }); 171 172 // update selection when text is selected 173 this.mousedown(function(event){ 174 // remember that a selection was started 175 that.selectionStarted = true; 176 }); 177 jQuery(document).mouseup(function(event) { 178 Selection.eventOriginalTarget = that; 179 if (that.selectionStarted) { 180 callback(event); 181 } 182 Selection.eventOriginalTarget = false; 183 that.selectionStarted = false; 184 }); 185 186 return this; 187 }; 188 189 /** 190 * Fetch the outerHTML of an Element 191 * @version 1.0.0 192 * @date February 01, 2011 193 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle} 194 * @author Benjamin Arthur Lupton {@link http://balupton.com} 195 * @copyright 2011 Benjamin Arthur Lupton {@link http://balupton.com} 196 * @license MIT License {@link http://creativecommons.org/licenses/MIT/} 197 * @return {String} outerHtml 198 */ 199 jQuery.fn.outerHtml = jQuery.fn.outerHtml || function(){ 200 var 201 $el = jQuery(this), 202 el = $el.get(0); 203 if (typeof el.outerHTML != 'undefined') { 204 return el.outerHTML; 205 } else { 206 try { 207 // Gecko-based browsers, Safari, Opera. 208 return (new XMLSerializer()).serializeToString(el); 209 } catch (e) { 210 try { 211 // Internet Explorer. 212 return el.xml; 213 } catch (e) {} 214 } 215 } 216 217 }; 218 219 220 jQuery.fn.zap = function () { 221 return this.each(function(){ jQuery(this.childNodes).insertBefore(this); }).remove(); 222 }; 223 224 jQuery.fn.textNodes = function(excludeBreaks, includeEmptyTextNodes) { 225 var 226 ret = [], 227 doSomething = function(el){ 228 if ( 229 (el.nodeType === 3 && jQuery.trim(el.data) && !includeEmptyTextNodes) || 230 (el.nodeType === 3 && includeEmptyTextNodes) || 231 (el.nodeName =="BR" && !excludeBreaks)) { 232 ret.push(el); 233 } else { 234 for (var i=0, childLength = el.childNodes.length; i < childLength; ++i) { 235 doSomething(el.childNodes[i]); 236 } 237 } 238 }; 239 240 doSomething(this[0]); 241 242 return jQuery(ret); 243 }; 244 245 /** 246 * extendObjects is like jQuery.extend, but it does not extend arrays 247 */ 248 jQuery.extendObjects = jQuery.fn.extendObjects = function() { 249 var options, name, src, copy, copyIsArray, clone, 250 target = arguments[0] || {}, 251 i = 1, 252 length = arguments.length, 253 deep = false; 254 255 // Handle a deep copy situation 256 if ( typeof target === "boolean" ) { 257 deep = target; 258 target = arguments[1] || {}; 259 // skip the boolean and the target 260 i = 2; 261 } 262 263 // Handle case when target is a string or something (possible in deep copy) 264 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { 265 target = {}; 266 } 267 268 // extend jQuery itself if only one argument is passed 269 if ( length === i ) { 270 target = this; 271 --i; 272 } 273 274 for ( ; i < length; i++ ) { 275 // Only deal with non-null/undefined values 276 if ( (options = arguments[ i ]) != null ) { 277 // Extend the base object 278 for ( name in options ) { 279 src = target[ name ]; 280 copy = options[ name ]; 281 282 // Prevent never-ending loop 283 if ( target === copy ) { 284 continue; 285 } 286 287 // Recurse if we're merging plain objects or arrays 288 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { 289 if ( copyIsArray ) { 290 copyIsArray = false; 291 clone = src && jQuery.isArray(src) ? src : []; 292 293 } else { 294 clone = src && jQuery.isPlainObject(src) ? src : {}; 295 } 296 297 // Never move original objects, clone them 298 if (jQuery.isArray(copy)) { 299 // don't extend arrays 300 target[ name ] = copy; 301 } else { 302 target[ name ] = jQuery.extendObjects( deep, clone, copy ); 303 } 304 305 // Don't bring in undefined values 306 } else if ( copy !== undefined ) { 307 target[ name ] = copy; 308 } 309 } 310 } 311 } 312 313 // Return the modified object 314 return target; 315 }; 316 317 jQuery.isBoolean = function(b) { 318 return b === true || b === false; 319 }, 320 321 jQuery.isNumeric = function(o) { 322 return ! isNaN (o-0); 323 }, 324 325 /** 326 * check if a mixed var is empty or not 327 * borrowed from http://phpjs.org/functions/empty:392 and a bit improved 328 * 329 * @param mixed_var 330 * @return {boolean} 331 */ 332 jQuery.isEmpty = function(mixed_var) { 333 // http://kevin.vanzonneveld.net 334 // + original by: Philippe Baumann 335 // + input by: Onno Marsman 336 // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 337 // + input by: LH 338 // + improved by: Onno Marsman 339 // + improved by: Francesco 340 // + improved by: Marc Jansen 341 // + improved by: Rene Kapusta 342 // + input by: Stoyan Kyosev (http://www.svest.org/) 343 // * example 1: empty(null); 344 // * returns 1: true 345 // * example 2: empty(undefined); 346 // * returns 2: true 347 // * example 3: empty([]); 348 // * returns 3: true 349 // * example 4: empty({}); 350 // * returns 4: true 351 // * example 5: empty({'aFunc' : function () { alert('humpty'); } }); 352 // * returns 5: false 353 var key; 354 355 if ( Array.isArray(mixed_var) || typeof mixed_var == 'string' ) { 356 return mixed_var.length === 0; 357 } 358 359 if (mixed_var === "" || mixed_var === 0 || mixed_var === "0" || mixed_var === null || mixed_var === false || typeof mixed_var === 'undefined') { 360 return true; 361 } 362 363 if (typeof mixed_var == 'object') { 364 for (key in mixed_var) { 365 return false; 366 } 367 return true; 368 } 369 370 return false; 371 } 372 }); 373