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,99 @@
'use strict';
/* exported CardSwipeMixin */
/* globals Swipeable */
(function(exports) {
/**
* Needs to be used with ObserverSubjectMixin
*/
var CardSwipeMixin = {
_swipeHandler: null,
initCardSwipe: function initCardSwipe(selector, tapCb, swipeCb, options) {
options = options || {
'element-width': 260,
'auto-scroll-trigger': 0.25,
'min-speed': 0.75
};
if(this._swipeHandler) {
this._swipeHandler.disable();
this._swipeHandler.onswipe = null;
this._swipeHandler.ontap = null;
this._swipeHandler.onvertswipe = null;
this._swipeHandler = null;
}
this._swipeHandler = new Swipeable(this._el, selector, options);
if(tapCb) {
this._swipeHandler.ontap = tapCb;
}
if(swipeCb) {
this._swipeHandler.onswipe = swipeCb;
}
this._swipeHandler.onvertswipe = (direction, id) => {
var itemEl = this._el.querySelector('#' + id);
var action = '';
if(direction === 'up' && !itemEl.classList.contains('selected')) {
action = 'card-selection';
this._changeItemSelection(itemEl, true);
}
if(direction === 'down' && itemEl.classList.contains('selected')) {
action = 'card-deselection';
this._changeItemSelection(itemEl, false);
}
if(action) {
this._notify({ id: id, action: action});
}
};
this._swipeHandler.enable();
},
updateSelection: function updateSelection(items) {
items.forEach((item, idx) => {
var itemEl = this._el.querySelector('#'+item.id);
if(item.selected && !itemEl.classList.contains('selected')) {
this._changeItemSelection(itemEl, true);
}
if(!item.selected && itemEl.classList.contains('selected')) {
this._changeItemSelection(itemEl, false);
}
});
},
enableCardSelection: function enablCardSelection() {
this._swipeHandler.unpauseSwipe();
},
disableCardSelection: function disableCardSelection() {
this._swipeHandler.pauseSwipe();
},
_changeItemSelection: function changeItemSelection(itemEl, select) {
var elIdx = this._swipeHandler._elts.indexOf(itemEl);
if(elIdx === -1) {
return;
}
if(select) {
itemEl.classList.remove('deselected');
itemEl.classList.add('selected');
this._swipeHandler.progressSelect(elIdx, true);
} else {
itemEl.classList.remove('selected');
itemEl.classList.add('deselected');
this._swipeHandler.progressSelect(elIdx, false);
}
},
};
exports.CardSwipeMixin = CardSwipeMixin;
}((typeof exports === 'undefined') ? window : exports));

View file

@ -0,0 +1,46 @@
'use strict';
/* exported DetailsViewMixin */
(function(exports) {
var DetailsViewMixin = {
_id: null,
_el: null,
_visible: false,
_template: '',
initDetailsView: function(id, data) {
this._id = id;
this._el = document.querySelector('#' + this._id);
this._el.innerHTML = this._template;
if(data) {
this._render(data);
}
},
isVisible: function() {
return this._visible;
},
show: function() {
this._visible = true;
this._el.classList.add(this._id+'-visible');
},
hide: function() {
this._visible = false;
this._el.classList.remove(this._id+'-visible');
},
_render: function(data) {},
refreshView: function(data) {
if(data) {
this._render(data);
}
}
};
exports.DetailsViewMixin = DetailsViewMixin;
}((typeof exports === 'undefined') ? window : exports));

View file

@ -0,0 +1,87 @@
'use strict';
/* globals CARD_UNKNOWN */
/* exported ListViewMixin */
(function(exports) {
var ListViewMixin = {
_id: null,
_el: null,
_visible: false,
_imgMap: null,
_itemTemplate:
'<div class="listview-element-pocket">' +
'<div class="listview-element-sew"></div>' +
'</div>' +
'<img class="listview-element-img" src="img/cards/unknown.png">',
initListView: function(id, imgMap, data) {
this._id = id;
this._el = document.querySelector('#' + this._id);
this._imgMap = imgMap;
if(data && data.length) {
this._render(data);
}
},
updateList(listItems) {
if(!Array.isArray(listItems) || !listItems.length) {
this.debug('list without elements', listItems);
this._renderEmpty();
return;
}
this._render(listItems);
},
isVisible: function() {
return this._visible;
},
show: function() {
this._visible = true;
this._el.classList.remove('listview-hide');
},
hide: function() {
this._visible = false;
this._el.classList.add('listview-hide');
},
_render: function(data){
var emptyLi = document.createElement('li');
emptyLi.innerHTML = this._itemTemplate;
var ulFragment = document.createDocumentFragment();
data.forEach((item) => {
var li = emptyLi.cloneNode(true);
li.id = item.id;
var src = this._imgMap(item.imgId);
// TODO remove CARD_UNKNOW dependency here
li.querySelector('img').src = src ? src : CARD_UNKNOWN;
ulFragment.appendChild(li);
});
var ul = this._el.querySelector('ul');
ul.innerHTML = '';
ul.appendChild(ulFragment);
},
_renderEmpty: function() {
var emptyLi = document.createElement('li');
emptyLi.innerHTML = this._itemTemplate;
var ul = this._el.querySelector('ul');
ul.innerHTML = '';
ul.appendChild(emptyLi);
},
};
exports.ListViewMixin = ListViewMixin;
}((typeof exports === 'undefined') ? window : exports));

View file

@ -0,0 +1,209 @@
'use strict';
/* globals GestureDetector */
/* exported Swipeable */
(function(exports) {
var Swipeable = function(container, eltSelector, options) {
this._container = container.querySelector('.swipeable-container');
this._eltSelector = eltSelector;
this.updateElements();
this._createProgress();
this._gestureDetector = new GestureDetector(this._container,
{ holdEvents: false });
this._panHandler = e => this._handlePan(e);
this._swipeHandler = e => this._handleSwipe(e);
this._container.addEventListener('tap', (e) => {
if(this._tapCallback) {
this._tapCallback(this._elts[this._activeEltIdx].id);
}
});
options = options || {};
this._elWidth = options['element-width'] || this._elts[0].offsetWidth;
this._triggerPoint = options['auto-scroll-trigger'] || this._triggerPoint;
this._minSpeed = options['min-speed'] || this._minSpeed;
};
Swipeable.prototype = {
_gestureDetector: null,
_container: null,
_eltSelector: null,
_elts: [],
_progressElts: null,
_elWidth: 0,
_triggerPoint: 0.25,
_minSpeed: 1.0,
_inTransition: false,
_offset: 0,
_activeEltIdx: 0,
_tapCallback: null,
_swipeCallback: null,
_vertSwipeCallback: null,
set ontap(callback) {
this._tapCallback = callback;
},
set onswipe(callback) {
this._swipeCallback = callback;
},
set onvertswipe(callback) {
this._vertSwipeCallback = callback;
},
enable: function() {
this._gestureDetector.startDetecting();
this.unpauseSwipe();
},
disable: function() {
this._gestureDetector.stopDetecting();
},
pauseSwipe: function() {
this._container.removeEventListener('pan', this._panHandler);
this._container.removeEventListener('swipe', this._swipeHandler);
},
unpauseSwipe: function() {
this._container.addEventListener('pan', this._panHandler);
this._container.addEventListener('swipe', this._swipeHandler);
},
updateElements: function() {
var nodeList = this._container.querySelectorAll('.swipeable > ' + this._eltSelector);
this._elts = Array.prototype.slice.call(nodeList);
},
_createProgress: function() {
if(!this._elts || !this._elts.length) {
return;
}
var progressDiv = this._container.querySelector('.swipeable-progress');
if(!progressDiv) {
return;
}
var progressFragment = document.createDocumentFragment();
this._progressElts = this._elts.map(() => {
var div = document.createElement('div');
progressFragment.appendChild(div);
return div;
});
this._progressElts[0].classList.add('swipeable-progress-active');
progressDiv.innerHTML = '';
progressDiv.appendChild(progressFragment);
},
_updateProgress: function() {
this._progressElts.forEach((elt, idx) => {
if(idx === this._activeEltIdx) {
elt.classList.add('swipeable-progress-active');
} else if(elt.classList.contains('swipeable-progress-active')) {
elt.classList.remove('swipeable-progress-active');
}
});
},
progressSelect: function progressSelect(idx, select) {
var pElt = this._progressElts[idx];
if(!pElt) {
return;
}
if(select) {
pElt.classList.add('swipeable-progress-selected');
} else if(pElt.classList.contains('swipeable-progress-selected')) {
pElt.classList.remove('swipeable-progress-selected');
}
},
_handlePan: function(event) {
if(this._inTransition) {
return;
}
if (Math.abs(event.detail.relative.dx) > Math.abs(event.detail.relative.dy)) {
this._offset += event.detail.relative.dx;
this._animate();
}
},
_handleSwipe: function(event) {
if(this._vertSwipeCallback &&
['up', 'down'].indexOf(event.detail.direction) !== -1) {
var id = this._elts[this._activeEltIdx].id;
this._vertSwipeCallback(event.detail.direction, id);
}
var elPos = Math.abs(this._offset)%this._elWidth;
var triggerPassed = elPos > this._elWidth * this._triggerPoint;
var velocity = event.detail.vx;
var fastEnough = Math.abs(velocity) > this._minSpeed;
var direction = (event.detail.direction === 'left') ? 1 : -1;
var newIdx = this._computeNewIndex(direction);
// Make sure that that the speed and pan amount are in the same direction
//var samedirection = velocity === 0 || this._offset / velocity >= 0;
if((triggerPassed || fastEnough) && newIdx !== this._activeEltIdx) {
var speed = Math.max(Math.abs(velocity), this._minSpeed);
var time = (this._elWidth - Math.abs(this._offset%this._elWidth)) / speed;
this._activeEltIdx = newIdx;
this._performScroll(time);
this._updateProgress();
if (this._swipeCallback) {
this._swipeCallback(this._activeEltIdx);
}
} else if(this._offset !== 0){
var time = Math.abs(this._offset%this._elWidth) / this._minSpeed;
this._performScroll(time);
if (this._swipeCallback) {
this._swipeCallback(this._activeEltIdx);
}
}
},
_animate: function(time) {
var handler = (e) => {
e.target.style.transition = null;
e.target.removeEventListener('transitionend', handler);
};
this._elts.forEach((el) => {
if(time) {
el.style.transition = 'transform ' + time + 'ms ease';
el.addEventListener('transitionend', handler);
}
el.style.transform = 'translate3d('+this._offset+'px,0,0)';
});
},
_performScroll: function(time) {
this._inTransition = true;
this._offset = -1 * this._activeEltIdx * this._elWidth;
this._animate(time);
setTimeout(() => { this._inTransition = false; }, time);
},
_computeNewIndex: function(direction) {
var index = this._activeEltIdx + direction;
var offBounds = index < 0 || index >= this._elts.length;
return offBounds ? this._activeEltIdx : index;
}
};
exports.Swipeable = Swipeable;
}((typeof exports === 'undefined') ? window : exports));

View file

@ -0,0 +1,82 @@
'use strict';
/* exported Timer, addMixin, ObserverSubjectMixin, DebugMixin */
(function(exports) {
function addMixin(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k) && !(k in destination)) {
destination[k] = source[k];
}
}
return destination;
}
var DebugMixin = {
DEBUG: true,
debug: function(msg, obj) {
if(!this.DEBUG) {
return;
}
var str = (this._id) ? ('[' + this._id + '] ' + msg) : msg;
if(obj) {
str += '|' + JSON.stringify(obj);
}
console.log(str);
}
};
var ObserverSubjectMixin = {
_notify: function mlv_notify(data) {
if(this._observers && Array.isArray(this._observers)) {
this._observers.forEach((obs) => obs.onEvent(this._id, data));
}
},
addListener: function mlv_addListener(observer) {
if('onEvent' in observer) {
if(!this._observers) {
this._observers = [];
}
this._observers.push(observer);
}
},
removeListener: function mlv_removeListener(observer) {
if(!this._observers || !Array.isArray(this._observers)) {
return;
}
var idx = this._observers.indexOf(observer);
if(idx !== -1) {
this._observers.splice(idx, 1);
}
},
};
var Timer = function() {
this._startTime = new Date();
};
Timer.prototype = {
_startTime: null,
getElapsedTime: function getElapsedTime() {
var endTime = new Date();
return endTime.getTime() - this._startTime.getTime();
},
reset: function reset() {
this._startTime = new Date();
}
};
exports.addMixin = addMixin;
exports.DebugMixin = DebugMixin;
exports.ObserverSubjectMixin = ObserverSubjectMixin;
exports.Timer = Timer;
}((typeof exports === 'undefined') ? window : exports));