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:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,38 @@
{
"name": "fast-list",
"homepage": "https://github.com/gaia-components/fast-list",
"authors": [
"Etienne Segonzac <etienne@segonzac.info>",
"Wilson Page <wilsonpage@me.com>"
],
"description": "No, really.",
"main": "fast-list.js",
"keywords": [
"web",
"performance",
"virtual",
"list"
],
"license": "MPL",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"examples",
"test",
"tests"
],
"dependencies": {
"dom-scheduler": "etiennesegonzac/dom-scheduler#~1.0.1"
},
"version": "0.1.5",
"_release": "0.1.5",
"_resolution": {
"type": "version",
"tag": "v0.1.5",
"commit": "7b2d68459c1c0710872045a8cb76edd61a013d05"
},
"_source": "git://github.com/gaia-components/fast-list.git",
"_target": "0.1.5",
"_originalSource": "gaia-components/fast-list"
}

View file

@ -0,0 +1,28 @@
{
"name": "fast-list",
"homepage": "https://github.com/gaia-components/fast-list",
"authors": [
"Etienne Segonzac <etienne@segonzac.info>",
"Wilson Page <wilsonpage@me.com>"
],
"description": "No, really.",
"main": "fast-list.js",
"keywords": [
"web",
"performance",
"virtual",
"list"
],
"license": "MPL",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"examples",
"test",
"tests"
],
"dependencies": {
"dom-scheduler": "etiennesegonzac/dom-scheduler#~1.0.1"
}
}

View file

@ -0,0 +1,445 @@
!(function(define){'use strict';define(function(require,exports,module){
/**
* Mini logger
* @type {Function}
*/
var debug = 0 ? console.log.bind(console, '[FastList<>Edit]') : function() {};
// Pointer/touch abstraction
var startEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown';
var moveEvent = ('ontouchstart' in window) ? 'touchmove' : 'mousemove';
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
var schedule = FastList.scheduler;
/**
* Exports
*/
exports = module.exports = fastListEdit;
function fastListEdit(list) {
list.editing = false;
/**
* Toggles the edit mode on and off
*
* Returns the DomScheduler promise of the edit mode transition
*
* @return {Promise}
*/
list.toggleEditMode = function() {
list.editing = !list.editing;
if (list.editing) list._startTouchListeners();
else list._stopTouchListeners();
return toggleEditClass(
list.els.itemContainer,
list.els.itemsInDOM,
list.editing
);
};
list._startTouchListeners = function() {
on(list.els.itemContainer, startEvent, list.handleEventEdit);
on(list.els.itemContainer, endEvent, list.handleEventEdit);
};
list._stopTouchListeners = function() {
off(list.els.itemContainer, startEvent, list.handleEventEdit);
off(list.els.itemContainer, endEvent, list.handleEventEdit);
};
list.handleEventEdit = function(evt) {
switch (evt.type) {
case startEvent:
if (!evt.target.classList.contains('cursor')) {
return;
}
evt.preventDefault();
list._reorderStart(evt);
break;
case endEvent:
if (!evt.target.classList.contains('cursor') &&
endEvent === 'touchend') {
return;
}
evt.preventDefault();
list._reorderEnd(evt);
break;
}
};
/**
* Starts the reordering gestures
*
* - initializes the reordering context (state)
* - attaches (see DomScheduler) a block to the move events
* - sets up the css to make the dragged element pop up
*
* @param {TouchEvent} evt the touchstart event
*/
list._reorderStart = function(evt) {
debug('reorder start');
var ctx = list.reorderingContext;
// Already tracking an item, bailing out
if (ctx.item) return;
var li = evt.target.parentNode.parentNode;
var touch = evt.touches && evt.touches[0] || evt;
ctx.item = li;
ctx.initialY = touch.pageY;
ctx.identifier = touch.identifier;
ctx.moveUp = new Set();
ctx.moveDown = new Set();
ctx.moveHandler = list._reorderMove.bind(list);
var listenToMove = (function() {
schedule.attachDirect(
list.els.itemContainer,
moveEvent,
ctx.moveHandler);
}).bind(list);
setupForDragging(li, true)
.then(listenToMove)
.then(toggleDraggingClass.bind(null, li, true));
};
/**
* Scheduled callback for move events during reordering
*
* - updates the position of the dragged item
* - rearranges the other items
*
* @param {TouchEvent} evt the touchmove event
*/
list._reorderMove = function(evt) {
debug('reorder move');
var ctx = list.reorderingContext;
if (!ctx.item) return;
// Multi touch
if (evt.touches && evt.touches.length > 1) return;
var li = ctx.item;
var touch = evt.touches && evt.touches[0] || evt;
var position = touch.pageY;
var delta = position - ctx.initialY;
list._rearrange(delta);
tweakTransform(li, delta);
// TODO: scroll when close to the beginning/end of the viewport
};
/**
* Rearranges the items below te dragged one
*
* - computes the transitions needed based on the context
* - triggers scheduled transitions for each one
*
* @param {Number} delta the distance traveled by the reordering gesture
*/
list._rearrange = function(delta) {
debug('rearrange', delta);
var ctx = list.reorderingContext;
var upCount = ctx.moveUp.size;
var downCount = ctx.moveDown.size;
computeChanges(
list.reorderingContext,
list.geometry,
list.els.itemsInDOM,
delta
);
if (ctx.moveUp.size === upCount &&
ctx.moveDown.size === downCount) {
return;
}
applyChanges(
list.reorderingContext,
list.geometry,
list.els.itemsInDOM
);
debug('rearranged');
};
/**
* Ends the reordering gesture
*
* - computes any remaining pending changes
* - detaches the block from the move events
* - schedules a transition to move the dragged item in place
* - schedules a transition to move other items in place
*
* @param {TouchEvent} evt the touchend event
*/
list._reorderEnd = function(evt) {
debug('reorder end');
var ctx = list.reorderingContext;
var li = ctx.item;
if (!li) return;
schedule.detachDirect(
list.els.itemContainer,
moveEvent,
ctx.moveHandler);
var touch = evt.touches && evt.touches[0] || evt;
if (touch.identifier == ctx.identifier) {
var position = touch.pageY;
var delta = position - ctx.initialY;
computeChanges(ctx, list.geometry, list.els.itemsInDOM, delta);
}
Promise.all([
applyChanges(ctx, list.geometry, list.els.itemsInDOM),
moveInPlace(ctx, list.geometry)
]).then(list._reorderFinish.bind(list, li))
.then(toggleDraggingClass.bind(null, li, false));
};
/**
* Final stage of the reordering
*
* In parallell
* - Commits the change to the document
* - cleans up the css on the dragged element
*
*/
list._reorderFinish = function(li) {
return Promise.all([
list._commitToDocument(),
setupForDragging(li, false)
]);
};
/**
* Commits the reordering changes to the document
*
* Inside a scheduled mutation
* - re-renders the list with the new ordering
* - cleans up the reordering context
*
*/
list._commitToDocument = function() {
debug('commit to document');
var ctx = list.reorderingContext;
var li = ctx.item;
var itemsInDOM = list.els.itemsInDOM;
var items = list.els.items;
var source = list.source;
return schedule.mutation((function() {
// Nothing to do
if (!ctx.moveUp && !ctx.moveDown) {
return;
}
var index = items.indexOf(li);
var section = li.dataset.section;
var newIndex = index + ctx.moveUp.size - ctx.moveDown.size;
items.splice(index, 1);
items.splice(newIndex, 0, li);
var c = source.removeAtIndex(index);
source.insertAtIndex(newIndex, c, section);
itemsInDOM.forEach(function(item) {
resetTransition(item);
resetTransform(item);
});
list.updateSections();
list.render();
ctx.item = null;
ctx.initialY = null;
ctx.identifier = null;
ctx.moveUp = null;
ctx.moveDown = null;
ctx.moveHandler = null;
}).bind(list));
};
}
/**
* Internals
*/
function resetTransform(item) {
debug('reset transform');
var position = parseInt(item.dataset.position);
item.style.webkitTransform =
item.style.transform = 'translateY(' + position + 'px)';
item.dataset.tweakDelta = '';
}
function tweakTransform(item, delta) {
debug('tweak transform', delta);
var position = ~~item.dataset.position + delta;
item.style.webkitTransform =
item.style.transform = 'translateY(' + position + 'px)';
item.dataset.tweakDelta = delta;
}
function resetTransition(item) {
item.style.transition = '';
item.style.webkitTransition = '';
}
function tweakAndTransition(item, tweak) {
return schedule.feedback(function() {
debug('tweak and transition', item, tweak);
item.style.transition = 'transform 0.15s ease';
item.style.webkitTransition = '-webkit-transform 0.15s ease';
if (tweak === 0) {
setTimeout(resetTransform.bind(null, item));
return;
}
setTimeout(tweakTransform.bind(null, item, tweak));
}, item, 'transitionend')
.then(resetTransition.bind(null, item));
}
function toggleEditClass(list, domItems, editing) {
if (!domItems.length) return Promise.resolve();
return schedule.feedback(function() {
for (var i = 0; i < domItems.length; i++) {
var item = domItems[i];
var overlay = item.querySelector('.overlay');
overlay.dataset.anim = editing ? 'reveal' : 'hide';
item.classList.toggle('edit', editing);
}
}, list, 'animationend');
}
function setupForDragging(item, on) {
return schedule.mutation(function() {
item.parentNode.classList.toggle('reordering', on);
item.style.zIndex = on ? '1000' : '';
item.style.boxShadow = on ? '0 0 3px 1px #bcbcbc' : '';
});
}
function toggleDraggingClass(item, on) {
return schedule.feedback(function() {
var overlay = item.querySelector('.overlay');
overlay.dataset.anim = on ? 'hide' : 'reveal';
}, item, 'animationend');
}
function computeChanges(context, geometry, domItems, delta) {
if (!context.item) return;
var draggedItem = context.item;
var draggedOriginal = parseInt(draggedItem.dataset.position);
var draggedTweaked = draggedOriginal + delta;
for (var i = 0; i < domItems.length; i++) {
var item = domItems[i];
if (item === draggedItem) continue;
// TODO: support re-ordering accross sections
if (item.dataset.section !== draggedItem.dataset.section) continue;
var itemOriginal = parseInt(item.dataset.position);
var itemTweaked = itemOriginal + parseInt(item.dataset.tweakDelta);
if ((itemOriginal < draggedOriginal) && (draggedTweaked < itemOriginal)) {
context.moveDown.add(item);
} else {
if (draggedTweaked > itemTweaked) {
context.moveDown.delete(item);
}
}
if ((itemOriginal > draggedOriginal) && (draggedTweaked > itemOriginal)) {
context.moveUp.add(item);
} else {
if (draggedTweaked < itemTweaked) {
context.moveUp.delete(item);
}
}
}
}
function applyChanges(context, geometry, domItems) {
debug('apply changes');
if (!context.item) return Promise.resolve();
var itemHeight = geometry.itemHeight;
var draggedItem = context.item;
var promises = [];
for (var i = 0; i < domItems.length; i++) {
var item = domItems[i];
if (item === draggedItem) continue;
// Reset
if (item.dataset.tweakDelta &&
!context.moveUp.has(item) && !context.moveDown.has(item)) {
promises.push(tweakAndTransition(item, 0));
}
// Moving down
if (context.moveDown.has(item)) {
if (parseInt(item.dataset.tweakDelta) !== itemHeight) {
promises.push(tweakAndTransition(item, itemHeight));
}
}
// Moving up
if (context.moveUp.has(item)) {
if (parseInt(item.dataset.tweakDelta) !== itemHeight * -1) {
promises.push(tweakAndTransition(item, itemHeight * -1));
}
}
}
return Promise.all(promises);
}
function moveInPlace(context, geometry) {
debug('move in place');
var li = context.item;
// We're already in place
if (!li.dataset.tweakDelta) return Promise.resolve();
var position = parseInt(li.dataset.position);
var taintedPosition = position + parseInt(li.dataset.tweakDelta);
var itemHeight = geometry.itemHeight;
var newPosition = position + context.moveUp.size * itemHeight -
context.moveDown.size * itemHeight;
var duration = (Math.abs(taintedPosition - newPosition) / itemHeight) * 150;
return schedule.feedback(function() {
li.style.transition = 'transform ' + duration + 'ms linear';
li.style.webkitTransition = '-webkit-transform ' + duration + 'ms linear';
setTimeout(tweakTransform.bind(null, li, (newPosition - position)));
}, li, 'transitionend');
}
// Shorthand
function on(el, name, fn) { el.addEventListener(name, fn); }
function off(el, name, fn) { el.removeEventListener(name, fn); }
});})((typeof define)[0]=='f'&&define.amd?define:(function(n,n2,w){'use strict';
return(typeof module)[0]=='o'?function(c){c(require,exports,module);}:
function(c){var m={exports:{}};c(function(n){w[n];},m.exports,m);
w[n]=w[n2]=m.exports;};})('fastListEdit','fast-list-edit',this));

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
{
"name": "fast-list",
"main": "fast-list.js",
"version": "0.1.5",
"devDependencies": {
"jshint": "^2.7.0",
"karma": "^0.12.31",
"karma-firefox-launcher": "^0.1.4",
"karma-chrome-launcher": "^0.1.4",
"karma-mocha": "^0.1.10",
"karma-sinon-chai": "^0.3.0"
},
"scripts": {
"lint": "./node_modules/jshint/bin/jshint fast-list.js test/*-test.js",
"test": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run",
"test-dev": "./node_modules/karma/bin/karma start test/karma.conf.js"
},
"repository": {
"type": "git",
"url": "git://github.com/gaia-components/fast-list.git"
},
"bugs": {
"url": "https://github.com/gaia-components/fast-list/issues"
}
}