~hatch/juju-gui/1130790-create-subapp

« back to all changes in this revision

Viewing changes to app/assets/javascripts/gallery-ellipsis-debug.js

  • Committer: Gary Poster
  • Date: 2012-12-20 21:59:21 UTC
  • mto: This revision was merged to the branch mainline in revision 293.
  • Revision ID: gary.poster@canonical.com-20121220215921-qw5hqm7a8uymvwfr
Correct release docs; improve review docs; make all files served locally, so https can work; update makefile to have better names for build artifacts and more correctly designate phony targets; reduce unnecessary duplication of file creation in repeated Makefile runs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
YUI.add('gallery-ellipsis', function(Y) {
 
2
 
 
3
/**
 
4
* Ellipsis plugin (YUI) - For when text is too l ...
 
5
*
 
6
* @fileOverview  A slightly smarter way of truncating text
 
7
* @author        Dan Beam <dan@danbeam.org>
 
8
* @param         {object} conf - configuration objects to override the defaults
 
9
* @return        {Node} the Node passed to the method
 
10
*
 
11
* Copyright (c) 2010 Dan Beam
 
12
* Licensed under the MIT License: http://www.opensource.org/licenses/mit-license.php
 
13
*
 
14
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
15
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
16
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
17
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
18
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
20
* THE SOFTWARE.
 
21
*/
 
22
 
 
23
    var // the allowable difference when comparing floating point numbers
 
24
        fp_epsilon  = 0.01,
 
25
 
 
26
        // floating point comparison
 
27
        fp_equals   = function (a, b) { return Math.abs(a - b) <= fp_epsilon; },
 
28
        fp_greater  = function (a, b) { return a - b >= fp_epsilon; },
 
29
        fp_lesser   = function (a, b) { return a - b <= fp_epsilon; },
 
30
 
 
31
        // do a quick feature test to see if native text-overflow: ellipsis is supported
 
32
        nativeRule  = false,
 
33
 
 
34
        // remember some native styles if we have support
 
35
        nativeStyles = {
 
36
            'white-space'   : 'nowrap',
 
37
            'overflow'      : 'hidden'
 
38
        },
 
39
 
 
40
        // determine whether we want to use currentStyle instead of some buggy .getComputedStyle() results
 
41
        currentStyle = false;
 
42
 
 
43
    // add this on all Y.Node instances (but only if imported
 
44
    Y.DOM.ellipsis = function (node, conf) {
 
45
 
 
46
        // homogenize conf to object
 
47
        conf = conf || {};
 
48
 
 
49
        // augment our conf object with some default settings
 
50
        Y.mix(conf, {
 
51
            // end marker
 
52
            'ellipsis' : '\u2026',
 
53
            
 
54
            // for stuff we *really* don't want to wrap, increase this number just in case
 
55
            'fudge'    : 3,
 
56
 
 
57
            // target number of lines to wrap
 
58
            'lines'    : 1,
 
59
 
 
60
            // whether or not to remember the original text to able to de-truncate
 
61
            'remember' : true,
 
62
 
 
63
            // should we use native browser support when it exists? (on by default)
 
64
            'native'   : true
 
65
        });
 
66
 
 
67
        // console.log(conf);
 
68
        // console.log(Y.one(node).getComputedStyle('lineHeight'));
 
69
        // console.log(Y.one(node).getComputedStyle('fontSize'));
 
70
 
 
71
            // the element we're trying to truncate
 
72
        var yEl           = Y.one(node),
 
73
 
 
74
            // the name of the field we use to store using .setData()
 
75
            dataAttrName  = 'ellipsis-original-text',
 
76
 
 
77
            // original text
 
78
            originalText  = conf.remember && yEl.getData(dataAttrName) || yEl.get('text'),
 
79
            
 
80
            // keep the current length of the text so far
 
81
            currentLength = originalText.length,
 
82
            
 
83
            // the number of characters to increment or decrement the text by
 
84
            charIncrement = currentLength,
 
85
      
 
86
            // copy the element so we can string length invisibly
 
87
            clone         = Y.one(document.createElement(yEl.get('nodeName'))),
 
88
 
 
89
            // some current values used to cache .getComputedStyle() accesses and compare to our goals
 
90
            lineHeight, targetHeight, currentHeight, lastKnownGood;
 
91
 
 
92
 
 
93
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
94
        // @ NOTE: I'm intentionally ignoring padding as .getComputedStyle('height') @
 
95
        // @ NOTE: and .getComputedStyle('width') both ignore this as well.          @
 
96
        // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 
97
 
 
98
        // copy styles to clone object
 
99
        clone.setStyles({
 
100
            'overflow'      : 'hidden',   // only at first
 
101
            'position'      : 'absolute',
 
102
            'visibility'    : 'hidden',
 
103
            'display'       : 'block',
 
104
            'bottom'        : '-10px',
 
105
            'left'          : '-10px',
 
106
            'width'         : currentStyle ? node.offsetWidth           : yEl.getComputedStyle('width'),
 
107
            'fontSize'      : currentStyle ? node.currentStyle.fontSize : yEl.getComputedStyle('fontSize'), /* weird IE7 + reset bug */
 
108
            'fontFamily'    : yEl.getComputedStyle('fontFamily'),
 
109
            'fontWeight'    : yEl.getComputedStyle('fontWeight'),
 
110
            'letterSpacing' : yEl.getComputedStyle('letterSpacing'),
 
111
            'lineHeight'    : yEl.getComputedStyle('lineHeight')
 
112
        });
 
113
 
 
114
        // insert some text to get the line-height (because .getComputedStyle('lineHeight') can be "normal" sometimes!)
 
115
        clone.set('text', 'some sample text');
 
116
 
 
117
        // unfortunately, we must insert into the DOM, :(
 
118
        Y.one('body').append(clone);
 
119
 
 
120
        // get the height of the node with only 1 character of text (should be 1 line)
 
121
        lineHeight    = parseFloat(clone.getComputedStyle('height'));
 
122
 
 
123
        // if we have the native support for text-overflow and we only want 1 line with the same style ellipsis
 
124
        if (Y.DOM.ellipsis.nativeSupport && conf['native'] && 1 == conf.lines && '\u2026' === conf.ellipsis) {
 
125
            // console.log('using native!');
 
126
            // apply the styles
 
127
            yEl.setStyles(nativeStyles);
 
128
            // this is needed to trigger the overflow in some browser (*cough* Opera *cough*)
 
129
            yEl.setStyle('height', lineHeight + 'px');
 
130
            // exit early and clean-up
 
131
            clone.remove();
 
132
            return;
 
133
        }
 
134
 
 
135
        // set overflow back to visible
 
136
        clone.setStyle('overflow', 'visible');
 
137
 
 
138
        // compute how high the node should be if it's the right number of lines
 
139
        targetHeight  = conf.lines * lineHeight;
 
140
 
 
141
        // insert the original text, in case we've already truncated
 
142
        clone.set('text', originalText);
 
143
 
 
144
        // ok, now that we have a node in the DOM with the right text, measure it's height
 
145
        currentHeight = parseFloat(clone.getComputedStyle('height'));
 
146
 
 
147
        // console.log('lineHeight', lineHeight);
 
148
        // console.log('currentHeight', currentHeight);
 
149
        // console.log('targetHeight', targetHeight);
 
150
        // console.log('originalText.length', originalText.length);
 
151
        // console.log('yEl.get(\'text\').length', yEl.get('text').length);
 
152
 
 
153
        // quick sanity check
 
154
        if (fp_lesser(currentHeight, targetHeight) && originalText.length === yEl.get('text').length) {
 
155
            // console.log('truncation not necessary!');
 
156
            clone.remove();
 
157
            return;
 
158
        }
 
159
 
 
160
        // now, let's start looping through and slicing the text as necessary
 
161
        for (; charIncrement >= 1; ) {
 
162
 
 
163
            // increment decays by half every time 
 
164
            charIncrement = Math.floor(charIncrement / 2);
 
165
            
 
166
            // if the height is too big, remove some chars, else add some
 
167
            currentLength += fp_greater(currentHeight, targetHeight) ? -charIncrement : +charIncrement;
 
168
            
 
169
            // try text at current length
 
170
            clone.set('text', originalText.slice(0, currentLength - conf.ellipsis.length) + conf.ellipsis);
 
171
            
 
172
            // compute the current height
 
173
            currentHeight = parseFloat(clone.getComputedStyle('height'));
 
174
 
 
175
            // we only want to store values that aren't too big
 
176
            if (fp_equals(currentHeight, targetHeight) || fp_lesser(currentHeight, targetHeight)) {
 
177
                lastKnownGood = currentLength;
 
178
            }
 
179
 
 
180
            // console.log('currentLength', currentLength);
 
181
            // console.log('currentHeight', currentHeight);
 
182
            // console.log('targetHeight' , targetHeight );
 
183
            // console.log('charIncrement', charIncrement);
 
184
            // console.log('lastKnownGood', lastKnownGood);
 
185
 
 
186
        }
 
187
 
 
188
        // remove from DOM
 
189
        clone.remove();
 
190
        
 
191
        // set the original text if we want to ever want to expand past the current truncation
 
192
        if (conf.remember && !yEl.getData(dataAttrName)) {
 
193
            yEl.setData(dataAttrName, originalText);
 
194
        }
 
195
 
 
196
        // console.log('originalText.length', originalText.length);
 
197
        // console.log('clone.get(\'text\').length', clone.get('text').length);
 
198
        // console.log('conf.ellipsis.length', conf.ellipsis.length);
 
199
 
 
200
        // if the text matches
 
201
        if (originalText.length === (clone.get('text').length - conf.ellipsis.length)) {
 
202
            // this means we *de-truncated* and can fit fully in the new space
 
203
            // console.log('de-truncated!');
 
204
            yEl.set('text', originalText);
 
205
        }
 
206
        // this should never happen, but it doesn't hurt to check
 
207
        else if ('undefined' !== typeof lastKnownGood) {
 
208
            // do this thing, already!
 
209
            yEl.set('text', originalText.slice(0, lastKnownGood - conf.ellipsis.length - conf.fudge) + conf.ellipsis);
 
210
        }
 
211
 
 
212
        // return myself for chainability
 
213
        return yEl;
 
214
 
 
215
    };
 
216
 
 
217
    Y.Node.importMethod(Y.DOM, 'ellipsis');
 
218
    Y.NodeList.importMethod(Y.Node.prototype, 'ellipsis');
 
219
 
 
220
    // must wait to append hidden node
 
221
    Y.on('domready', function () {
 
222
 
 
223
        // create a hidden node and try to style it
 
224
        var cloned,
 
225
            hidden  = Y.Node.create('<div style="visibility:hidden;position:absolute;white-space:nowrap;overflow:hidden;"></div>'),
 
226
            rules   = ['textOverflow', 'OTextOverflow'];
 
227
 
 
228
        // pseudo feature detection to detect browsers with currentStyle but without a more standards-ish implementation (currently IE6-8)
 
229
        currentStyle = !!(document.body.currentStyle && (window.CSSCurrentStyleDeclaration || !window.CSSStyleDeclaration));
 
230
 
 
231
        Y.each(rules, function (rule) {
 
232
            hidden.setStyle(rule, 'ellipsis');
 
233
        });
 
234
 
 
235
        // add to DOM
 
236
        Y.one('body').appendChild(hidden);
 
237
 
 
238
        // deep clone the node (include attributes)
 
239
        cloned = hidden.cloneNode(true);
 
240
 
 
241
        Y.some(rules, function (rule) {
 
242
            if ('ellipsis' === cloned.getStyle(rule)) {
 
243
                nativeRule = rule;
 
244
                nativeStyles[nativeRule] = 'ellipsis';
 
245
                Y.DOM.ellipsis.nativeSupport = true;
 
246
                return true;
 
247
            }
 
248
        });
 
249
 
 
250
        // clean-up
 
251
        hidden.remove();
 
252
        hidden = cloned = null;
 
253
 
 
254
    });
 
255
 
 
256
 
 
257
}, 'gallery-2011.04.13-22-38' ,{requires:['base','node']});