<commit_before>if (typeof i18n == 'undefined') i18n = {};
i18n.txt = function(key){ return key; };
i18n.translate_html = function(map, a) {
	var list = document.querySelectorAll('['+a+']');
	for (var i = 0, node; node = list[i]; ++i) {
		var key = node.getAttribute(a) || node.textContent.trim();
		if (key in map) {
			var data = node.getAttribute(a + '-var');
			var attr = node.getAttribute(a + '-attr');
			var str = map[key](data && JSON.parse('{' + data.replace(/[^,:]+/g, '"$&"') + '}'));
			if (attr) node.setAttribute(attr, str);
			else node.innerHTML = str;
		}
	}
}

function _log(msg, lvl) {
	if (window.console) switch (lvl) {
		case 'error': console.error(msg); break;
		case 'warn':  console.warn(msg); break;
		default:      console.log(msg);
	}
}

function _el(id) { return id && document.getElementById(id); }

function _new_elem(tag, cl, text, hide) {
	var e = document.createElement(tag);
	if (cl) e.className = cl;
	if (text) e.textContent = text;
	if (hide) e.style.display = 'none';
	return e;
}

KonOpas.link_to_short_url = function(url) {
	var u = encodeURIComponent(url.replace(/^http:\/\//, ''));
	return 'http://is.gd/create.php?url=' + u;
}
KonOpas.link_to_qr_code = function(url) {
	var u = encodeURIComponent(url.replace(/^http:\/\//, ''));
	return 'http://chart.apis.google.com/chart?cht=qr&chs=350x350&chl=' + u;
}

KonOpas.hash_encode = function(s) { return encodeURIComponent(s).replace(/%20/g, '+'); }
KonOpas.hash_decode = function(s) { return decodeURIComponent(s.replace(/\+/g, '%20')); }

KonOpas.glob_to_re = function(pat) {
	var re_re = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\/-]', 'g');
	pat = pat.replace(re_re, '\\$&').replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
	var terms = pat.match(/"[^"]*"|'[^']*'|\S+/g).map(function(el){
		var t = '\\b' + el.replace(/^(['"])(.*)\1$/, '$2') + '\\b';
		return t; //.replace('\\b.*', '').replace('.*\\b', '');
	});
	return new RegExp(terms.join('|'), 'i');
}

// ------------------------------------------------------------------------------------------------ storage

KonOpas.Store = function(id) {
	try {
		sessionStorage.setItem('konopas.test_var', '1');
		sessionStorage.removeItem('konopas.test_var', '1');
		this.get = function(k) {
			var v = sessionStorage.getItem('konopas.' + id + '.' + k);
			return v ? JSON.parse(v) : v;
		}
		this.set = function(k, v) {
			sessionStorage.setItem('konopas.' + id + '.' + k, JSON.stringify(v));
		}
		this.limit = '';
	} catch (e) {
		var data = {};
		this.get = function(k) { return data[k]; };
		this.set = function(k, v) { data[k] = v; };
		this.limit = (e.name == 'SecurityError') ? 'FFcookies'
				   : ((e.code === DOMException.QUOTA_EXCEEDED_ERR) && (sessionStorage.length === 0)) ? 'IOSprivate'
				   : '?';
	}
}

KonOpas.VarStore = function() {
	var data = {};
	this.getItem = function(k) { return data[k]; };
	this.setItem = function(k, v) { data[k] = v; };
}

// ------------------------------------------------------------------------------------------------ string generation

KonOpas.clean_name = function(p, span_parts) {
	var fn = '', ln = '';
	switch (p.name.length) {
		case 1:
			ln = p.name[0];
			break;
		case 2:
			if (p.name[1]) {
				fn = p.name[0];
				ln = p.name[1];
			} else {
				ln = p.name[0];
			}
			break;
		case 3:
			fn = p.name[2] + ' ' + p.name[0];
			ln = p.name[1];
			break;
		case 4:
			fn = p.name[2] + ' ' + p.name[0];
			ln = p.name[1] + (p.name[3] ? ', ' + p.name[3] : '');
			break;
	}
	return span_parts
		? '' + fn.trim() + ' ' + ln.trim() + ''
		: (fn + ' ' + ln).trim();
}

KonOpas.clean_links = function(p) {
	var ok = false, o = {};
	if (p && ('links' in p)) {
		if (p.links.img || p.links.photo) {
			var img = (p.links.img || p.links.photo).trim();
			if (/^www/.test(img)) img = 'http://' + img;
			if (/:\/\//.test(img)) { o['img'] = { 'tgt': img }; ok = true; }
		}
		if (p.links.url) {
			var url = p.links.url.trim();
			if (!/:\/\//.test(url)) url = 'http://' + url;
			o['URL'] = { 'tgt': url, 'txt': url.replace(/^https?:\/\//, '') };
			ok = true;
		}
		if (p.links.fb) {
			var fb = p.links.fb.trim().replace(/^(https?:\/\/)?(www\.)?facebook.com(\/#!)?\//, '');
			o['Facebook'] = { 'txt': fb };
			if (/[^a-zA-Z0-9.]/.test(fb) && !/^pages\//.test(fb)) fb = 'search.php?q=' + encodeURI(fb).replace(/%20/g, '+');
			o['Facebook']['tgt'] = 'https://www.facebook.com/' + fb;
			ok = true;
		}
		if (p.links.twitter) {
			var tw = p.links.twitter.trim().replace(/[@＠﹫]/g, '').replace(/^(https?:\/\/)?(www\.)?twitter.com(\/#!)?\//, '');
			o['Twitter'] = { 'txt': '@' + tw };
			if (/[^a-zA-Z0-9_]/.test(tw)) tw = 'search/users?q=' + encodeURI(tw).replace(/%20/g, '+');
			o['Twitter']['tgt'] = 'https://www.twitter.com/' + tw;
			ok = true;
		}
	}
	return ok ? o : false;
}

// ------------------------------------------------------------------------------------------------ array comparison

KonOpas.arrays_equal = function(a, b) {
	if (!a || !b) return false;
	if (a.length != b.length) return false;
	for (var i = 0; i  b.length) return KonOpas.array_overlap(b, a);
	var n = 0, i, j;
	for (i = 0; i < a.length; ++i) {
		for (j = 0; j < b.length; ++j) if (a[i] == b[j]) { ++n; break; }
	}
	return n;
}

// ------------------------------------------------------------------------------------------------ DOM manipulation

KonOpas.popup_open = function(ev) {
	ev = ev || window.event;
	if (ev.which != 1) return;
	var src_el = ev.target, pop_el = src_el.nextElementSibling;
	if (!pop_el || !pop_el.classList.contains('popup')) {
		if (src_el.href && /\.(gif|jpe?g|png)$/i.test(src_el.href)) {
			pop_el = _new_elem('img', 'popup');
			pop_el.src = src_el.href;
			src_el.parentNode.insertBefore(pop_el, src_el.nextSibling);
		} else return;
	}
	var wrap_el = _new_elem('div', 'popup-wrap');
	wrap_el.onclick = function() {
		pop_el.parentNode.removeChild(pop_el);
		wrap_el.parentNode.removeChild(wrap_el);
		src_el.parentNode.insertBefore(pop_el, src_el.nextSibling);
	};
	var pop_title = pop_el.getAttribute('data-title') || '';
	if (pop_title) wrap_el.appendChild(_new_elem('div', 'popup-title', pop_title));
	pop_el.parentNode.removeChild(pop_el);
	wrap_el.appendChild(pop_el);
	document.body.appendChild(wrap_el);
	if (src_el.href) ev.preventDefault();
}

KonOpas.toggle_collapse = function(ev) {
	ev = ev || window.event;
	var title = ev.target, body = title && title.nextElementSibling;
	if (!body) return;
	if (window.getComputedStyle(body).getPropertyValue('display') == 'none') {
		title.classList.remove('collapse');
		title.classList.add('collapse-open');
	} else {
		title.classList.add('collapse');
		title.classList.remove('collapse-open');
	}
}

// ------------------------------------------------------------------------------------------------ time & date

KonOpas.pretty_time = function(t, opt) {
	function pre0(n) { return (n < 10 ? '0' : '') + n; }
	function _pretty_time(h, m) {
		if (opt.time_show_am_pm) {
			var h12 = h % 12; if (h12 == 0) h12 = 12;
			var m_str = ((m == 0) && opt.abbrev_00_minutes ) ? '' : ':' + pre0(m);
			return h12 + m_str + (h < 12 ? 'am' : 'pm');
		} else {
			return pre0(h) + ':' + pre0(m);
		}
	}
	opt = opt || {};
	if (t instanceof Date) {
		return _pretty_time(t.getHours(), t.getMinutes());
	} else if (typeof t == 'string' || t instanceof String) {
		if (opt.time_show_am_pm) {
			var a = t.split(':'); // hh:mm
			return _pretty_time(parseInt(a[0], 10), parseInt(a[1], 10));
		} else return t;
	} else return '';
}

KonOpas.pretty_time_diff = function(t) {
	var d = (Date.now() - t) / 1e3,
	    a = Math.abs(d),
	    s = [1, 60, 60, 24, 7, 4.333, 12, 1e9];
	if (a < 20) return i18n.txt('just now');
	for (var i = 0, l = s.length; i < l; ++i) {
		if ((a /= s[i]) < 2) return i18n.txt('time_diff', { 'T':~~(a *= s[i]), 'T_UNIT':i-1, 'T_PAST':d>0 });
	}
}

KonOpas.parse_date = function(day_str) {
	if (!day_str) return false;
	var a = day_str.match(/(\d+)/g); if (!a || (a.length < 3)) return false;
	var y = parseInt(a[0], 10), m = parseInt(a[1], 10), d = parseInt(a[2], 10);
	if (!y || !m || !d) return false;
	return new Date(y, m - 1, d);
}

KonOpas.pretty_date = function(d, opt) {
	opt = opt || {};
	var o = { weekday: "long", month: "long", day: "numeric" },
	    t = (d instanceof Date) ? d : KonOpas.parse_date(d);
	if (!t) return d;
	if (Math.abs(t - Date.now()) > 1000*3600*24*60) o.year = "numeric";
	var s = t.toLocaleDateString(opt.lc, o);
	return s.charAt(0).toUpperCase() + s.slice(1);
}

KonOpas.time_sum = function(t0_str, m_str) {
	var t = 60 * t0_str.substr(0,2) + 1 * t0_str.substr(3,2) + 1 * m_str,
	    h = (t / 60) >> 0,
	    m = t - 60 * h;
	return '' + (h % 24) + ':' + (m<10?'0':'') + m;
}
<commit_msg>bugfix: parse_date adds 12h offset as workaround for buggy DST implementations<commit_after>if (typeof i18n == 'undefined') i18n = {};
i18n.txt = function(key){ return key; };
i18n.translate_html = function(map, a) {
	var list = document.querySelectorAll('['+a+']');
	for (var i = 0, node; node = list[i]; ++i) {
		var key = node.getAttribute(a) || node.textContent.trim();
		if (key in map) {
			var data = node.getAttribute(a + '-var');
			var attr = node.getAttribute(a + '-attr');
			var str = map[key](data && JSON.parse('{' + data.replace(/[^,:]+/g, '"$&"') + '}'));
			if (attr) node.setAttribute(attr, str);
			else node.innerHTML = str;
		}
	}
}

function _log(msg, lvl) {
	if (window.console) switch (lvl) {
		case 'error': console.error(msg); break;
		case 'warn':  console.warn(msg); break;
		default:      console.log(msg);
	}
}

function _el(id) { return id && document.getElementById(id); }

function _new_elem(tag, cl, text, hide) {
	var e = document.createElement(tag);
	if (cl) e.className = cl;
	if (text) e.textContent = text;
	if (hide) e.style.display = 'none';
	return e;
}

KonOpas.link_to_short_url = function(url) {
	var u = encodeURIComponent(url.replace(/^http:\/\//, ''));
	return 'http://is.gd/create.php?url=' + u;
}
KonOpas.link_to_qr_code = function(url) {
	var u = encodeURIComponent(url.replace(/^http:\/\//, ''));
	return 'http://chart.apis.google.com/chart?cht=qr&chs=350x350&chl=' + u;
}

KonOpas.hash_encode = function(s) { return encodeURIComponent(s).replace(/%20/g, '+'); }
KonOpas.hash_decode = function(s) { return decodeURIComponent(s.replace(/\+/g, '%20')); }

KonOpas.glob_to_re = function(pat) {
	var re_re = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\/-]', 'g');
	pat = pat.replace(re_re, '\\$&').replace(/\\\*/g, '.*').replace(/\\\?/g, '.');
	var terms = pat.match(/"[^"]*"|'[^']*'|\S+/g).map(function(el){
		var t = '\\b' + el.replace(/^(['"])(.*)\1$/, '$2') + '\\b';
		return t; //.replace('\\b.*', '').replace('.*\\b', '');
	});
	return new RegExp(terms.join('|'), 'i');
}

// ------------------------------------------------------------------------------------------------ storage

KonOpas.Store = function(id) {
	try {
		sessionStorage.setItem('konopas.test_var', '1');
		sessionStorage.removeItem('konopas.test_var', '1');
		this.get = function(k) {
			var v = sessionStorage.getItem('konopas.' + id + '.' + k);
			return v ? JSON.parse(v) : v;
		}
		this.set = function(k, v) {
			sessionStorage.setItem('konopas.' + id + '.' + k, JSON.stringify(v));
		}
		this.limit = '';
	} catch (e) {
		var data = {};
		this.get = function(k) { return data[k]; };
		this.set = function(k, v) { data[k] = v; };
		this.limit = (e.name == 'SecurityError') ? 'FFcookies'
				   : ((e.code === DOMException.QUOTA_EXCEEDED_ERR) && (sessionStorage.length === 0)) ? 'IOSprivate'
				   : '?';
	}
}

KonOpas.VarStore = function() {
	var data = {};
	this.getItem = function(k) { return data[k]; };
	this.setItem = function(k, v) { data[k] = v; };
}

// ------------------------------------------------------------------------------------------------ string generation

KonOpas.clean_name = function(p, span_parts) {
	var fn = '', ln = '';
	switch (p.name.length) {
		case 1:
			ln = p.name[0];
			break;
		case 2:
			if (p.name[1]) {
				fn = p.name[0];
				ln = p.name[1];
			} else {
				ln = p.name[0];
			}
			break;
		case 3:
			fn = p.name[2] + ' ' + p.name[0];
			ln = p.name[1];
			break;
		case 4:
			fn = p.name[2] + ' ' + p.name[0];
			ln = p.name[1] + (p.name[3] ? ', ' + p.name[3] : '');
			break;
	}
	return span_parts
		? '' + fn.trim() + ' ' + ln.trim() + ''
		: (fn + ' ' + ln).trim();
}

KonOpas.clean_links = function(p) {
	var ok = false, o = {};
	if (p && ('links' in p)) {
		if (p.links.img || p.links.photo) {
			var img = (p.links.img || p.links.photo).trim();
			if (/^www/.test(img)) img = 'http://' + img;
			if (/:\/\//.test(img)) { o['img'] = { 'tgt': img }; ok = true; }
		}
		if (p.links.url) {
			var url = p.links.url.trim();
			if (!/:\/\//.test(url)) url = 'http://' + url;
			o['URL'] = { 'tgt': url, 'txt': url.replace(/^https?:\/\//, '') };
			ok = true;
		}
		if (p.links.fb) {
			var fb = p.links.fb.trim().replace(/^(https?:\/\/)?(www\.)?facebook.com(\/#!)?\//, '');
			o['Facebook'] = { 'txt': fb };
			if (/[^a-zA-Z0-9.]/.test(fb) && !/^pages\//.test(fb)) fb = 'search.php?q=' + encodeURI(fb).replace(/%20/g, '+');
			o['Facebook']['tgt'] = 'https://www.facebook.com/' + fb;
			ok = true;
		}
		if (p.links.twitter) {
			var tw = p.links.twitter.trim().replace(/[@＠﹫]/g, '').replace(/^(https?:\/\/)?(www\.)?twitter.com(\/#!)?\//, '');
			o['Twitter'] = { 'txt': '@' + tw };
			if (/[^a-zA-Z0-9_]/.test(tw)) tw = 'search/users?q=' + encodeURI(tw).replace(/%20/g, '+');
			o['Twitter']['tgt'] = 'https://www.twitter.com/' + tw;
			ok = true;
		}
	}
	return ok ? o : false;
}

// ------------------------------------------------------------------------------------------------ array comparison

KonOpas.arrays_equal = function(a, b) {
	if (!a || !b) return false;
	if (a.length != b.length) return false;
	for (var i = 0; i  b.length) return KonOpas.array_overlap(b, a);
	var n = 0, i, j;
	for (i = 0; i < a.length; ++i) {
		for (j = 0; j < b.length; ++j) if (a[i] == b[j]) { ++n; break; }
	}
	return n;
}

// ------------------------------------------------------------------------------------------------ DOM manipulation

KonOpas.popup_open = function(ev) {
	ev = ev || window.event;
	if (ev.which != 1) return;
	var src_el = ev.target, pop_el = src_el.nextElementSibling;
	if (!pop_el || !pop_el.classList.contains('popup')) {
		if (src_el.href && /\.(gif|jpe?g|png)$/i.test(src_el.href)) {
			pop_el = _new_elem('img', 'popup');
			pop_el.src = src_el.href;
			src_el.parentNode.insertBefore(pop_el, src_el.nextSibling);
		} else return;
	}
	var wrap_el = _new_elem('div', 'popup-wrap');
	wrap_el.onclick = function() {
		pop_el.parentNode.removeChild(pop_el);
		wrap_el.parentNode.removeChild(wrap_el);
		src_el.parentNode.insertBefore(pop_el, src_el.nextSibling);
	};
	var pop_title = pop_el.getAttribute('data-title') || '';
	if (pop_title) wrap_el.appendChild(_new_elem('div', 'popup-title', pop_title));
	pop_el.parentNode.removeChild(pop_el);
	wrap_el.appendChild(pop_el);
	document.body.appendChild(wrap_el);
	if (src_el.href) ev.preventDefault();
}

KonOpas.toggle_collapse = function(ev) {
	ev = ev || window.event;
	var title = ev.target, body = title && title.nextElementSibling;
	if (!body) return;
	if (window.getComputedStyle(body).getPropertyValue('display') == 'none') {
		title.classList.remove('collapse');
		title.classList.add('collapse-open');
	} else {
		title.classList.add('collapse');
		title.classList.remove('collapse-open');
	}
}

// ------------------------------------------------------------------------------------------------ time & date

KonOpas.pretty_time = function(t, opt) {
	function pre0(n) { return (n < 10 ? '0' : '') + n; }
	function _pretty_time(h, m) {
		if (opt.time_show_am_pm) {
			var h12 = h % 12; if (h12 == 0) h12 = 12;
			var m_str = ((m == 0) && opt.abbrev_00_minutes ) ? '' : ':' + pre0(m);
			return h12 + m_str + (h < 12 ? 'am' : 'pm');
		} else {
			return pre0(h) + ':' + pre0(m);
		}
	}
	opt = opt || {};
	if (t instanceof Date) {
		return _pretty_time(t.getHours(), t.getMinutes());
	} else if (typeof t == 'string' || t instanceof String) {
		if (opt.time_show_am_pm) {
			var a = t.split(':'); // hh:mm
			return _pretty_time(parseInt(a[0], 10), parseInt(a[1], 10));
		} else return t;
	} else return '';
}

KonOpas.pretty_time_diff = function(t) {
	var d = (Date.now() - t) / 1e3,
	    a = Math.abs(d),
	    s = [1, 60, 60, 24, 7, 4.333, 12, 1e9];
	if (a < 20) return i18n.txt('just now');
	for (var i = 0, l = s.length; i < l; ++i) {
		if ((a /= s[i]) < 2) return i18n.txt('time_diff', { 'T':~~(a *= s[i]), 'T_UNIT':i-1, 'T_PAST':d>0 });
	}
}

KonOpas.parse_date = function(day_str) {
	if (!day_str) return false;
	var a = day_str.match(/(\d+)/g); if (!a || (a.length < 3)) return false;
	var y = parseInt(a[0], 10), m = parseInt(a[1], 10), d = parseInt(a[2], 10);
	if (!y || !m || !d) return false;
	return new Date(y, m - 1, d, 12);
}

KonOpas.pretty_date = function(d, opt) {
	opt = opt || {};
	var o = { weekday: "long", month: "long", day: "numeric" },
	    t = (d instanceof Date) ? d : KonOpas.parse_date(d);
	if (!t) return d;
	if (Math.abs(t - Date.now()) > 1000*3600*24*60) o.year = "numeric";
	var s = t.toLocaleDateString(opt.lc, o);
	return s.charAt(0).toUpperCase() + s.slice(1);
}

KonOpas.time_sum = function(t0_str, m_str) {
	var t = 60 * t0_str.substr(0,2) + 1 * t0_str.substr(3,2) + 1 * m_str,
	    h = (t / 60) >> 0,
	    m = t - 60 * h;
	return '' + (h % 24) + ':' + (m<10?'0':'') + m;
}
<|endoftext|>


<commit_before>// A higher order component for loading.

import Component from 'react-pure-render/component';
import Loading from './Loading.react';
import NotFound from '../notfound/Page.react';
import React from 'react';

export default function loading(Wrapped, propsToCheck, customLoading) {
  return class LoadingWrapper extends Component {

    render() {
      const props = propsToCheck.map(prop => this.props[prop]);
      // Null is evidence of absence.
      if (props.some(prop => prop === null)) {
        return <NotFound />;
      }
      // Undefined is absence of evidence.
      if (props.some(prop => prop === undefined)) {
        return <Loading />;
      }
      // For better loading granularity.
      if (customLoading && customLoading(this.props)) {
        return <Loading />;
      }
      return <Wrapped {...this.props} />;
    }

  };
}
<commit_msg>Wrapper / Wrapped pattern for higher order components<commit_after>// A higher order component for loading.

import Component from 'react-pure-render/component';
import Loading from './Loading.react';
import NotFound from '../notfound/Page.react';
import React from 'react';

export default function loading(Wrapped, propsToCheck, customLoading) {
  return class Wrapper extends Component {

    render() {
      const props = propsToCheck.map(prop => this.props[prop]);
      // Null is evidence of absence.
      if (props.some(prop => prop === null)) {
        return <NotFound />;
      }
      // Undefined is absence of evidence.
      if (props.some(prop => prop === undefined)) {
        return <Loading />;
      }
      // For better loading granularity.
      if (customLoading && customLoading(this.props)) {
        return <Loading />;
      }
      return <Wrapped {...this.props} />;
    }

  };
}
<|endoftext|>


<commit_before>// a kitchen sink for small generic functions and jQuery plugins
'use strict'
D.util={
  dict:function(x){var r={};for(var i=0;i<x.length;i++)r[x[i][0]]=x[i][1];return r}, // dictionary from key-value pairs
  ESC:{'<':'<','>':'>','&':'&',"'":''','"':'"'},
  esc:function(s){return s.replace(/[<>&'"]/g,function(x){return D.util.ESC[x]})},
  cmOnDblClick:function(cm,f){
    // CodeMirror supports 'dblclick' events but they are unreliable and seem to require rather a short time between the
    // two clicks.  So, let's track clicks manually:
    var t=0,x=0,y=0 // last click's timestamp and coordinates
    cm.on('mousedown',function(cm,e){
      e.timeStamp-t<400&&Math.abs(x-e.x)+Math.abs(y-e.y)<10&&!$(e.target).closest('.CodeMirror-gutter-wrapper').length&&f(e)
      t=e.timeStamp;x=e.x;y=e.y})},
  throttle1:function(f,dt){ // f: a 1-arg function that doesn't return a result; dt: minimum time between invocations
    var next=0,tid=0 // next: the earliest time f can be called again; tid: id returned by setTimeout
    dt==null&&(dt=500)
    return function(x){
      if(+new Date<next){clearTimeout(tid);tid=setTimeout(function(){tid=0;next=+new Date+dt;f(x)},dt);next=+new Date+dt}
      else{next=+new Date+dt;f(x)}}},
  cmOpts:{specialChars:/[\0-\x1f\x7f\xad\u200b-\u200f\u2028\u2029\ufeff]/g}} // default CodeMirror options in RIDE
$.alert=function(m,t,f){ // m:message, t:title, f:callback
  $('
').text(m).dialog({modal:1,title:t,buttons:[
    {html:'OK',click:function(){$(this).dialog('close');f&&f()}}]})},
$.confirm=function(m,t,f){ // m:message, t:title, f:callback
  var r;$('
').text(m).dialog({modal:1,title:t,close:function(){f&&f(r)},buttons:[
    {html:'Yes',click:function(){r=1;$(this).dialog('close')}},
    {html:'No' ,click:function(){r=0;$(this).dialog('close')}}]})}
$.fn.insert=function(s){ // replace selection in an <input> or <textarea> with s
  return this.each(function(){
    if(!$(this).is(':text,textarea')||this.readOnly)return
    var e=this,i=e.selectionStart,j=e.selectionEnd  // TODO: IE
    if(i!=null&&j!=null){e.value=e.value.slice(0,i)+s+e.value.slice(j);e.selectionStart=e.selectionEnd=i+s.length}})}
$.fn.elastic=function(){ // as you type in an <input>, it stretches as necessary to accommodate the text
  return this.each(function(){
    var m=$(this).data('minSize')
    m||$(this).data('minSize',m=+this.size||1).on('keyup keypress change',function(){$(this).elastic()})
    this.size=Math.max(m,this.value.length+1)})}
<commit_msg>rm unused function utils.throttle1()<commit_after>// a kitchen sink for small generic functions and jQuery plugins
'use strict'
D.util={
  dict:function(x){var r={};for(var i=0;i<x.length;i++)r[x[i][0]]=x[i][1];return r}, // dictionary from key-value pairs
  ESC:{'<':'<','>':'>','&':'&',"'":''','"':'"'},
  esc:function(s){return s.replace(/[<>&'"]/g,function(x){return D.util.ESC[x]})},
  cmOnDblClick:function(cm,f){
    // CodeMirror supports 'dblclick' events but they are unreliable and seem to require rather a short time between the
    // two clicks.  So, let's track clicks manually:
    var t=0,x=0,y=0 // last click's timestamp and coordinates
    cm.on('mousedown',function(cm,e){
      e.timeStamp-t<400&&Math.abs(x-e.x)+Math.abs(y-e.y)<10&&!$(e.target).closest('.CodeMirror-gutter-wrapper').length&&f(e)
      t=e.timeStamp;x=e.x;y=e.y})},
  cmOpts:{specialChars:/[\0-\x1f\x7f\xad\u200b-\u200f\u2028\u2029\ufeff]/g}} // default CodeMirror options in RIDE
$.alert=function(m,t,f){ // m:message, t:title, f:callback
  $('
').text(m).dialog({modal:1,title:t,buttons:[
    {html:'OK',click:function(){$(this).dialog('close');f&&f()}}]})},
$.confirm=function(m,t,f){ // m:message, t:title, f:callback
  var r;$('
').text(m).dialog({modal:1,title:t,close:function(){f&&f(r)},buttons:[
    {html:'Yes',click:function(){r=1;$(this).dialog('close')}},
    {html:'No' ,click:function(){r=0;$(this).dialog('close')}}]})}
$.fn.insert=function(s){ // replace selection in an <input> or <textarea> with s
  return this.each(function(){
    if(!$(this).is(':text,textarea')||this.readOnly)return
    var e=this,i=e.selectionStart,j=e.selectionEnd  // TODO: IE
    if(i!=null&&j!=null){e.value=e.value.slice(0,i)+s+e.value.slice(j);e.selectionStart=e.selectionEnd=i+s.length}})}
$.fn.elastic=function(){ // as you type in an <input>, it stretches as necessary to accommodate the text
  return this.each(function(){
    var m=$(this).data('minSize')
    m||$(this).data('minSize',m=+this.size||1).on('keyup keypress change',function(){$(this).elastic()})
    this.size=Math.max(m,this.value.length+1)})}
<|endoftext|>


<commit_before>/**
 * Created by ostap on 16.04.2016.
 */
function initMap() {
    var map = new google.maps.Map(document.getElementById('googleMap'), {
        center: {lat: 50.464379, lng: 30.519131},
        zoom: 13
    });

    ////GEOLOCATING
    var input = document.getElementById('pac-input');

    var autocomplete = new google.maps.places.Autocomplete(input);
    autocomplete.bindTo('bounds', map);

    map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

    var infowindow = new google.maps.InfoWindow();
    var marker = new google.maps.Marker({
        map: map
    });
    marker.addListener('click', function() {
        infowindow.open(map, marker);
    });

    autocomplete.addListener('place_changed', function() {
        infowindow.close();
        var place = autocomplete.getPlace();
        if (!place.geometry) {
            return;
        }

        if (place.geometry.viewport) {
            map.fitBounds(place.geometry.viewport);
        } else {
            map.setCenter(place.geometry.location);
            map.setZoom(17);
        }

        marker.setPlace({
            placeId: place.place_id,
            location: place.geometry.location
        });
        marker.setVisible(true);

        infowindow.setContent('
' + place.name + '
' +
            '
' +
            place.formatted_address);
        infowindow.open(map, marker);
    });

    ///MARKER
    map.addListener('click', function(e) {
        placeMarkerAndPanTo(e.latLng, map);

    });
}

////

var marker;
function placeMarkerAndPanTo(latLng, map) {

    var infowindow = new google.maps.InfoWindow();

    if (marker && marker.setMap) {
        marker.setMap(null);
    }

    marker=new google.maps.Marker({
        position: latLng,
        map: map
    });

    map.panTo(latLng);
    marker.addListener('click', function() {
        infowindow.setContent(marker.position.toString());
        infowindow.open(map, marker);
    });

}

function getMarkersLat(){
    return  marker.position.lat;
}
function getMarkersLng(){
    return  marker.position.lng;
}<commit_msg>added function to delete all markers<commit_after>/**
 * Created by ostap on 16.04.2016.
 */
function initMap() {
    var map = new google.maps.Map(document.getElementById('googleMap'), {
        center: {lat: 50.464379, lng: 30.519131},
        zoom: 13
    });

    ////GEOLOCATING
    var input = document.getElementById('pac-input');

    var autocomplete = new google.maps.places.Autocomplete(input);
    autocomplete.bindTo('bounds', map);

    map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

    var infowindow = new google.maps.InfoWindow();
    var marker = new google.maps.Marker({
        map: map
    });
    marker.addListener('click', function() {
        infowindow.open(map, marker);
    });

    autocomplete.addListener('place_changed', function() {
        infowindow.close();
        var place = autocomplete.getPlace();
        if (!place.geometry) {
            return;
        }

        if (place.geometry.viewport) {
            map.fitBounds(place.geometry.viewport);
        } else {
            map.setCenter(place.geometry.location);
            map.setZoom(17);
        }

        marker.setPlace({
            placeId: place.place_id,
            location: place.geometry.location
        });
        marker.setVisible(true);

        infowindow.setContent('
' + place.name + '
' +
            '
' +
            place.formatted_address);
        infowindow.open(map, marker);
    });

    ///MARKER
    map.addListener('click', function(e) {
        placeMarkerAndPanTo(e.latLng, map);

    });
}

////

var marker;
var markersArray = [];
function placeMarkerAndPanTo(latLng, map) {

    var infowindow = new google.maps.InfoWindow();

    if (marker && marker.setMap) {
        marker.setMap(null);
    }

    marker=new google.maps.Marker({
        position: latLng,
        map: map
    });

    map.panTo(latLng);
    markersArray.push(marker);
    marker.addListener('click', function() {
        infowindow.setContent(marker.position.toString());
        infowindow.open(map, marker);

    });

    markersArray.push(marker);

}

//ФУНКЦІЯ, ЩО ВИДАЛЯЄ ВСІ МАРКЕРИ
function clear() {
    markersArray.forEach(function (marker) {
        marker.setMap(null);
    });
    markersArray = [];
}

function getMarkersLat(){
    return  marker.position.lat;
}
function getMarkersLng(){
    return  marker.position.lng;
}<|endoftext|>


<commit_before>/**
 * @name Util module
 * @description
 * Contains various miscellaneous utility functions and extensions
 */

angular.module('Mac.Util', []).factory('util', ['$filter', function($filter) {
  var inflectionConstants = {
    "uncountables": ["sheep", "fish", "moose", "series", "species", "money", "rice", "information", "info", "equipment", "min"],
    "irregulars": {
      "child": "children",
      "man": "men",
      "woman": "women",
      "person": "people",
      "ox": "oxen",
      "goose": "geese"
    },
    "pluralizers": [
      [/(quiz)$/i, "$1zes"],
      [/([m|l])ouse$/i, "$1ice"],
      [/(matr|vert|ind)(ix|ex)$/i, "$1ices"],
      [/(x|ch|ss|sh)$/i, "$1es"],
      [/([^aeiouy]|qu)y$/i, "$1ies"],
      [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
      [/sis$/i, "ses"],
      [/([ti])um$/i, "$1a"],
      [/(buffal|tomat)o$/i, "$1oes"],
      [/(bu)s$/i, "$1ses"],
      [/(alias|status)$/i, "$1es"],
      [/(octop|vir)us$/i, "$1i"],
      [/(ax|test)is$/i, "$1es"],
      [/x$/i, "xes"],
      [/s$/i, "s"],
      [/$/, "s"]
    ]
  };

  /**
   * @name timeRegex
   * @example
   * 01:30 PM or 9:45 AM
   */
  var timeRegexStr = '^' +
    '(0?[1-9]|1[0-2])' +  // hours (starting zero optional)
    ':' +                 // colon
    '([0-5][0-9])' +      // minute
    '[\\s]' +             // space
    '([AP]M)' +           // meridian
    '$';
  var timeRegex = new RegExp(timeRegexStr);

  // http://tools.ietf.org/html/rfc3986#section-2.2
  var urlRegexStr = '(?:' +
      '(http[s]?):\\/\\/' +     // protocol (optional)
    ')?' +
    '(?:' +
      '(www|[\\d\\w\\-]+)\\.' + // subdomain (optional)
    ')?' +
    '([\\d\\w\\-]+)\\.' +       // domain
    '([A-Za-z]{2,6})' +         // tld
    '(:[\\d]*)?'  +             // port (optional)
    '([' +                      // path (optional)
      ':\\/?#\\[\\]@' +         // rfc3986 gen-delims
      '!$&\'()*+,;=' +          // rfc3986 sub-delims
      '\\w\\d-._~' +            // rfc3986 unreserved characters
      '%\\\\' +                 // additional characters
    ']*)?';
  var urlRegex = new RegExp(urlRegexStr, 'i');

  var emailRegex =  /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

  return {
    /**
     * @name pluralize
     * @description
     * Pluralize string based on the count
     *
     * @param {String} string String to pluralize (default '')
     * @param {Integer} count Object counts
     * @param {Boolean} includeCount Include the number or not (default false)
     *
     * @returns {String} Pluralized string based on the count
     */
    pluralize: function(string, count, includeCount) {
      var irregulars, isUppercase, lowercaseWord, pluralizedString, pluralizedWord, pluralizer, pluralizers, uncountables, word, i;
      if (string == null) {
        string = "";
      }
      if (includeCount == null) {
        includeCount = false;
      }
      if (!angular.isString(string) || this.trim(string).length === 0) {
        return string;
      }
      if (includeCount && isNaN(+count)) {
        return "";
      }
      if (count == null) {
        count = 2;
      }

      pluralizers = inflectionConstants.pluralizers;
      uncountables = inflectionConstants.uncountables;
      irregulars = inflectionConstants.irregulars;

      word = string.split(/\s/).pop();
      isUppercase = word.toUpperCase() === word;
      lowercaseWord = word.toLowerCase();

      pluralizedWord = count === 1 || uncountables.indexOf(lowercaseWord) >= 0 ? word : null;

      if (pluralizedWord == null && irregulars[lowercaseWord] != null) {
        pluralizedWord = irregulars[lowercaseWord];
      }

      if (pluralizedWord == null) {
        for (i = 0; i < pluralizers.length; i++) {
          pluralizer = pluralizers[i];
          if (!(pluralizer[0].test(lowercaseWord))) {
            continue;
          }
          pluralizedWord = word.replace(pluralizer[0], pluralizer[1]);
          break;
        }
      }
      pluralizedWord || (pluralizedWord = word);
      if (isUppercase) {
        pluralizedWord = pluralizedWord.toUpperCase();
      }
      pluralizedString = string.slice(0, -word.length) + pluralizedWord;
      if (includeCount) {
        return ($filter("number")(count)) + " " + pluralizedString;
      } else {
        return pluralizedString;
      }
    },

    /**
     * @name trim
     * @description
     * Trimming whitespaces on strings
     * @param {String} string input
     * @returns {String} Trimmed string
     */
    trim: function(string) {
      var str;
      str = String(string) || "";
      if (String.prototype.trim != null) {
        return str.trim();
      } else {
        return str.replace(/^\s+|\s+$/gm, "");
      }
    },

    /**
     * @name capitalize
     * @description
     * Capitalize string
     * @param {String} string
     * @returns {String}
     */
    capitalize: function(string) {
      var str;
      str = String(string) || "";
      return str.charAt(0).toUpperCase() + str.substring(1);
    },

    /**
     * @name uncapitalize
     * @description
     * Convert the first character to lowercase
     * @param {String} string
     * @returns {String}
     */
    uncapitalize: function(string) {
      var str;
      str = String(string) || "";
      return str.charAt(0).toLowerCase() + str.substring(1);
    },

    /**
     * @name toCamelCase
     * @description
     * Convert string with dashes, underscores and spaces to camel case
     * ```
     * this-is-a-test => thisIsATest
     * another_test => anotherTest
     * hello world again => helloWorldAgain
     * a mix_of-everything => aMixOfEverything
     * ```
     * @param {String} string
     * @returns {String}
     */
    toCamelCase: function(string) {
      if (string == null) {
        string = "";
      }
      return this.trim(string).replace(/[-_\s]+(.)?/g, function(match, c) {
        return c.toUpperCase();
      });
    },

    /**
     * @name toSnakeCase
     * @description
     * Convert other cases into snake case (separated by underscores)
     * @param {String} string
     * @returns {String}
     */
    toSnakeCase: function(string) {
      if (string == null) {
        string = "";
      }
      return this.trim(string).replace(/([a-z\d])([A-Z]+)/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
    },

    /**
     * @name convertKeysToCamelCase
     * @description
     * Convert object keys to camel case
     * @param {Object} object
     * @returns {Object}
     */
    convertKeysToCamelCase: function(object) {
      var key, result = {}, value;
      for (key in object) {
        value = object[key];
        key = this.toCamelCase(key);

        if (value != null && typeof value === 'object' && value.constructor !== Array) {
          value = this.convertKeysToCamelCase(value);
        }

        result[key] = value;
      }
      return result;
    },

    /**
     * @name convertKeysToSnakeCase
     * @description
     * Convert object keys to snake case
     * @param {Object} object
     * @returns {Object}
     */
    convertKeysToSnakeCase: function(object) {
      var key, result = {}, value;
      for (key in object) {
        value = object[key];
        key = this.toSnakeCase(key);

        if (value != null && typeof value === 'object' && value.constructor !== Array) {
          value = this.convertKeysToSnakeCase(value);
        }

        result[key] = value;
      }
      return result;
    },

    /**
     * @name pyth
     * @description
     * pythagoras theorem
     * @param {Integer} a
     * @param {Integer} b
     * @returns {Integer}
     */
    pyth: function(a, b) {
      return Math.sqrt(a * a + b * b);
    },

    /**
     * @name degrees
     * @description
     * Convert from radian to degrees
     * @param {Number} radian
     * @returns {Number}
     */
    degrees: function(radian) {
      return (radian * 180) / Math.PI;
    },

    /**
     * @name radian
     * @description
     * Convert degree to radian
     * @param {Number} degrees
     * @returns {Number}
     */
    radian: function(degrees) {
      return (degrees * Math.PI) / 180;
    },

    /**
     * @name hex2rgb
     * @description
     * Convert hex color value to rgb
     * @param {String} hex
     * @returns {Object} Object with r, g, and b values
     */
    hex2rgb: function(hex) {
      var color, rgb, value;
      if (hex.indexOf('#') === 0) {
        hex = hex.substring(1);
      }
      hex = hex.toLowerCase();
      rgb = {};
      if (hex.length === 3) {
        rgb.r = hex.charAt(0) + hex.charAt(0);
        rgb.g = hex.charAt(1) + hex.charAt(1);
        rgb.b = hex.charAt(2) + hex.charAt(2);
      } else {
        rgb.r = hex.substring(0, 2);
        rgb.g = hex.substring(2, 4);
        rgb.b = hex.substring(4);
      }
      for (color in rgb) {
        value = rgb[color];
        rgb[color] = parseInt(value, 16);
      }
      return rgb;
    },

    /**
     * @name validateUrl
     * @description
     * Parse url
     * @param {String} url
     * @returns {Object} Object with url sections parsed out
     * ```
     * input: www.example.com:9000/testing
     * output: {
     *   url: 'www.example.com:9000/testing',
     *   protocol: 'http',
     *   subdomain: 'www',
     *   name: 'example',
     *   domain: 'com',
     *   port: '9000',
     *   path: '/testing'
     * }
     * ```
     */
    validateUrl: function(url) {
      var match;
      match = urlRegex.exec(url);
      if (match != null) {
        match = {
          url: match[0],
          protocol: match[1] || "http",
          subdomain: match[2],
          name: match[3],
          domain: match[4],
          port: match[5],
          path: match[6] || "/"
        };
        match["url"] = match.url;
      }
      return match;
    },

    /**
     * @name validateEmail
     * @description
     * Check if input string is a valid email address
     * @param {String} email
     * @returns {Boolean}
     */
    validateEmail: function(email) {
      return emailRegex.test(email);
    },

    /**
     * @name validateTime
     * @description
     * Check if time input match time regex
     * @param {String} time
     * @returns {Object}
     */
    validateTime: function(time) {
      return timeRegex.exec(time);
    },

    /**
     * @name getQueryString
     * @description
     * Return param value in querystring
     * credits: http://www.netlobo.com/url_query_string_javascript.html
     * @param {String} url
     * @param {String} name
     * @returns {String}
     */
    getQueryString: function(url, name) {
      var regex, regexS, results;
      if (name == null) {
        name = "";
      }
      name = name.replace(/[[]/, "\[").replace(/[]]/, "\]");
      regexS = "[\?&]" + name + "=([^&#]*)";
      regex = new RegExp(regexS);
      results = regex.exec(url);
      if (results != null) {
        return results[1];
      } else {
        return "";
      }
    },

    /**
     * @name parseUrlPath
     * @param {String} fullPath
     * @returns {Object}
     */
    parseUrlPath: function(fullPath) {
      var path, pathComponents, queries, queryString, queryStrings, urlComponents, values, verb, _i, _len, _ref;
      urlComponents = fullPath.split("?");
      pathComponents = urlComponents[0].split("/");
      path = pathComponents.slice(0, pathComponents.length - 1).join("/");
      verb = pathComponents[pathComponents.length - 1];
      queries = {};
      if (urlComponents.length > 1) {
        queryStrings = urlComponents[urlComponents.length - 1];
        _ref = queryStrings.split("&");
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          queryString = _ref[_i];
          values = queryString.split("=");
          queries[values[0]] = values[1] != null ? values[1] : "";
        }
      }
      return {
        fullPath: fullPath,
        path: path,
        pathComponents: pathComponents,
        verb: verb,
        queries: queries
      };
    },

    /**
     * @name extendAttributes
     * @description
     * Extend default values with attribute
     * @param {String} prefix Prefix of all attributes
     * @param {Object} defaults Default set of attributes
     * @param {Object} attributes User set attributes
     * @returns {Object}
     */
    extendAttributes: function(prefix, defaults, attributes) {
      var altKey, key, macKey, output, value, _ref, _ref1, outputValue;
      if (prefix == null) {
        prefix = "";
      }
      output = {};
      for (key in defaults) {
        if (!defaults.hasOwnProperty(key)) continue;

        value = defaults[key];
        altKey = prefix ? this.capitalize(key) : key;
        macKey = "" + prefix + altKey;
        outputValue = attributes[macKey] != null ? attributes[macKey] || true : value;

        // Convert to true boolean if passing in boolean string
        if (outputValue === "true" || outputValue === "false") {
          outputValue = outputValue === "true";

        // Convert to integer or numbers from strings
        } else if (outputValue != null && outputValue.length > 0 && !isNaN(+outputValue)) {
          outputValue = +outputValue;
        }

        output[key] = outputValue
      }
      return output;
    }
  }
}]);
<commit_msg>refactor(macUtil): Cleaned up JS code<commit_after>/**
 * @name Util module
 * @description
 * Contains various miscellaneous utility functions and extensions
 */

angular.module('Mac.Util', []).factory('util', ['$filter', function($filter) {
  var inflectionConstants = {
    "uncountables": ["sheep", "fish", "moose", "series", "species", "money", "rice", "information", "info", "equipment", "min"],
    "irregulars": {
      "child": "children",
      "man": "men",
      "woman": "women",
      "person": "people",
      "ox": "oxen",
      "goose": "geese"
    },
    "pluralizers": [
      [/(quiz)$/i, "$1zes"],
      [/([m|l])ouse$/i, "$1ice"],
      [/(matr|vert|ind)(ix|ex)$/i, "$1ices"],
      [/(x|ch|ss|sh)$/i, "$1es"],
      [/([^aeiouy]|qu)y$/i, "$1ies"],
      [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
      [/sis$/i, "ses"],
      [/([ti])um$/i, "$1a"],
      [/(buffal|tomat)o$/i, "$1oes"],
      [/(bu)s$/i, "$1ses"],
      [/(alias|status)$/i, "$1es"],
      [/(octop|vir)us$/i, "$1i"],
      [/(ax|test)is$/i, "$1es"],
      [/x$/i, "xes"],
      [/s$/i, "s"],
      [/$/, "s"]
    ]
  };

  /**
   * @name timeRegex
   * @example
   * 01:30 PM or 9:45 AM
   */
  var timeRegexStr = '^' +
    '(0?[1-9]|1[0-2])' +  // hours (starting zero optional)
    ':' +                 // colon
    '([0-5][0-9])' +      // minute
    '[\\s]' +             // space
    '([AP]M)' +           // meridian
    '$';
  var timeRegex = new RegExp(timeRegexStr);

  // http://tools.ietf.org/html/rfc3986#section-2.2
  var urlRegexStr = '(?:' +
      '(http[s]?):\\/\\/' +     // protocol (optional)
    ')?' +
    '(?:' +
      '(www|[\\d\\w\\-]+)\\.' + // subdomain (optional)
    ')?' +
    '([\\d\\w\\-]+)\\.' +       // domain
    '([A-Za-z]{2,6})' +         // tld
    '(:[\\d]*)?'  +             // port (optional)
    '([' +                      // path (optional)
      ':\\/?#\\[\\]@' +         // rfc3986 gen-delims
      '!$&\'()*+,;=' +          // rfc3986 sub-delims
      '\\w\\d-._~' +            // rfc3986 unreserved characters
      '%\\\\' +                 // additional characters
    ']*)?';
  var urlRegex = new RegExp(urlRegexStr, 'i');

  var emailRegex =  /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

  return {
    /**
     * @name pluralize
     * @description
     * Pluralize string based on the count
     *
     * @param {String} string String to pluralize (default '')
     * @param {Integer} count Object counts
     * @param {Boolean} includeCount Include the number or not (default false)
     *
     * @returns {String} Pluralized string based on the count
     */
    pluralize: function(string, count, includeCount) {
      var irregulars, isUppercase, lowercaseWord, pluralizedString, pluralizedWord, pluralizer, pluralizers, uncountables, word, i;
      string = string || '';
      includeCount = includeCount || false;
      if (!angular.isString(string) || this.trim(string).length === 0) {
        return string;
      }
      if (includeCount && isNaN(+count)) {
        return "";
      }
      if (count === undefined) {
        count = 2;
      }

      pluralizers = inflectionConstants.pluralizers;
      uncountables = inflectionConstants.uncountables;
      irregulars = inflectionConstants.irregulars;

      word = string.split(/\s/).pop();
      isUppercase = word.toUpperCase() === word;
      lowercaseWord = word.toLowerCase();

      pluralizedWord = count === 1 || uncountables.indexOf(lowercaseWord) >= 0 ? word : null;

      if (pluralizedWord === null && irregulars[lowercaseWord]) {
        pluralizedWord = irregulars[lowercaseWord];
      }

      if (!pluralizedWord) {
        for (i = 0; i < pluralizers.length; i++) {
          pluralizer = pluralizers[i];
          if (!(pluralizer[0].test(lowercaseWord))) {
            continue;
          }
          pluralizedWord = word.replace(pluralizer[0], pluralizer[1]);
          break;
        }
      }
      pluralizedWord = pluralizedWord || word;
      if (isUppercase) {
        pluralizedWord = pluralizedWord.toUpperCase();
      }
      pluralizedString = string.slice(0, -word.length) + pluralizedWord;
      if (includeCount) {
        return ($filter("number")(count)) + " " + pluralizedString;
      } else {
        return pluralizedString;
      }
    },

    /**
     * @name trim
     * @description
     * Trimming whitespaces on strings
     * @param {String} string input
     * @returns {String} Trimmed string
     */
    trim: function(string) {
      var str;
      str = String(string) || "";
      if (String.prototype.trim !== null) {
        return str.trim();
      } else {
        return str.replace(/^\s+|\s+$/gm, "");
      }
    },

    /**
     * @name capitalize
     * @description
     * Capitalize string
     * @param {String} string
     * @returns {String}
     */
    capitalize: function(string) {
      var str;
      str = String(string) || "";
      return str.charAt(0).toUpperCase() + str.substring(1);
    },

    /**
     * @name uncapitalize
     * @description
     * Convert the first character to lowercase
     * @param {String} string
     * @returns {String}
     */
    uncapitalize: function(string) {
      var str;
      str = String(string) || "";
      return str.charAt(0).toLowerCase() + str.substring(1);
    },

    /**
     * @name toCamelCase
     * @description
     * Convert string with dashes, underscores and spaces to camel case
     * ```
     * this-is-a-test => thisIsATest
     * another_test => anotherTest
     * hello world again => helloWorldAgain
     * a mix_of-everything => aMixOfEverything
     * ```
     * @param {String} string
     * @returns {String}
     */
    toCamelCase: function(string) {
      string = string || '';
      return this.trim(string).replace(/[-_\s]+(.)?/g, function(match, c) {
        return c.toUpperCase();
      });
    },

    /**
     * @name toSnakeCase
     * @description
     * Convert other cases into snake case (separated by underscores)
     * @param {String} string
     * @returns {String}
     */
    toSnakeCase: function(string) {
      string = string || '';
      return this.trim(string).replace(/([a-z\d])([A-Z]+)/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
    },

    /**
     * @name convertKeysToCamelCase
     * @description
     * Convert object keys to camel case
     * @param {Object} object
     * @returns {Object}
     */
    convertKeysToCamelCase: function(object) {
      var key, result = {}, value;
      for (key in object) {
        value = object[key];
        key = this.toCamelCase(key);

        if (value && typeof value === 'object' && value.constructor !== Array) {
          value = this.convertKeysToCamelCase(value);
        }

        result[key] = value;
      }
      return result;
    },

    /**
     * @name convertKeysToSnakeCase
     * @description
     * Convert object keys to snake case
     * @param {Object} object
     * @returns {Object}
     */
    convertKeysToSnakeCase: function(object) {
      var key, result = {}, value;
      for (key in object) {
        value = object[key];
        key = this.toSnakeCase(key);

        if (value && typeof value === 'object' && value.constructor !== Array) {
          value = this.convertKeysToSnakeCase(value);
        }

        result[key] = value;
      }
      return result;
    },

    /**
     * @name pyth
     * @description
     * pythagoras theorem
     * @param {Integer} a
     * @param {Integer} b
     * @returns {Integer}
     */
    pyth: function(a, b) {
      return Math.sqrt(a * a + b * b);
    },

    /**
     * @name degrees
     * @description
     * Convert from radian to degrees
     * @param {Number} radian
     * @returns {Number}
     */
    degrees: function(radian) {
      return (radian * 180) / Math.PI;
    },

    /**
     * @name radian
     * @description
     * Convert degree to radian
     * @param {Number} degrees
     * @returns {Number}
     */
    radian: function(degrees) {
      return (degrees * Math.PI) / 180;
    },

    /**
     * @name hex2rgb
     * @description
     * Convert hex color value to rgb
     * @param {String} hex
     * @returns {Object} Object with r, g, and b values
     */
    hex2rgb: function(hex) {
      var color, rgb, value;
      if (hex.indexOf('#') === 0) {
        hex = hex.substring(1);
      }
      hex = hex.toLowerCase();
      rgb = {};
      if (hex.length === 3) {
        rgb.r = hex.charAt(0) + hex.charAt(0);
        rgb.g = hex.charAt(1) + hex.charAt(1);
        rgb.b = hex.charAt(2) + hex.charAt(2);
      } else {
        rgb.r = hex.substring(0, 2);
        rgb.g = hex.substring(2, 4);
        rgb.b = hex.substring(4);
      }
      for (color in rgb) {
        value = rgb[color];
        rgb[color] = parseInt(value, 16);
      }
      return rgb;
    },

    /**
     * @name validateUrl
     * @description
     * Parse url
     * @param {String} url
     * @returns {Object} Object with url sections parsed out
     * ```
     * input: www.example.com:9000/testing
     * output: {
     *   url: 'www.example.com:9000/testing',
     *   protocol: 'http',
     *   subdomain: 'www',
     *   name: 'example',
     *   domain: 'com',
     *   port: '9000',
     *   path: '/testing'
     * }
     * ```
     */
    validateUrl: function(url) {
      var match = urlRegex.exec(url);
      if (match !== null) {
        match = {
          url: match[0],
          protocol: match[1] || "http",
          subdomain: match[2],
          name: match[3],
          domain: match[4],
          port: match[5],
          path: match[6] || "/"
        };
      }
      return match;
    },

    /**
     * @name validateEmail
     * @description
     * Check if input string is a valid email address
     * @param {String} email
     * @returns {Boolean}
     */
    validateEmail: function(email) {
      return emailRegex.test(email);
    },

    /**
     * @name validateTime
     * @description
     * Check if time input match time regex
     * @param {String} time
     * @returns {Object}
     */
    validateTime: function(time) {
      return timeRegex.exec(time);
    },

    /**
     * @name getQueryString
     * @description
     * Return param value in querystring
     * credits: http://www.netlobo.com/url_query_string_javascript.html
     * @param {String} url
     * @param {String} name
     * @returns {String}
     */
    getQueryString: function(url, name) {
      var regex, regexS, results;
      name = name || '';
      name = name.replace(/[[]/, "\[").replace(/[]]/, "\]");
      regexS = "[\?&]" + name + "=([^&#]*)";
      regex = new RegExp(regexS);
      results = regex.exec(url);

      return results ? results[1] : '';
    },

    /**
     * @name parseUrlPath
     * @param {String} fullPath
     * @returns {Object}
     */
    parseUrlPath: function(fullPath) {
      var path, pathComponents, queries, queryString, queryStrings, urlComponents, values, verb, _i, _len, _ref;
      urlComponents = fullPath.split("?");
      pathComponents = urlComponents[0].split("/");
      path = pathComponents.slice(0, pathComponents.length - 1).join("/");
      verb = pathComponents[pathComponents.length - 1];
      queries = {};
      if (urlComponents.length > 1) {
        queryStrings = urlComponents[urlComponents.length - 1];
        _ref = queryStrings.split("&");
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          queryString = _ref[_i];
          values = queryString.split("=");
          queries[values[0]] = values[1] != null ? values[1] : "";
        }
      }
      return {
        fullPath: fullPath,
        path: path,
        pathComponents: pathComponents,
        verb: verb,
        queries: queries
      };
    },

    /**
     * @name extendAttributes
     * @description
     * Extend default values with attribute
     * @param {String} prefix Prefix of all attributes
     * @param {Object} defaults Default set of attributes
     * @param {Object} attributes User set attributes
     * @returns {Object}
     */
    extendAttributes: function(prefix, defaults, attributes) {
      var altKey, key, macKey, output, value, _ref, _ref1, outputValue;
      prefix = prefix || '';
      output = {};
      for (key in defaults) {
        if (!defaults.hasOwnProperty(key)) continue;

        value = defaults[key];
        altKey = prefix ? this.capitalize(key) : key;
        macKey = "" + prefix + altKey;
        outputValue = attributes[macKey] != null ? attributes[macKey] || true : value;

        // Convert to true boolean if passing in boolean string
        if (outputValue === "true" || outputValue === "false") {
          outputValue = outputValue === "true";

        // Convert to integer or numbers from strings
        } else if (outputValue != null && outputValue.length > 0 && !isNaN(+outputValue)) {
          outputValue = +outputValue;
        }

        output[key] = outputValue;
      }
      return output;
    }
  }
}]);
<|endoftext|>


<commit_before>const toString = Object.prototype.toString;

exports.isDef = isDef;
exports.isUnDef = isUnDef;
exports.encode = encode;
exports.decode = decode;
exports.type = valueType;
exports.parse = de_parse;
exports.formatCacheKey = formatCacheKey;

function isDef(result) {
  return result !== undefined && result !== null;
}

function isUnDef(result) {
  return result === undefined || result === null;
}

function valueType(value) {
  return toString.call(value).replace(/\[object\s([^\]]+)\]/, '$1').toLowerCase();
}

function encode(value, cb) {
  const res = {};
  for (const i in value) {
    res[i] = en_parse(value[i]);
  }
  return res;
}

function decode(res) {
  const _res = {};
  for (const i in res){
    const value = res[i];
    const ret = de_format(value);
    if (ret) {
      _res[i] = de_parse(ret.type, ret.value);
    }
  }
  return _res;
}

function en_parse(value) {
  const type = valueType(value);
  switch (type) {
    case 'date': value = `${type}:${new Date(value).getTime()}`; break;
    case 'object':
    case 'array': value = `${type}:${JSON.stringify(value)}`; break;
    default: value = `${type}:${value}`;
  }
  return value;
}

function de_parse(type, _value) {
  _value = JSON.parse(_value);
  switch (type) {
    case 'number': value = Number(_value); break;
    case 'date': value = new Date(_value); break;
  }
  return _value;
}

function de_format(value) {
  const index = value.indexOf(':');
  if (~index) {
    const type = value.substring(0, index);
    const _value = value.substring(index + 1);
    return {
      type, value: _value
    }
  }
}

function formatCacheKey(name) {
  const items = name.split(':');
  const domain = items[0];
  return {
    domain: items[0],
    pathname: items.slice(1)
  }
}<commit_msg>update<commit_after>const toString = Object.prototype.toString;

exports.isDef = isDef;
exports.isUnDef = isUnDef;
exports.encode = encode;
exports.decode = decode;
exports.type = valueType;
exports.parse = de_parse;
exports.formatCacheKey = formatCacheKey;

function isDef(result) {
  return result !== undefined && result !== null;
}

function isUnDef(result) {
  return result === undefined || result === null;
}

function valueType(value) {
  return toString.call(value).replace(/\[object\s([^\]]+)\]/, '$1').toLowerCase();
}

function encode(value, cb) {
  const res = {};
  for (const i in value) {
    res[i] = en_parse(value[i]);
  }
  return res;
}

function decode(res) {
  const _res = {};
  for (const i in res){
    const value = res[i];
    const ret = de_format(value);
    if (ret) {
      _res[i] = de_parse(ret.type, ret.value);
    }
  }
  return _res;
}

function en_parse(value) {
  const type = valueType(value);
  switch (type) {
    case 'date': value = `${type}:${new Date(value).getTime()}`; break;
    case 'object':
    case 'array': value = `${type}:${JSON.stringify(value)}`; break;
    default: value = `${type}:${value}`;
  }
  return value;
}

function de_parse(type, _value) {
  _value = JSON.parse(_value);
  switch (type) {
    case 'number': _value = Number(_value); break;
    case 'date': _value = new Date(_value); break;
  }
  return _value;
}

function de_format(value) {
  const index = value.indexOf(':');
  if (~index) {
    const type = value.substring(0, index);
    const _value = value.substring(index + 1);
    return {
      type, value: _value
    }
  }
}

function formatCacheKey(name) {
  const items = name.split(':');
  const domain = items[0];
  return {
    domain: items[0],
    pathname: items.slice(1)
  }
}<|endoftext|>


<commit_before>/**
 * Creates a new meta data model instance.
 *
 * @param itemCount {number}
 *            the number of items that vectors in this instances will hold.
 * @implements {morpheus.MetadataModelInterface}
 * @constructor
 */
morpheus.MetadataModel = function (itemCount) {
  this.itemCount = itemCount;
  this.vectors = [];
};
morpheus.MetadataModel.prototype = {
  add: function (name, options) {
    var index = morpheus.MetadataUtil.indexOf(this, name);
    var oldVector;
    if (index !== -1) {
      oldVector = this.remove(index);
    }
    var v = new morpheus.Vector(name, this.getItemCount());
    if (oldVector != null) {
      // copy properties?
//			oldVector.getProperties().forEach(function(val, key) {
//				if (!morpheus.VectorKeys.COPY_IGNORE.has(key)) {
//					v.getProperties().set(key, val);
//				}
//
//			});
      // copy values
      for (var i = 0, size = oldVector.size(); i < size; i++) {
        var val = oldVector.getValue(i);
        v.setValue(i, val);
      }
    }
    this.vectors.push(v);
    return v;
  },
  getItemCount: function () {
    return this.itemCount;
  },
  get: function (index) {
    if (index < 0 || index >= this.vectors.length) {
      throw 'index ' + index + ' out of range';
    }
    return this.vectors[index];
  },
  remove: function (index) {
    if (index < 0 || index >= this.vectors.length) {
      throw 'index ' + index + ' out of range';
    }
    return this.vectors.splice(index, 1)[0];
  },
  getByName: function (name) {
    var index = morpheus.MetadataUtil.indexOf(this, name);
    return index !== -1 ? this.get(index) : undefined;
  },
  getMetadataCount: function () {
    return this.vectors.length;
  }
};
<commit_msg>keep vector at old index when replacing<commit_after>/**
 * Creates a new meta data model instance.
 *
 * @param itemCount {number}
 *            the number of items that vectors in this instances will hold.
 * @implements {morpheus.MetadataModelInterface}
 * @constructor
 */
morpheus.MetadataModel = function (itemCount) {
  this.itemCount = itemCount;
  this.vectors = [];
};
morpheus.MetadataModel.prototype = {
  add: function (name, options) {
    var index = morpheus.MetadataUtil.indexOf(this, name);
    var oldVector;
    if (index !== -1) {
      oldVector = this.get(index);
    }
    var v = new morpheus.Vector(name, this.getItemCount());
    if (oldVector != null) {
      // copy values
      for (var i = 0, size = oldVector.size(); i < size; i++) {
        var val = oldVector.getValue(i);
        v.setValue(i, val);
      }
    }
    if (index !== -1) {
      // replace old vector
      this.vectors.splice(index, 1, v);
    } else {
      this.vectors.push(v);
    }
    return v;
  },
  getItemCount: function () {
    return this.itemCount;
  },
  get: function (index) {
    return this.vectors[index];
  },
  remove: function (index) {
    return this.vectors.splice(index, 1)[0];
  },
  getByName: function (name) {
    var index = morpheus.MetadataUtil.indexOf(this, name);
    return index !== -1 ? this.get(index) : undefined;
  },
  getMetadataCount: function () {
    return this.vectors.length;
  }
};
<|endoftext|>


<commit_before>
// exports
fc.intersectionToSeg = intersectionToSeg;
fc.applyAll = applyAll;
fc.debounce = debounce;

/* FullCalendar-specific DOM Utilities
----------------------------------------------------------------------------------------------------------------------*/

// Given the scrollbar widths of some other container, create borders/margins on rowEls in order to match the left
// and right space that was offset by the scrollbars. A 1-pixel border first, then margin beyond that.
function compensateScroll(rowEls, scrollbarWidths) {
	if (scrollbarWidths.left) {
		rowEls.css({
			'border-left-width': 1,
			'margin-left': scrollbarWidths.left - 1
		});
	}
	if (scrollbarWidths.right) {
		rowEls.css({
			'border-right-width': 1,
			'margin-right': scrollbarWidths.right - 1
		});
	}
}

// Undoes compensateScroll and restores all borders/margins
function uncompensateScroll(rowEls) {
	rowEls.css({
		'margin-left': '',
		'margin-right': '',
		'border-left-width': '',
		'border-right-width': ''
	});
}

// Make the mouse cursor express that an event is not allowed in the current area
function disableCursor() {
	$('body').addClass('fc-not-allowed');
}

// Returns the mouse cursor to its original look
function enableCursor() {
	$('body').removeClass('fc-not-allowed');
}

// Given a total available height to fill, have `els` (essentially child rows) expand to accomodate.
// By default, all elements that are shorter than the recommended height are expanded uniformly, not considering
// any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and
// reduces the available height.
function distributeHeight(els, availableHeight, shouldRedistribute) {

	// *FLOORING NOTE*: we floor in certain places because zoom can give inaccurate floating-point dimensions,
	// and it is better to be shorter than taller, to avoid creating unnecessary scrollbars.

	var minOffset1 = Math.floor(availableHeight / els.length); // for non-last element
	var minOffset2 = Math.floor(availableHeight - minOffset1 * (els.length - 1)); // for last element *FLOORING NOTE*
	var flexEls = []; // elements that are allowed to expand. array of DOM nodes
	var flexOffsets = []; // amount of vertical space it takes up
	var flexHeights = []; // actual css height
	var usedHeight = 0;

	undistributeHeight(els); // give all elements their natural height

	// find elements that are below the recommended height (expandable).
	// important to query for heights in a single first pass (to avoid reflow oscillation).
	els.each(function(i, el) {
		var minOffset = i === els.length - 1 ? minOffset2 : minOffset1;
		var naturalOffset = $(el).outerHeight(true);

		if (naturalOffset < minOffset) {
			flexEls.push(el);
			flexOffsets.push(naturalOffset);
			flexHeights.push($(el).height());
		}
		else {
			// this element stretches past recommended height (non-expandable). mark the space as occupied.
			usedHeight += naturalOffset;
		}
	});

	// readjust the recommended height to only consider the height available to non-maxed-out rows.
	if (shouldRedistribute) {
		availableHeight -= usedHeight;
		minOffset1 = Math.floor(availableHeight / flexEls.length);
		minOffset2 = Math.floor(availableHeight - minOffset1 * (flexEls.length - 1)); // *FLOORING NOTE*
	}

	// assign heights to all expandable elements
	$(flexEls).each(function(i, el) {
		var minOffset = i === flexEls.length - 1 ? minOffset2 : minOffset1;
		var naturalOffset = flexOffsets[i];
		var naturalHeight = flexHeights[i];
		var newHeight = minOffset - (naturalOffset - naturalHeight); // subtract the margin/padding

		if (naturalOffset < minOffset) { // we check this again because redistribution might have changed things
			$(el).height(newHeight);
		}
	});
}

// Undoes distrubuteHeight, restoring all els to their natural height
function undistributeHeight(els) {
	els.height('');
}

// Given `els`, a jQuery set of <td> cells, find the cell with the largest natural width and set the widths of all the
// cells to be that width.
// PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline
function matchCellWidths(els) {
	var maxInnerWidth = 0;

	els.find('> *').each(function(i, innerEl) {
		var innerWidth = $(innerEl).outerWidth();
		if (innerWidth > maxInnerWidth) {
			maxInnerWidth = innerWidth;
		}
	});

	maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance

	els.width(maxInnerWidth);

	return maxInnerWidth;
}

// Turns a container element into a scroller if its contents is taller than the allotted height.
// Returns true if the element is now a scroller, false otherwise.
// NOTE: this method is best because it takes weird zooming dimensions into account
function setPotentialScroller(containerEl, height) {
	containerEl.height(height).addClass('fc-scroller');

	// are scrollbars needed?
	if (containerEl[0].scrollHeight - 1 > containerEl[0].clientHeight) { // !!! -1 because IE is often off-by-one :(
		return true;
	}

	unsetScroller(containerEl); // undo
	return false;
}

// Takes an element that might have been a scroller, and turns it back into a normal element.
function unsetScroller(containerEl) {
	containerEl.height('').removeClass('fc-scroller');
}

/* General DOM Utilities
----------------------------------------------------------------------------------------------------------------------*/

// borrowed from https://github.com/jquery/jquery-ui/blob/1.11.0/ui/core.js#L51
function getScrollParent(el) {
	var position = el.css('position'),
		scrollParent = el.parents().filter(function() {
			var parent = $(this);
			return (/(auto|scroll)/).test(
				parent.css('overflow') + parent.css('overflow-y') + parent.css('overflow-x')
			);
		}).eq(0);

	return position === 'fixed' || !scrollParent.length ? $(el[0].ownerDocument || document) : scrollParent;
}

// Given a container element, return an object with the pixel values of the left/right scrollbars.
// Left scrollbars might occur on RTL browsers (IE maybe?) but I have not tested.
// PREREQUISITE: container element must have a single child with display:block
function getScrollbarWidths(container) {
	var containerLeft = container.offset().left;
	var containerRight = containerLeft + container.width();
	var inner = container.children();
	var innerLeft = inner.offset().left;
	var innerRight = innerLeft + inner.outerWidth();

	return {
		left: innerLeft - containerLeft,
		right: containerRight - innerRight
	};
}

// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
function isPrimaryMouseButton(ev) {
	return ev.which == 1 && !ev.ctrlKey;
}

/* FullCalendar-specific Misc Utilities
----------------------------------------------------------------------------------------------------------------------*/

// Creates a basic segment with the intersection of the two ranges. Returns undefined if no intersection.
// Expects all dates to be normalized to the same timezone beforehand.
// TODO: move to date section?
function intersectionToSeg(subjectRange, constraintRange) {
	var subjectStart = subjectRange.start;
	var subjectEnd = subjectRange.end;
	var constraintStart = constraintRange.start;
	var constraintEnd = constraintRange.end;
	var segStart, segEnd;
	var isStart, isEnd;

	if (subjectEnd > constraintStart && subjectStart < constraintEnd) { // in bounds at all?

		if (subjectStart >= constraintStart) {
			segStart = subjectStart.clone();
			isStart = true;
		}
		else {
			segStart = constraintStart.clone();
			isStart =  false;
		}

		if (subjectEnd <= constraintEnd) {
			segEnd = subjectEnd.clone();
			isEnd = true;
		}
		else {
			segEnd = constraintEnd.clone();
			isEnd = false;
		}

		return {
			start: segStart,
			end: segEnd,
			isStart: isStart,
			isEnd: isEnd
		};
	}
}

/* Date Utilities
----------------------------------------------------------------------------------------------------------------------*/

var dayIDs = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ];
var intervalUnits = [ 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond' ];

// Diffs the two moments into a Duration where full-days are recorded first, then the remaining time.
// Moments will have their timezones normalized.
function diffDayTime(a, b) {
	return moment.duration({
		days: a.clone().stripTime().diff(b.clone().stripTime(), 'days'),
		ms: a.time() - b.time() // time-of-day from day start. disregards timezone
	});
}

// Diffs the two moments via their start-of-day (regardless of timezone). Produces whole-day durations.
function diffDay(a, b) {
	return moment.duration({
		days: a.clone().stripTime().diff(b.clone().stripTime(), 'days')
	});
}

// Diffs two moments, producing a duration, made of a whole-unit-increment of the given unit. Uses rounding.
function diffByUnit(a, b, unit) {
	return moment.duration(
		Math.round(a.diff(b, unit, true)), // returnFloat=true
		unit
	);
}

// Computes the unit name of the largest whole-unit period of time.
// For example, 48 hours will be "days" whereas 49 hours will be "hours".
// Accepts start/end, a range object, or an original duration object.
function computeIntervalUnit(start, end) {
	var i, unit;
	var val;

	for (i = 0; i < intervalUnits.length; i++) {
		unit = intervalUnits[i];
		val = computeRangeAs(unit, start, end);

		if (val >= 1 && isInt(val)) {
			break;
		}
	}

	return unit; // will be "milliseconds" if nothing else matches
}

// Computes the number of units (like "hours") in the given range.
// Range can be a {start,end} object, separate start/end args, or a Duration.
// Results are based on Moment's .as() and .diff() methods, so results can depend on internal handling
// of month-diffing logic (which tends to vary from version to version).
function computeRangeAs(unit, start, end) {

	if (end != null) { // given start, end
		return end.diff(start, unit, true);
	}
	else if (moment.isDuration(start)) { // given duration
		return start.as(unit);
	}
	else { // given { start, end } range object
		return start.end.diff(start.start, unit, true);
	}
}

function isNativeDate(input) {
	return  Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date;
}

// Returns a boolean about whether the given input is a time string, like "06:40:00" or "06:00"
function isTimeString(str) {
	return /^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(str);
}

/* General Utilities
----------------------------------------------------------------------------------------------------------------------*/

var hasOwnPropMethod = {}.hasOwnProperty;

// Create an object that has the given prototype. Just like Object.create
function createObject(proto) {
	var f = function() {};
	f.prototype = proto;
	return new f();
}

function copyOwnProps(src, dest) {
	for (var name in src) {
		if (hasOwnProp(src, name)) {
			dest[name] = src[name];
		}
	}
}

// Copies over certain methods with the same names as Object.prototype methods. Overcomes an IE<=8 bug:
// https://developer.mozilla.org/en-US/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
function copyNativeMethods(src, dest) {
	var names = [ 'constructor', 'toString', 'valueOf' ];
	var i, name;

	for (i = 0; i < names.length; i++) {
		name = names[i];

		if (src[name] !== Object.prototype[name]) {
			dest[name] = src[name];
		}
	}
}

function hasOwnProp(obj, name) {
	return hasOwnPropMethod.call(obj, name);
}

// Is the given value a non-object non-function value?
function isAtomic(val) {
	return /undefined|null|boolean|number|string/.test($.type(val));
}

function applyAll(functions, thisObj, args) {
	if ($.isFunction(functions)) {
		functions = [ functions ];
	}
	if (functions) {
		var i;
		var ret;
		for (i=0; i<functions.length; i++) {
			ret = functions[i].apply(thisObj, args) || ret;
		}
		return ret;
	}
}

function firstDefined() {
	for (var i=0; i<arguments.length; i++) {
		if (arguments[i] !== undefined) {
			return arguments[i];
		}
	}
}

function htmlEscape(s) {
	return (s + '').replace(/&/g, '&')
		.replace(/</g, '<')
		.replace(/>/g, '>')
		.replace(/'/g, ''')
		.replace(/"/g, '"')
		.replace(/\n/g, '<br />');
}

function stripHtmlEntities(text) {
	return text.replace(/&.*?;/g, '');
}

function capitaliseFirstLetter(str) {
	return str.charAt(0).toUpperCase() + str.slice(1);
}

function compareNumbers(a, b) { // for .sort()
	return a - b;
}

function isInt(n) {
	return n % 1 === 0;
}

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
// https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714
function debounce(func, wait) {
	var timeoutId;
	var args;
	var context;
	var timestamp; // of most recent call
	var later = function() {
		var last = +new Date() - timestamp;
		if (last < wait && last > 0) {
			timeoutId = setTimeout(later, wait - last);
		}
		else {
			timeoutId = null;
			func.apply(context, args);
			if (!timeoutId) {
				context = args = null;
			}
		}
	};

	return function() {
		context = this;
		args = arguments;
		timestamp = +new Date();
		if (!timeoutId) {
			timeoutId = setTimeout(later, wait);
		}
	};
}
<commit_msg>geometry utilities<commit_after>
// exports
fc.intersectionToSeg = intersectionToSeg;
fc.applyAll = applyAll;
fc.debounce = debounce;

/* FullCalendar-specific DOM Utilities
----------------------------------------------------------------------------------------------------------------------*/

// Given the scrollbar widths of some other container, create borders/margins on rowEls in order to match the left
// and right space that was offset by the scrollbars. A 1-pixel border first, then margin beyond that.
function compensateScroll(rowEls, scrollbarWidths) {
	if (scrollbarWidths.left) {
		rowEls.css({
			'border-left-width': 1,
			'margin-left': scrollbarWidths.left - 1
		});
	}
	if (scrollbarWidths.right) {
		rowEls.css({
			'border-right-width': 1,
			'margin-right': scrollbarWidths.right - 1
		});
	}
}

// Undoes compensateScroll and restores all borders/margins
function uncompensateScroll(rowEls) {
	rowEls.css({
		'margin-left': '',
		'margin-right': '',
		'border-left-width': '',
		'border-right-width': ''
	});
}

// Make the mouse cursor express that an event is not allowed in the current area
function disableCursor() {
	$('body').addClass('fc-not-allowed');
}

// Returns the mouse cursor to its original look
function enableCursor() {
	$('body').removeClass('fc-not-allowed');
}

// Given a total available height to fill, have `els` (essentially child rows) expand to accomodate.
// By default, all elements that are shorter than the recommended height are expanded uniformly, not considering
// any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and
// reduces the available height.
function distributeHeight(els, availableHeight, shouldRedistribute) {

	// *FLOORING NOTE*: we floor in certain places because zoom can give inaccurate floating-point dimensions,
	// and it is better to be shorter than taller, to avoid creating unnecessary scrollbars.

	var minOffset1 = Math.floor(availableHeight / els.length); // for non-last element
	var minOffset2 = Math.floor(availableHeight - minOffset1 * (els.length - 1)); // for last element *FLOORING NOTE*
	var flexEls = []; // elements that are allowed to expand. array of DOM nodes
	var flexOffsets = []; // amount of vertical space it takes up
	var flexHeights = []; // actual css height
	var usedHeight = 0;

	undistributeHeight(els); // give all elements their natural height

	// find elements that are below the recommended height (expandable).
	// important to query for heights in a single first pass (to avoid reflow oscillation).
	els.each(function(i, el) {
		var minOffset = i === els.length - 1 ? minOffset2 : minOffset1;
		var naturalOffset = $(el).outerHeight(true);

		if (naturalOffset < minOffset) {
			flexEls.push(el);
			flexOffsets.push(naturalOffset);
			flexHeights.push($(el).height());
		}
		else {
			// this element stretches past recommended height (non-expandable). mark the space as occupied.
			usedHeight += naturalOffset;
		}
	});

	// readjust the recommended height to only consider the height available to non-maxed-out rows.
	if (shouldRedistribute) {
		availableHeight -= usedHeight;
		minOffset1 = Math.floor(availableHeight / flexEls.length);
		minOffset2 = Math.floor(availableHeight - minOffset1 * (flexEls.length - 1)); // *FLOORING NOTE*
	}

	// assign heights to all expandable elements
	$(flexEls).each(function(i, el) {
		var minOffset = i === flexEls.length - 1 ? minOffset2 : minOffset1;
		var naturalOffset = flexOffsets[i];
		var naturalHeight = flexHeights[i];
		var newHeight = minOffset - (naturalOffset - naturalHeight); // subtract the margin/padding

		if (naturalOffset < minOffset) { // we check this again because redistribution might have changed things
			$(el).height(newHeight);
		}
	});
}

// Undoes distrubuteHeight, restoring all els to their natural height
function undistributeHeight(els) {
	els.height('');
}

// Given `els`, a jQuery set of <td> cells, find the cell with the largest natural width and set the widths of all the
// cells to be that width.
// PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline
function matchCellWidths(els) {
	var maxInnerWidth = 0;

	els.find('> *').each(function(i, innerEl) {
		var innerWidth = $(innerEl).outerWidth();
		if (innerWidth > maxInnerWidth) {
			maxInnerWidth = innerWidth;
		}
	});

	maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance

	els.width(maxInnerWidth);

	return maxInnerWidth;
}

// Turns a container element into a scroller if its contents is taller than the allotted height.
// Returns true if the element is now a scroller, false otherwise.
// NOTE: this method is best because it takes weird zooming dimensions into account
function setPotentialScroller(containerEl, height) {
	containerEl.height(height).addClass('fc-scroller');

	// are scrollbars needed?
	if (containerEl[0].scrollHeight - 1 > containerEl[0].clientHeight) { // !!! -1 because IE is often off-by-one :(
		return true;
	}

	unsetScroller(containerEl); // undo
	return false;
}

// Takes an element that might have been a scroller, and turns it back into a normal element.
function unsetScroller(containerEl) {
	containerEl.height('').removeClass('fc-scroller');
}

/* General DOM Utilities
----------------------------------------------------------------------------------------------------------------------*/

// borrowed from https://github.com/jquery/jquery-ui/blob/1.11.0/ui/core.js#L51
function getScrollParent(el) {
	var position = el.css('position'),
		scrollParent = el.parents().filter(function() {
			var parent = $(this);
			return (/(auto|scroll)/).test(
				parent.css('overflow') + parent.css('overflow-y') + parent.css('overflow-x')
			);
		}).eq(0);

	return position === 'fixed' || !scrollParent.length ? $(el[0].ownerDocument || document) : scrollParent;
}

// Given a container element, return an object with the pixel values of the left/right scrollbars.
// Left scrollbars might occur on RTL browsers (IE maybe?) but I have not tested.
// PREREQUISITE: container element must have a single child with display:block
function getScrollbarWidths(container) {
	var containerLeft = container.offset().left;
	var containerRight = containerLeft + container.width();
	var inner = container.children();
	var innerLeft = inner.offset().left;
	var innerRight = innerLeft + inner.outerWidth();

	return {
		left: innerLeft - containerLeft,
		right: containerRight - innerRight
	};
}

// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
function isPrimaryMouseButton(ev) {
	return ev.which == 1 && !ev.ctrlKey;
}

/* Geometry
----------------------------------------------------------------------------------------------------------------------*/

// Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
function intersectRects(rect1, rect2) {
	var res = {
		left: Math.max(rect1.left, rect2.left),
		right: Math.min(rect1.right, rect2.right),
		top: Math.max(rect1.top, rect2.top),
		bottom: Math.min(rect1.bottom, rect2.bottom)
	};

	if (res.left < res.right && res.top < res.bottom) {
		return res;
	}
	return false;
}

// Returns a new point that will have been moved to reside within the given rectangle
function constrainPoint(point, rect) {
	return {
		left: Math.min(Math.max(point.left, rect.left), rect.right),
		top: Math.min(Math.max(point.top, rect.top), rect.bottom)
	};
}

// Returns a point that is the center of the given rectangle
function getRectCenter(rect) {
	return {
		left: (rect.left + rect.right) / 2,
		top: (rect.top + rect.bottom) / 2
	};
}

// Subtracts point2's coordinates from point1's coordinates, returning a delta
function diffPoints(point1, point2) {
	return {
		left: point1.left - point2.left,
		top: point1.top - point2.top
	};
}

/* FullCalendar-specific Misc Utilities
----------------------------------------------------------------------------------------------------------------------*/

// Creates a basic segment with the intersection of the two ranges. Returns undefined if no intersection.
// Expects all dates to be normalized to the same timezone beforehand.
// TODO: move to date section?
function intersectionToSeg(subjectRange, constraintRange) {
	var subjectStart = subjectRange.start;
	var subjectEnd = subjectRange.end;
	var constraintStart = constraintRange.start;
	var constraintEnd = constraintRange.end;
	var segStart, segEnd;
	var isStart, isEnd;

	if (subjectEnd > constraintStart && subjectStart < constraintEnd) { // in bounds at all?

		if (subjectStart >= constraintStart) {
			segStart = subjectStart.clone();
			isStart = true;
		}
		else {
			segStart = constraintStart.clone();
			isStart =  false;
		}

		if (subjectEnd <= constraintEnd) {
			segEnd = subjectEnd.clone();
			isEnd = true;
		}
		else {
			segEnd = constraintEnd.clone();
			isEnd = false;
		}

		return {
			start: segStart,
			end: segEnd,
			isStart: isStart,
			isEnd: isEnd
		};
	}
}

/* Date Utilities
----------------------------------------------------------------------------------------------------------------------*/

var dayIDs = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ];
var intervalUnits = [ 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond' ];

// Diffs the two moments into a Duration where full-days are recorded first, then the remaining time.
// Moments will have their timezones normalized.
function diffDayTime(a, b) {
	return moment.duration({
		days: a.clone().stripTime().diff(b.clone().stripTime(), 'days'),
		ms: a.time() - b.time() // time-of-day from day start. disregards timezone
	});
}

// Diffs the two moments via their start-of-day (regardless of timezone). Produces whole-day durations.
function diffDay(a, b) {
	return moment.duration({
		days: a.clone().stripTime().diff(b.clone().stripTime(), 'days')
	});
}

// Diffs two moments, producing a duration, made of a whole-unit-increment of the given unit. Uses rounding.
function diffByUnit(a, b, unit) {
	return moment.duration(
		Math.round(a.diff(b, unit, true)), // returnFloat=true
		unit
	);
}

// Computes the unit name of the largest whole-unit period of time.
// For example, 48 hours will be "days" whereas 49 hours will be "hours".
// Accepts start/end, a range object, or an original duration object.
function computeIntervalUnit(start, end) {
	var i, unit;
	var val;

	for (i = 0; i < intervalUnits.length; i++) {
		unit = intervalUnits[i];
		val = computeRangeAs(unit, start, end);

		if (val >= 1 && isInt(val)) {
			break;
		}
	}

	return unit; // will be "milliseconds" if nothing else matches
}

// Computes the number of units (like "hours") in the given range.
// Range can be a {start,end} object, separate start/end args, or a Duration.
// Results are based on Moment's .as() and .diff() methods, so results can depend on internal handling
// of month-diffing logic (which tends to vary from version to version).
function computeRangeAs(unit, start, end) {

	if (end != null) { // given start, end
		return end.diff(start, unit, true);
	}
	else if (moment.isDuration(start)) { // given duration
		return start.as(unit);
	}
	else { // given { start, end } range object
		return start.end.diff(start.start, unit, true);
	}
}

function isNativeDate(input) {
	return  Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date;
}

// Returns a boolean about whether the given input is a time string, like "06:40:00" or "06:00"
function isTimeString(str) {
	return /^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(str);
}

/* General Utilities
----------------------------------------------------------------------------------------------------------------------*/

var hasOwnPropMethod = {}.hasOwnProperty;

// Create an object that has the given prototype. Just like Object.create
function createObject(proto) {
	var f = function() {};
	f.prototype = proto;
	return new f();
}

function copyOwnProps(src, dest) {
	for (var name in src) {
		if (hasOwnProp(src, name)) {
			dest[name] = src[name];
		}
	}
}

// Copies over certain methods with the same names as Object.prototype methods. Overcomes an IE<=8 bug:
// https://developer.mozilla.org/en-US/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
function copyNativeMethods(src, dest) {
	var names = [ 'constructor', 'toString', 'valueOf' ];
	var i, name;

	for (i = 0; i < names.length; i++) {
		name = names[i];

		if (src[name] !== Object.prototype[name]) {
			dest[name] = src[name];
		}
	}
}

function hasOwnProp(obj, name) {
	return hasOwnPropMethod.call(obj, name);
}

// Is the given value a non-object non-function value?
function isAtomic(val) {
	return /undefined|null|boolean|number|string/.test($.type(val));
}

function applyAll(functions, thisObj, args) {
	if ($.isFunction(functions)) {
		functions = [ functions ];
	}
	if (functions) {
		var i;
		var ret;
		for (i=0; i<functions.length; i++) {
			ret = functions[i].apply(thisObj, args) || ret;
		}
		return ret;
	}
}

function firstDefined() {
	for (var i=0; i<arguments.length; i++) {
		if (arguments[i] !== undefined) {
			return arguments[i];
		}
	}
}

function htmlEscape(s) {
	return (s + '').replace(/&/g, '&')
		.replace(/</g, '<')
		.replace(/>/g, '>')
		.replace(/'/g, ''')
		.replace(/"/g, '"')
		.replace(/\n/g, '<br />');
}

function stripHtmlEntities(text) {
	return text.replace(/&.*?;/g, '');
}

function capitaliseFirstLetter(str) {
	return str.charAt(0).toUpperCase() + str.slice(1);
}

function compareNumbers(a, b) { // for .sort()
	return a - b;
}

function isInt(n) {
	return n % 1 === 0;
}

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
// https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714
function debounce(func, wait) {
	var timeoutId;
	var args;
	var context;
	var timestamp; // of most recent call
	var later = function() {
		var last = +new Date() - timestamp;
		if (last < wait && last > 0) {
			timeoutId = setTimeout(later, wait - last);
		}
		else {
			timeoutId = null;
			func.apply(context, args);
			if (!timeoutId) {
				context = args = null;
			}
		}
	};

	return function() {
		context = this;
		args = arguments;
		timestamp = +new Date();
		if (!timeoutId) {
			timeoutId = setTimeout(later, wait);
		}
	};
}
<|endoftext|>