utils.js
  1  /** @returns {void} */
  2  export function noop() {}
  3  
  4  export const identity = (x) => x;
  5  
  6  /**
  7   * @template T
  8   * @template S
  9   * @param {T} tar
 10   * @param {S} src
 11   * @returns {T & S}
 12   */
 13  export function assign(tar, src) {
 14  	// @ts-ignore
 15  	for (const k in src) tar[k] = src[k];
 16  	return /** @type {T & S} */ (tar);
 17  }
 18  
 19  // Adapted from https://github.com/then/is-promise/blob/master/index.js
 20  // Distributed under MIT License https://github.com/then/is-promise/blob/master/LICENSE
 21  /**
 22   * @param {any} value
 23   * @returns {value is PromiseLike<any>}
 24   */
 25  export function is_promise(value) {
 26  	return (
 27  		!!value &&
 28  		(typeof value === 'object' || typeof value === 'function') &&
 29  		typeof (/** @type {any} */ (value).then) === 'function'
 30  	);
 31  }
 32  
 33  /** @returns {void} */
 34  export function add_location(element, file, line, column, char) {
 35  	element.__svelte_meta = {
 36  		loc: { file, line, column, char }
 37  	};
 38  }
 39  
 40  export function run(fn) {
 41  	return fn();
 42  }
 43  
 44  export function blank_object() {
 45  	return Object.create(null);
 46  }
 47  
 48  /**
 49   * @param {Function[]} fns
 50   * @returns {void}
 51   */
 52  export function run_all(fns) {
 53  	fns.forEach(run);
 54  }
 55  
 56  /**
 57   * @param {any} thing
 58   * @returns {thing is Function}
 59   */
 60  export function is_function(thing) {
 61  	return typeof thing === 'function';
 62  }
 63  
 64  /** @returns {boolean} */
 65  export function safe_not_equal(a, b) {
 66  	return a != a ? b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function';
 67  }
 68  
 69  let src_url_equal_anchor;
 70  
 71  /**
 72   * @param {string} element_src
 73   * @param {string} url
 74   * @returns {boolean}
 75   */
 76  export function src_url_equal(element_src, url) {
 77  	if (element_src === url) return true;
 78  	if (!src_url_equal_anchor) {
 79  		src_url_equal_anchor = document.createElement('a');
 80  	}
 81  	// This is actually faster than doing URL(..).href
 82  	src_url_equal_anchor.href = url;
 83  	return element_src === src_url_equal_anchor.href;
 84  }
 85  
 86  /** @param {string} srcset */
 87  function split_srcset(srcset) {
 88  	return srcset.split(',').map((src) => src.trim().split(' ').filter(Boolean));
 89  }
 90  
 91  /**
 92   * @param {HTMLSourceElement | HTMLImageElement} element_srcset
 93   * @param {string | undefined | null} srcset
 94   * @returns {boolean}
 95   */
 96  export function srcset_url_equal(element_srcset, srcset) {
 97  	const element_urls = split_srcset(element_srcset.srcset);
 98  	const urls = split_srcset(srcset || '');
 99  
100  	return (
101  		urls.length === element_urls.length &&
102  		urls.every(
103  			([url, width], i) =>
104  				width === element_urls[i][1] &&
105  				// We need to test both ways because Vite will create an a full URL with
106  				// `new URL(asset, import.meta.url).href` for the client when `base: './'`, and the
107  				// relative URLs inside srcset are not automatically resolved to absolute URLs by
108  				// browsers (in contrast to img.src). This means both SSR and DOM code could
109  				// contain relative or absolute URLs.
110  				(src_url_equal(element_urls[i][0], url) || src_url_equal(url, element_urls[i][0]))
111  		)
112  	);
113  }
114  
115  /** @returns {boolean} */
116  export function not_equal(a, b) {
117  	return a != a ? b == b : a !== b;
118  }
119  
120  /** @returns {boolean} */
121  export function is_empty(obj) {
122  	return Object.keys(obj).length === 0;
123  }
124  
125  /** @returns {void} */
126  export function validate_store(store, name) {
127  	if (store != null && typeof store.subscribe !== 'function') {
128  		throw new Error(`'${name}' is not a store with a 'subscribe' method`);
129  	}
130  }
131  
132  export function subscribe(store, ...callbacks) {
133  	if (store == null) {
134  		for (const callback of callbacks) {
135  			callback(undefined);
136  		}
137  		return noop;
138  	}
139  	const unsub = store.subscribe(...callbacks);
140  	return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub;
141  }
142  
143  /**
144   * Get the current value from a store by subscribing and immediately unsubscribing.
145   *
146   * https://svelte.dev/docs/svelte-store#get
147   * @template T
148   * @param {import('../store/public.js').Readable<T>} store
149   * @returns {T}
150   */
151  export function get_store_value(store) {
152  	let value;
153  	subscribe(store, (_) => (value = _))();
154  	return value;
155  }
156  
157  /** @returns {void} */
158  export function component_subscribe(component, store, callback) {
159  	component.$$.on_destroy.push(subscribe(store, callback));
160  }
161  
162  export function create_slot(definition, ctx, $$scope, fn) {
163  	if (definition) {
164  		const slot_ctx = get_slot_context(definition, ctx, $$scope, fn);
165  		return definition[0](slot_ctx);
166  	}
167  }
168  
169  function get_slot_context(definition, ctx, $$scope, fn) {
170  	return definition[1] && fn ? assign($$scope.ctx.slice(), definition[1](fn(ctx))) : $$scope.ctx;
171  }
172  
173  export function get_slot_changes(definition, $$scope, dirty, fn) {
174  	if (definition[2] && fn) {
175  		const lets = definition[2](fn(dirty));
176  		if ($$scope.dirty === undefined) {
177  			return lets;
178  		}
179  		if (typeof lets === 'object') {
180  			const merged = [];
181  			const len = Math.max($$scope.dirty.length, lets.length);
182  			for (let i = 0; i < len; i += 1) {
183  				merged[i] = $$scope.dirty[i] | lets[i];
184  			}
185  			return merged;
186  		}
187  		return $$scope.dirty | lets;
188  	}
189  	return $$scope.dirty;
190  }
191  
192  /** @returns {void} */
193  export function update_slot_base(
194  	slot,
195  	slot_definition,
196  	ctx,
197  	$$scope,
198  	slot_changes,
199  	get_slot_context_fn
200  ) {
201  	if (slot_changes) {
202  		const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
203  		slot.p(slot_context, slot_changes);
204  	}
205  }
206  
207  /** @returns {void} */
208  export function update_slot(
209  	slot,
210  	slot_definition,
211  	ctx,
212  	$$scope,
213  	dirty,
214  	get_slot_changes_fn,
215  	get_slot_context_fn
216  ) {
217  	const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
218  	update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn);
219  }
220  
221  /** @returns {any[] | -1} */
222  export function get_all_dirty_from_scope($$scope) {
223  	if ($$scope.ctx.length > 32) {
224  		const dirty = [];
225  		const length = $$scope.ctx.length / 32;
226  		for (let i = 0; i < length; i++) {
227  			dirty[i] = -1;
228  		}
229  		return dirty;
230  	}
231  	return -1;
232  }
233  
234  /** @returns {{}} */
235  export function exclude_internal_props(props) {
236  	const result = {};
237  	for (const k in props) if (k[0] !== '$') result[k] = props[k];
238  	return result;
239  }
240  
241  /** @returns {{}} */
242  export function compute_rest_props(props, keys) {
243  	const rest = {};
244  	keys = new Set(keys);
245  	for (const k in props) if (!keys.has(k) && k[0] !== '$') rest[k] = props[k];
246  	return rest;
247  }
248  
249  /** @returns {{}} */
250  export function compute_slots(slots) {
251  	const result = {};
252  	for (const key in slots) {
253  		result[key] = true;
254  	}
255  	return result;
256  }
257  
258  /** @returns {(this: any, ...args: any[]) => void} */
259  export function once(fn) {
260  	let ran = false;
261  	return function (...args) {
262  		if (ran) return;
263  		ran = true;
264  		fn.call(this, ...args);
265  	};
266  }
267  
268  export function null_to_empty(value) {
269  	return value == null ? '' : value;
270  }
271  
272  export function set_store_value(store, ret, value) {
273  	store.set(value);
274  	return ret;
275  }
276  
277  export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
278  
279  export function action_destroyer(action_result) {
280  	return action_result && is_function(action_result.destroy) ? action_result.destroy : noop;
281  }
282  
283  /** @param {number | string} value
284   * @returns {[number, string]}
285   */
286  export function split_css_unit(value) {
287  	const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/);
288  	return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px'];
289  }
290  
291  export const contenteditable_truthy_values = ['', true, 1, 'true', 'contenteditable'];