Release
This commit is contained in:
320
client/core/chitalka/chitalka.js
Normal file
320
client/core/chitalka/chitalka.js
Normal file
@@ -0,0 +1,320 @@
|
||||
modules.define(
|
||||
'chitalka',
|
||||
[
|
||||
'y-block',
|
||||
'jquery',
|
||||
'inherit',
|
||||
'y-extend',
|
||||
'chitalka-ui',
|
||||
'storage'
|
||||
],
|
||||
function (
|
||||
provide,
|
||||
YBlock,
|
||||
$,
|
||||
inherit,
|
||||
extend,
|
||||
ChitalkaUI,
|
||||
Storage
|
||||
) {
|
||||
|
||||
var doc = $(document);
|
||||
|
||||
var reportUnimplemented = function (method) {
|
||||
throw new Error('UNIMPLEMENTED METHOD: ' + method);
|
||||
};
|
||||
|
||||
/**
|
||||
* Расширение объекта Math для вычисления медианы массива
|
||||
*
|
||||
* @param {Array} array
|
||||
* @returns {Number} медиана
|
||||
*/
|
||||
Math.median = function (array) {
|
||||
if (!array) {
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = array.length;
|
||||
var median;
|
||||
|
||||
if (entries % 2 === 0) {
|
||||
median = (array[entries / 2] + array[entries / 2 - 1]) / 2;
|
||||
} else {
|
||||
median = array[(entries - 1) / 2];
|
||||
}
|
||||
|
||||
return median;
|
||||
};
|
||||
|
||||
/**
|
||||
* Выбирает из массива массив медиан в заданном количестве
|
||||
*
|
||||
* @param {Array} array
|
||||
* @param {Number} q количество
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
var limitArrayByMedians = function (array, q) {
|
||||
var result = [];
|
||||
|
||||
if (!Array.isArray(array)) {
|
||||
return result;
|
||||
}
|
||||
if (array.length <= q) {
|
||||
return array;
|
||||
}
|
||||
|
||||
var median = Math.median(array);
|
||||
var index = array.indexOf(median);
|
||||
var start = Math.round(index - q / 2);
|
||||
|
||||
return array.splice(start, q);
|
||||
};
|
||||
|
||||
/**
|
||||
* Хэлпер для сортировки массивов чисел
|
||||
*
|
||||
* @param {Number} a
|
||||
* @param {Number} b
|
||||
* @returns {Number} 1 - a >=b, else -1
|
||||
*/
|
||||
var numSort = function (a, b) {
|
||||
a = parseInt(a, 10);
|
||||
b = parseInt(b, 10);
|
||||
|
||||
return a >= b ? 1 : -1;
|
||||
};
|
||||
|
||||
var Chitalka = inherit(YBlock, {
|
||||
__constructor: function () {
|
||||
this.__base.apply(this, arguments);
|
||||
|
||||
var params = extend({
|
||||
keyboard: false,
|
||||
touch: false,
|
||||
controls: false,
|
||||
|
||||
fontSize: [9, 21],
|
||||
|
||||
// Длина свайпа в пикселах
|
||||
swipeLength: 20
|
||||
}, this._getOptions());
|
||||
|
||||
this._defaultFontSize = 15;
|
||||
this._settings = new Storage('settings');
|
||||
|
||||
// Если читалка не доступна, то кидаем событие и больше
|
||||
// ничего не делаем
|
||||
if (!this._isAvailable()) {
|
||||
this.emit('unavailable');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.keyboard) {
|
||||
this._initKeyboardEvents();
|
||||
}
|
||||
|
||||
if (params.touch) {
|
||||
this._initTouchEvents();
|
||||
}
|
||||
|
||||
this._fontSizeLimits = params.fontSize;
|
||||
|
||||
this._setUpSpeed();
|
||||
this._initUI();
|
||||
},
|
||||
|
||||
/**
|
||||
* Выставить скорость чтения книги
|
||||
*/
|
||||
_setUpSpeed: function () {
|
||||
this._speed = Math.median(this._settings.get('speeds')) || 500;
|
||||
},
|
||||
|
||||
_isAvailable: function () {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Активирует реакцию читалки на события с клавиатуры
|
||||
*/
|
||||
_initKeyboardEvents: function () {
|
||||
this._bindTo(doc, 'keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
/**
|
||||
* Активирует реакцию читалки на события блока «Controls»
|
||||
*/
|
||||
_initUI: function () {
|
||||
this._ui = ChitalkaUI.find(doc).init(this);
|
||||
|
||||
//var controls = Controls.find(this.getDomNode());
|
||||
},
|
||||
|
||||
/**
|
||||
* Активация обработки тач-событий (в частности события swipe)
|
||||
* в функции выполняется навешивание соответствующих событий
|
||||
*/
|
||||
_initTouchEvents: function () {
|
||||
},
|
||||
|
||||
_onKeyDown: function (e) {
|
||||
switch (e.keyCode) {
|
||||
// Fn + Right
|
||||
case 35:
|
||||
this.lastPage();
|
||||
break;
|
||||
|
||||
// Fn + Left
|
||||
case 36:
|
||||
this.firstPage();
|
||||
break;
|
||||
|
||||
// Left
|
||||
case 37:
|
||||
this.previousPage();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
// Right
|
||||
case 39:
|
||||
this.nextPage();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
// +
|
||||
case 61:
|
||||
case 187:
|
||||
this.zoomIn();
|
||||
|
||||
if (e.metaKey) {
|
||||
e.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
// -
|
||||
case 173:
|
||||
case 189:
|
||||
this.zoomOut();
|
||||
if (e.metaKey) {
|
||||
e.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
// reset
|
||||
case 48:
|
||||
if (e.metaKey) {
|
||||
this.zoomReset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* События перемещения по книге
|
||||
*/
|
||||
firstPage: function () {
|
||||
reportUnimplemented('firstPage');
|
||||
},
|
||||
previousPage: function () {
|
||||
reportUnimplemented('previousPage');
|
||||
},
|
||||
nextPage: function () {
|
||||
reportUnimplemented('nextPage');
|
||||
},
|
||||
lastPage: function () {
|
||||
reportUnimplemented('lastPage');
|
||||
},
|
||||
|
||||
/**
|
||||
* Функция сохранения скорости в аккумулируемый объект
|
||||
*
|
||||
* @param {Number} speed
|
||||
*/
|
||||
_storeSpeed: function (speed) {
|
||||
this._speedAccumulator = this._speedAccumulator || [];
|
||||
|
||||
this._speedAccumulator.push(speed);
|
||||
|
||||
this._speedAccumulator = this._speedAccumulator.sort(numSort);
|
||||
},
|
||||
|
||||
/**
|
||||
* Функция проверки скорости и её корректировки
|
||||
* общий принцип работы:
|
||||
* есть два массива
|
||||
* this._speedAccumulator – аккумулирует чтение текущей книги
|
||||
* speeds, который хранится в сторадже settings – хранит 10 меток скорости для пользователя
|
||||
* метки – это медианы, которые всегда вычисляются из аккумулятора
|
||||
* как только пользователь прочитывает 10 и более страниц, мы начинаем считать медиану и
|
||||
* править speeds и класть туда новую скорость, вычисленную из аккумулятора
|
||||
* При этом глобальная скорость чтения значительно изменится только если пользователь прочитает
|
||||
* 15 страниц значительно быстрее/медленнее чем раньше.
|
||||
* Во всех остальных случаях медиана поменяется совсем незначительно
|
||||
*/
|
||||
_checkSpeed: function () {
|
||||
var speedEntries = this._speedAccumulator.length;
|
||||
if (speedEntries > 10) {
|
||||
this._speedAccumulator = this._speedAccumulator.sort(numSort);
|
||||
|
||||
var median = Math.median(this._speedAccumulator);
|
||||
|
||||
// Отсекаем совсем неадекватные скорости
|
||||
if (median < 100000 && median > 10) {
|
||||
this._speedAccumulator = this._speedAccumulator.sort(numSort);
|
||||
if (!this._settings.get('speeds')) {
|
||||
this._settings.save({
|
||||
speeds: this._speedAccumulator
|
||||
});
|
||||
} else {
|
||||
var speeds = limitArrayByMedians(this._settings.get('speeds'), 10);
|
||||
|
||||
if (speeds.length < 10) {
|
||||
speeds.push(median);
|
||||
} else {
|
||||
if (median <= speeds[5]) {
|
||||
speeds.pop();
|
||||
speeds.unshift(median);
|
||||
} else {
|
||||
speeds.shift();
|
||||
speeds.push(median);
|
||||
}
|
||||
}
|
||||
speeds = speeds.sort(numSort);
|
||||
|
||||
this._settings.save({
|
||||
speeds: speeds
|
||||
});
|
||||
}
|
||||
this._speed = Math.median(this._settings.get('speeds'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Вернуть текущую скорость чтения
|
||||
* @returns {Number}
|
||||
*/
|
||||
getSpeed: function () {
|
||||
return this._speed;
|
||||
},
|
||||
|
||||
/**
|
||||
* События зума книги
|
||||
*/
|
||||
zoomIn: function () {
|
||||
reportUnimplemented('zoomIn');
|
||||
},
|
||||
zoomOut: function () {
|
||||
reportUnimplemented('zoomOut');
|
||||
},
|
||||
zoomReset: function () {
|
||||
reportUnimplemented('zoomReset');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
provide(Chitalka);
|
||||
});
|
||||
Reference in New Issue
Block a user