]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sf.js
d42dbd80c44e1524e7ab91ed1d9f15bc64e6968e
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
9 var SF
= ( function() {
12 SF
.attachListener = function( elem
, evtName
, callback
) {
13 if ( elem
.addEventListener
) {
14 elem
.addEventListener( evtName
, callback
, false );
15 } else if ( elem
.attachEvent
) {
16 elem
.attachEvent( 'on' + evtName
, function() {
17 callback
.apply( elem
, arguments
);
20 throw new Error( 'Could not attach event.' );
24 SF
.indexOf
= ( function() {
25 var indexOf
= Array
.prototype.indexOf
;
27 if ( indexOf
=== 'function' ) {
28 return function( arr
, elem
) {
29 return indexOf
.call( arr
, elem
);
32 return function( arr
, elem
) {
35 for ( var i
= 0; i
< max
; i
++ ) {
36 if ( arr
[ i
] === elem
) {
47 SF
.accept = function( node
, visitor
) {
50 // Handling node as a node and array
51 if ( node
.children
) {
52 children
= node
.children
;
55 } else if ( typeof node
.length
=== 'number' ) {
59 var i
= children
? ( children
.length
|| 0 ) : 0;
61 SF
.accept( children
[ i
], visitor
);
65 SF
.getByClass
= ( function( ) {
66 var getByClass
= document
.getElementsByClassName
;
67 if ( typeof getByClass
=== 'function' ) {
68 return function( root
, className
) {
69 if ( typeof root
=== 'string' ) {
74 return getByClass
.call( root
, className
);
78 return function( root
, className
) {
79 if ( typeof root
=== 'string' ) {
81 root
= document
.getElementsByTagName( 'html' )[ 0 ];
85 SF
.accept( root
, function( elem
) {
86 if ( SF
.classList
.contains( elem
, className
) ) {
97 SF
.classList
.add = function( elem
, className
) {
98 var classes
= parseClasses( elem
);
99 classes
.push( className
);
101 elem
.attributes
.setNamedItem( createClassAttr( classes
) );
104 SF
.classList
.remove = function( elem
, className
) {
105 var classes
= parseClasses( elem
, className
),
106 foundAt
= SF
.indexOf( classes
, className
);
108 if ( foundAt
=== -1 ) {
112 classes
.splice( foundAt
, 1 );
113 elem
.attributes
.setNamedItem( createClassAttr( classes
) );
116 SF
.classList
.contains = function( elem
, className
) {
117 return findIndex( elem
, className
) !== -1;
120 SF
.classList
.toggle = function( elem
, className
) {
121 this.contains( elem
, className
) ? this.remove( elem
, className
) : this.add( elem
, className
);
124 function findIndex( elem
, className
) {
125 return SF
.indexOf( parseClasses( elem
), className
);
128 function parseClasses( elem
) {
129 var classAttr
= elem
.attributes
? elem
.attributes
.getNamedItem( 'class' ) : null;
131 return classAttr
? classAttr
.value
.split( ' ' ) : [];
134 function createClassAttr( classesArray
) {
135 var attr
= document
.createAttribute( 'class' );
137 attr
.value
= classesArray
.join( ' ' );
145 /* global SF, picoModal */
150 // Purges all styles in passed object.
151 function purgeStyles( styles
) {
152 for ( var i
in styles
) {
157 SF
.modal = function( config
) {
158 // Modal should use the same style set as the rest of the page (.content component).
159 config
.modalClass
= 'modal content';
160 config
.closeClass
= 'modal-close';
162 // Purge all pre-defined pico styles. Use the lessfile instead.
163 config
.modalStyles
= purgeStyles
;
165 // Close button styles are customized via lessfile.
166 config
.closeStyles
= purgeStyles
;
168 var userDefinedAfterCreate
= config
.afterCreate
,
169 userDefinedAfterClose
= config
.afterClose
;
171 // Close modal on ESC key.
172 function onKeyDown( event
) {
173 if ( event
.keyCode
== 27 ) {
178 // Use afterCreate as a config option rather than function chain.
179 config
.afterCreate = function( modal
) {
180 userDefinedAfterCreate
&& userDefinedAfterCreate( modal
);
182 window
.addEventListener( 'keydown', onKeyDown
);
185 // Use afterClose as a config option rather than function chain.
186 config
.afterClose = function( modal
) {
187 userDefinedAfterClose
&& userDefinedAfterClose( modal
);
189 window
.removeEventListener( 'keydown', onKeyDown
);
192 var modal
= new picoModal( config
)
193 .afterCreate( config
.afterCreate
)
194 .afterClose( config
.afterClose
);
202 // All .tree-a elements in DOM.
203 var expanders
= SF
.getByClass( 'toggler' );
205 var i
= expanders
.length
;
207 var expander
= expanders
[ i
];
209 SF
.attachListener( expander
, 'click', function() {
210 var containsIcon
= SF
.classList
.contains( this, 'icon-toggler-expanded' ) || SF
.classList
.contains( this, 'icon-toggler-collapsed' ),
211 related
= document
.getElementById( this.getAttribute( 'data-for' ) );
213 SF
.classList
.toggle( this, 'collapsed' );
215 if ( SF
.classList
.contains( this, 'collapsed' ) ) {
216 SF
.classList
.add( related
, 'collapsed' );
217 if ( containsIcon
) {
218 SF
.classList
.remove( this, 'icon-toggler-expanded' );
219 SF
.classList
.add( this, 'icon-toggler-collapsed' );
222 SF
.classList
.remove( related
, 'collapsed' );
223 if ( containsIcon
) {
224 SF
.classList
.remove( this, 'icon-toggler-collapsed' );
225 SF
.classList
.add( this, 'icon-toggler-expanded' );
236 // All .tree-a elements in DOM.
237 var trees
= SF
.getByClass( 'tree-a' );
239 for ( var i
= trees
.length
; i
--; ) {
240 var tree
= trees
[ i
];
242 SF
.attachListener( tree
, 'click', function( evt
) {
243 var target
= evt
.target
|| evt
.srcElement
;
245 // Collapse or expand item groups.
246 if ( target
.nodeName
=== 'H2' && !SF
.classList
.contains( target
, 'tree-a-no-sub' ) ) {
247 SF
.classList
.toggle( target
, 'tree-a-active' );
252 // jshint ignore:start
255 * Permission is hereby granted, free of charge, to any person obtaining a copy
256 * of this software and associated documentation files (the "Software"), to deal
257 * in the Software without restriction, including without limitation the rights
258 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
259 * copies of the Software, and to permit persons to whom the Software is
260 * furnished to do so, subject to the following conditions:
262 * The above copyright notice and this permission notice shall be included in
263 * all copies or substantial portions of the Software.
265 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
266 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
267 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
268 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
269 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
270 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
275 * A self-contained modal library
277 (function(window
, document
) {
280 /** Returns whether a value is a dom node */
281 function isNode(value
) {
282 if ( typeof Node
=== "object" ) {
283 return value
instanceof Node
;
287 typeof value
=== "object" &&
288 typeof value
.nodeType
=== "number";
292 /** Returns whether a value is a string */
293 function isString(value
) {
294 return typeof value
=== "string";
298 * Generates observable objects that can be watched and triggered
300 function observable() {
303 watch: callbacks
.push
.bind(callbacks
),
304 trigger: function( modal
) {
306 var unprevented
= true;
308 preventDefault: function preventDefault () {
313 for (var i
= 0; i
< callbacks
.length
; i
++) {
314 callbacks
[i
](modal
, event
);
324 * A small interface for creating and managing a dom element
326 function Elem( elem
) {
333 Elem
.div = function ( parent
) {
334 var elem
= document
.createElement('div');
335 (parent
|| document
.body
).appendChild(elem
);
336 return new Elem(elem
);
341 /** Creates a child of this node */
343 return Elem
.div(this.elem
);
346 /** Applies a set of styles to an element */
347 stylize: function(styles
) {
348 styles
= styles
|| {};
350 if ( typeof styles
.opacity
!== "undefined" ) {
352 "alpha(opacity=" + (styles
.opacity
* 100) + ")";
355 for (var prop
in styles
) {
356 if (styles
.hasOwnProperty(prop
)) {
357 this.elem
.style
[prop
] = styles
[prop
];
364 /** Adds a class name */
365 clazz: function (clazz
) {
366 this.elem
.className
+= " " + clazz
;
371 html: function (content
) {
372 if ( isNode(content
) ) {
373 this.elem
.appendChild( content
);
376 this.elem
.innerHTML
= content
;
381 /** Adds a click handler to this element */
382 onClick: function(callback
) {
383 this.elem
.addEventListener('click', callback
);
387 /** Removes this element from the DOM */
388 destroy: function() {
389 document
.body
.removeChild(this.elem
);
392 /** Hides this element */
394 this.elem
.style
.display
= "none";
397 /** Shows this element */
399 this.elem
.style
.display
= "block";
402 /** Sets an attribute on this element */
403 attr: function ( name
, value
) {
404 this.elem
.setAttribute(name
, value
);
408 /** Executes a callback on all the ancestors of an element */
409 anyAncestor: function ( predicate
) {
410 var elem
= this.elem
;
412 if ( predicate( new Elem(elem
) ) ) {
416 elem
= elem
.parentNode
;
424 /** Generates the grey-out effect */
425 function buildOverlay( getOption
, close
) {
427 .clazz("pico-overlay")
428 .clazz( getOption("overlayClass", "") )
438 .stylize(getOption('overlayStyles', {
442 .onClick(function () {
443 if ( getOption('overlayClose', true) ) {
449 /** Builds the content of a modal */
450 function buildModal( getOption
, close
) {
451 var width
= getOption('width', 'auto');
452 if ( typeof width
=== "number" ) {
453 width
= "" + width
+ "px";
456 var elem
= Elem
.div()
457 .clazz("pico-content")
458 .clazz( getOption("modalClass", "") )
466 '-ms-transform': 'translateX(-50%)',
467 '-moz-transform': 'translateX(-50%)',
468 '-webkit-transform': 'translateX(-50%)',
469 '-o-transform': 'translateX(-50%)',
470 'transform': 'translateX(-50%)'
472 .stylize(getOption('modalStyles', {
473 backgroundColor: "white",
477 .html( getOption('content') )
478 .attr("role", "dialog")
479 .onClick(function (event
) {
480 var isCloseClick
= new Elem(event
.target
)
481 .anyAncestor(function (elem
) {
482 return /\bpico-close\b/.test(elem
.elem
.className
);
484 if ( isCloseClick
) {
492 /** Builds the close button */
493 function buildClose ( elem
, getOption
) {
494 if ( getOption('closeButton', true) ) {
496 .html( getOption('closeHtml', "×") )
498 .clazz( getOption("closeClass") )
499 .stylize( getOption('closeStyles', {
504 position: "absolute",
515 /** Builds a method that calls a method and returns an element */
516 function buildElemAccessor( builder
) {
518 return builder().elem
;
526 function picoModal(options
) {
528 if ( isString(options
) || isNode(options
) ) {
529 options
= { content: options
};
532 var afterCreateEvent
= observable();
533 var beforeShowEvent
= observable();
534 var afterShowEvent
= observable();
535 var beforeCloseEvent
= observable();
536 var afterCloseEvent
= observable();
539 * Returns a named option if it has been explicitly defined. Otherwise,
540 * it returns the given default value
542 function getOption ( opt
, defaultValue
) {
543 var value
= options
[opt
];
544 if ( typeof value
=== "function" ) {
545 value
= value( defaultValue
);
547 return value
=== undefined ? defaultValue : value
;
550 /** Hides this modal */
551 function forceClose () {
554 afterCloseEvent
.trigger(iface
);
557 /** Gracefully hides this modal */
559 if ( beforeCloseEvent
.trigger(iface
) ) {
564 /** Wraps a method so it returns the modal interface */
565 function returnIface ( callback
) {
567 callback
.apply(this, arguments
);
573 // The constructed dom nodes
576 /** Builds a method that calls a method and returns an element */
577 function build ( name
) {
579 var modal
= buildModal(getOption
, close
);
582 overlay: buildOverlay(getOption
, close
),
583 close: buildClose(modal
, getOption
)
585 afterCreateEvent
.trigger(iface
);
590 var modalElem
= build
.bind(window
, 'modal');
591 var shadowElem
= build
.bind(window
, 'overlay');
592 var closeElem
= build
.bind(window
, 'close');
597 /** Returns the wrapping modal element */
598 modalElem: buildElemAccessor(modalElem
),
600 /** Returns the close button element */
601 closeElem: buildElemAccessor(closeElem
),
603 /** Returns the overlay element */
604 overlayElem: buildElemAccessor(shadowElem
),
606 /** Shows this modal */
608 if ( beforeShowEvent
.trigger(iface
) ) {
612 afterShowEvent
.trigger(iface
);
617 /** Hides this modal */
618 close: returnIface(close
),
621 * Force closes this modal. This will not call beforeClose
622 * events and will just immediately hide the modal
624 forceClose: returnIface(forceClose
),
626 /** Destroys this modal */
627 destroy: function () {
628 modalElem
= modalElem().destroy();
629 shadowElem
= shadowElem().destroy();
630 closeElem
= undefined;
634 * Updates the options for this modal. This will only let you
635 * change options that are re-evaluted regularly, such as
638 options: function ( opts
) {
642 /** Executes after the DOM nodes are created */
643 afterCreate: returnIface(afterCreateEvent
.watch
),
645 /** Executes a callback before this modal is closed */
646 beforeShow: returnIface(beforeShowEvent
.watch
),
648 /** Executes a callback after this modal is shown */
649 afterShow: returnIface(afterShowEvent
.watch
),
651 /** Executes a callback before this modal is closed */
652 beforeClose: returnIface(beforeCloseEvent
.watch
),
654 /** Executes a callback after this modal is closed */
655 afterClose: returnIface(afterCloseEvent
.watch
)
661 if ( typeof window
.define
=== "function" && window
.define
.amd
) {
662 window
.define(function () {
667 window
.picoModal
= picoModal
;
670 }(window
, document
));