[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 window.wp = window.wp || {}; 2 3 (function( exports, $ ){ 4 var api, extend, ctor, inherits, 5 slice = Array.prototype.slice; 6 7 /* ===================================================================== 8 * Micro-inheritance - thank you, backbone.js. 9 * ===================================================================== */ 10 11 extend = function( protoProps, classProps ) { 12 var child = inherits( this, protoProps, classProps ); 13 child.extend = this.extend; 14 return child; 15 }; 16 17 // Shared empty constructor function to aid in prototype-chain creation. 18 ctor = function() {}; 19 20 // Helper function to correctly set up the prototype chain, for subclasses. 21 // Similar to `goog.inherits`, but uses a hash of prototype properties and 22 // class properties to be extended. 23 inherits = function( parent, protoProps, staticProps ) { 24 var child; 25 26 // The constructor function for the new subclass is either defined by you 27 // (the "constructor" property in your `extend` definition), or defaulted 28 // by us to simply call `super()`. 29 if ( protoProps && protoProps.hasOwnProperty( 'constructor' ) ) { 30 child = protoProps.constructor; 31 } else { 32 child = function() { 33 // Storing the result `super()` before returning the value 34 // prevents a bug in Opera where, if the constructor returns 35 // a function, Opera will reject the return value in favor of 36 // the original object. This causes all sorts of trouble. 37 var result = parent.apply( this, arguments ); 38 return result; 39 }; 40 } 41 42 // Inherit class (static) properties from parent. 43 $.extend( child, parent ); 44 45 // Set the prototype chain to inherit from `parent`, without calling 46 // `parent`'s constructor function. 47 ctor.prototype = parent.prototype; 48 child.prototype = new ctor(); 49 50 // Add prototype properties (instance properties) to the subclass, 51 // if supplied. 52 if ( protoProps ) 53 $.extend( child.prototype, protoProps ); 54 55 // Add static properties to the constructor function, if supplied. 56 if ( staticProps ) 57 $.extend( child, staticProps ); 58 59 // Correctly set child's `prototype.constructor`. 60 child.prototype.constructor = child; 61 62 // Set a convenience property in case the parent's prototype is needed later. 63 child.__super__ = parent.prototype; 64 65 return child; 66 }; 67 68 api = {}; 69 70 /* ===================================================================== 71 * Base class. 72 * ===================================================================== */ 73 74 api.Class = function( applicator, argsArray, options ) { 75 var magic, args = arguments; 76 77 if ( applicator && argsArray && api.Class.applicator === applicator ) { 78 args = argsArray; 79 $.extend( this, options || {} ); 80 } 81 82 magic = this; 83 if ( this.instance ) { 84 magic = function() { 85 return magic.instance.apply( magic, arguments ); 86 }; 87 88 $.extend( magic, this ); 89 } 90 91 magic.initialize.apply( magic, args ); 92 return magic; 93 }; 94 95 api.Class.applicator = {}; 96 97 api.Class.prototype.initialize = function() {}; 98 99 /* 100 * Checks whether a given instance extended a constructor. 101 * 102 * The magic surrounding the instance parameter causes the instanceof 103 * keyword to return inaccurate results; it defaults to the function's 104 * prototype instead of the constructor chain. Hence this function. 105 */ 106 api.Class.prototype.extended = function( constructor ) { 107 var proto = this; 108 109 while ( typeof proto.constructor !== 'undefined' ) { 110 if ( proto.constructor === constructor ) 111 return true; 112 if ( typeof proto.constructor.__super__ === 'undefined' ) 113 return false; 114 proto = proto.constructor.__super__; 115 } 116 return false; 117 }; 118 119 api.Class.extend = extend; 120 121 /* ===================================================================== 122 * Events mixin. 123 * ===================================================================== */ 124 125 api.Events = { 126 trigger: function( id ) { 127 if ( this.topics && this.topics[ id ] ) 128 this.topics[ id ].fireWith( this, slice.call( arguments, 1 ) ); 129 return this; 130 }, 131 132 bind: function( id ) { 133 this.topics = this.topics || {}; 134 this.topics[ id ] = this.topics[ id ] || $.Callbacks(); 135 this.topics[ id ].add.apply( this.topics[ id ], slice.call( arguments, 1 ) ); 136 return this; 137 }, 138 139 unbind: function( id ) { 140 if ( this.topics && this.topics[ id ] ) 141 this.topics[ id ].remove.apply( this.topics[ id ], slice.call( arguments, 1 ) ); 142 return this; 143 } 144 }; 145 146 /* ===================================================================== 147 * Observable values that support two-way binding. 148 * ===================================================================== */ 149 150 api.Value = api.Class.extend({ 151 initialize: function( initial, options ) { 152 this._value = initial; // @todo: potentially change this to a this.set() call. 153 this.callbacks = $.Callbacks(); 154 155 $.extend( this, options || {} ); 156 157 this.set = $.proxy( this.set, this ); 158 }, 159 160 /* 161 * Magic. Returns a function that will become the instance. 162 * Set to null to prevent the instance from extending a function. 163 */ 164 instance: function() { 165 return arguments.length ? this.set.apply( this, arguments ) : this.get(); 166 }, 167 168 get: function() { 169 return this._value; 170 }, 171 172 set: function( to ) { 173 var from = this._value; 174 175 to = this._setter.apply( this, arguments ); 176 to = this.validate( to ); 177 178 // Bail if the sanitized value is null or unchanged. 179 if ( null === to || this._value === to ) 180 return this; 181 182 this._value = to; 183 184 this.callbacks.fireWith( this, [ to, from ] ); 185 186 return this; 187 }, 188 189 _setter: function( to ) { 190 return to; 191 }, 192 193 setter: function( callback ) { 194 var from = this.get(); 195 this._setter = callback; 196 // Temporarily clear value so setter can decide if it's valid. 197 this._value = null; 198 this.set( from ); 199 return this; 200 }, 201 202 resetSetter: function() { 203 this._setter = this.constructor.prototype._setter; 204 this.set( this.get() ); 205 return this; 206 }, 207 208 validate: function( value ) { 209 return value; 210 }, 211 212 bind: function() { 213 this.callbacks.add.apply( this.callbacks, arguments ); 214 return this; 215 }, 216 217 unbind: function() { 218 this.callbacks.remove.apply( this.callbacks, arguments ); 219 return this; 220 }, 221 222 link: function() { // values* 223 var set = this.set; 224 $.each( arguments, function() { 225 this.bind( set ); 226 }); 227 return this; 228 }, 229 230 unlink: function() { // values* 231 var set = this.set; 232 $.each( arguments, function() { 233 this.unbind( set ); 234 }); 235 return this; 236 }, 237 238 sync: function() { // values* 239 var that = this; 240 $.each( arguments, function() { 241 that.link( this ); 242 this.link( that ); 243 }); 244 return this; 245 }, 246 247 unsync: function() { // values* 248 var that = this; 249 $.each( arguments, function() { 250 that.unlink( this ); 251 this.unlink( that ); 252 }); 253 return this; 254 } 255 }); 256 257 /* ===================================================================== 258 * A collection of observable values. 259 * ===================================================================== */ 260 261 api.Values = api.Class.extend({ 262 defaultConstructor: api.Value, 263 264 initialize: function( options ) { 265 $.extend( this, options || {} ); 266 267 this._value = {}; 268 this._deferreds = {}; 269 }, 270 271 instance: function( id ) { 272 if ( arguments.length === 1 ) 273 return this.value( id ); 274 275 return this.when.apply( this, arguments ); 276 }, 277 278 value: function( id ) { 279 return this._value[ id ]; 280 }, 281 282 has: function( id ) { 283 return typeof this._value[ id ] !== 'undefined'; 284 }, 285 286 add: function( id, value ) { 287 if ( this.has( id ) ) 288 return this.value( id ); 289 290 this._value[ id ] = value; 291 value.parent = this; 292 if ( value.extended( api.Value ) ) 293 value.bind( this._change ); 294 295 this.trigger( 'add', value ); 296 297 if ( this._deferreds[ id ] ) 298 this._deferreds[ id ].resolve(); 299 300 return this._value[ id ]; 301 }, 302 303 create: function( id ) { 304 return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) ); 305 }, 306 307 each: function( callback, context ) { 308 context = typeof context === 'undefined' ? this : context; 309 310 $.each( this._value, function( key, obj ) { 311 callback.call( context, obj, key ); 312 }); 313 }, 314 315 remove: function( id ) { 316 var value; 317 318 if ( this.has( id ) ) { 319 value = this.value( id ); 320 this.trigger( 'remove', value ); 321 if ( value.extended( api.Value ) ) 322 value.unbind( this._change ); 323 delete value.parent; 324 } 325 326 delete this._value[ id ]; 327 delete this._deferreds[ id ]; 328 }, 329 330 /** 331 * Runs a callback once all requested values exist. 332 * 333 * when( ids*, [callback] ); 334 * 335 * For example: 336 * when( id1, id2, id3, function( value1, value2, value3 ) {} ); 337 * 338 * @returns $.Deferred.promise(); 339 */ 340 when: function() { 341 var self = this, 342 ids = slice.call( arguments ), 343 dfd = $.Deferred(); 344 345 // If the last argument is a callback, bind it to .done() 346 if ( $.isFunction( ids[ ids.length - 1 ] ) ) 347 dfd.done( ids.pop() ); 348 349 $.when.apply( $, $.map( ids, function( id ) { 350 if ( self.has( id ) ) 351 return; 352 353 return self._deferreds[ id ] = self._deferreds[ id ] || $.Deferred(); 354 })).done( function() { 355 var values = $.map( ids, function( id ) { 356 return self( id ); 357 }); 358 359 // If a value is missing, we've used at least one expired deferred. 360 // Call Values.when again to generate a new deferred. 361 if ( values.length !== ids.length ) { 362 // ids.push( callback ); 363 self.when.apply( self, ids ).done( function() { 364 dfd.resolveWith( self, values ); 365 }); 366 return; 367 } 368 369 dfd.resolveWith( self, values ); 370 }); 371 372 return dfd.promise(); 373 }, 374 375 _change: function() { 376 this.parent.trigger( 'change', this ); 377 } 378 }); 379 380 $.extend( api.Values.prototype, api.Events ); 381 382 /* ===================================================================== 383 * An observable value that syncs with an element. 384 * 385 * Handles inputs, selects, and textareas by default. 386 * ===================================================================== */ 387 388 api.ensure = function( element ) { 389 return typeof element == 'string' ? $( element ) : element; 390 }; 391 392 api.Element = api.Value.extend({ 393 initialize: function( element, options ) { 394 var self = this, 395 synchronizer = api.Element.synchronizer.html, 396 type, update, refresh; 397 398 this.element = api.ensure( element ); 399 this.events = ''; 400 401 if ( this.element.is('input, select, textarea') ) { 402 this.events += 'change'; 403 synchronizer = api.Element.synchronizer.val; 404 405 if ( this.element.is('input') ) { 406 type = this.element.prop('type'); 407 if ( api.Element.synchronizer[ type ] ) 408 synchronizer = api.Element.synchronizer[ type ]; 409 if ( 'text' === type || 'password' === type ) 410 this.events += ' keyup'; 411 } else if ( this.element.is('textarea') ) { 412 this.events += ' keyup'; 413 } 414 } 415 416 api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) ); 417 this._value = this.get(); 418 419 update = this.update; 420 refresh = this.refresh; 421 422 this.update = function( to ) { 423 if ( to !== refresh.call( self ) ) 424 update.apply( this, arguments ); 425 }; 426 this.refresh = function() { 427 self.set( refresh.call( self ) ); 428 }; 429 430 this.bind( this.update ); 431 this.element.bind( this.events, this.refresh ); 432 }, 433 434 find: function( selector ) { 435 return $( selector, this.element ); 436 }, 437 438 refresh: function() {}, 439 440 update: function() {} 441 }); 442 443 api.Element.synchronizer = {}; 444 445 $.each( [ 'html', 'val' ], function( i, method ) { 446 api.Element.synchronizer[ method ] = { 447 update: function( to ) { 448 this.element[ method ]( to ); 449 }, 450 refresh: function() { 451 return this.element[ method ](); 452 } 453 }; 454 }); 455 456 api.Element.synchronizer.checkbox = { 457 update: function( to ) { 458 this.element.prop( 'checked', to ); 459 }, 460 refresh: function() { 461 return this.element.prop( 'checked' ); 462 } 463 }; 464 465 api.Element.synchronizer.radio = { 466 update: function( to ) { 467 this.element.filter( function() { 468 return this.value === to; 469 }).prop( 'checked', true ); 470 }, 471 refresh: function() { 472 return this.element.filter( ':checked' ).val(); 473 } 474 }; 475 476 /* ===================================================================== 477 * Messenger for postMessage. 478 * ===================================================================== */ 479 480 $.support.postMessage = !! window.postMessage; 481 482 api.Messenger = api.Class.extend({ 483 add: function( key, initial, options ) { 484 return this[ key ] = new api.Value( initial, options ); 485 }, 486 487 /** 488 * Initialize Messenger. 489 * 490 * @param {object} params Parameters to configure the messenger. 491 * {string} .url The URL to communicate with. 492 * {window} .targetWindow The window instance to communicate with. Default window.parent. 493 * {string} .channel If provided, will send the channel with each message and only accept messages a matching channel. 494 * @param {object} options Extend any instance parameter or method with this object. 495 */ 496 initialize: function( params, options ) { 497 // Target the parent frame by default, but only if a parent frame exists. 498 var defaultTarget = window.parent == window ? null : window.parent; 499 500 $.extend( this, options || {} ); 501 502 this.add( 'channel', params.channel ); 503 this.add( 'url', params.url || '' ); 504 this.add( 'targetWindow', params.targetWindow || defaultTarget ); 505 this.add( 'origin', this.url() ).link( this.url ).setter( function( to ) { 506 return to.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ); 507 }); 508 509 // Since we want jQuery to treat the receive function as unique 510 // to this instance, we give the function a new guid. 511 // 512 // This will prevent every Messenger's receive function from being 513 // unbound when calling $.off( 'message', this.receive ); 514 this.receive = $.proxy( this.receive, this ); 515 this.receive.guid = $.guid++; 516 517 $( window ).on( 'message', this.receive ); 518 }, 519 520 destroy: function() { 521 $( window ).off( 'message', this.receive ); 522 }, 523 524 receive: function( event ) { 525 var message; 526 527 event = event.originalEvent; 528 529 if ( ! this.targetWindow() ) 530 return; 531 532 // Check to make sure the origin is valid. 533 if ( this.origin() && event.origin !== this.origin() ) 534 return; 535 536 // Ensure we have a string that's JSON.parse-able 537 if ( typeof event.data !== 'string' || event.data[0] !== '{' ) { 538 return; 539 } 540 541 message = JSON.parse( event.data ); 542 543 // Check required message properties. 544 if ( ! message || ! message.id || typeof message.data === 'undefined' ) 545 return; 546 547 // Check if channel names match. 548 if ( ( message.channel || this.channel() ) && this.channel() !== message.channel ) 549 return; 550 551 this.trigger( message.id, message.data ); 552 }, 553 554 send: function( id, data ) { 555 var message; 556 557 data = typeof data === 'undefined' ? null : data; 558 559 if ( ! this.url() || ! this.targetWindow() ) 560 return; 561 562 message = { id: id, data: data }; 563 if ( this.channel() ) 564 message.channel = this.channel(); 565 566 this.targetWindow().postMessage( JSON.stringify( message ), this.origin() ); 567 } 568 }); 569 570 // Add the Events mixin to api.Messenger. 571 $.extend( api.Messenger.prototype, api.Events ); 572 573 /* ===================================================================== 574 * Core customize object. 575 * ===================================================================== */ 576 577 api = $.extend( new api.Values(), api ); 578 api.get = function() { 579 var result = {}; 580 581 this.each( function( obj, key ) { 582 result[ key ] = obj.get(); 583 }); 584 585 return result; 586 }; 587 588 // Expose the API to the world. 589 exports.customize = api; 590 })( wp, jQuery );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |