Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
35
gaia/apps/calendar/js/utils/animated_scroll.js
Executable file
35
gaia/apps/calendar/js/utils/animated_scroll.js
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
|
||||
module.exports = function animatedScroll(opts) {
|
||||
var {container, content, scrollTop} = opts;
|
||||
|
||||
var maxScroll = container.scrollHeight - container.clientHeight;
|
||||
// safeguard against negative or `null` scrollTop (0 < scrollTop < maxScroll)
|
||||
scrollTop = Math.min(Math.max(scrollTop, 0), maxScroll);
|
||||
|
||||
var diff = container.scrollTop - scrollTop;
|
||||
var seconds = Math.abs(diff) / 500;
|
||||
|
||||
// hide scrollbar during animation to avoid errors introduced by user trying
|
||||
// to scroll while transition is still running; it also avoids weird
|
||||
// sensation of content moving while scrollbar is still at same position
|
||||
container.style.overflowY = 'hidden';
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
content.style.transform = 'translateY(' + diff + 'px)';
|
||||
// easeOutQuart borrowed from http://matthewlein.com/ceaser/
|
||||
content.style.transition = 'transform ' + seconds + 's ' +
|
||||
'cubic-bezier(0.165, 0.840, 0.440, 1.000)';
|
||||
});
|
||||
|
||||
content.addEventListener('transitionend', function setScrollTop() {
|
||||
content.removeEventListener('transitionend', setScrollTop);
|
||||
content.style.transform = '';
|
||||
content.style.transition = '';
|
||||
container.scrollTop = scrollTop;
|
||||
container.style.overflowY = 'scroll';
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
29
gaia/apps/calendar/js/utils/color.js
Executable file
29
gaia/apps/calendar/js/utils/color.js
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
define(function(require, exports) {
|
||||
'use strict';
|
||||
|
||||
// right now the calendar app only displays 8 different colors so it's a good
|
||||
// idea to memoize the results of hexToBackground to avoid calculating it for
|
||||
// each busytime
|
||||
var memoized = {};
|
||||
|
||||
exports.hexToBackground = function(hex) {
|
||||
if (!(hex in memoized)) {
|
||||
// we need 20% opacity for background; it's simpler to use rgba than to
|
||||
// create a new layer and set opacity:20%
|
||||
var {r, g, b} = hexToChannels(hex);
|
||||
memoized[hex] = `rgba(${r}, ${g}, ${b}, 0.2)`;
|
||||
}
|
||||
|
||||
return memoized[hex];
|
||||
};
|
||||
|
||||
function hexToChannels(hex) {
|
||||
var val = parseInt(hex.replace(/#/, ''), 16);
|
||||
return {
|
||||
r: val >> 16,
|
||||
g: val >> 8 & 0xFF,
|
||||
b: val & 0xFF
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
61
gaia/apps/calendar/js/utils/dom.js
Executable file
61
gaia/apps/calendar/js/utils/dom.js
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
define(function(require, exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Gets the element absolute offset top relative to the window top.
|
||||
*/
|
||||
exports.absoluteOffsetTop = function(el) {
|
||||
var top = 0;
|
||||
while (el) {
|
||||
var pos = window.getComputedStyle(el).position;
|
||||
if (pos === 'absolute' || pos === 'relative') {
|
||||
top += el.offsetTop;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return top;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the closest (parent) element that matches the given selector.
|
||||
*/
|
||||
exports.closest = function(el, selector) {
|
||||
while (el) {
|
||||
if (matches(el, selector)) {
|
||||
return el;
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
};
|
||||
|
||||
function matches(el, selector) {
|
||||
// "matches" is only unprefixed on Fx 34
|
||||
return 'matches' in el ?
|
||||
el.matches(selector) :
|
||||
el.mozMatchesSelector(selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* converts HTML string into a DOM element
|
||||
*/
|
||||
exports.buildElement = function(html) {
|
||||
var fragment = document.createElement('div');
|
||||
fragment.innerHTML = html;
|
||||
return fragment.firstChild;
|
||||
};
|
||||
|
||||
/**
|
||||
* detach elements from DOM
|
||||
* supports single element or array-like objects
|
||||
*/
|
||||
exports.removeElements = function(els) {
|
||||
if (!els) {
|
||||
return;
|
||||
}
|
||||
els = 'length' in els ? Array.from(els) : Array.from(arguments);
|
||||
els.forEach(el => {
|
||||
el && el.parentNode && el.parentNode.removeChild(el);
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
93
gaia/apps/calendar/js/utils/mout.js
Executable file
93
gaia/apps/calendar/js/utils/mout.js
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
// these methods are all borrowed from MOUT.js (released under MIT license)
|
||||
define(function(require, exports) {
|
||||
'use strict';
|
||||
|
||||
// TODO: add MOUT.js as a dependency!!!! this is just a temporary solution
|
||||
|
||||
exports.norm = function norm(val, min, max){
|
||||
// 0 / 0 === NaN
|
||||
if (val === min && min === max) {
|
||||
return 1;
|
||||
}
|
||||
return (val - min) / (max - min);
|
||||
};
|
||||
|
||||
exports.clamp = function clamp(val, min, max) {
|
||||
return val < min ? min : (val > max ? max : val);
|
||||
};
|
||||
|
||||
exports.round = function round(value, radix){
|
||||
radix = radix || 1; // default round 1
|
||||
return Math.round(value / radix) * radix;
|
||||
};
|
||||
|
||||
exports.floor = function floor(val, step) {
|
||||
step = Math.abs(step || 1);
|
||||
return Math.floor(val / step) * step;
|
||||
};
|
||||
|
||||
exports.ceil = function ceil(val, step) {
|
||||
step = Math.abs(step || 1);
|
||||
return Math.ceil(val / step) * step;
|
||||
};
|
||||
|
||||
exports.lerp = function lerp(ratio, start, end){
|
||||
return start + (end - start) * ratio;
|
||||
};
|
||||
|
||||
exports.debounce = function debounce(fn, threshold, isAsap){
|
||||
var timeout, result;
|
||||
function debounced(){
|
||||
//jshint -W040
|
||||
var args = arguments, context = this;
|
||||
//jshint +W040
|
||||
function delayed(){
|
||||
if (! isAsap) {
|
||||
result = fn.apply(context, args);
|
||||
}
|
||||
timeout = null;
|
||||
}
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
} else if (isAsap) {
|
||||
result = fn.apply(context, args);
|
||||
}
|
||||
timeout = setTimeout(delayed, threshold);
|
||||
return result;
|
||||
}
|
||||
debounced.cancel = function(){
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
return debounced;
|
||||
};
|
||||
|
||||
exports.throttle = function throttle(fn, delay) {
|
||||
var context, timeout, result, args,
|
||||
diff,
|
||||
prevCall = 0;
|
||||
function delayed() {
|
||||
prevCall = Date.now();
|
||||
timeout = null;
|
||||
result = fn.apply(context, args);
|
||||
}
|
||||
function throttled() {
|
||||
//jshint -W040
|
||||
context = this;
|
||||
//jshint +W040
|
||||
args = arguments;
|
||||
diff = delay - (Date.now() - prevCall);
|
||||
if (diff <= 0) {
|
||||
clearTimeout(timeout);
|
||||
delayed();
|
||||
} else if (!timeout) {
|
||||
timeout = setTimeout(delayed, diff);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
throttled.cancel = function() {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
return throttled;
|
||||
};
|
||||
|
||||
});
|
||||
135
gaia/apps/calendar/js/utils/overlap.js
Executable file
135
gaia/apps/calendar/js/utils/overlap.js
Executable file
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Conflict manager
|
||||
*/
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
var ConflictSpan = require('conflict_span');
|
||||
var IntervalTree = require('interval_tree');
|
||||
var Timespan = require('common/timespan');
|
||||
|
||||
function Overlap() {
|
||||
this.reset();
|
||||
}
|
||||
module.exports = Overlap;
|
||||
|
||||
Overlap.prototype = {
|
||||
reset: function() {
|
||||
this.tree = new IntervalTree();
|
||||
this.conflicts = {};
|
||||
this.elements = {};
|
||||
},
|
||||
|
||||
add: function(myBusytime, element) {
|
||||
this.tree.add(myBusytime);
|
||||
this.elements[myBusytime._id] = element;
|
||||
|
||||
// Check for conflicts, bail if none
|
||||
var related = this._findRelated(myBusytime);
|
||||
if (0 === related.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var myID = myBusytime._id;
|
||||
var myCS = this.conflicts[myID];
|
||||
|
||||
var self = this;
|
||||
related.forEach(function(otherBusytime) {
|
||||
// Get the other's ID, skip the current
|
||||
var otherID = otherBusytime._id;
|
||||
if (otherID === myID) {
|
||||
return;
|
||||
}
|
||||
|
||||
var otherCS = self.conflicts[otherID];
|
||||
if (!myCS && !otherCS) {
|
||||
// This is a brand new conflict.
|
||||
myCS = new ConflictSpan(self);
|
||||
myCS.add(myBusytime).add(otherBusytime);
|
||||
} else if (myCS && !otherCS) {
|
||||
// Other time can join this one's existing span
|
||||
myCS.add(otherBusytime);
|
||||
} else if (!myCS && otherCS) {
|
||||
// This time can join the other's existing span
|
||||
myCS = otherCS.add(myBusytime);
|
||||
} else if (myCS && otherCS && myCS != otherCS) {
|
||||
// Both already in different spans, so absorb other into this
|
||||
myCS.absorb(otherCS);
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a busytime from the collection.
|
||||
* Unlike other methods you must pass a real
|
||||
* busytime object.
|
||||
*
|
||||
* @param {Object} busytime full busytime object.
|
||||
*/
|
||||
remove: function(busytime) {
|
||||
this._clearLayout(busytime);
|
||||
this.tree.remove(busytime);
|
||||
delete this.elements[busytime._id];
|
||||
var myID = busytime._id;
|
||||
var myCS = this.conflicts[myID];
|
||||
if (myCS) {
|
||||
myCS.remove(busytime);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the ConflictSpan associated with this busytime, if any.
|
||||
*
|
||||
* @param {Object|String} busytime id or busytime object.
|
||||
* @return {Object} associated ConflictSpan, if any.
|
||||
*/
|
||||
getConflictSpan: function(busytime) {
|
||||
var id = this._busytimeId(busytime);
|
||||
return this.conflicts[id];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object|String} busytime id or busytime object.
|
||||
* @return {HTMLElement} associated dom element.
|
||||
*/
|
||||
getElement: function(busytime) {
|
||||
var id = this._busytimeId(busytime);
|
||||
return this.elements[id];
|
||||
},
|
||||
|
||||
/** private */
|
||||
|
||||
_busytimeId: function(busytime) {
|
||||
return (typeof(busytime) === 'string') ? busytime : busytime._id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Search tree for busytimes that overlap with the given.
|
||||
*/
|
||||
_findRelated: function(busytime) {
|
||||
//XXX: this is bad encapsulation but
|
||||
// we generate these when we insert
|
||||
// the points in the tree.
|
||||
var span = new Timespan(busytime._startDateMS, busytime._endDateMS);
|
||||
return this.tree.query(span);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the layout from a busytime element, presumably because it has just
|
||||
* been removed from conflict.
|
||||
*
|
||||
* @param {Object} busytime full busytime object.
|
||||
*/
|
||||
_clearLayout: function(busytime) {
|
||||
var el = this.elements[busytime._id];
|
||||
el.style.width = '';
|
||||
el.style.left = '';
|
||||
el.classList.remove('has-overlaps', 'many-overlaps');
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
58
gaia/apps/calendar/js/utils/uri.js
Executable file
58
gaia/apps/calendar/js/utils/uri.js
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
define(function(require, exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Get the port of the url.
|
||||
* @param {string} url full url.
|
||||
* @return {?number} port number, null if none found.
|
||||
*/
|
||||
exports.getPort = function(url) {
|
||||
var parts = url.split(':');
|
||||
if (parts.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we found a port and path, it's the last part
|
||||
var candidate = parts[parts.length - 1];
|
||||
parts = candidate.split('/');
|
||||
|
||||
// If we found a port, it's the first part
|
||||
candidate = parts[0];
|
||||
if (!isInteger(candidate)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parseInt(candidate, 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the scheme of the url. Note that this only detects http and https.
|
||||
* @param {string} url full url.
|
||||
* @return {?string} uri scheme (ie http), null if none found.
|
||||
*/
|
||||
exports.getScheme = function(url) {
|
||||
var parts = url.split(':');
|
||||
if (parts.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// If we found a scheme, it's the first part
|
||||
var candidate = parts[0];
|
||||
if (candidate !== 'http' && candidate !== 'https') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decide whether or not this string represents an integer.
|
||||
* @param {string} str some string.
|
||||
* @param {boolean} whether or not str represents an integer.
|
||||
*/
|
||||
function isInteger(str) {
|
||||
return (/^\d+$/).test(str);
|
||||
}
|
||||
|
||||
});
|
||||
Loading…
Reference in a new issue