1 | /*
|
2 | * @namespace Util
|
3 | *
|
4 | * Various utility functions, used by Leaflet internally.
|
5 | */
|
6 |
|
7 | export var freeze = Object.freeze;
|
8 | Object.freeze = function (obj) { return obj; };
|
9 |
|
10 | // @function extend(dest: Object, src?: Object): Object
|
11 | // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
|
12 | export function extend(dest) {
|
13 | var i, j, len, src;
|
14 |
|
15 | for (j = 1, len = arguments.length; j < len; j++) {
|
16 | src = arguments[j];
|
17 | for (i in src) {
|
18 | dest[i] = src[i];
|
19 | }
|
20 | }
|
21 | return dest;
|
22 | }
|
23 |
|
24 | // @function create(proto: Object, properties?: Object): Object
|
25 | // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
|
26 | export var create = Object.create || (function () {
|
27 | function F() {}
|
28 | return function (proto) {
|
29 | F.prototype = proto;
|
30 | return new F();
|
31 | };
|
32 | })();
|
33 |
|
34 | // @function bind(fn: Function, …): Function
|
35 | // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
|
36 | // Has a `L.bind()` shortcut.
|
37 | export function bind(fn, obj) {
|
38 | var slice = Array.prototype.slice;
|
39 |
|
40 | if (fn.bind) {
|
41 | return fn.bind.apply(fn, slice.call(arguments, 1));
|
42 | }
|
43 |
|
44 | var args = slice.call(arguments, 2);
|
45 |
|
46 | return function () {
|
47 | return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
|
48 | };
|
49 | }
|
50 |
|
51 | // @property lastId: Number
|
52 | // Last unique ID used by [`stamp()`](#util-stamp)
|
53 | export var lastId = 0;
|
54 |
|
55 | // @function stamp(obj: Object): Number
|
56 | // Returns the unique ID of an object, assigning it one if it doesn't have it.
|
57 | export function stamp(obj) {
|
58 | /*eslint-disable */
|
59 | obj._leaflet_id = obj._leaflet_id || ++lastId;
|
60 | return obj._leaflet_id;
|
61 | /* eslint-enable */
|
62 | }
|
63 |
|
64 | // @function throttle(fn: Function, time: Number, context: Object): Function
|
65 | // Returns a function which executes function `fn` with the given scope `context`
|
66 | // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
|
67 | // `fn` will be called no more than one time per given amount of `time`. The arguments
|
68 | // received by the bound function will be any arguments passed when binding the
|
69 | // function, followed by any arguments passed when invoking the bound function.
|
70 | // Has an `L.throttle` shortcut.
|
71 | export function throttle(fn, time, context) {
|
72 | var lock, args, wrapperFn, later;
|
73 |
|
74 | later = function () {
|
75 | // reset lock and call if queued
|
76 | lock = false;
|
77 | if (args) {
|
78 | wrapperFn.apply(context, args);
|
79 | args = false;
|
80 | }
|
81 | };
|
82 |
|
83 | wrapperFn = function () {
|
84 | if (lock) {
|
85 | // called too soon, queue to call later
|
86 | args = arguments;
|
87 |
|
88 | } else {
|
89 | // call and lock until later
|
90 | fn.apply(context, arguments);
|
91 | setTimeout(later, time);
|
92 | lock = true;
|
93 | }
|
94 | };
|
95 |
|
96 | return wrapperFn;
|
97 | }
|
98 |
|
99 | // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
|
100 | // Returns the number `num` modulo `range` in such a way so it lies within
|
101 | // `range[0]` and `range[1]`. The returned value will be always smaller than
|
102 | // `range[1]` unless `includeMax` is set to `true`.
|
103 | export function wrapNum(x, range, includeMax) {
|
104 | var max = range[1],
|
105 | min = range[0],
|
106 | d = max - min;
|
107 | return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
|
108 | }
|
109 |
|
110 | // @function falseFn(): Function
|
111 | // Returns a function which always returns `false`.
|
112 | export function falseFn() { return false; }
|
113 |
|
114 | // @function formatNum(num: Number, digits?: Number): Number
|
115 | // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
|
116 | export function formatNum(num, digits) {
|
117 | digits = (digits === undefined ? 6 : digits);
|
118 | return +(Math.round(num + ('e+' + digits)) + ('e-' + digits));
|
119 | }
|
120 |
|
121 | // @function trim(str: String): String
|
122 | // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
|
123 | export function trim(str) {
|
124 | return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
|
125 | }
|
126 |
|
127 | // @function splitWords(str: String): String[]
|
128 | // Trims and splits the string on whitespace and returns the array of parts.
|
129 | export function splitWords(str) {
|
130 | return trim(str).split(/\s+/);
|
131 | }
|
132 |
|
133 | // @function setOptions(obj: Object, options: Object): Object
|
134 | // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
|
135 | export function setOptions(obj, options) {
|
136 | if (!obj.hasOwnProperty('options')) {
|
137 | obj.options = obj.options ? create(obj.options) : {};
|
138 | }
|
139 | for (var i in options) {
|
140 | obj.options[i] = options[i];
|
141 | }
|
142 | return obj.options;
|
143 | }
|
144 |
|
145 | // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
|
146 | // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
|
147 | // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
|
148 | // be appended at the end. If `uppercase` is `true`, the parameter names will
|
149 | // be uppercased (e.g. `'?A=foo&B=bar'`)
|
150 | export function getParamString(obj, existingUrl, uppercase) {
|
151 | var params = [];
|
152 | for (var i in obj) {
|
153 | params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
|
154 | }
|
155 | return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
|
156 | }
|
157 |
|
158 | var templateRe = /\{ *([\w_-]+) *\}/g;
|
159 |
|
160 | // @function template(str: String, data: Object): String
|
161 | // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
|
162 | // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
|
163 | // `('Hello foo, bar')`. You can also specify functions instead of strings for
|
164 | // data values — they will be evaluated passing `data` as an argument.
|
165 | export function template(str, data) {
|
166 | return str.replace(templateRe, function (str, key) {
|
167 | var value = data[key];
|
168 |
|
169 | if (value === undefined) {
|
170 | throw new Error('No value provided for variable ' + str);
|
171 |
|
172 | } else if (typeof value === 'function') {
|
173 | value = value(data);
|
174 | }
|
175 | return value;
|
176 | });
|
177 | }
|
178 |
|
179 | // @function isArray(obj): Boolean
|
180 | // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
|
181 | export var isArray = Array.isArray || function (obj) {
|
182 | return (Object.prototype.toString.call(obj) === '[object Array]');
|
183 | };
|
184 |
|
185 | // @function indexOf(array: Array, el: Object): Number
|
186 | // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
|
187 | export function indexOf(array, el) {
|
188 | for (var i = 0; i < array.length; i++) {
|
189 | if (array[i] === el) { return i; }
|
190 | }
|
191 | return -1;
|
192 | }
|
193 |
|
194 | // @property emptyImageUrl: String
|
195 | // Data URI string containing a base64-encoded empty GIF image.
|
196 | // Used as a hack to free memory from unused images on WebKit-powered
|
197 | // mobile devices (by setting image `src` to this string).
|
198 | export var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
199 |
|
200 | // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
201 |
|
202 | function getPrefixed(name) {
|
203 | return window['webkit' + name] || window['moz' + name] || window['ms' + name];
|
204 | }
|
205 |
|
206 | var lastTime = 0;
|
207 |
|
208 | // fallback for IE 7-8
|
209 | function timeoutDefer(fn) {
|
210 | var time = +new Date(),
|
211 | timeToCall = Math.max(0, 16 - (time - lastTime));
|
212 |
|
213 | lastTime = time + timeToCall;
|
214 | return window.setTimeout(fn, timeToCall);
|
215 | }
|
216 |
|
217 | export var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
|
218 | export var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
|
219 | getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
|
220 |
|
221 | // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
|
222 | // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
|
223 | // `context` if given. When `immediate` is set, `fn` is called immediately if
|
224 | // the browser doesn't have native support for
|
225 | // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
|
226 | // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
|
227 | export function requestAnimFrame(fn, context, immediate) {
|
228 | if (immediate && requestFn === timeoutDefer) {
|
229 | fn.call(context);
|
230 | } else {
|
231 | return requestFn.call(window, bind(fn, context));
|
232 | }
|
233 | }
|
234 |
|
235 | // @function cancelAnimFrame(id: Number): undefined
|
236 | // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
|
237 | export function cancelAnimFrame(id) {
|
238 | if (id) {
|
239 | cancelFn.call(window, id);
|
240 | }
|
241 | }
|