source: extensions/url_uploader/template/jquery.textarea-lines-numbers.js @ 19805

Last change on this file since 19805 was 19805, checked in by mistic100, 11 years ago

convert textarea liner to jquery

File size: 9.6 KB
Line 
1/**
2 * jQuery textareaLinesNumbers 2.0
3 *
4 * Copyright 2012, Damien "Mistic" Sorel
5 *    http://www.strangeplanet.fr
6 *
7 * Dual licensed under the MIT or GPL Version 3 licenses.
8 *    http://www.opensource.org/licenses/mit-license.php
9 *    http://www.gnu.org/licenses/gpl.html
10 *
11 * Depends:
12 *        jquery.js
13 *    jquery-ui.js | resizable (optional)
14 */
15 
16
17(function($) {
18    /**
19     * Plugin declaration
20     */
21    $.fn.textareaLinesNumbers = function(options) {
22        // callable public methods
23        var callable = [];
24       
25        var plugin = $(this).data('textareaLinesNumbers');
26       
27        // already instantiated and trying to execute a method
28        if (plugin && typeof options === 'string') {
29            if ($.inArray(options,callable)!==false) {
30                return plugin[options].apply(plugin, Array.prototype.slice.call(arguments, 1));
31            }
32            else {
33                throw 'Method "' + options + '" does not exist on jQuery.textareaLinesNumbers';
34            }
35        }
36        // not instantiated and trying to pass options object (or nothing)
37        else if (!plugin && (typeof options === 'object' || !options)) {
38            if (!options) {
39                options = {};
40            }
41           
42            // extend defaults
43            options = $.extend({}, $.fn.textareaLinesNumbers.defaults, options);
44
45            // for each element instantiate the plugin
46            return this.each(function() {
47                var plugin = $(this).data('textareaLinesNumbers');
48
49                // create new instance of the plugin if the plugin isn't initialised
50                if (!plugin) {
51                    plugin = new $.textareaLinesNumbers($(this), options);
52                    plugin.init();
53                    $(this).data('textareaLinesNumbers', plugin);
54                }
55            });
56        }
57    }
58   
59    /**
60     * Defaults
61     */
62    $.fn.textareaLinesNumbers.defaults = {
63      lines: 100,
64      trailing: '',
65      resizable: false,
66      id: null
67    };
68
69    /**
70     * Main plugin function
71     */
72    $.textareaLinesNumbers = function(element, options) {
73        this.options = options;
74       
75        if (element instanceof jQuery) {
76            this.$textarea = element;
77        }
78        else {
79            this.$textarea = $(element);
80        }
81       
82        this.$main = null;
83        this.$linesContainer = null;
84       
85       
86        /*
87         * init the plugin
88         * scope: private
89         */
90        this.init = function() {
91            // build the HTML wrapper
92            if (this.$textarea.closest('.textareaLinesNumbers').length <= 0) {
93                this.$textarea.wrap('<div class="textareaLinesNumbers" />');
94            }
95            this.$main = this.$textarea.parent('.textareaLinesNumbers');
96
97            if (this.$main.find('.linesContainer').length <= 0) {
98                this.$main.prepend('<textarea class="linesContainer"></textareay>');
99            }
100            this.$linesContainer = this.$main.children('.linesContainer');
101
102            // set id
103            if (this.options.id != null) {
104                this.$main.attr('id', this.options.id);
105            }
106           
107            // add liner
108            this.setupLiner();
109
110            // bind the events
111            this.bindEvents();
112
113            // apply the resizeable
114            this.applyResizable();
115
116            // highlight content
117            this.setLine();
118        }
119       
120        /*
121         * add events handlers
122         * scope: private
123         */
124        this.bindEvents = function() {
125            var events = this.$textarea.data('textareaLinesNumbersEvents');
126           
127            if (typeof events != 'boolean' || events !== true) {
128                // add triggers to textarea
129                this.$textarea.on({
130                    'input.textareaLinesNumbers' :  $.proxy(function(){ this.setLine(); }, this),
131                    'scroll.textareaLinesNumbers' :  $.proxy(function(){ this.setLine(); }, this),
132                    'blur.textareaLinesNumbers' :  $.proxy(function(){ this.setLine(); }, this),
133                    'focus.textareaLinesNumbers' :  $.proxy(function(){ this.setLine(); }, this),
134                    'resize.textareaLinesNumbers' :  $.proxy(function(){ this.updateSize(); this.setLine(); }, this)
135                });
136
137                this.$textarea.data('textareaLinesNumbersEvents', true);
138            }
139        }
140
141        /*
142         * set style of containers
143         * scope: private
144         */
145        this.setupLiner = function() {
146            // liner content
147            var string = '1'+this.options.trailing;
148            for (var no=2; no<=this.options.lines; no++) {
149              string+= '\n'+no+this.options.trailing;
150            }
151            this.$linesContainer.html(string);
152           
153            // the main container has the same size and position than the original textarea
154            this.cloneCss(this.$textarea, this.$main, [
155                'float','vertical-align','margin-top','margin-bottom','margin-right','margin-left'
156            ]);
157           
158            // the liner has the same font than the original textarea
159            this.cloneCss(this.$textarea, this.$linesContainer, [
160                'font-size','line-height','font-family','vertical-align','padding-top'
161            ]);
162           
163            var width = (this.options.lines.toString().length+this.options.trailing.toString().length)*this.charWidth(this.$linesContainer.css('font-family'));
164           
165            this.$linesContainer.css({
166                'padding-top': 0
167                    + this.toPx(this.$textarea.css('padding-top')) 
168                    + this.toPx(this.$textarea.css('border-top-width')) 
169                    - this.toPx(this.$linesContainer.css('border-top-width')),
170                'padding-bottom': 0
171                    + this.toPx(this.$textarea.css('padding-bottom')) 
172                    + this.toPx(this.$textarea.css('border-bottom-width')) 
173                    - this.toPx(this.$linesContainer.css('border-bottom-width')),
174                'top'  : this.$textarea.position().top,
175                'left' : this.$textarea.position().left,
176                'width' : width
177            });
178           
179            this.updateSize();
180           
181            this.$textarea.css({
182                'margin': 0,
183                'margin-left': this.$linesContainer.outerWidth(),
184                'width': this.$textarea.width() - width
185            });
186           
187            this.$textarea.attr("wrap", "off");
188        }
189       
190        /*
191         * set textarea as resizable
192         * scope: private
193         */
194        this.applyResizable = function() {
195            if (this.options.resizable && jQuery.ui) {
196                this.$textarea.resizable({
197                    'handles': 'se',
198                    'resize':  $.proxy(function() { this.updateSize(); }, this)
199                });
200            }
201        }
202
203        /*
204         * scroll $linesConatainer according to $textarea scroll
205         * scope: private
206         */
207        this.setLine = function() {
208            this.$linesContainer.scrollTop(this.$textarea.scrollTop());
209        }
210       
211        /*
212         * update liner height
213         * scope: private
214         */
215        this.updateSize = function() {
216            this.$main.css({
217                'width':  this.$textarea.outerWidth(),
218                'height': this.$textarea.outerHeight()
219            });
220           
221            this.$linesContainer.css({
222                'height': this.$textarea.outerHeight() 
223                    - this.toPx(this.$textarea.css('padding-top'))
224                    - this.toPx(this.$textarea.css('padding-bottom'))
225                    - this.toPx(this.$textarea.css('border-top-width'))
226                    - this.toPx(this.$textarea.css('border-bottom-width')),
227            });
228        }
229
230        /*
231         * set 'to' css attributes listed in 'what' as defined for 'from'
232         * scope: private
233         */
234        this.cloneCss = function(from, to, what) {
235            for (var i=0; i<what.length; i++) {
236                to.css(what[i], from.css(what[i]));
237            }
238        }
239
240        /*
241         * clean/convert px and em size to px size (without 'px' suffix)
242         * scope: private
243         */
244        this.toPx = function(value) {
245            if (value != value.replace('em', '')) {
246                // https://github.com/filamentgroup/jQuery-Pixel-Em-Converter
247                var that = parseFloat(value.replace('em', '')),
248                    scopeTest = $('<div style="display:none;font-size:1em;margin:0;padding:0;height:auto;line-height:1;border:0;">&nbsp;</div>').appendTo('body'),
249                    scopeVal = scopeTest.height();
250                scopeTest.remove();
251                return Math.round(that * scopeVal);
252            }
253            else if (value != value.replace('px', '')) {
254                return parseInt(value.replace('px', ''));
255            }
256            else {
257                return parseInt(value);
258            }
259        }
260       
261        /*
262         * get chard width for given font (should be monospace)
263         * scope: private
264         */
265        this.charWidth = function(font_family) {
266            var scopeTest = $('<div style="display:none;font-size:1em;font-family:'+font_family+'margin:0;padding:0;border:0;">0123456789</div>').appendTo('body'),
267                scopeVal = scopeTest.width();
268            scopeTest.remove();
269            return Math.floor(scopeVal/10);
270        }
271    };
272})(jQuery);
Note: See TracBrowser for help on using the repository browser.