source: Dev/trunk/d3/lib/env-js/envjs/css.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 18.3 KB
Line 
1
2/**
3 * DOM Style Level 2
4 
5Leaked globally
6
7var CSS2Properties,
8    CSSRule,
9    CSSStyleRule,
10    CSSImportRule,
11    CSSMediaRule,
12    CSSFontFaceRule,
13    CSSPageRule,
14    CSSRuleList,
15    CSSStyleSheet,
16    StyleSheet,
17    StyleSheetList;
18
19*/
20       
21var Envjs = Envjs || require('./platform/core').Envjs,
22        Document = require('./dom').Document,
23        HTMLElement = require('./html').HTMLElement;
24/*
25 * Envjs css.1.3.pre03
26 * Pure JavaScript Browser Environment
27 * By John Resig <http://ejohn.org/> and the Envjs Team
28 * Copyright 2008-2010 John Resig, under the MIT License
29 */
30
31//CLOSURE_START
32(function(){
33
34
35
36
37
38/**
39 * @author john resig
40 */
41// Helper method for extending one object with another.
42function __extend__(a,b) {
43    for ( var i in b ) {
44        if(b.hasOwnProperty(i)){
45            var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
46            if ( g || s ) {
47                if ( g ) { a.__defineGetter__(i, g); }
48                if ( s ) { a.__defineSetter__(i, s); }
49            } else {
50                a[i] = b[i];
51            }
52        }
53    }
54    return a;
55}
56
57/**
58 * @author john resig
59 */
60//from jQuery
61function __setArray__( target, array ) {
62    // Resetting the length to 0, then using the native Array push
63    // is a super-fast way to populate an object with array-like properties
64    target.length = 0;
65    Array.prototype.push.apply( target, array );
66}
67
68/**
69 * @author ariel flesler
70 *    http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html
71 * @param {Object} str
72 */
73function __trim__( str ){
74    return (str || "").replace( /^\s+|\s+$/g, "" );
75}
76
77
78(function(){
79   
80var log = Envjs.logger();
81
82Envjs.once('tick', function(){
83   log = Envjs.logger('Envjs.DOM.Document').debug('available');
84});
85
86/**
87 * Interface DocumentStyle (introduced in DOM Level 2)
88 * http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113/stylesheets.html#StyleSheets-StyleSheet-DocumentStyle
89 *
90 * interface DocumentStyle {
91 *   readonly attribute StyleSheetList   styleSheets;
92 * };
93 *
94 */
95
96__extend__(Document.prototype, {
97    get styleSheets() {
98        if (! this._styleSheets) {
99            this._styleSheets = new StyleSheetList();
100        }
101        return this._styleSheets;
102    }
103});
104
105}(/*Envjs.DOM.Document*/));
106/*
107 * CSS2Properties - DOM Level 2 CSS
108 * Renamed to CSSStyleDeclaration??
109 */
110
111var __toCamelCase__ = function(name) {
112    if (name) {
113        return name.replace(/\-(\w)/g, function(all, letter) {
114            return letter.toUpperCase();
115        });
116    }
117    return name;
118};
119
120var __toDashed__ = function(camelCaseName) {
121    if (camelCaseName) {
122        return camelCaseName.replace(/[A-Z]/g, function(all) {
123            return '-' + all.toLowerCase();
124        });
125    }
126    return camelCaseName;
127};
128
129var __cssTextToStyles__,
130    __supportedStyles__;
131   
132
133(function(){
134   
135var log = Envjs.logger();
136
137Envjs.once('tick', function(){
138   log = Envjs.logger('Envjs.CSS.CSS2Properties').debug('available');
139});
140
141exports.CSS2Properties = CSS2Properties = function(element){
142    //console.log('css2properties %s', __cssproperties__++);
143    this.styleIndex = __supportedStyles__;//non-standard
144    this.type = element.tagName;//non-standard
145    __setArray__(this, []);
146    __cssTextToStyles__(this, element.cssText || '');
147};
148__extend__(CSS2Properties.prototype, {
149    get cssText() {
150        var i, css = [];
151        for (i = 0; i < this.length; ++i) {
152            css.push(this[i] + ': ' + this.getPropertyValue(this[i]) + ';');
153        }
154        return css.join(' ');
155    },
156    set cssText(cssText) {
157        __cssTextToStyles__(this, cssText);
158    },
159    getPropertyCSSValue: function(name) {
160        //?
161    },
162    getPropertyPriority: function() {
163
164    },
165    getPropertyValue: function(name) {
166        var index, cname = __toCamelCase__(name);
167        if (cname in this.styleIndex) {
168            return this[cname];
169        } else {
170            index = Array.prototype.indexOf.apply(this, [name]);
171            if (index > -1) {
172                return this[index];
173            }
174        }
175        return null;
176    },
177    item: function(index) {
178        return this[index];
179    },
180    removeProperty: function(name) {
181        this.styleIndex[name] = null;
182        name = __toDashed__(name);
183        var index = Array.prototype.indexOf.apply(this, [name]);
184        if (index > -1) {
185            Array.prototype.splice.apply(this, [1,index]);
186        }
187    },
188    setProperty: function(name, value, priority) {
189        var nval;
190        name = __toCamelCase__(name);
191        if (value !== undefined && name in this.styleIndex) {
192            // NOTE:  parseFloat('300px') ==> 300  no
193            // NOTE:  Number('300px') ==> Nan      yes
194            nval = Number(value);
195            this.styleIndex[name] = isNaN(nval) ? value : nval;
196            name = __toDashed__(name);
197            if (Array.prototype.indexOf.apply(this, [name]) === -1 ){
198                Array.prototype.push.apply(this,[name]);
199            }
200        }
201    },
202    toString: function() {
203        return '[object CSS2Properties]';
204    }
205});
206
207
208
209__cssTextToStyles__ = function(css2props, cssText) {
210    //console.log('__cssTextToStyles__ %s %s', css2props, cssText);
211    var i, style, styles = cssText.split(';');
212    for (i = 0; i < styles.length; ++i) {
213        style = styles[i].split(':');
214        if (style.length === 2) {
215            css2props.setProperty(
216                style[0].replace(' ', '', 'g'),
217                style[1].replace(' ', '', 'g')
218            );
219        }
220    }
221};
222
223//Obviously these arent all supported but by commenting out various
224//sections this provides a single location to configure what is
225//exposed as supported.
226__supportedStyles__ = {
227    azimuth:                null,
228    background:             null,
229    backgroundAttachment:   null,
230    backgroundColor:        'rgb(0,0,0)',
231    backgroundImage:        null,
232    backgroundPosition:     null,
233    backgroundRepeat:       null,
234    border:                 null,
235    borderBottom:           null,
236    borderBottomColor:      null,
237    borderBottomStyle:      null,
238    borderBottomWidth:      null,
239    borderCollapse:         null,
240    borderColor:            null,
241    borderLeft:             null,
242    borderLeftColor:        null,
243    borderLeftStyle:        null,
244    borderLeftWidth:        null,
245    borderRight:            null,
246    borderRightColor:       null,
247    borderRightStyle:       null,
248    borderRightWidth:       null,
249    borderSpacing:          null,
250    borderStyle:            null,
251    borderTop:              null,
252    borderTopColor:         null,
253    borderTopStyle:         null,
254    borderTopWidth:         null,
255    borderWidth:            null,
256    bottom:                 null,
257    captionSide:            null,
258    clear:                  null,
259    clip:                   null,
260    color:                  null,
261    content:                null,
262    counterIncrement:       null,
263    counterReset:           null,
264    cssFloat:               null,
265    cue:                    null,
266    cueAfter:               null,
267    cueBefore:              null,
268    cursor:                 null,
269    direction:              'ltr',
270    display:                null,
271    elevation:              null,
272    emptyCells:             null,
273    font:                   null,
274    fontFamily:             null,
275    fontSize:               '1em',
276    fontSizeAdjust:         null,
277    fontStretch:            null,
278    fontStyle:              null,
279    fontVariant:            null,
280    fontWeight:             null,
281    height:                 '',
282    left:                   null,
283    letterSpacing:          null,
284    lineHeight:             null,
285    listStyle:              null,
286    listStyleImage:         null,
287    listStylePosition:      null,
288    listStyleType:          null,
289    margin:                 null,
290    marginBottom:           '0px',
291    marginLeft:             '0px',
292    marginRight:            '0px',
293    marginTop:              '0px',
294    markerOffset:           null,
295    marks:                  null,
296    maxHeight:              null,
297    maxWidth:               null,
298    minHeight:              null,
299    minWidth:               null,
300    opacity:                1,
301    orphans:                null,
302    outline:                null,
303    outlineColor:           null,
304    outlineOffset:          null,
305    outlineStyle:           null,
306    outlineWidth:           null,
307    overflow:               null,
308    overflowX:              null,
309    overflowY:              null,
310    padding:                null,
311    paddingBottom:          '0px',
312    paddingLeft:            '0px',
313    paddingRight:           '0px',
314    paddingTop:             '0px',
315    page:                   null,
316    pageBreakAfter:         null,
317    pageBreakBefore:        null,
318    pageBreakInside:        null,
319    pause:                  null,
320    pauseAfter:             null,
321    pauseBefore:            null,
322    pitch:                  null,
323    pitchRange:             null,
324    position:               null,
325    quotes:                 null,
326    richness:               null,
327    right:                  null,
328    size:                   null,
329    speak:                  null,
330    speakHeader:            null,
331    speakNumeral:           null,
332    speakPunctuation:       null,
333    speechRate:             null,
334    stress:                 null,
335    tableLayout:            null,
336    textAlign:              null,
337    textDecoration:         null,
338    textIndent:             null,
339    textShadow:             null,
340    textTransform:          null,
341    top:                    null,
342    unicodeBidi:            null,
343    verticalAlign:          null,
344    visibility:             '',
345    voiceFamily:            null,
346    volume:                 null,
347    whiteSpace:             null,
348    widows:                 null,
349    width:                  '1px',
350    wordSpacing:            null,
351    zIndex:                 1
352};
353
354var __displayMap__ = {
355    DIV      : 'block',
356    P        : 'block',
357    A        : 'inline',
358    CODE     : 'inline',
359    PRE      : 'block',
360    SPAN     : 'inline',
361    TABLE    : 'table',
362    THEAD    : 'table-header-group',
363    TBODY    : 'table-row-group',
364    TR       : 'table-row',
365    TH       : 'table-cell',
366    TD       : 'table-cell',
367    UL       : 'block',
368    LI       : 'list-item'
369};
370
371var __addStyleAccessor__ = function(name){
372    if (name === 'width' || name === 'height') {
373        CSS2Properties.prototype.__defineGetter__(name, function() {
374            if (this.display === 'none'){
375                return '0px';
376            }
377            return this.styleIndex[name];
378        });
379    } else if (name === 'display') {
380        //display will be set to a tagName specific value if ''
381        CSS2Properties.prototype.__defineGetter__(name, function() {
382            var val = this.styleIndex[name];
383            val = val ? val :__displayMap__[this.type];
384            return val;
385        });
386    } else {
387        CSS2Properties.prototype.__defineGetter__(name, function() {
388            return this.styleIndex[name];
389        });
390    }
391    CSS2Properties.prototype.__defineSetter__(name, function(value) {
392        this.setProperty(name, value);
393    });
394};
395
396for (var style in __supportedStyles__) {
397    if (__supportedStyles__.hasOwnProperty(style)) {
398        __addStyleAccessor__(style);
399    }
400}
401
402}(/*Envjs.CSS.CSS2Properties*/));
403
404
405(function(){
406   
407var log = Envjs.logger();
408
409Envjs.once('tick', function(){
410   log = Envjs.logger('Envjs.CSS.CSSRule').debug('available');
411});
412
413/*
414 * CSSRule - DOM Level 2
415 */
416exports.CSSRule = CSSRule = function(options) {
417
418    var $style,
419        $selectorText = options.selectorText ? options.selectorText : '';
420        $style = new CSS2Properties({
421            cssText: options.cssText ? options.cssText : null
422        });
423
424    return __extend__(this, {
425        get style(){
426            return $style;
427        },
428        get selectorText(){
429            return $selectorText;
430        },
431        set selectorText(selectorText){
432            $selectorText = selectorText;
433        },
434        toString : function(){
435            return "[object CSSRule]";
436        }
437    });
438};
439
440CSSRule.STYLE_RULE     =  1;
441CSSRule.IMPORT_RULE    =  3;
442CSSRule.MEDIA_RULE     =  4;
443CSSRule.FONT_FACE_RULE =  5;
444CSSRule.PAGE_RULE      =  6;
445//CSSRule.NAMESPACE_RULE = 10;
446
447
448CSSStyleRule = function() {
449
450};
451
452CSSImportRule = function() {
453
454};
455
456CSSMediaRule = function() {
457
458};
459
460CSSFontFaceRule = function() {
461
462};
463
464CSSPageRule = function() {
465
466};
467
468
469CSSRuleList = function(data) {
470    this.length = 0;
471    __setArray__(this, data);
472};
473
474__extend__(CSSRuleList.prototype, {
475    item : function(index) {
476        if ((index >= 0) && (index < this.length)) {
477            // bounds check
478            return this[index];
479        }
480        return null;
481    },
482    toString: function() {
483        return '[object CSSRuleList]';
484    }
485});
486
487}(/*Envjs.CSS.CSSRuleList*/));
488
489
490(function(){
491   
492var log = Envjs.logger();
493
494Envjs.once('tick', function(){
495   log = Envjs.logger('Envjs.CSS.CSSStyleSheet').debug('available');
496});
497
498/**
499 * StyleSheet
500 * http://dev.w3.org/csswg/cssom/#stylesheet
501 *
502 * interface StyleSheet {
503 *   readonly attribute DOMString type;
504 *   readonly attribute DOMString href;
505 *   readonly attribute Node ownerNode;
506 *   readonly attribute StyleSheet parentStyleSheet;
507 *   readonly attribute DOMString title;
508 *   [PutForwards=mediaText] readonly attribute MediaList media;
509 *          attribute boolean disabled;
510 * };
511 */
512StyleSheet = function() {};
513
514/*
515 * CSSStyleSheet
516 * http://dev.w3.org/csswg/cssom/#cssstylesheet
517 *
518 * interface CSSStyleSheet : StyleSheet {
519 *   readonly attribute CSSRule ownerRule;
520 *   readonly attribute CSSRuleList cssRules;
521 *   unsigned long insertRule(DOMString rule, unsigned long index);
522 *   void deleteRule(unsigned long index);
523 * };
524 */
525exports.CSSStyleSheets = CSSStyleSheet = function(options){
526    var $cssRules,
527        $disabled = options.disabled ? options.disabled : false,
528        $href = options.href ? options.href : null,
529        $parentStyleSheet = options.parentStyleSheet ? options.parentStyleSheet : null,
530        $title = options.title ? options.title : "",
531        $type = "text/css";
532
533    function parseStyleSheet(text){
534        //$debug("parsing css");
535        //this is pretty ugly, but text is the entire text of a stylesheet
536        var cssRules = [];
537        if (!text) {
538            text = '';
539        }
540        text = __trim__(text.replace(/\/\*(\r|\n|.)*\*\//g,""));
541        // TODO: @import
542        var blocks = text.split("}");
543        blocks.pop();
544        var i, j, len = blocks.length;
545        var definition_block, properties, selectors;
546        for (i=0; i<len; i++) {
547            definition_block = blocks[i].split("{");
548            if (definition_block.length === 2) {
549                selectors = definition_block[0].split(",");
550                for (j=0; j<selectors.length; j++) {
551                    cssRules.push(new CSSRule({
552                        selectorText : __trim__(selectors[j]),
553                        cssText      : definition_block[1]
554                    }));
555                }
556            }
557        }
558        return cssRules;
559    }
560
561    $cssRules = new CSSRuleList(parseStyleSheet(options.textContent));
562
563    return __extend__(this, {
564        get cssRules(){
565            return $cssRules;
566        },
567        get rule(){
568            return $cssRules;
569        },//IE - may be deprecated
570        get href(){
571            return $href;
572        },
573        get parentStyleSheet(){
574            return $parentStyleSheet;
575        },
576        get title(){
577            return $title;
578        },
579        get type(){
580            return $type;
581        },
582        addRule: function(selector, style, index){/*TODO*/},
583        deleteRule: function(index){/*TODO*/},
584        insertRule: function(rule, index){/*TODO*/},
585        //IE - may be deprecated
586        removeRule: function(index){
587            this.deleteRule(index);
588        }
589    });
590};
591
592exports.StyleSheetList = StyleSheetList = function() {};
593
594StyleSheetList.prototype = [];
595
596__extend__(StyleSheetList.prototype, {
597    item : function(index) {
598        if ((index >= 0) && (index < this.length)) {
599            // bounds check
600            return this[index];
601        }
602        return null;
603    },
604    toString: function() {
605        return '[object StyleSheetList]';
606    }
607});
608
609}(/*Envjs.CSS.CSSStyleSheet*/));
610/**
611 * This extends HTMLElement to handle CSS-specific interfaces.
612 *
613 * More work / research would be needed to extend just (DOM) Element
614 * for xml use and additional changes for just HTMLElement.
615 */
616
617(function(){
618   
619var log = Envjs.logger();
620
621Envjs.once('tick', function(){
622   log = Envjs.logger('Envjs.HTML.HTMLElement').debug('available');
623});
624
625
626/**
627 * Replace or add  the getter for 'style'
628 *
629 * This could be wrapped in a closure
630 */
631var $css2properties = [{}];
632
633__extend__(HTMLElement.prototype, {
634    get style(){
635        if ( !this.css2uuid ) {
636            this.css2uuid = $css2properties.length;
637            $css2properties[this.css2uuid] = new CSS2Properties(this);
638        }
639        return $css2properties[this.css2uuid];
640    }
641});
642
643/**
644 * Change for how 'setAttribute("style", ...)' works
645 *
646 * We are truly adding functionality to HtmlElement.setAttribute, not
647 * replacing it.  So we need to save the old one first, call it, then
648 * do our stuff.  If we need to do more hacks like this, HTMLElement
649 * (or regular Element) needs to have a hooks array or dispatch table
650 * for global changes.
651 *
652 * This could be wrapped in a closure if desired.
653 */
654var updateCss2Props = function(elem, values) {
655    //console.log('__updateCss2Props__ %s %s', elem, values);
656    if ( !elem.css2uuid ) {
657        elem.css2uuid = $css2properties.length;
658        $css2properties[elem.css2uuid] = new CSS2Properties(elem);
659    }
660    __cssTextToStyles__($css2properties[elem.css2uuid], values);
661};
662
663var origSetAttribute =  HTMLElement.prototype.setAttribute;
664
665HTMLElement.prototype.setAttribute = function(name, value) {
666    //console.log("CSS set attribute: " + name + ", " + value);
667    origSetAttribute.apply(this, arguments);
668    if (name === "style") {
669        updateCss2Props(this, value);
670    }
671};
672
673var origGetAttribute =  HTMLElement.prototype.getAttribute;
674
675HTMLElement.prototype.getAttribute = function(name) {
676    //console.log("CSS set attribute: " + name + ", " + value);
677        var style;
678    if (name === "style") {
679        style = this.style.cssText;
680                return style===""?null:style;
681    }else{
682            return origGetAttribute.apply(this, arguments);
683        }
684};
685
686}(/*Envjs.HTML.HTMLElement*/));
687
688/**
689 * @author john resig & the envjs team
690 * @uri http://www.envjs.com/
691 * @copyright 2008-2010
692 * @license MIT
693 */
694//CLOSURE_END
695}());
Note: See TracBrowser for help on using the repository browser.