Open Media Library
5877
static/epub.js/epub.js
Normal file
24
static/epub.js/epub.js.map
Normal file
1
static/epub.js/epub.min.map
Normal file
5876
static/epub.js/epub_no_underscore.js
Normal file
23
static/epub.js/epub_no_underscore.js.map
Normal file
318
static/epub.js/hooks.js
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
EPUBJS.Hooks.register("beforeChapterDisplay").endnotes = function(callback, renderer){
|
||||
|
||||
var notes = renderer.contents.querySelectorAll('a[href]'),
|
||||
items = Array.prototype.slice.call(notes), //[].slice.call()
|
||||
attr = "epub:type",
|
||||
type = "noteref",
|
||||
folder = EPUBJS.core.folder(location.pathname),
|
||||
cssPath = (folder + EPUBJS.cssPath) || folder,
|
||||
popups = {};
|
||||
|
||||
EPUBJS.core.addCss(cssPath + "popup.css", false, renderer.render.document.head);
|
||||
|
||||
|
||||
items.forEach(function(item){
|
||||
var epubType = item.getAttribute(attr),
|
||||
href,
|
||||
id,
|
||||
el,
|
||||
pop,
|
||||
pos,
|
||||
left,
|
||||
top,
|
||||
txt;
|
||||
|
||||
if(epubType != type) return;
|
||||
|
||||
href = item.getAttribute("href");
|
||||
id = href.replace("#", '');
|
||||
el = renderer.render.document.getElementById(id);
|
||||
|
||||
|
||||
item.addEventListener("mouseover", showPop, false);
|
||||
item.addEventListener("mouseout", hidePop, false);
|
||||
|
||||
function showPop(){
|
||||
var poppos,
|
||||
iheight = renderer.height,
|
||||
iwidth = renderer.width,
|
||||
tip,
|
||||
pop,
|
||||
maxHeight = 225,
|
||||
itemRect;
|
||||
|
||||
if(!txt) {
|
||||
pop = el.cloneNode(true);
|
||||
txt = pop.querySelector("p");
|
||||
}
|
||||
|
||||
// chapter.replaceLinks.bind(this) //TODO:Fred - update?
|
||||
//-- create a popup with endnote inside of it
|
||||
if(!popups[id]) {
|
||||
popups[id] = document.createElement("div");
|
||||
popups[id].setAttribute("class", "popup");
|
||||
|
||||
pop_content = document.createElement("div");
|
||||
|
||||
popups[id].appendChild(pop_content);
|
||||
|
||||
pop_content.appendChild(txt);
|
||||
pop_content.setAttribute("class", "pop_content");
|
||||
|
||||
renderer.render.document.body.appendChild(popups[id]);
|
||||
|
||||
//-- TODO: will these leak memory? - Fred
|
||||
popups[id].addEventListener("mouseover", onPop, false);
|
||||
popups[id].addEventListener("mouseout", offPop, false);
|
||||
|
||||
//-- Add hide on page change
|
||||
// chapter.book.listenUntil("book:pageChanged", "book:chapterDestroy", hidePop);
|
||||
// chapter.book.listenUntil("book:pageChanged", "book:chapterDestroy", offPop);
|
||||
renderer.on("renderer:pageChanged", hidePop, this);
|
||||
renderer.on("renderer:pageChanged", offPop, this);
|
||||
// chapter.book.on("renderer:chapterDestroy", hidePop, this);
|
||||
}
|
||||
|
||||
pop = popups[id];
|
||||
|
||||
|
||||
//-- get location of item
|
||||
itemRect = item.getBoundingClientRect();
|
||||
left = itemRect.left;
|
||||
top = itemRect.top;
|
||||
|
||||
//-- show the popup
|
||||
pop.classList.add("show");
|
||||
|
||||
//-- locations of popup
|
||||
popRect = pop.getBoundingClientRect();
|
||||
|
||||
//-- position the popup
|
||||
pop.style.left = left - popRect.width / 2 + "px";
|
||||
pop.style.top = top + "px";
|
||||
|
||||
|
||||
//-- Adjust max height
|
||||
if(maxHeight > iheight / 2.5) {
|
||||
maxHeight = iheight / 2.5;
|
||||
pop_content.style.maxHeight = maxHeight + "px";
|
||||
}
|
||||
|
||||
//-- switch above / below
|
||||
if(popRect.height + top >= iheight - 25) {
|
||||
pop.style.top = top - popRect.height + "px";
|
||||
pop.classList.add("above");
|
||||
}else{
|
||||
pop.classList.remove("above");
|
||||
}
|
||||
|
||||
//-- switch left
|
||||
if(left - popRect.width <= 0) {
|
||||
pop.style.left = left + "px";
|
||||
pop.classList.add("left");
|
||||
}else{
|
||||
pop.classList.remove("left");
|
||||
}
|
||||
|
||||
//-- switch right
|
||||
if(left + popRect.width / 2 >= iwidth) {
|
||||
//-- TEMP MOVE: 300
|
||||
pop.style.left = left - 300 + "px";
|
||||
|
||||
popRect = pop.getBoundingClientRect();
|
||||
pop.style.left = left - popRect.width + "px";
|
||||
//-- switch above / below again
|
||||
if(popRect.height + top >= iheight - 25) {
|
||||
pop.style.top = top - popRect.height + "px";
|
||||
pop.classList.add("above");
|
||||
}else{
|
||||
pop.classList.remove("above");
|
||||
}
|
||||
|
||||
pop.classList.add("right");
|
||||
}else{
|
||||
pop.classList.remove("right");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function onPop(){
|
||||
popups[id].classList.add("on");
|
||||
}
|
||||
|
||||
function offPop(){
|
||||
popups[id].classList.remove("on");
|
||||
}
|
||||
|
||||
function hidePop(){
|
||||
setTimeout(function(){
|
||||
popups[id].classList.remove("show");
|
||||
}, 100);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if(callback) callback();
|
||||
|
||||
}
|
||||
|
||||
EPUBJS.Hooks.register("beforeChapterDisplay").mathml = function(callback, renderer){
|
||||
|
||||
// check of currentChapter properties contains 'mathml'
|
||||
if(renderer.currentChapter.manifestProperties.indexOf("mathml") !== -1 ){
|
||||
|
||||
// Assign callback to be inside iframe window
|
||||
renderer.iframe.contentWindow.mathmlCallback = callback;
|
||||
|
||||
// add MathJax config script tag to the renderer body
|
||||
var s = document.createElement("script");
|
||||
s.type = 'text/x-mathjax-config';
|
||||
s.innerHTML = '\
|
||||
MathJax.Hub.Register.StartupHook("End",function () { \
|
||||
window.mathmlCallback(); \
|
||||
});\
|
||||
MathJax.Hub.Config({jax: ["input/TeX","input/MathML","output/SVG"],extensions: ["tex2jax.js","mml2jax.js","MathEvents.js"],TeX: {extensions: ["noErrors.js","noUndefined.js","autoload-all.js"]},MathMenu: {showRenderer: false},menuSettings: {zoom: "Click"},messageStyle: "none"}); \
|
||||
';
|
||||
renderer.doc.body.appendChild(s);
|
||||
// add MathJax.js to renderer head
|
||||
EPUBJS.core.addScript("http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML", null, renderer.doc.head);
|
||||
|
||||
} else {
|
||||
if(callback) callback();
|
||||
}
|
||||
}
|
||||
|
||||
EPUBJS.Hooks.register("beforeChapterDisplay").smartimages = function(callback, renderer){
|
||||
var images = renderer.contents.querySelectorAll('img'),
|
||||
items = Array.prototype.slice.call(images),
|
||||
iheight = renderer.height,//chapter.bodyEl.clientHeight,//chapter.doc.body.getBoundingClientRect().height,
|
||||
oheight;
|
||||
|
||||
if(renderer.layoutSettings.layout != "reflowable") {
|
||||
callback();
|
||||
return; //-- Only adjust images for reflowable text
|
||||
}
|
||||
|
||||
items.forEach(function(item){
|
||||
|
||||
function size() {
|
||||
var itemRect = item.getBoundingClientRect(),
|
||||
rectHeight = itemRect.height,
|
||||
top = itemRect.top,
|
||||
oHeight = item.getAttribute('data-height'),
|
||||
height = oHeight || rectHeight,
|
||||
newHeight,
|
||||
fontSize = Number(getComputedStyle(item, "").fontSize.match(/(\d*(\.\d*)?)px/)[1]),
|
||||
fontAdjust = fontSize ? fontSize / 2 : 0;
|
||||
|
||||
iheight = renderer.contents.clientHeight;
|
||||
if(top < 0) top = 0;
|
||||
|
||||
if(height + top >= iheight) {
|
||||
|
||||
if(top < iheight/2) {
|
||||
// Remove top and half font-size from height to keep container from overflowing
|
||||
newHeight = iheight - top - fontAdjust;
|
||||
item.style.maxHeight = newHeight + "px";
|
||||
item.style.width= "auto";
|
||||
}else{
|
||||
if(height > iheight) {
|
||||
item.style.maxHeight = iheight + "px";
|
||||
item.style.width= "auto";
|
||||
itemRect = item.getBoundingClientRect();
|
||||
height = itemRect.height;
|
||||
}
|
||||
item.style.display = "block";
|
||||
item.style["WebkitColumnBreakBefore"] = "always";
|
||||
item.style["breakBefore"] = "column";
|
||||
|
||||
}
|
||||
|
||||
item.setAttribute('data-height', newHeight);
|
||||
|
||||
}else{
|
||||
item.style.removeProperty('max-height');
|
||||
item.style.removeProperty('margin-top');
|
||||
}
|
||||
}
|
||||
|
||||
item.addEventListener('load', size, false);
|
||||
|
||||
renderer.on("renderer:resized", size);
|
||||
|
||||
renderer.on("renderer:chapterUnloaded", function(){
|
||||
item.removeEventListener('load', size);
|
||||
renderer.off("renderer:resized", size);
|
||||
});
|
||||
|
||||
size();
|
||||
|
||||
});
|
||||
|
||||
if(callback) callback();
|
||||
|
||||
}
|
||||
|
||||
EPUBJS.Hooks.register("beforeChapterDisplay").transculsions = function(callback, renderer){
|
||||
/*
|
||||
<aside ref="http://www.youtube.com/embed/DUL6MBVKVLI?html5=1" transclusion="video" width="560" height="315">
|
||||
<a href="http://www.youtube.com/embed/DUL6MBVKVLI"> Watch the National Geographic: The Last Roll of Kodachrome</a>
|
||||
</aside>
|
||||
*/
|
||||
|
||||
var trans = renderer.contents.querySelectorAll('[transclusion]'),
|
||||
items = Array.prototype.slice.call(trans);
|
||||
|
||||
items.forEach(function(item){
|
||||
var src = item.getAttribute("ref"),
|
||||
iframe = document.createElement('iframe'),
|
||||
orginal_width = item.getAttribute("width"),
|
||||
orginal_height = item.getAttribute("height"),
|
||||
parent = item.parentNode,
|
||||
width = orginal_width,
|
||||
height = orginal_height,
|
||||
ratio;
|
||||
|
||||
|
||||
function size() {
|
||||
width = orginal_width;
|
||||
height = orginal_height;
|
||||
|
||||
if(width > chapter.colWidth){
|
||||
ratio = chapter.colWidth / width;
|
||||
|
||||
width = chapter.colWidth;
|
||||
height = height * ratio;
|
||||
}
|
||||
|
||||
iframe.width = width;
|
||||
iframe.height = height;
|
||||
}
|
||||
|
||||
|
||||
size();
|
||||
|
||||
//-- resize event
|
||||
|
||||
|
||||
renderer.listenUntil("renderer:resized", "renderer:chapterUnloaded", size);
|
||||
|
||||
iframe.src = src;
|
||||
|
||||
//<iframe width="560" height="315" src="http://www.youtube.com/embed/DUL6MBVKVLI" frameborder="0" allowfullscreen="true"></iframe>
|
||||
parent.replaceChild(iframe, item);
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
if(callback) callback();
|
||||
|
||||
|
||||
}
|
||||
|
||||
//# sourceMappingURL=hooks.js.map
|
||||
12
static/epub.js/hooks.js.map
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "hooks.js",
|
||||
"sources": [
|
||||
"hooks/default/endnotes.js",
|
||||
"hooks/default/mathml.js",
|
||||
"hooks/default/smartimages.js",
|
||||
"hooks/default/transculsions.js"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9JA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"
|
||||
}
|
||||
1
static/epub.js/hooks.min.map
Normal file
2
static/epub.js/libs/inflate.js
Normal file
1
static/epub.js/libs/inflate.map
Normal file
1
static/epub.js/libs/zip.min.map
Normal file
1116
static/epub.js/reader.js
Normal file
17
static/epub.js/reader.js.map
Normal file
1
static/epub.js/reader.min.map
Normal file
103
static/html/epub.html
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>epub.js</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
|
||||
<!-- EPUBJS Renderer -->
|
||||
<script src="/static/epub.js/epub.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#main {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#area {
|
||||
position: absolute;
|
||||
right: 44px;
|
||||
left: 44px;
|
||||
top: 32px;
|
||||
bottom: 32px;
|
||||
}
|
||||
|
||||
#area iframe {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#prev {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 36px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 36px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.arrow:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.arrow:active {
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
function bookUrl() {
|
||||
return document.location.pathname.replace(/\/reader\//, '/epub/');
|
||||
}
|
||||
|
||||
var Book = ePub({store: false});
|
||||
Book.open(bookUrl());
|
||||
Book.getMetadata().then(function(meta) {
|
||||
document.title = meta.bookTitle + " – " + meta.creator;
|
||||
document.addEventListener("keydown", function(event) {
|
||||
if (event.keyCode == 39 || event.keyCode == 40) {
|
||||
Book.nextPage();
|
||||
} else if (event.keyCode == 37 || event.keyCode == 38) {
|
||||
Book.prevPage();
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="prev" onclick="Book.prevPage();" class="arrow"></div>
|
||||
<div id="area"></div>
|
||||
<div id="next" onclick="Book.nextPage();"class="arrow"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Book.renderTo("area");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
11
static/html/oml.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Open Media Library</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<link href="/static/png/oml.png" rel="icon" type="image/png">
|
||||
<script src="/static/js/oml.js?1" type="text/javascript"></script>
|
||||
<meta name="google" value="notranslate"/>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
355
static/html/pdf.html
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright 2012 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html dir="ltr" mozdisallowselectionprint moznomarginboxes>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta name="google" content="notranslate">
|
||||
<title></title>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/static/pdf.js/viewer.css"/>
|
||||
|
||||
<script type="text/javascript" src="/static/oxjs/build/Ox.js"></script>
|
||||
<script type="text/javascript" src="/static/oxjs/source/Ox.UI/js/Core/Message.js"></script>
|
||||
<script type="text/javascript" src="/static/pdf.js/compatibility.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- This snippet is used in production, see Makefile -->
|
||||
<link rel="resource" type="application/l10n" href="/static/pdf.js/locale/locale.properties"/>
|
||||
<script type="text/javascript" src="/static/pdf.js/l10n.js"></script>
|
||||
<script type="text/javascript" src="/static/pdf.js/pdf.js"></script>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="/static/pdf.js/debugger.js"></script>
|
||||
<script type="text/javascript" src="/static/pdf.js/embeds.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/static/pdf.js/viewer.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/pdf.js/css/videopdf.css"/>
|
||||
|
||||
</head>
|
||||
|
||||
<body tabindex="1">
|
||||
<div id="outerContainer" class="loadingInProgress">
|
||||
|
||||
<div id="sidebarContainer">
|
||||
<div id="toolbarSidebar">
|
||||
<div class="splitToolbarButton toggled">
|
||||
<button id="viewThumbnail" class="toolbarButton group toggled" title="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
|
||||
<span data-l10n-id="thumbs_label">Thumbnails</span>
|
||||
</button>
|
||||
<button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="3" data-l10n-id="outline">
|
||||
<span data-l10n-id="outline_label">Document Outline</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sidebarContent">
|
||||
<div id="thumbnailView">
|
||||
</div>
|
||||
<div id="outlineView" class="hidden">
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- sidebarContainer -->
|
||||
|
||||
<div id="mainContainer">
|
||||
<div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
|
||||
<label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
|
||||
<input id="findInput" class="toolbarField" tabindex="41">
|
||||
<div class="splitToolbarButton">
|
||||
<button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="42" data-l10n-id="find_previous">
|
||||
<span data-l10n-id="find_previous_label">Previous</span>
|
||||
</button>
|
||||
<div class="splitToolbarButtonSeparator"></div>
|
||||
<button class="toolbarButton findNext" title="" id="findNext" tabindex="43" data-l10n-id="find_next">
|
||||
<span data-l10n-id="find_next_label">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
<input type="checkbox" id="findHighlightAll" class="toolbarField">
|
||||
<label for="findHighlightAll" class="toolbarLabel" tabindex="44" data-l10n-id="find_highlight">Highlight all</label>
|
||||
<input type="checkbox" id="findMatchCase" class="toolbarField">
|
||||
<label for="findMatchCase" class="toolbarLabel" tabindex="45" data-l10n-id="find_match_case_label">Match case</label>
|
||||
<span id="findMsg" class="toolbarLabel"></span>
|
||||
</div> <!-- findbar -->
|
||||
|
||||
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
|
||||
<div id="secondaryToolbarButtonContainer">
|
||||
<button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="18" data-l10n-id="presentation_mode">
|
||||
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||
</button>
|
||||
|
||||
<button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="19" data-l10n-id="open_file">
|
||||
<span data-l10n-id="open_file_label">Open</span>
|
||||
</button>
|
||||
|
||||
<button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="20" data-l10n-id="print">
|
||||
<span data-l10n-id="print_label">Print</span>
|
||||
</button>
|
||||
|
||||
<button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="21" data-l10n-id="download">
|
||||
<span data-l10n-id="download_label">Download</span>
|
||||
</button>
|
||||
|
||||
<a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="22" data-l10n-id="bookmark">
|
||||
<span data-l10n-id="bookmark_label">Current View</span>
|
||||
</a>
|
||||
|
||||
<div class="horizontalToolbarSeparator visibleLargeView"></div>
|
||||
|
||||
<button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="23" data-l10n-id="first_page">
|
||||
<span data-l10n-id="first_page_label">Go to First Page</span>
|
||||
</button>
|
||||
<button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="24" data-l10n-id="last_page">
|
||||
<span data-l10n-id="last_page_label">Go to Last Page</span>
|
||||
</button>
|
||||
|
||||
<div class="horizontalToolbarSeparator"></div>
|
||||
|
||||
<button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="25" data-l10n-id="page_rotate_cw">
|
||||
<span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
|
||||
</button>
|
||||
<button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="26" data-l10n-id="page_rotate_ccw">
|
||||
<span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
|
||||
</button>
|
||||
|
||||
<div class="horizontalToolbarSeparator"></div>
|
||||
|
||||
<button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="27" data-l10n-id="hand_tool_enable">
|
||||
<span data-l10n-id="hand_tool_enable_label">Enable hand tool</span>
|
||||
</button>
|
||||
</div>
|
||||
</div> <!-- secondaryToolbar -->
|
||||
|
||||
<div class="toolbar">
|
||||
<div id="toolbarContainer">
|
||||
<div id="toolbarViewer">
|
||||
<div id="toolbarViewerLeft">
|
||||
<button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="4" data-l10n-id="toggle_sidebar">
|
||||
<span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
|
||||
</button>
|
||||
<div class="toolbarButtonSpacer"></div>
|
||||
<button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="5" data-l10n-id="findbar">
|
||||
<span data-l10n-id="findbar_label">Find</span>
|
||||
</button>
|
||||
<div class="splitToolbarButton">
|
||||
<button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="6" data-l10n-id="previous">
|
||||
<span data-l10n-id="previous_label">Previous</span>
|
||||
</button>
|
||||
<div class="splitToolbarButtonSeparator"></div>
|
||||
<button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="7" data-l10n-id="next">
|
||||
<span data-l10n-id="next_label">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
|
||||
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="8">
|
||||
<span id="numPages" class="toolbarLabel"></span>
|
||||
</div>
|
||||
<div id="toolbarViewerRight">
|
||||
<button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="12" data-l10n-id="presentation_mode">
|
||||
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
|
||||
</button>
|
||||
|
||||
<button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="13" data-l10n-id="open_file">
|
||||
<span data-l10n-id="open_file_label">Open</span>
|
||||
</button>
|
||||
|
||||
<button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="14" data-l10n-id="print">
|
||||
<span data-l10n-id="print_label">Print</span>
|
||||
</button>
|
||||
|
||||
<button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="15" data-l10n-id="download">
|
||||
<span data-l10n-id="download_label">Download</span>
|
||||
</button>
|
||||
<!-- <div class="toolbarButtonSpacer"></div> -->
|
||||
<a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="16" data-l10n-id="bookmark">
|
||||
<span data-l10n-id="bookmark_label">Current View</span>
|
||||
</a>
|
||||
|
||||
<div class="verticalToolbarSeparator hiddenSmallView"></div>
|
||||
|
||||
<button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="17" data-l10n-id="tools">
|
||||
<span data-l10n-id="tools_label">Tools</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="outerCenter">
|
||||
<div class="innerCenter" id="toolbarViewerMiddle">
|
||||
<div class="splitToolbarButton">
|
||||
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="9" data-l10n-id="zoom_out">
|
||||
<span data-l10n-id="zoom_out_label">Zoom Out</span>
|
||||
</button>
|
||||
<div class="splitToolbarButtonSeparator"></div>
|
||||
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="10" data-l10n-id="zoom_in">
|
||||
<span data-l10n-id="zoom_in_label">Zoom In</span>
|
||||
</button>
|
||||
</div>
|
||||
<span id="scaleSelectContainer" class="dropdownToolbarButton">
|
||||
<select id="scaleSelect" title="Zoom" tabindex="11" data-l10n-id="zoom">
|
||||
<option id="pageAutoOption" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
|
||||
<option id="pageActualOption" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
|
||||
<option id="pageFitOption" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
|
||||
<option id="pageWidthOption" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
|
||||
<option id="customScaleOption" value="custom"></option>
|
||||
<option value="0.5">50%</option>
|
||||
<option value="0.75">75%</option>
|
||||
<option value="1">100%</option>
|
||||
<option value="1.25">125%</option>
|
||||
<option value="1.5">150%</option>
|
||||
<option value="2">200%</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loadingBar">
|
||||
<div class="progress">
|
||||
<div class="glimmer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<menu type="context" id="viewerContextMenu">
|
||||
<menuitem id="contextFirstPage" label="First Page"
|
||||
data-l10n-id="first_page"></menuitem>
|
||||
<menuitem id="contextLastPage" label="Last Page"
|
||||
data-l10n-id="last_page"></menuitem>
|
||||
<menuitem id="contextPageRotateCw" label="Rotate Clockwise"
|
||||
data-l10n-id="page_rotate_cw"></menuitem>
|
||||
<menuitem id="contextPageRotateCcw" label="Rotate Counter-Clockwise"
|
||||
data-l10n-id="page_rotate_ccw"></menuitem>
|
||||
</menu>
|
||||
|
||||
<div id="viewerContainer" tabindex="0">
|
||||
<div id="viewer"></div>
|
||||
</div>
|
||||
|
||||
<div id="errorWrapper" hidden='true'>
|
||||
<div id="errorMessageLeft">
|
||||
<span id="errorMessage"></span>
|
||||
<button id="errorShowMore" data-l10n-id="error_more_info">
|
||||
More Information
|
||||
</button>
|
||||
<button id="errorShowLess" data-l10n-id="error_less_info" hidden='true'>
|
||||
Less Information
|
||||
</button>
|
||||
</div>
|
||||
<div id="errorMessageRight">
|
||||
<button id="errorClose" data-l10n-id="error_close">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearBoth"></div>
|
||||
<textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
|
||||
</div>
|
||||
</div> <!-- mainContainer -->
|
||||
|
||||
<div id="overlayContainer" class="hidden">
|
||||
<div id="promptContainer">
|
||||
<div id="passwordContainer" class="prompt doorHanger">
|
||||
<div class="row">
|
||||
<p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="password" id="password" class="toolbarField" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<button id="passwordCancel" class="promptButton"><span data-l10n-id="password_cancel">Cancel</span></button>
|
||||
<button id="passwordSubmit" class="promptButton"><span data-l10n-id="password_ok">OK</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- outerContainer -->
|
||||
<div id="printContainer"></div>
|
||||
<div id="mozPrintCallback-shim" hidden>
|
||||
<style scoped>
|
||||
#mozPrintCallback-shim {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 9999999;
|
||||
|
||||
display: block;
|
||||
text-align: center;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#mozPrintCallback-shim[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@media print {
|
||||
#mozPrintCallback-shim {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#mozPrintCallback-shim .mozPrintCallback-dialog-box {
|
||||
display: inline-block;
|
||||
margin: -50px auto 0;
|
||||
position: relative;
|
||||
top: 45%;
|
||||
left: 0;
|
||||
min-width: 220px;
|
||||
max-width: 400px;
|
||||
|
||||
padding: 9px;
|
||||
|
||||
border: 1px solid hsla(0, 0%, 0%, .5);
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
background-color: #474747;
|
||||
|
||||
color: hsl(0, 0%, 85%);
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
}
|
||||
#mozPrintCallback-shim .progress-row {
|
||||
clear: both;
|
||||
padding: 1em 0;
|
||||
}
|
||||
#mozPrintCallback-shim progress {
|
||||
width: 100%;
|
||||
}
|
||||
#mozPrintCallback-shim .relative-progress {
|
||||
clear: both;
|
||||
float: right;
|
||||
}
|
||||
#mozPrintCallback-shim .progress-actions {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
<div class="mozPrintCallback-dialog-box">
|
||||
<!-- TODO: Localise the following strings -->
|
||||
Preparing document for printing...
|
||||
<div class="progress-row">
|
||||
<progress value="0" max="100"></progress>
|
||||
<span class="relative-progress">0%</span>
|
||||
</div>
|
||||
<div class="progress-actions">
|
||||
<input type="button" value="Cancel" class="mozPrintCallback-cancel">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
12
static/html/txt.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="/static/oxjs/build/Ox.js" type="text/javascript"></script>
|
||||
<script src="/static/txt.js/txt.js" type="text/javascript"></script>
|
||||
<script>
|
||||
txtjs.open(document.location.pathname.replace(/\/reader\//, '/txt/'));
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
41
static/js/Preferences.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
oml.Preferences = (function() {
|
||||
|
||||
var that = {};
|
||||
|
||||
that.set = function() {
|
||||
|
||||
var args = Ox.isObject(arguments[0])
|
||||
? args
|
||||
: Ox.makeObject([arguments[0], arguments[1]]),
|
||||
set = {},
|
||||
preferences = oml.user.preferences,
|
||||
previousPreferences = Ox.clone(preferences, true);
|
||||
|
||||
Ox.forEach(args, function(value, key) {
|
||||
if (!Ox.isEqual(preferences[key], value)) {
|
||||
preferences[key] = value;
|
||||
set[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (Ox.len(set)) {
|
||||
oml.api.setPreferences(set);
|
||||
Ox.forEach(set, function(value, key) {
|
||||
Ox.forEach(oml.$ui, function($element) {
|
||||
if (Ox.UI.isElement($element)) {
|
||||
$element.triggerEvent('oml_' + key.toLowerCase(), {
|
||||
value: value,
|
||||
previousValue: previousPreferences[key]
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
}());
|
||||
181
static/js/UI.js
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
'use strict';
|
||||
|
||||
oml.UI = (function() {
|
||||
|
||||
var previousUI = {},
|
||||
that = {};
|
||||
|
||||
that.encode = function(value) {
|
||||
return value.replace(/\./g, '\\.');
|
||||
};
|
||||
|
||||
that.reset = function() {
|
||||
var ui = oml.user.ui;
|
||||
oml.api.resetUI({}, function() {
|
||||
ui = oml.config.user.ui;
|
||||
ui._list = oml.getListState(ui.find);
|
||||
ui._filterState = oml.getFilterState(ui.find);
|
||||
ui._findState = oml.getFindState(ui.find);
|
||||
Ox.Theme(ui.theme);
|
||||
oml.$ui.appPanel.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// sets oml.user.ui.key to value
|
||||
// key foo.bar.baz sets oml.user.ui.foo.bar.baz
|
||||
// value null removes a key
|
||||
that.set = function(/* {key: value}[, flag] or key, value[, flag] */) {
|
||||
|
||||
var add = {},
|
||||
args,
|
||||
item,
|
||||
list,
|
||||
listSettings = oml.config.listSettings,
|
||||
listView,
|
||||
set = {},
|
||||
trigger = {},
|
||||
triggerEvents,
|
||||
ui = oml.user.ui;
|
||||
|
||||
if (Ox.isObject(arguments[0])) {
|
||||
args = arguments[0];
|
||||
triggerEvents = Ox.isUndefined(arguments[1]) ? true : arguments[1];
|
||||
} else {
|
||||
args = Ox.makeObject([arguments[0], arguments[1]]);
|
||||
triggerEvents = Ox.isUndefined(arguments[2]) ? true : arguments[1];
|
||||
}
|
||||
|
||||
Ox.print('UI SET', JSON.stringify(args));
|
||||
|
||||
previousUI = Ox.clone(ui, true);
|
||||
previousUI._list = oml.getListState(previousUI.find);
|
||||
|
||||
if ('find' in args) {
|
||||
// the challenge here is that find may change list,
|
||||
// and list may then change listSort and listView,
|
||||
// which we don't want to trigger, since find triggers
|
||||
// (values we put in add will be changed, but won't trigger)
|
||||
list = oml.getListState(args.find);
|
||||
ui._list = list;
|
||||
ui._filterState = oml.getFilterState(args.find);
|
||||
ui._findState = oml.getFindState(args.find);
|
||||
if (oml.$ui.appPanel && !oml.stayInItemView) {
|
||||
// if we're not on page load, and if find isn't a context change
|
||||
// caused by an edit, then switch from item view to list view
|
||||
args.item = '';
|
||||
}
|
||||
if (list != previousUI._list) {
|
||||
// if find has changed list
|
||||
Ox.forEach(listSettings, function(listSetting, setting) {
|
||||
// then for each setting that corresponds to a list setting
|
||||
if (!ui.lists[list]) {
|
||||
// either add the default setting
|
||||
add[setting] = oml.config.user.ui[setting];
|
||||
} else {
|
||||
// or the existing list setting
|
||||
add[setting] = ui.lists[list][listSetting]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
list = previousUI._list;
|
||||
}
|
||||
// it is important to check for find first, so that
|
||||
// if find changes list, list is correct here
|
||||
item = args.item || ui.item;
|
||||
listView = add.listView || args.listView;
|
||||
|
||||
if (!ui.lists[list]) {
|
||||
add['lists.' + that.encode(list)] = {};
|
||||
}
|
||||
Ox.forEach(listSettings, function(listSetting, setting) {
|
||||
// for each setting that corresponds to a list setting
|
||||
// set that list setting to
|
||||
var key = 'lists.' + that.encode(list) + '.' + listSetting;
|
||||
if (setting in args) {
|
||||
// the setting passed to UI.set
|
||||
add[key] = args[setting];
|
||||
} else if (setting in add) {
|
||||
// or the setting changed via find
|
||||
add[key] = add[setting];
|
||||
} else if (!ui.lists[list]) {
|
||||
// or the default setting
|
||||
add[key] = oml.config.user.ui[setting];
|
||||
}
|
||||
});
|
||||
|
||||
if (args.item) {
|
||||
// when switching to an item, update list selection
|
||||
add['listSelection'] = [args.item];
|
||||
add['lists.' + that.encode(list) + '.selection'] = [args.item];
|
||||
if (
|
||||
!args.itemView
|
||||
&& ui.itemView == 'book'
|
||||
&& !ui.mediaState[item]
|
||||
&& !args['mediaState.' + item]
|
||||
) {
|
||||
// if the item view doesn't change, remains a media view,
|
||||
// media state doesn't exist yet, and won't be set, add
|
||||
// default media state
|
||||
add['mediaState.' + item] = {position: 0, zoom: 1};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
args.itemView == 'book'
|
||||
&& !ui.mediaState[item]
|
||||
&& !args['mediaState.' + item]
|
||||
) {
|
||||
// when switching to a media view, media state doesn't exist
|
||||
// yet, and won't be set, add default media state
|
||||
add['mediaState.' + item] = {position: 0, zoom: 1};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// items in args trigger events, items in add do not
|
||||
[args, add].forEach(function(object, isAdd) {
|
||||
Ox.forEach(object, function(value, key) {
|
||||
// make sure to not split at escaped dots ('\.')
|
||||
var keys = key.replace(/\\\./g, '\n').split('.').map(function(key) {
|
||||
return key.replace(/\n/g, '.');
|
||||
}),
|
||||
ui_ = ui;
|
||||
while (keys.length > 1) {
|
||||
ui_ = ui_[keys.shift()];
|
||||
}
|
||||
if (!Ox.isEqual(ui_[keys[0]], value)) {
|
||||
if (value === null) {
|
||||
delete ui_[keys[0]];
|
||||
} else {
|
||||
ui_[keys[0]] = value;
|
||||
}
|
||||
set[key] = value;
|
||||
if (!isAdd) {
|
||||
trigger[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (Ox.len(set)) {
|
||||
oml.api.setUI(set);
|
||||
}
|
||||
if (triggerEvents) {
|
||||
Ox.forEach(trigger, function(value, key) {
|
||||
Ox.print('UI TRIGGER', 'oml_' + key.toLowerCase(), value);
|
||||
Ox.forEach(oml.$ui, function($elements) {
|
||||
Ox.makeArray($elements).forEach(function($element) {
|
||||
$element.triggerEvent('oml_' + key.toLowerCase(), {
|
||||
value: value,
|
||||
previousValue: previousUI[key]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
oml.URL.update(Object.keys(!oml.$ui.appPanel ? args : trigger));
|
||||
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
}());
|
||||
349
static/js/URL.js
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
'use strict';
|
||||
|
||||
oml.URL = (function() {
|
||||
|
||||
var self = {}, that = {};
|
||||
|
||||
function getHash(state, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
function getItem(state, string, callback) {
|
||||
oml.api.get({id: string, keys: ['id']}, function(result) {
|
||||
if (result.status.code == 200) {
|
||||
state.item = result.data.id;
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function getPart(state, string, callback) {
|
||||
var parts = Ox.getObjectById(oml.config.pages, state.page).parts || [];
|
||||
if (Ox.contains(parts, string)) {
|
||||
state.part = string;
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
function getSort(state, value, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
function getSpan(state, value, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
// translates UI settings to URL state
|
||||
function getState() {
|
||||
|
||||
var state = {},
|
||||
ui = oml.user.ui;
|
||||
|
||||
if (ui.page) {
|
||||
state.page = ui.page;
|
||||
if (Ox.contains(Object.keys(oml.config.user.ui.part), state.page)) {
|
||||
state.part = ui.part[state.page];
|
||||
}
|
||||
} else {
|
||||
|
||||
state.type = ui.section;
|
||||
state.item = ui.item;
|
||||
|
||||
if (ui.section == 'books') {
|
||||
if (!ui.item) {
|
||||
state.view = ui.listView;
|
||||
state.sort = [ui.listSort[0]];
|
||||
state.find = ui.find;
|
||||
} else {
|
||||
state.view = ui.itemView;
|
||||
if (ui.itemView == 'book') {
|
||||
state.span = ui.mediaState[state.item] || [0, 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
}
|
||||
|
||||
function getURLOptions() {
|
||||
|
||||
var sortKeys = {},
|
||||
ui = oml.user.ui,
|
||||
views = {};
|
||||
|
||||
views['books'] = {
|
||||
// ui.listView is the default view
|
||||
list: [ui.listView].concat(
|
||||
oml.config.listViews.filter(function(view) {
|
||||
return view.id != ui.listView;
|
||||
}).map(function(view) {
|
||||
return view.id;
|
||||
})
|
||||
),
|
||||
// ui.itemView is the default view,
|
||||
item: [ui.itemView].concat(
|
||||
oml.config.itemViews.filter(function(view) {
|
||||
return view.id != ui.itemView;
|
||||
}).map(function(view) {
|
||||
return view.id;
|
||||
})
|
||||
)
|
||||
};
|
||||
|
||||
sortKeys['books'] = {list: {}, item: {}};
|
||||
views['books'].list.forEach(function(view) {
|
||||
sortKeys['books'].list[view] = [].concat(
|
||||
// ui.listSort[0].key is the default sort key
|
||||
Ox.getObjectById(oml.config.sortKeys, ui.listSort[0].key),
|
||||
oml.config.sortKeys.filter(function(key) {
|
||||
return key.id != ui.listSort[0].key;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
findKeys: [{id: 'list', type: 'string'}].concat(
|
||||
oml.config.itemKeys
|
||||
),
|
||||
pages: oml.config.pages.map(function(page) {
|
||||
return page.id;
|
||||
}),
|
||||
spanType: {
|
||||
books: {
|
||||
list: {},
|
||||
item: {
|
||||
book: 'FIXME, no idea'
|
||||
}
|
||||
}
|
||||
},
|
||||
sortKeys: sortKeys,
|
||||
types: ['books'],
|
||||
views: views
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// translates URL state to UI settings
|
||||
function setState(state, callback) {
|
||||
|
||||
var set = {},
|
||||
ui = oml.user.ui;
|
||||
|
||||
ui._list = oml.getListState(ui.find);
|
||||
ui._filterState = oml.getFilterState(ui.find);
|
||||
ui._findState = oml.getFindState(ui.find);
|
||||
|
||||
if (Ox.isEmpty(state)) {
|
||||
|
||||
callback && callback();
|
||||
|
||||
} else {
|
||||
|
||||
if (state.page) {
|
||||
|
||||
set.page = state.page;
|
||||
if (
|
||||
Ox.contains(Object.keys(oml.config.user.ui.part), state.page)
|
||||
&& state.part
|
||||
) {
|
||||
set['part.' + state.page] = state.part;
|
||||
}
|
||||
oml.UI.set(set);
|
||||
callback && callback();
|
||||
|
||||
} else {
|
||||
|
||||
set.page = '';
|
||||
|
||||
if (state.type) {
|
||||
set.section = state.type;
|
||||
set.item = state.item;
|
||||
}
|
||||
|
||||
if (set.section == 'books') {
|
||||
|
||||
if (state.view) {
|
||||
set[!state.item ? 'listView' : 'itemView'] = state.view;
|
||||
}
|
||||
|
||||
if (state.sort) {
|
||||
set[!state.item ? 'listSort' : 'itemSort'] = state.sort;
|
||||
}
|
||||
|
||||
if (state.span) {
|
||||
if (state.view == 'book') {
|
||||
set['mediaState.' + state.item] = {
|
||||
position: state.span[0],
|
||||
zoom: state.span[1]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.item) {
|
||||
if (state.find) {
|
||||
set.find = state.find;
|
||||
} else if (!oml.$ui.appPanel) {
|
||||
// when loading results without find, clear find, so that
|
||||
// removing a query and reloading works as expected
|
||||
set.find = oml.config.user.ui.find;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ox.Request.cancel();
|
||||
|
||||
if (!oml.$ui.appPanel && state.item && ui.find) {
|
||||
// on page load, if item is set and there was a query,
|
||||
// we have to check if the item actually matches the query,
|
||||
// and otherwise reset find
|
||||
oml.api.find({
|
||||
query: ui.find,
|
||||
positions: [state.item],
|
||||
sort: [{key: 'id', operator: ''}]
|
||||
}, function(result) {
|
||||
if (Ox.isUndefined(result.data.positions[state.item])) {
|
||||
set.find = oml.config.user.ui.find
|
||||
}
|
||||
oml.UI.set(set);
|
||||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
oml.UI.set(set);
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
that.init = function() {
|
||||
|
||||
self.URL = Ox.URL(Ox.extend({
|
||||
getHash: getHash,
|
||||
getItem: getItem,
|
||||
getPart: getPart,
|
||||
getSort: getSort,
|
||||
getSpan: getSpan,
|
||||
}, getURLOptions()));
|
||||
|
||||
window.addEventListener('hashchange', function() {
|
||||
Ox.Request.cancel();
|
||||
that.parse();
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', function(e) {
|
||||
Ox.Request.cancel();
|
||||
self.isPopState = true;
|
||||
$('.OxDialog:visible').each(function() {
|
||||
Ox.UI.elements[$(this).data('oxid')].close();
|
||||
});
|
||||
if (e.state && !Ox.isEmpty(e.state)) {
|
||||
document.title = Ox.decodeHTMLEntities(e.state.title);
|
||||
setState(e.state);
|
||||
} else {
|
||||
that.parse();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
|
||||
// on page load, this sets the state from the URL
|
||||
// can also be used to parse a URL
|
||||
that.parse = function(url, callback) {
|
||||
if (arguments.length == 2) {
|
||||
self.URL.parse(url, callback);
|
||||
} else {
|
||||
callback = arguments[0];
|
||||
url = null;
|
||||
if (document.location.pathname.slice(0, 4) == 'url=') {
|
||||
document.location.href = Ox.decodeURI(document.location.pathname.slice(4));
|
||||
} else {
|
||||
self.URL.parse(function(state) {
|
||||
// setState -> UI.set -> URL.update
|
||||
setState(state, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
// sets the URL to the previous URL
|
||||
that.pop = function() {
|
||||
self.URL.pop() || that.update();
|
||||
return that;
|
||||
};
|
||||
|
||||
// pushes a new URL (as string or from state)
|
||||
that.push = function(stateOrURL, expandURL) {
|
||||
var state,
|
||||
title = oml.getPageTitle(stateOrURL)
|
||||
url;
|
||||
oml.replaceURL = expandURL;
|
||||
if (Ox.isObject(stateOrURL)) {
|
||||
state = stateOrURL;
|
||||
} else {
|
||||
url = stateOrURL;
|
||||
}
|
||||
self.URL.push(state, title, url, setState);
|
||||
return that;
|
||||
};
|
||||
|
||||
// replaces the current URL (as string or from state)
|
||||
that.replace = function(stateOrURL, title) {
|
||||
var state,
|
||||
title = oml.getPageTitle(stateOrURL)
|
||||
url;
|
||||
if (Ox.isObject(stateOrURL)) {
|
||||
state = stateOrURL;
|
||||
} else {
|
||||
url = stateOrURL;
|
||||
}
|
||||
self.URL.push(state, title, url, setState);
|
||||
return that;
|
||||
};
|
||||
|
||||
// this gets called from oml.UI
|
||||
that.update = function(keys) {
|
||||
var action, state;
|
||||
if (keys.some(function(key) {
|
||||
return Ox.contains(['itemView', 'listSort', 'listView'], key);
|
||||
})) {
|
||||
self.URL.options(getURLOptions());
|
||||
}
|
||||
if (self.isPopState) {
|
||||
self.isPopState = false;
|
||||
} else {
|
||||
if (
|
||||
!oml.$ui.appPanel
|
||||
|| oml.replaceURL
|
||||
|| keys.every(function(key) {
|
||||
return Ox.contains([
|
||||
'listColumnWidth', 'listColumns', 'listSelection'
|
||||
], key) || /^mediaState/.test(key);
|
||||
})
|
||||
) {
|
||||
action = 'replace';
|
||||
} else {
|
||||
action = 'push';
|
||||
}
|
||||
state = getState();
|
||||
self.URL[action](
|
||||
state,
|
||||
oml.getPageTitle(state)
|
||||
);
|
||||
oml.replaceURL = false;
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
}());
|
||||
69
static/js/allItems.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.allItems = function(user) {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
format: function() {
|
||||
return $('<img>')
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL(user ? 'symbolUser' : 'symbolData')
|
||||
})
|
||||
.css({
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '2px -2px 2px 0'
|
||||
});
|
||||
},
|
||||
id: 'id',
|
||||
title: 'ID',
|
||||
visible: true,
|
||||
width: 16
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
visible: true,
|
||||
width: ui.sidebarSize - 58,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return value > -1
|
||||
? '<span class="OxLight">'
|
||||
+ Ox.formatNumber(value)
|
||||
+ '</span>'
|
||||
: '';
|
||||
},
|
||||
id: 'items',
|
||||
title: 'Items',
|
||||
visible: true,
|
||||
width: 42
|
||||
}
|
||||
],
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
title: Ox._(user ? 'Library' : 'All Libraries'),
|
||||
items: -1
|
||||
}
|
||||
],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
selected: [],
|
||||
unique: 'id'
|
||||
})
|
||||
.css({
|
||||
width: ui.sidebarSize + 'px',
|
||||
height: '16px'
|
||||
});
|
||||
|
||||
that.resizeElement = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
108
static/js/appDialog.js
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.appDialog = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
tabs = Ox.getObjectById(oml.config.pages, 'app').parts.map(function(tab) {
|
||||
return {
|
||||
id: tab.id,
|
||||
title: tab.title.replace(/ Open Media Library$/, ''),
|
||||
selected: tab.id == ui.part.app
|
||||
};
|
||||
}),
|
||||
|
||||
$panel = Ox.TabPanel({
|
||||
content: function(id) {
|
||||
var $logo = Ox.Element(),
|
||||
$text = Ox.Element()
|
||||
.addClass('OxTextPage'),
|
||||
title = Ox.getObjectById(
|
||||
Ox.getObjectById(oml.config.pages, 'app').parts,
|
||||
id
|
||||
).title;
|
||||
$('<img>')
|
||||
.attr({
|
||||
src: '/static/png/oml.png'
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
top: '16px',
|
||||
width: '192px',
|
||||
height: '192px'
|
||||
})
|
||||
.appendTo($logo);
|
||||
$('<div>')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
right: '24px',
|
||||
top: '24px',
|
||||
overflowY: 'auto'
|
||||
})
|
||||
.html(
|
||||
'<h1><b>' + title + '</b></h1>'
|
||||
+ '<p>The lazy brown fox jumped over the lazy black fox, but otherwise not really much happened here since you last checked.'
|
||||
)
|
||||
.appendTo($text);
|
||||
return Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: $logo,
|
||||
size: 208
|
||||
},
|
||||
{
|
||||
element: $text
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
},
|
||||
tabs: tabs
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
oml.UI.set({'part.app': data.selected});
|
||||
}
|
||||
}),
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'close',
|
||||
title: Ox._('Close')
|
||||
}).bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: $panel,
|
||||
fixedSize: true,
|
||||
height: 384,
|
||||
removeOnClose: true,
|
||||
title: 'Open Media Library',
|
||||
width: 768
|
||||
})
|
||||
.bindEvent({
|
||||
close: function() {
|
||||
if (ui.page == 'app') {
|
||||
oml.UI.set({page: ''});
|
||||
}
|
||||
},
|
||||
'oml_part.app': function() {
|
||||
if (ui.page == 'app') {
|
||||
that.update();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function(section) {
|
||||
$panel.selectTab(section);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
51
static/js/appPanel.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.appPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: oml.$ui.mainMenu = oml.ui.mainMenu(),
|
||||
size: 20
|
||||
},
|
||||
{
|
||||
element: oml.$ui.mainPanel = oml.ui.mainPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_page: function(data) {
|
||||
setPage(data.value);
|
||||
}
|
||||
});
|
||||
|
||||
setPage(ui.page);
|
||||
|
||||
function setPage(page) {
|
||||
// close dialogs
|
||||
$('.OxDialog:visible').each(function() {
|
||||
Ox.UI.elements[$(this).data('oxid')].close();
|
||||
});
|
||||
// open dialog
|
||||
if (Ox.contains([
|
||||
'welcome', 'app', 'preferences', 'users',
|
||||
'notifications', 'transfers', 'help'
|
||||
], page)) {
|
||||
oml.$ui[page + 'Dialog'] = oml.ui[page + 'Dialog']().open();
|
||||
}
|
||||
}
|
||||
|
||||
that.reload = function() {
|
||||
Ox.Request.cancel();
|
||||
Ox.Request.clearCache();
|
||||
oml.$ui.appPanel.remove();
|
||||
oml.$ui.appPanel = oml.ui.appPanel().appendTo(Ox.$body);
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
25
static/js/backButton.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.backButton = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Button({
|
||||
style: 'squared',
|
||||
title: 'arrowLeft',
|
||||
tooltip: Ox._('Back to Books'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px 4px 4px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({item: ''});
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
106
static/js/browser.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.browser = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.IconList({
|
||||
centered: true,
|
||||
defaultRatio: oml.config.coverRatio,
|
||||
draggable: true,
|
||||
item: function(data, sort, size) {
|
||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
||||
return rgb.concat(0.8)
|
||||
}),
|
||||
ratio = data.coverRatio || oml.config.coverRatio,
|
||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||
height = Math.round(ratio <= 1 ? size : size / ratio),
|
||||
sortKey = sort[0].key,
|
||||
info = Ox.getObjectById(oml.config.sortKeys, sortKey).format(
|
||||
Ox.contains(['title', 'random'], sortKey)
|
||||
? (data.author || '') : data[sortKey]
|
||||
);
|
||||
size = size || 64;
|
||||
return {
|
||||
extra: ui.showFileInfo ? $('<div>')
|
||||
.css({
|
||||
width: width + 'px',
|
||||
height: Math.round(size / 12.8) + 'px',
|
||||
borderWidth: Math.round(size / 64) + 'px 0',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||
fontSize: Math.round(size / 16) + 'px',
|
||||
textAlign: 'center',
|
||||
color: 'rgba(' + color[1].join(', ') + ')',
|
||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
||||
WebkitTransform: 'rotate(45deg)'
|
||||
})
|
||||
.html(
|
||||
ui.fileInfo == 'extension'
|
||||
? data.extension.toUpperCase()
|
||||
: Ox.formatValue(data.size, 'B')
|
||||
) : null,
|
||||
height: height,
|
||||
id: data.id,
|
||||
info: info,
|
||||
title: data.title,
|
||||
url: '/' + data.id + '/cover128.jpg',
|
||||
width: width
|
||||
};
|
||||
},
|
||||
items: function(data, callback) {
|
||||
oml.api.find(Ox.extend(data, {
|
||||
query: ui.find
|
||||
}), callback);
|
||||
},
|
||||
keys: [
|
||||
'author', 'coverRatio', 'extension',
|
||||
'id', 'size', 'textsize', 'title'
|
||||
],
|
||||
max: 1,
|
||||
min: 1,
|
||||
orientation: 'horizontal',
|
||||
// FIXME: is this correct?:
|
||||
selected: ui.item ? [ui.item]
|
||||
: ui.listSelection.length ? [ui.listSelection[0]]
|
||||
: [],
|
||||
size: 64,
|
||||
sort: ui.listSort,
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
open: function() {
|
||||
oml.UI.set({itemView: 'book'});
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({
|
||||
item: data.ids[0],
|
||||
itemView: 'info',
|
||||
listSelection: data.ids
|
||||
});
|
||||
},
|
||||
oml_find: function() {
|
||||
that.reloadList();
|
||||
},
|
||||
oml_item: function(data) {
|
||||
if (data.value && !data.previousValue) {
|
||||
that.gainFocus();
|
||||
}
|
||||
},
|
||||
oml_listselection: function(data) {
|
||||
if (data.value.length) {
|
||||
that.options({selected: [data.value[0]]});
|
||||
}
|
||||
},
|
||||
oml_listsort: function(data) {
|
||||
that.options({sort: data.value});
|
||||
},
|
||||
oml_sidebarsize: function(data) {
|
||||
that.size(); // FIXME: DOESN'T CENTER
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
148
static/js/columnView.js
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.columnView = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.CustomColumnList({
|
||||
columns: [
|
||||
{
|
||||
id: 'authors',
|
||||
item: getItemFunction('authors'),
|
||||
itemHeight: 32,
|
||||
items: function(data, selected, callback) {
|
||||
oml.api.find(Ox.extend({
|
||||
group: 'author',
|
||||
query: {conditions: [], operator: '&'}
|
||||
}, data), callback);
|
||||
},
|
||||
keys: ['name', 'items'],
|
||||
max: -1,
|
||||
sort: [{key: 'name', operator: '+'}],
|
||||
selected: [],
|
||||
title: Ox._('Authors'),
|
||||
unique: 'name'
|
||||
},
|
||||
{
|
||||
id: 'items',
|
||||
item: getItemFunction('items'),
|
||||
itemHeight: 32,
|
||||
items: function(data, selected, callback) {
|
||||
if (selected[0].length) {
|
||||
oml.api.find(Ox.extend({
|
||||
query: {
|
||||
conditions: selected[0].map(function(name) {
|
||||
return {
|
||||
key: 'author',
|
||||
operator: '==',
|
||||
value: name
|
||||
};
|
||||
}),
|
||||
operator: '|'
|
||||
}
|
||||
}, data), callback);
|
||||
} else {
|
||||
callback({
|
||||
data: {
|
||||
items: data.keys ? [] : 0
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
keys: ['author', 'title', 'date'],
|
||||
max: -1,
|
||||
selected: [],
|
||||
sort: [{key: 'title', operator: '+'}],
|
||||
title: Ox._('Items'),
|
||||
unique: 'id'
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
item: getItemFunction('files'),
|
||||
itemHeight: 32,
|
||||
items: function(data, selected, callback) {
|
||||
oml.api.find(Ox.extend({
|
||||
query: {
|
||||
conditions: selected[0].map(function(name) {
|
||||
return {
|
||||
key: 'author',
|
||||
operator: '==',
|
||||
value: name
|
||||
};
|
||||
}),
|
||||
operator: '|'
|
||||
}
|
||||
}, data), callback);
|
||||
},
|
||||
keys: ['id', 'name'],
|
||||
selected: [],
|
||||
sort: [{key: 'name', operator: '+'}],
|
||||
title: Ox._('Files'),
|
||||
unique: 'id'
|
||||
}
|
||||
],
|
||||
width: window.innerWidth - (ui.showSidebar * ui.sidebarSize) - 1
|
||||
});
|
||||
|
||||
function getItemFunction(id) {
|
||||
return function(data, width) {
|
||||
var $item = $('<div>')
|
||||
.css({
|
||||
height: '32px',
|
||||
width: width + 'px'
|
||||
})
|
||||
if (!Ox.isEmpty(data)) {
|
||||
$('<img>')
|
||||
.attr({
|
||||
src: '/static/png/oml.png'
|
||||
})
|
||||
.css({
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
left: '2px',
|
||||
top: '2px',
|
||||
width: '26px',
|
||||
height: '26px',
|
||||
border: '1px solid rgb(192, 192, 192)',
|
||||
backgroundImage: '-webkit-linear-gradient(top, rgb(255, 255, 255), rgb(224, 224, 224))'
|
||||
})
|
||||
.appendTo($item);
|
||||
$('<div>')
|
||||
.css({
|
||||
position: 'relative',
|
||||
left: '34px',
|
||||
top: '-28px',
|
||||
width: width - 36 + 'px',
|
||||
height: '16px',
|
||||
fontSize: '13px',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
cursor: 'default'
|
||||
})
|
||||
.html(id == 'authors' ? data.name : 'foo')
|
||||
.appendTo($item);
|
||||
$('<div>')
|
||||
.addClass('OxLight')
|
||||
.css({
|
||||
position: 'relative',
|
||||
left: '34px',
|
||||
top: '-28px',
|
||||
width: width - 36 + 'px',
|
||||
height: '12px',
|
||||
fontSize: '9px',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
cursor: 'default'
|
||||
})
|
||||
.html(id == 'authors' ? Ox.formatCount(data.items, 'Item') : 'bar')
|
||||
.appendTo($item);
|
||||
}
|
||||
return $item;
|
||||
};
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
23
static/js/confirmDialog.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.confirmDialog = function(options, callback) {
|
||||
|
||||
options = Ox.extend(options, {
|
||||
buttons: options.buttons.map(function($button, index) {
|
||||
return $button
|
||||
.options({id: index ? 'yes' : 'no'})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
index && callback();
|
||||
}
|
||||
});
|
||||
}),
|
||||
keys: {enter: 'yes', escape: 'no'}
|
||||
});
|
||||
|
||||
var that = oml.ui.iconDialog(options);
|
||||
|
||||
return that.open();
|
||||
|
||||
}
|
||||
33
static/js/connectionButton.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.connectionButton = function() {
|
||||
|
||||
var that = Ox.Element({
|
||||
tooltip: Ox._('Disconnected')
|
||||
})
|
||||
.css({
|
||||
marginRight: '3px'
|
||||
})
|
||||
.bindEvent({
|
||||
// ...
|
||||
});
|
||||
|
||||
/*
|
||||
oml.ui.statusIcon(oml.user.online ? 'connected' : 'disconnected')
|
||||
.css({float: 'left'})
|
||||
.appendTo(that);
|
||||
*/
|
||||
|
||||
Ox.Element()
|
||||
.addClass('OxLight')
|
||||
.css({
|
||||
float: 'left',
|
||||
marginTop: '2px',
|
||||
fontSize: '9px'
|
||||
})
|
||||
.html('↓0K/↑0K')
|
||||
.appendTo(that);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
76
static/js/errorDialog.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.errorDialog = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = oml.ui.iconDialog({
|
||||
buttons: getButtons(),
|
||||
content: Ox.Element(),
|
||||
keys: {enter: 'close', escape: 'close'}
|
||||
})
|
||||
.addClass('OxErrorDialog')
|
||||
.bindEvent({
|
||||
oml_enabledebugmenu: function() {
|
||||
that.options({buttons: getButtons()});
|
||||
}
|
||||
}),
|
||||
|
||||
open = that.open;
|
||||
|
||||
function getButtons() {
|
||||
return (ui.enableDebugMenu ? [
|
||||
Ox.Button({
|
||||
title: Ox._('View Error Logs...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
oml.UI.set({page: 'errorlogs'});
|
||||
}
|
||||
}),
|
||||
{}
|
||||
] : []).concat([
|
||||
Ox.Button({
|
||||
id: 'close',
|
||||
title: Ox._('Close')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
that.open = function() {
|
||||
// on window unload, pending request will time out, so
|
||||
// in order to keep the dialog from appearing, delay it
|
||||
setTimeout(function() {
|
||||
if ($('.OxErrorDialog').length == 0 && !oml.isUnloading) {
|
||||
open();
|
||||
}
|
||||
}, 250);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.update = function(data) {
|
||||
// 0 (timeout) or 500 (error)
|
||||
var error = data.status.code == 0 ? 'a timeout' : 'an error',
|
||||
title = data.status.code == 0 ? 'Timeout' : 'Error';
|
||||
that.options({
|
||||
content: Ox.Element().html(
|
||||
Ox._(
|
||||
'Sorry, {0} occured while handling your request.'
|
||||
+ ' In case this happens repeatedly, you may want to file a bug report.'
|
||||
+ ' Otherwise, please try again later.', [error]
|
||||
)
|
||||
),
|
||||
title: title
|
||||
});
|
||||
return that;
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
192
static/js/filter.js
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.filter = function(id) {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
filter = Ox.getObjectById(oml.config.filters, id),
|
||||
filterIndex = Ox.getIndexById(ui.filters, id),
|
||||
filterSize = oml.getFilterSizes()[filterIndex],
|
||||
|
||||
that = Ox.TableList({
|
||||
_selected: !ui.showFilters
|
||||
? ui._filterState[filterIndex].selected
|
||||
: false,
|
||||
columns: [
|
||||
{
|
||||
id: 'name',
|
||||
operator: '+',
|
||||
title: Ox._(filter.title),
|
||||
visible: true,
|
||||
width: filterSize - 44 - Ox.UI.SCROLLBAR_SIZE
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return Ox.formatNumber(value);
|
||||
},
|
||||
id: 'items',
|
||||
operator: '-',
|
||||
title: '#',
|
||||
visible: true,
|
||||
width: 44
|
||||
}
|
||||
],
|
||||
columnsVisible: true,
|
||||
items: function(data, callback) {
|
||||
if (ui.showFilters) {
|
||||
delete data.keys;
|
||||
return oml.api.find(Ox.extend(data, {
|
||||
group: filter.id,
|
||||
query: ui._filterState[filterIndex].find
|
||||
}), callback);
|
||||
} else {
|
||||
callback({
|
||||
data: {items: data.keys ? [] : 0}
|
||||
});
|
||||
}
|
||||
},
|
||||
scrollbarVisible: true,
|
||||
selected: ui.showFilters
|
||||
? ui._filterState[filterIndex].selected
|
||||
: [],
|
||||
sort: Ox.clone(ui.filters[filterIndex].sort, true),
|
||||
unique: 'name'
|
||||
})
|
||||
.bindEvent({
|
||||
init: function(data) {
|
||||
that.setColumnTitle(
|
||||
'name',
|
||||
Ox._(filter.title)
|
||||
+ '<div class="OxColumnStatus OxLight">'
|
||||
+ Ox.formatNumber(data.items) + '</div>'
|
||||
);
|
||||
},
|
||||
select: function(data) {
|
||||
Ox.print('UI FILTER STATE', ui._filterState)
|
||||
// fixme: cant index be an empty array, instead of -1?
|
||||
// FIXME: this is still incorrect when deselecting a filter item
|
||||
// makes a selected item in another filter disappear
|
||||
var conditions = data.ids.map(function(value) {
|
||||
return {
|
||||
key: id,
|
||||
value: value,
|
||||
operator: '=='
|
||||
};
|
||||
}),
|
||||
index = ui._filterState[filterIndex].index,
|
||||
find = Ox.clone(ui.find, true);
|
||||
if (Ox.isArray(index)) {
|
||||
// this filter had multiple selections and the | query
|
||||
// was on the top level, i.e. not bracketed
|
||||
find = {
|
||||
conditions: conditions,
|
||||
operator: conditions.length > 1 ? '|' : '&'
|
||||
}
|
||||
} else {
|
||||
if (index == -1) {
|
||||
// this filter had no selection, i.e. no query
|
||||
index = find.conditions.length;
|
||||
if (find.operator == '|') {
|
||||
find = {
|
||||
conditions: [find],
|
||||
operator: '&'
|
||||
};
|
||||
index = 1;
|
||||
} else {
|
||||
find.operator = '&';
|
||||
}
|
||||
}
|
||||
if (conditions.length == 0) {
|
||||
// nothing selected
|
||||
find.conditions.splice(index, 1);
|
||||
if (find.conditions.length == 1) {
|
||||
if (find.conditions[0].conditions) {
|
||||
// unwrap single remaining bracketed query
|
||||
find = {
|
||||
conditions: find.conditions[0].conditions,
|
||||
operator: '|'
|
||||
};
|
||||
} else {
|
||||
find.operator = '&';
|
||||
}
|
||||
}
|
||||
} else if (conditions.length == 1) {
|
||||
// one item selected
|
||||
find.conditions[index] = conditions[0];
|
||||
} else {
|
||||
// multiple items selected
|
||||
if (ui.find.conditions.length == 1) {
|
||||
find = {
|
||||
conditions: conditions,
|
||||
operator: '|'
|
||||
};
|
||||
} else {
|
||||
find.conditions[index] = {
|
||||
conditions: conditions,
|
||||
operator: '|'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
oml.UI.set({find: find});
|
||||
oml.updateFilterMenus();
|
||||
},
|
||||
sort: function(data) {
|
||||
var filters = Ox.clone(ui.filters, true);
|
||||
filters[filterIndex].sort = [Ox.clone(data)];
|
||||
oml.UI.set({filters: filters});
|
||||
},
|
||||
oml_find: function() {
|
||||
Ox.print('%%%%', 'RELOADING FILTER')
|
||||
that.reloadList(true);
|
||||
}
|
||||
}),
|
||||
|
||||
$menu = Ox.MenuButton({
|
||||
items: [
|
||||
{id: 'clearFilter', title: Ox._('Clear Filter'), keyboard: 'shift control a'},
|
||||
{id: 'clearFilters', title: Ox._('Clear All Filters'), keyboard: 'shift alt control a'},
|
||||
{},
|
||||
{group: 'filter', max: 1, min: 1, items: oml.config.filters.map(function(filter) {
|
||||
return Ox.extend({checked: filter.id == id}, filter);
|
||||
})}
|
||||
],
|
||||
type: 'image',
|
||||
})
|
||||
.css(Ox.UI.SCROLLBAR_SIZE == 16 ? {
|
||||
right: 0,
|
||||
width: '14px'
|
||||
} : {
|
||||
right: '-1px',
|
||||
width: '8px',
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
|
||||
},
|
||||
click: function(data) {
|
||||
|
||||
}
|
||||
})
|
||||
.appendTo(that.$bar.$element);
|
||||
|
||||
if (Ox.UI.SCROLLBAR_SIZE < 16) {
|
||||
$($menu.find('input')[0]).css({
|
||||
marginRight: '-3px',
|
||||
marginTop: '1px',
|
||||
width: '8px',
|
||||
height: '8px'
|
||||
});
|
||||
}
|
||||
|
||||
that.disableMenuItem = function(id) {
|
||||
$menu.disableItem(id);
|
||||
};
|
||||
|
||||
that.enableMenuItem = function(id) {
|
||||
$menu.enableItem(id);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
26
static/js/filtersInnerPanel.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.filtersInnerPanel = function() {
|
||||
|
||||
var filterSizes = oml.getFilterSizes(),
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: oml.$ui.filters[1],
|
||||
size: filterSizes[1]
|
||||
},
|
||||
{
|
||||
element: oml.$ui.filters[2]
|
||||
},
|
||||
{
|
||||
element: oml.$ui.filters[3],
|
||||
size: filterSizes[3]
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
82
static/js/filtersOuterPanel.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.filtersOuterPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
$filters = oml.$ui.filters = ui.filters.map(function(filter) {
|
||||
return oml.ui.filter(filter.id);
|
||||
}),
|
||||
|
||||
filterSizes = oml.getFilterSizes(),
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: $filters[0],
|
||||
size: filterSizes[0]
|
||||
},
|
||||
{
|
||||
element: oml.$ui.filtersInnerPanel = oml.ui.filtersInnerPanel()
|
||||
},
|
||||
{
|
||||
element: $filters[4],
|
||||
size: filterSizes[4]
|
||||
},
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
})
|
||||
.bindEvent({
|
||||
resize: function() {
|
||||
oml.$ui.filters.forEach(function($filter) {
|
||||
$filter.size();
|
||||
});
|
||||
},
|
||||
resizeend: function(data) {
|
||||
oml.UI.set({filtersSize: data.size});
|
||||
},
|
||||
toggle: function(data) {
|
||||
if (data.collapsed) {
|
||||
oml.$ui.list.gainFocus();
|
||||
}
|
||||
oml.UI.set({showFilters: !data.collapsed});
|
||||
if (!data.collapsed) {
|
||||
oml.$ui.filters.forEach(function($filter) {
|
||||
var selected = $filter.options('_selected');
|
||||
if (selected) {
|
||||
$filter.bindEventOnce({
|
||||
load: function() {
|
||||
$filter.options({
|
||||
_selected: false,
|
||||
selected: selected
|
||||
});
|
||||
}
|
||||
}).reloadList();
|
||||
}
|
||||
});
|
||||
oml.updateFilterMenus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
oml.updateFilterMenus();
|
||||
|
||||
that.update = function() {
|
||||
var filterSizes = oml.getFilterSizes();
|
||||
that.size(0, filterSizes[0])
|
||||
.size(2, filterSizes[4]);
|
||||
oml.$ui.filtersInnerPanel
|
||||
.size(0, filterSizes[1])
|
||||
.size(2, filterSizes[3]);
|
||||
oml.$ui.filters.forEach(function($filter, index) {
|
||||
$filter.resizeColumn(
|
||||
'name',
|
||||
filterSizes[index] - 44 - Ox.UI.SCROLLBAR_SIZE
|
||||
);
|
||||
});
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
203
static/js/findElement.js
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.findElement = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
findIndex = ui._findState.index,
|
||||
findKey = ui._findState.key,
|
||||
findValue = ui._findState.value,
|
||||
hasPressedClear = false,
|
||||
previousFindKey = findKey,
|
||||
|
||||
that = Ox.FormElementGroup({
|
||||
|
||||
elements: [
|
||||
|
||||
oml.$ui.findScopeSelect = renderFindScopeSelect(),
|
||||
|
||||
oml.$ui.findSelect = Ox.Select({
|
||||
id: 'select',
|
||||
items: [].concat(
|
||||
oml.config.findKeys.map(function(key) {
|
||||
return {
|
||||
id: key.id,
|
||||
title: Ox._('Find: {0}', [Ox._(key.title)])
|
||||
};
|
||||
}),
|
||||
[{}, {
|
||||
id: 'advanced',
|
||||
title: Ox._('Find: Advanced...')
|
||||
}]
|
||||
),
|
||||
overlap: 'right',
|
||||
style: 'squared',
|
||||
value: findKey,
|
||||
width: 160
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
if (data.value == 'advanced') {
|
||||
that.updateElement();
|
||||
//oml.$ui.mainMenu.checkItem('findMenu_find_' + previousFindKey);
|
||||
oml.$ui.filterDialog = oml.ui.filterDialog().open();
|
||||
} else {
|
||||
//oml.$ui.mainMenu.checkItem('findMenu_find_' + data.value);
|
||||
oml.$ui.findInput.options({
|
||||
autocomplete: getAutocomplete(),
|
||||
placeholder: ''
|
||||
}).focusInput(true);
|
||||
previousFindKey = data.value;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
oml.$ui.findInput = Ox.Input({
|
||||
autocomplete: getAutocomplete(),
|
||||
autocompleteSelect: true,
|
||||
autocompleteSelectHighlight: true,
|
||||
autocompleteSelectMaxWidth: 256,
|
||||
autocompleteSelectSubmit: true,
|
||||
clear: true,
|
||||
clearTooltip: Ox._('Click to clear or doubleclick to reset query'),
|
||||
id: 'input',
|
||||
placeholder: findKey == 'advanced' ? Ox._('Edit Query...') : '',
|
||||
style: 'squared',
|
||||
value: findValue,
|
||||
width: 240
|
||||
})
|
||||
.bindEvent({
|
||||
clear: function() {
|
||||
hasPressedClear = true;
|
||||
},
|
||||
focus: function(data) {
|
||||
if (oml.$ui.findSelect.value() == 'advanced') {
|
||||
if (hasPressedClear) {
|
||||
oml.UI.set({find: oml.site.user.ui.find});
|
||||
that.updateElement();
|
||||
hasPressedClear = false;
|
||||
}
|
||||
oml.$ui.findInput.blurInput();
|
||||
oml.$ui.filterDialog = oml.ui.filterDialog().open();
|
||||
}
|
||||
},
|
||||
submit: function(data) {
|
||||
var scope = oml.$ui.findScopeSelect.value(),
|
||||
key = oml.$ui.findSelect.value(),
|
||||
conditions = [].concat(
|
||||
scope == 'list' ? [{
|
||||
key: 'list',
|
||||
value: ui._list,
|
||||
operator: '=='
|
||||
}] : [],
|
||||
scope == 'user' ? [{
|
||||
key: 'list',
|
||||
value: ui._list.split(':')[0],
|
||||
operator: '=='
|
||||
}] : [],
|
||||
data.value ? [{
|
||||
key: key,
|
||||
value: data.value,
|
||||
operator: '='
|
||||
}] : []
|
||||
);
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: conditions,
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
]
|
||||
})
|
||||
.css({
|
||||
float: 'right',
|
||||
margin: '4px 4px 4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_find: function() {
|
||||
that.replaceElement(
|
||||
0, oml.$ui.findScopeSelect = renderFindScopeSelect()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function getAutocomplete() {
|
||||
var key = !that
|
||||
? ui._findState.key
|
||||
: that.value()[ui._list ? 1 : 0],
|
||||
findKey = Ox.getObjectById(oml.config.findKeys, key);
|
||||
return findKey && findKey.autocomplete ? function(value, callback) {
|
||||
oml.api.autocomplete({
|
||||
key: key,
|
||||
query: {
|
||||
conditions: ui._list
|
||||
&& oml.$ui.findScopeSelect.value() == 'list'
|
||||
? [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: ui._list
|
||||
}]
|
||||
: [],
|
||||
operator: '&'
|
||||
},
|
||||
range: [0, 20],
|
||||
sort: findKey.autocompleteSort,
|
||||
value: value
|
||||
}, function(result) {
|
||||
callback(result.data.items.map(function(item) {
|
||||
return Ox.decodeHTMLEntities(item);
|
||||
}));
|
||||
});
|
||||
} : null;
|
||||
}
|
||||
|
||||
function renderFindScopeSelect() {
|
||||
var scope = !ui._list ? 'all'
|
||||
: Ox.endsWith(ui._list, ':') ? 'user'
|
||||
: 'list';
|
||||
return Ox.Select({
|
||||
items: [
|
||||
{id: 'all', title: Ox._('Find: All Libraries')},
|
||||
].concat(scope != 'all' ? [
|
||||
{id: 'user', title: Ox._('Find: This Library')},
|
||||
] : []).concat(scope == 'list' ? [
|
||||
{id: 'list', title: Ox._('Find: This List')}
|
||||
] : []),
|
||||
overlap: 'right',
|
||||
style: 'squared',
|
||||
title: scope == 'all' ? 'data' : scope,
|
||||
type: 'image',
|
||||
tooltip: Ox._('Find: FIXME'),
|
||||
value: scope
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
oml.$ui.findScopeSelect.options({
|
||||
title: data.value == 'all' ? 'data' : data.value,
|
||||
tooltip: data.title
|
||||
});
|
||||
oml.$ui.findInput.focusInput(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
that.updateElement = function() {
|
||||
var findState = ui._findState;
|
||||
oml.$ui.findSelect.value(findState.key);
|
||||
oml.$ui.findInput.options(
|
||||
findState.key == 'advanced' ? {
|
||||
placeholder: Ox._('Edit Query...'),
|
||||
value: ''
|
||||
} : {
|
||||
autocomplete: getAutocomplete(),
|
||||
placeholder: '',
|
||||
value: findState.value
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
67
static/js/folderList.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.folderList = function(options) {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
format: function(value) {
|
||||
return $('<img>')
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL(
|
||||
value == 'libraries' ? 'symbolData'
|
||||
: value == 'library' ? 'symbolUser'
|
||||
: value == 'static' ? 'symbolClick'
|
||||
: 'symbolFind'
|
||||
)
|
||||
})
|
||||
.css({
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '2px -2px 2px 0'
|
||||
});
|
||||
},
|
||||
id: 'type',
|
||||
visible: true,
|
||||
width: 16
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
visible: true,
|
||||
width: ui.sidebarSize - 58,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: function(value) {
|
||||
return value > -1
|
||||
? '<span class="OxLight">'
|
||||
+ Ox.formatNumber(value)
|
||||
+ '</span>'
|
||||
: '';
|
||||
},
|
||||
id: 'items',
|
||||
visible: true,
|
||||
width: 42
|
||||
}
|
||||
],
|
||||
draggable: options.draggable,
|
||||
items: options.items,
|
||||
sort: [{key: 'index', operator: '+'}],
|
||||
sortable: options.sortable,
|
||||
selected: [],
|
||||
unique: 'id'
|
||||
})
|
||||
.css({
|
||||
width: ui.sidebarSize + 'px',
|
||||
height: '16px'
|
||||
});
|
||||
|
||||
that.resizeElement = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
20
static/js/folderPlaceholder.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
// FIXME: UNUSED
|
||||
|
||||
oml.ui.folderPlaceholder = function(text) {
|
||||
|
||||
var that = Ox.Element()
|
||||
.addClass('OxLight')
|
||||
.css({
|
||||
height: '14px',
|
||||
padding: '1px 4px',
|
||||
});
|
||||
|
||||
that.updateText = function(text) {
|
||||
return that.html(text);
|
||||
};
|
||||
|
||||
return that.updateText(text);
|
||||
|
||||
};
|
||||
286
static/js/folders.js
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.folders = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
userIndex = {},
|
||||
|
||||
$lists = [],
|
||||
|
||||
that = Ox.Element()
|
||||
.css({
|
||||
//overflowX: 'hidden',
|
||||
//overflowY: 'auto',
|
||||
})
|
||||
.bindEvent({
|
||||
oml_find: selectList
|
||||
});
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.librariesList = oml.ui.folderList({
|
||||
items: [
|
||||
{
|
||||
id: '',
|
||||
name: Ox._('All Libraries'),
|
||||
type: 'libraries',
|
||||
items: -1
|
||||
}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
load: function() {
|
||||
oml.api.find({query: getFind()}, function(result) {
|
||||
oml.$ui.librariesList.value('', 'items', result.data.items);
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
|
||||
},
|
||||
select: function() {
|
||||
oml.UI.set({find: getFind('')});
|
||||
oml.$ui.librariesList.options({selected: ['']});
|
||||
},
|
||||
selectnext: function() {
|
||||
oml.UI.set(Ox.extend(
|
||||
{find: getFind(':')},
|
||||
'showFolder.' + oml.user.preferences.username,
|
||||
true
|
||||
));
|
||||
},
|
||||
})
|
||||
.css({height: '16px'})
|
||||
.appendTo(that)
|
||||
);
|
||||
oml.$ui.librariesList.$body.css({height: '16px'}); // FIXME!
|
||||
|
||||
oml.$ui.folder = [];
|
||||
oml.$ui.libraryList = [];
|
||||
oml.$ui.folderList = [];
|
||||
|
||||
oml.api.getUsers(function(result) {
|
||||
|
||||
var peers = result.data.users.filter(function(user) {
|
||||
return user.peered;
|
||||
});
|
||||
|
||||
oml.api.getLists(function(result) {
|
||||
|
||||
Ox.print('GOT LISTS', result.data);
|
||||
|
||||
var users = [
|
||||
{
|
||||
id: oml.user.id,
|
||||
nickname: oml.user.preferences.username,
|
||||
online: oml.user.online
|
||||
}
|
||||
].concat(peers),
|
||||
|
||||
lists = result.data.lists;
|
||||
|
||||
users.forEach(function(user, index) {
|
||||
|
||||
var $content,
|
||||
libraryId = (!index ? '' : user.nickname) + ':';
|
||||
|
||||
userIndex[user.nickname] = index;
|
||||
|
||||
oml.$ui.folder[index] = Ox.CollapsePanel({
|
||||
collapsed: false,
|
||||
extras: [
|
||||
oml.ui.statusIcon(
|
||||
!oml.user.online ? 'unknown'
|
||||
: user.online ? 'connected'
|
||||
: 'disconnected'
|
||||
),
|
||||
{},
|
||||
Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'info',
|
||||
tooltip: Ox._(!index ? 'Preferences' : 'Profile'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
if (!index) {
|
||||
oml.UI.set({
|
||||
page: 'preferences',
|
||||
'part.preferences': 'account'
|
||||
});
|
||||
} else {
|
||||
oml.UI.set({page: 'users'})
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
title: Ox.encodeHTMLEntities(user.nickname)
|
||||
})
|
||||
.css({
|
||||
width: ui.sidebarSize
|
||||
})
|
||||
.bindEvent({
|
||||
toggle: function(data) {
|
||||
oml.UI.set('showFolder.' + user.nickname, !data.collapsed);
|
||||
}
|
||||
})
|
||||
.bindEvent(
|
||||
'oml_showfolder.' + user.nickname.toLowerCase(),
|
||||
function(data) {
|
||||
oml.$ui.folder[index].options({collapsed: !data.value});
|
||||
}
|
||||
)
|
||||
.appendTo(that);
|
||||
|
||||
$content = oml.$ui.folder[index].$content
|
||||
.css({
|
||||
height: (1 + lists[user.id].length) * 16 + 'px'
|
||||
});
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.libraryList[index] = oml.ui.folderList({
|
||||
items: [
|
||||
{
|
||||
id: libraryId,
|
||||
name: Ox._('Library'),
|
||||
type: 'library',
|
||||
items: -1
|
||||
}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
load: function() {
|
||||
oml.api.find({
|
||||
query: getFind(libraryId)
|
||||
}, function(result) {
|
||||
oml.$ui.libraryList[index].value(
|
||||
libraryId, 'items', result.data.items
|
||||
);
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
oml.UI.set({find: getFind(lists[user.id][0].id)});
|
||||
},
|
||||
selectprevious: function() {
|
||||
var userId = !index ? null : users[index - 1].id,
|
||||
set = {
|
||||
find: getFind(
|
||||
!index
|
||||
? ''
|
||||
: Ox.last(lists[userId]).id
|
||||
)
|
||||
};
|
||||
if (userId) {
|
||||
Ox.extend(set, 'showFolder.' + userId, true);
|
||||
}
|
||||
oml.UI.set(set);
|
||||
}
|
||||
})
|
||||
.appendTo($content)
|
||||
);
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.folderList[index] = oml.ui.folderList({
|
||||
draggable: !!index,
|
||||
items: lists[user.id],
|
||||
sortable: true
|
||||
})
|
||||
.bindEvent({
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
'delete': function() {
|
||||
!index && oml.deleteList();
|
||||
},
|
||||
key_control_d: function() {
|
||||
oml.addList(ui._list);
|
||||
},
|
||||
load: function() {
|
||||
// ...
|
||||
},
|
||||
move: function(data) {
|
||||
lists[user.id] = data.ids.map(function(listId) {
|
||||
return Ox.getObjectById(lists[user.id], listId);
|
||||
});
|
||||
oml.api.sortLists({
|
||||
ids: data.ids,
|
||||
user: user.id
|
||||
}, function(result) {
|
||||
// ...
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
if (index < users.length - 1) {
|
||||
oml.UI.set(Ox.extend(
|
||||
{find: getFind(users[index + 1].nickname + ':')},
|
||||
'showFolder.' + users[index + 1].nickname,
|
||||
true
|
||||
));
|
||||
}
|
||||
},
|
||||
selectprevious: function() {
|
||||
oml.UI.set({find: getFind(libraryId)});
|
||||
}
|
||||
})
|
||||
.bindEvent(function(data, event) {
|
||||
if (!index) {
|
||||
Ox.print('LIST EVENT', event, data);
|
||||
}
|
||||
})
|
||||
.css({height: lists[user.id].length * 16 + 'px'})
|
||||
.appendTo($content)
|
||||
);
|
||||
|
||||
oml.$ui.folderList[index].$body.css({top: '16px'});
|
||||
|
||||
});
|
||||
|
||||
selectList();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
function getFind(list) {
|
||||
return {
|
||||
conditions: list ? [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: list
|
||||
}] : [],
|
||||
operator: '&'
|
||||
};
|
||||
}
|
||||
|
||||
function selectList() {
|
||||
var split = ui._list.split(':'),
|
||||
index = userIndex[split[0] || oml.user.preferences.username],
|
||||
list = split[1],
|
||||
$selectedList = !ui._list ? oml.$ui.librariesList
|
||||
: !list ? oml.$ui[!list ? 'libraryList' : 'folderList'][index]
|
||||
: oml.$ui.folderList[index];
|
||||
$lists.forEach(function($list) {
|
||||
if ($list == $selectedList) {
|
||||
$list.options({selected: [ui._list]}).gainFocus();
|
||||
} else {
|
||||
$list.options({selected: []})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
34
static/js/fullscreenButton.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.fullscreenButton = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Button({
|
||||
style: 'squared',
|
||||
title: 'grow',
|
||||
tooltip: Ox._('Enter Fullscreen'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
Ox.Fullscreen.enter(oml.$ui.viewer.find('iframe')[0]);
|
||||
},
|
||||
oml_itemview: function() {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function() {
|
||||
return that.options({
|
||||
disabled: ui.itemView != 'book'
|
||||
});
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
68
static/js/gridView.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.gridView = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.IconList({
|
||||
defaultRatio: oml.config.coverRatio,
|
||||
draggable: true,
|
||||
item: function(data, sort, size) {
|
||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
||||
return rgb.concat(0.8)
|
||||
}),
|
||||
ratio = data.coverRatio || oml.config.coverRatio,
|
||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||
height = Math.round(ratio <= 1 ? size : size / ratio),
|
||||
sortKey = sort[0].key,
|
||||
info = Ox.getObjectById(oml.config.sortKeys, sortKey).format(
|
||||
Ox.contains(['title', 'random'], sortKey)
|
||||
? (data.author || '') : data[sortKey]
|
||||
);
|
||||
size = size || 128;
|
||||
return {
|
||||
extra: ui.showFileInfo ? $('<div>')
|
||||
.css({
|
||||
width: width + 'px',
|
||||
height: Math.round(size / 12.8) + 'px',
|
||||
borderWidth: Math.round(size / 64) + 'px 0',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||
fontSize: Math.round(size / 16) + 'px',
|
||||
textAlign: 'center',
|
||||
color: 'rgba(' + color[1].join(', ') + ')',
|
||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
||||
WebkitTransform: 'rotate(45deg)'
|
||||
})
|
||||
.html(
|
||||
ui.fileInfo == 'extension'
|
||||
? data.extension.toUpperCase()
|
||||
: Ox.formatValue(data.size, 'B')
|
||||
) : null,
|
||||
height: height,
|
||||
id: data.id,
|
||||
info: info,
|
||||
title: data.title,
|
||||
url: '/' + data.id + '/cover128.jpg',
|
||||
width: width
|
||||
};
|
||||
},
|
||||
items: function(data, callback) {
|
||||
oml.api.find(Ox.extend(data, {
|
||||
query: ui.find
|
||||
}), callback);
|
||||
},
|
||||
keys: [
|
||||
'author', 'coverRatio', 'extension',
|
||||
'id', 'size', 'textsize', 'title'
|
||||
],
|
||||
selected: ui.listSelection,
|
||||
size: 128,
|
||||
sort: ui.listSort,
|
||||
unique: 'id'
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
42
static/js/iconDialog.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.iconDialog = function(options) {
|
||||
|
||||
var options = Ox.extend({
|
||||
closeButton: false,
|
||||
content: '',
|
||||
height: 128,
|
||||
keys: null,
|
||||
title: '',
|
||||
width: 368,
|
||||
}, options),
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: options.buttons,
|
||||
closeButton: options.closeButton,
|
||||
content: Ox.Element()
|
||||
.append(
|
||||
$('<img>')
|
||||
.attr({src: '/static/png/oml.png'})
|
||||
.css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'})
|
||||
)
|
||||
.append(
|
||||
Ox.isObject(options.content)
|
||||
? options.content
|
||||
.css({position: 'absolute', left: '96px', top: '16px', width: options.width - 112 + 'px'})
|
||||
: $('<div>')
|
||||
.addClass('OxTextPage')
|
||||
.css({position: 'absolute', left: '96px', top: '16px', width: options.width - 112 + 'px'})
|
||||
.html(options.content)
|
||||
),
|
||||
fixedSize: true,
|
||||
height: options.height,
|
||||
keys: options.keys,
|
||||
removeOnClose: true,
|
||||
title: options.title,
|
||||
width: options.width
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
69
static/js/info.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.info = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Element()
|
||||
.addClass('OxTextPage')
|
||||
.css({
|
||||
padding: '0 16px',
|
||||
textAlign: 'center',
|
||||
overflowY: 'auto'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_item: function() {
|
||||
that.update();
|
||||
},
|
||||
oml_listselection: function() {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function() {
|
||||
var id = ui.item || ui.listSelection[0];
|
||||
if (id) {
|
||||
oml.api.get({
|
||||
id: id,
|
||||
keys: [
|
||||
'author', 'coverRatio',
|
||||
'description', 'title'
|
||||
]
|
||||
}, function(result) {
|
||||
var data = result.data;
|
||||
that.empty();
|
||||
$('<img>')
|
||||
.attr({src: '/' + id + '/cover128.jpg'})
|
||||
.css({margin: '16px 0 8px 0'})
|
||||
.appendTo(that);
|
||||
$('<div>')
|
||||
.css({
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.html(data.title || '')
|
||||
.appendTo(that);
|
||||
$('<div>')
|
||||
.css({
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.html((data.author || []).join(', '))
|
||||
.appendTo(that);
|
||||
$('<div>')
|
||||
.css({marginTop: '8px'})
|
||||
.html(
|
||||
Ox.encodeHTMLEntities(result.data.description || '')
|
||||
)
|
||||
.appendTo(that);
|
||||
$('<div>')
|
||||
.css({height: '16px'})
|
||||
.appendTo(that);
|
||||
});
|
||||
} else {
|
||||
that.empty();
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
457
static/js/infoView.js
Normal file
|
|
@ -0,0 +1,457 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.infoView = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Element()
|
||||
.addClass('OxTextPage')
|
||||
.css({overflowY: 'auto'})
|
||||
.bindEvent({
|
||||
oml_item: function() {
|
||||
if (ui.item) {
|
||||
that.update(ui.item);
|
||||
}
|
||||
},
|
||||
oml_listselection: function(data) {
|
||||
if (data.value && data.value.length) {
|
||||
that.update(data.value[0]);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
$identifyPanel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: Ox.Element(), size: 120},
|
||||
{element: Ox.Element()}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
}),
|
||||
|
||||
$identifyDialog = Ox.Dialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'dontupdate',
|
||||
title: Ox._('No, Don\'t Update')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
$identifyDialog.close();
|
||||
}
|
||||
}),
|
||||
Ox.Button({
|
||||
disabled: true,
|
||||
id: 'update',
|
||||
title: Ox._('Yes, Update')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
$identifyDialog.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: $identifyPanel,
|
||||
fixedSize: true,
|
||||
height: 384,
|
||||
//removeOnClose: true,
|
||||
title: Ox._('Identify Book'),
|
||||
width: 768
|
||||
}),
|
||||
|
||||
$cover = Ox.Element()
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
top: '16px',
|
||||
width: '256px'
|
||||
})
|
||||
.appendTo(that),
|
||||
|
||||
$info = Ox.Element()
|
||||
.addClass('OxSelectable')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '288px',
|
||||
right: '176px',
|
||||
top: '16px'
|
||||
})
|
||||
.appendTo(that),
|
||||
|
||||
$data = Ox.Element()
|
||||
.addClass('OxSelectable')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
right: '16px',
|
||||
top: '16px',
|
||||
width: '128px'
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
function identify(data) {
|
||||
var $form = Ox.Form({
|
||||
items: [
|
||||
'title', 'author', 'publisher', 'date'
|
||||
].map(function(key) {
|
||||
return Ox.Input({
|
||||
id: key,
|
||||
labelWidth: 128,
|
||||
label: Ox.getObjectById(oml.config.itemKeys, key).title,
|
||||
value: key == 'author' ? (data[key] || []).join(', ') : data[key],
|
||||
width: 736
|
||||
});
|
||||
})
|
||||
})
|
||||
.css({padding: '16px'})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
Ox.print('FORM CHANGE', data);
|
||||
}
|
||||
}),
|
||||
$list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
id: 'index'
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
visible: true,
|
||||
width: 288,
|
||||
},
|
||||
{
|
||||
id: 'author',
|
||||
visible: true,
|
||||
width: 224
|
||||
},
|
||||
{
|
||||
id: 'publisher',
|
||||
visible: true,
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
visible: true,
|
||||
width: 96
|
||||
}
|
||||
],
|
||||
items: [],
|
||||
max: 1,
|
||||
sort: [{key: 'index', operator: '+'}],
|
||||
unique: 'index'
|
||||
})
|
||||
.bindEvent({
|
||||
select: function(data) {
|
||||
$identifyDialog.options('buttons')[1].options({
|
||||
disabled: data.ids.length == 0
|
||||
});
|
||||
}
|
||||
});
|
||||
$identifyPanel.replaceElement(0, $form);
|
||||
$identifyPanel.replaceElement(1, $list);
|
||||
$identifyDialog.open();
|
||||
identify(data);
|
||||
function identify(data) {
|
||||
oml.api.identify(data, function(result) {
|
||||
$list.options({
|
||||
items: result.data.items.map(function(item, index) {
|
||||
return Ox.extend(item, {index: index});
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function renderMediaButton(data) {
|
||||
return data.mediastate == 'transferring'
|
||||
? Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Button({
|
||||
title: Ox._('Transferring...'),
|
||||
width: 112
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: 'transfers'});
|
||||
}
|
||||
}),
|
||||
Ox.Button({
|
||||
overlap: 'left',
|
||||
title: 'close',
|
||||
tooltip: Ox._('Cancel Transfer'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
data.mediastate = 'unavailable';
|
||||
that.update(data, $data);
|
||||
oml.api.cancelDownload({id: ui.item}, function() {
|
||||
that.update(ui.item, $data);
|
||||
});
|
||||
}
|
||||
})
|
||||
],
|
||||
float: 'right'
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
: Ox.Button({
|
||||
title: Ox._(
|
||||
data.mediastate == 'available' ? 'Read Book' : 'Download Book'
|
||||
),
|
||||
width: 128
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
if (data.mediastate == 'available') {
|
||||
oml.UI.set({itemView: 'book'});
|
||||
} else {
|
||||
data.mediastate = 'transferring';
|
||||
that.update(data, $data);
|
||||
oml.api.download({id: ui.item}, function(result) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
that.update = function(idOrData, $elements) {
|
||||
|
||||
var data = Ox.isObject(idOrData) ? idOrData : null,
|
||||
id = data ? null : idOrData;
|
||||
|
||||
$elements = $elements
|
||||
? Ox.makeArray($elements)
|
||||
: [$cover, $info, $data];
|
||||
|
||||
(data ? Ox.noop : oml.api.get)({
|
||||
id: id,
|
||||
keys: []
|
||||
}, function(result) {
|
||||
|
||||
if (result) {
|
||||
data = result.data;
|
||||
}
|
||||
Ox.print('BOOK DATA', data)
|
||||
|
||||
var $reflection, $mediaButton,
|
||||
ratio = data.coverRatio || 0.75,
|
||||
size = 256,
|
||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||
height = Math.round(ratio <= 1 ? size : size / ratio),
|
||||
left = Math.floor((size - width) / 2),
|
||||
src = '/' + data.id + '/cover256.jpg',
|
||||
reflectionSize = Math.round(size / 2);
|
||||
|
||||
$elements.forEach(function($element) {
|
||||
|
||||
$element.empty();
|
||||
|
||||
if ($element == $cover) {
|
||||
|
||||
$('<img>')
|
||||
.attr({src: src})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: left + 'px',
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
})
|
||||
.appendTo($cover);
|
||||
|
||||
$reflection = $('<div>')
|
||||
.addClass('OxReflection')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
top: height + 'px',
|
||||
width: size + 'px',
|
||||
height: reflectionSize + 'px',
|
||||
overflow: 'hidden'
|
||||
})
|
||||
.appendTo($cover);
|
||||
|
||||
$('<img>')
|
||||
.attr({src: src})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: left + 'px',
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
})
|
||||
.appendTo($reflection);
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
position: 'absolute',
|
||||
width: size + 'px',
|
||||
height: reflectionSize + 'px'
|
||||
})
|
||||
.appendTo($reflection);
|
||||
|
||||
} else if ($element == $info) {
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '-4px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.text(data.title || '')
|
||||
.appendTo($info);
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '4px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.text((data.author || []).join(', '))
|
||||
.appendTo($info);
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.text(
|
||||
(data.place || '')
|
||||
+ (data.place && (data.publisher || data.date) ? ' : ' : '')
|
||||
+ (data.publisher || '')
|
||||
+ (data.publisher && data.date ? ', ' : '')
|
||||
+ (data.date || '')
|
||||
)
|
||||
.appendTo($info);
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
textAlign: 'justify'
|
||||
})
|
||||
.html(
|
||||
data.description
|
||||
? Ox.encodeHTMLEntities(data.description)
|
||||
: '<span class="OxLight">No description</span>'
|
||||
)
|
||||
.appendTo($info);
|
||||
|
||||
} else if ($element == $data) {
|
||||
|
||||
Ox.Button({
|
||||
title: Ox._('Identify Book...'),
|
||||
width: 128
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
identify(data);
|
||||
}
|
||||
})
|
||||
.appendTo($data);
|
||||
|
||||
[
|
||||
'isbn10', 'isbn13', 'lccn', 'olid', 'oclc', 'mainid'
|
||||
].forEach(function(id, index) {
|
||||
|
||||
var title = Ox.getObjectById(oml.config.itemKeys, id).title,
|
||||
placeholder = id == 'mainid' ? 'none' : 'unknown';
|
||||
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: (index == 0 ? 10 : 6) + 'px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.text(title)
|
||||
.appendTo($data);
|
||||
|
||||
Ox.EditableContent({
|
||||
editable: true,
|
||||
format: function(value) {
|
||||
return id == 'mainid'
|
||||
? Ox.getObjectById(oml.config.itemKeys, value).title
|
||||
: value;
|
||||
},
|
||||
placeholder: placeholder,
|
||||
tooltip: Ox._('Doubleclick to edit'),
|
||||
value: data[id] || ''
|
||||
})
|
||||
.bindEvent({
|
||||
submit: function(data) {
|
||||
editMetadata(id, data.value);
|
||||
}
|
||||
})
|
||||
.appendTo($data);
|
||||
|
||||
});
|
||||
|
||||
$mediaButton = renderMediaButton(data)
|
||||
.appendTo($data);
|
||||
|
||||
$('<div>')
|
||||
.addClass('OxSelectable')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
})
|
||||
.text(
|
||||
[
|
||||
data.extension.toUpperCase(),
|
||||
Ox.formatValue(data.size, 'B')
|
||||
].join(', ')
|
||||
)
|
||||
.appendTo($data);
|
||||
|
||||
['accessed', 'modified', 'added', 'created'].forEach(function(id) {
|
||||
var title = Ox.getObjectById(oml.config.itemKeys, id).title;
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.text(title)
|
||||
.appendTo($data);
|
||||
Ox.EditableContent({
|
||||
editable: false,
|
||||
format: function(value) {
|
||||
return value ? Ox.formatDate(value, '%b %e, %Y') : '';
|
||||
},
|
||||
placeholder: Ox._('unknown'),
|
||||
value: data[id] || ''
|
||||
})
|
||||
.appendTo($data);
|
||||
});
|
||||
|
||||
$('<div>').css({height: '16px'}).appendTo($data);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function editMetadata(key, value) {
|
||||
var edit;
|
||||
if (value != data[key]) {
|
||||
edit = Ox.extend({id: ui.item}, key, value);
|
||||
oml.api.edit(edit, function(result) {
|
||||
that.update(result.data, $data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
ui.item && that.update(ui.item);
|
||||
|
||||
oml.bindEvent({
|
||||
transfer: function(data) {
|
||||
if (data.id == ui.item && data.progress == 1) {
|
||||
Ox.Request.clearCache(); // FIXME: too much
|
||||
that.update(ui.item, $data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
33
static/js/itemInnerPanel.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemInnerPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
collapsed: !ui.showBrowser,
|
||||
collapsible: true,
|
||||
element: oml.$ui.browser = oml.ui.browser(),
|
||||
size: 112 + Ox.UI.SCROLLBAR_SIZE,
|
||||
tooltip: Ox._('browser')
|
||||
+ ' <span class="OxBright">'
|
||||
+ Ox.SYMBOLS.SHIFT + 'B</span>'
|
||||
},
|
||||
{
|
||||
element: oml.$ui.itemViewPanel = oml.ui.itemViewPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_showbrowser: function(data) {
|
||||
data.value == that.options('elements')[0].collapsed && that.toggle(0);
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
|
||||
20
static/js/itemMenu.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemMenu = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Button({
|
||||
style: 'squared',
|
||||
title: 'set',
|
||||
tooltip: Ox._('Options'),
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
25
static/js/itemOuterPanel.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemOuterPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: oml.$ui.itemToolbar = oml.ui.itemToolbar(),
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: oml.$ui.itemInnerPanel = oml.ui.itemInnerPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
// ...
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
19
static/js/itemToolbar.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemToolbar = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Bar({size: 24})
|
||||
.css({zIndex: 2})
|
||||
.append(
|
||||
oml.$ui.backButton = oml.ui.backButton()
|
||||
).append(
|
||||
oml.$ui.fullscreenButton = oml.ui.fullscreenButton()
|
||||
).append(
|
||||
oml.$ui.itemViewButtons = oml.ui.itemViewButtons()
|
||||
);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
73
static/js/itemViewButtons.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemViewButtons = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.ButtonGroup({
|
||||
buttons: [
|
||||
{
|
||||
id: 'info',
|
||||
title: 'iconlist',
|
||||
tooltip: Ox._('View Info')
|
||||
},
|
||||
{
|
||||
id: 'book',
|
||||
title: 'book',
|
||||
tooltip: Ox._('Read Book')
|
||||
}
|
||||
],
|
||||
selectable: true,
|
||||
style: 'squared',
|
||||
type: 'image'
|
||||
}).css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
oml.UI.set({itemView: data.value});
|
||||
},
|
||||
oml_item: function() {
|
||||
if (ui.item) {
|
||||
that.update();
|
||||
} else {
|
||||
that.disableButton('book');
|
||||
}
|
||||
},
|
||||
oml_itemview: function(data) {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function() {
|
||||
var item = ui.item;
|
||||
that.options({
|
||||
disabled: ui.itemView != 'book',
|
||||
value: ui.itemView
|
||||
});
|
||||
oml.api.get({
|
||||
id: item,
|
||||
keys: ['mediastate']
|
||||
}, function(result) {
|
||||
if (item == ui.item) {
|
||||
that[
|
||||
result.data.mediastate == 'available'
|
||||
? 'enableButton' : 'disableButton'
|
||||
]('book');
|
||||
}
|
||||
});
|
||||
return that;
|
||||
};
|
||||
|
||||
oml.bindEvent({
|
||||
transfer: function(data) {
|
||||
if (data.id == ui.item && data.progress == 1) {
|
||||
that.enableButton('book');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
30
static/js/itemViewPanel.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.itemViewPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SlidePanel({
|
||||
elements: [
|
||||
{
|
||||
id: 'info',
|
||||
element: oml.$ui.infoView = oml.ui.infoView()
|
||||
},
|
||||
{
|
||||
id: 'book',
|
||||
element: oml.$ui.viewer = oml.ui.viewer()
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal',
|
||||
selected: ui.itemView,
|
||||
size: window.innerWidth - ui.sidebarSize - 1
|
||||
})
|
||||
.bindEvent({
|
||||
oml_itemview: function(data) {
|
||||
that.options({selected: data.value});
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
59
static/js/leftPanel.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.leftPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: oml.$ui.sectionbar = oml.ui.sectionbar(),
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: oml.$ui.folders = oml.ui.folders()
|
||||
},
|
||||
{
|
||||
collapsed: !oml.user.ui.showInfo,
|
||||
collapsible: true,
|
||||
element: oml.$ui.info = oml.ui.info(),
|
||||
size: ui.sidebarSize,
|
||||
tooltip: Ox._('info') + ' <span class="OxBright">'
|
||||
+ Ox.SYMBOLS.SHIFT + 'I</span>'
|
||||
}
|
||||
],
|
||||
id: 'leftPanel',
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
resize: function(data) {
|
||||
ui.sidebarSize = data.size;
|
||||
oml.resizeListFolders();
|
||||
that.size(2, data.size);
|
||||
if (!ui.showInfo) {
|
||||
that.css({bottom: -data.size + 'px'});
|
||||
}
|
||||
},
|
||||
resizeend: function(data) {
|
||||
// set to 0 so that UI.set registers a change of the value
|
||||
ui.sidebarSize = 0;
|
||||
oml.UI.set({sidebarSize: data.size});
|
||||
},
|
||||
toggle: function(data) {
|
||||
oml.UI.set({showSidebar: !data.collapsed});
|
||||
if (data.collapsed) {
|
||||
oml.$ui.folderList.forEach(function($list) {
|
||||
$list.loseFocus();
|
||||
});
|
||||
}
|
||||
},
|
||||
oml_showinfo: function(data) {
|
||||
if (data.value == that.options('elements')[2].collapsed) {
|
||||
that.toggle(2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
68
static/js/list.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.list = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = oml.ui[ui.listView + 'View']()
|
||||
.bindEvent({
|
||||
closepreview: function() {
|
||||
oml.$ui.previewButton.options({value: false});
|
||||
oml.$ui.previewDialog.close();
|
||||
delete oml.$ui.previewDialog;
|
||||
},
|
||||
init: function(data) {
|
||||
oml.$ui.statusbar.set('total', data);
|
||||
},
|
||||
open: function(data) {
|
||||
oml.UI.set({
|
||||
item: data.ids[0],
|
||||
itemView: 'info'
|
||||
});
|
||||
},
|
||||
openpreview: function() {
|
||||
if (!oml.$ui.previewDialog) {
|
||||
oml.$ui.previewButton.options({value: true});
|
||||
oml.$ui.previewDialog = oml.ui.previewDialog()
|
||||
.open()
|
||||
.bindEvent({
|
||||
close: function() {
|
||||
that.closePreview();
|
||||
delete oml.$ui.previewDialog;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
oml.$ui.previewDialog.update();
|
||||
}
|
||||
},
|
||||
resize: function(data) {
|
||||
// this is the resize event of the split panel
|
||||
that.size();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({listSelection: data.ids});
|
||||
},
|
||||
oml_find: function() {
|
||||
that.reloadList();
|
||||
},
|
||||
oml_item: function() {
|
||||
if (!ui.item) {
|
||||
that.gainFocus();
|
||||
} else {
|
||||
that.options({selected: [ui.item]});
|
||||
}
|
||||
},
|
||||
oml_listselection: function(data) {
|
||||
that.options({selected: data.value});
|
||||
},
|
||||
oml_listsort: function(data) {
|
||||
that.options({sort: data.value});
|
||||
},
|
||||
oml_sidebarsize: function(data) {
|
||||
that.size();
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
98
static/js/listDialog.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listDialog = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
list = ui._list,
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [].concat(list && !Ox.endsWith(list, ':') ? [
|
||||
Ox.Button({
|
||||
id: 'duplicate',
|
||||
title: Ox._('Duplicate List...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
oml.addList(list);
|
||||
}
|
||||
})
|
||||
] : []).concat(Ox.startsWith(list, ':') && list != '' ? [
|
||||
Ox.Button({
|
||||
id: 'delete',
|
||||
title: Ox._('Delete List...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
oml.deleteList();
|
||||
}
|
||||
})
|
||||
] : []).concat([
|
||||
{},
|
||||
Ox.Button({
|
||||
id: 'done',
|
||||
title: Ox._('Done')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
]),
|
||||
closeButton: true,
|
||||
content: Ox.LoadingScreen().start(),
|
||||
height: 256,
|
||||
title: Ox._('List – {0}', [
|
||||
ui._list == '' ? Ox._('All Libraries')
|
||||
: ui._list
|
||||
.replace(/^:/, oml.user.preferences.username + ':')
|
||||
.replace(/:$/, Ox._(':Library'))
|
||||
]),
|
||||
width: 648 + Ox.UI.SCROLLBAR_SIZE
|
||||
});
|
||||
|
||||
oml.api.getLists(function(result) {
|
||||
var lists = result.data.lists[oml.user.id],
|
||||
listData = Ox.getObjectById(lists, list),
|
||||
listNames = lists.map(function(list) {
|
||||
return list.name;
|
||||
}),
|
||||
$content = Ox.Element()
|
||||
.css({margin: '16px'}),
|
||||
$nameInput = Ox.Input({
|
||||
label: Ox._('Name'),
|
||||
labelWidth: 128,
|
||||
value: listData.name,
|
||||
width: 616
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var value = oml.validateName(data.value, listNames);
|
||||
$nameInput.value(value);
|
||||
oml.api.editList({
|
||||
id: list,
|
||||
name: value
|
||||
}, function(result) {
|
||||
oml.updateLists(function() {
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: ':' + value
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
.appendTo($content);
|
||||
that.options({content: $content})
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
40
static/js/listInnerPanel.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listInnerPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
collapsed: !ui.showFilters,
|
||||
collapsible: true,
|
||||
element: oml.$ui.filtersOuterPanel = oml.ui.filtersOuterPanel(),
|
||||
resizable: true,
|
||||
resize: [96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256],
|
||||
size: ui.filtersSize,
|
||||
tooltip: Ox._('filters') + ' <span class="OxBright">'
|
||||
+ Ox.SYMBOLS.SHIFT + 'F</span>'
|
||||
},
|
||||
{
|
||||
element: oml.$ui.list = oml.ui.list()
|
||||
},
|
||||
{
|
||||
element: oml.$ui.statusbar = oml.ui.statusbar(),
|
||||
size: 16
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_listview: function() {
|
||||
that.replaceElement(1, oml.$ui.list = oml.ui.list());
|
||||
},
|
||||
oml_showfilters: function(data) {
|
||||
data.value == that.options('elements')[0].collapsed && that.toggle(0);
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
25
static/js/listOuterPanel.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listOuterPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: oml.$ui.listToolbar = oml.ui.listToolbar(),
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: oml.$ui.listInnerPanel = oml.ui.listInnerPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
// ...
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
23
static/js/listToolbar.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listToolbar = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Bar({size: 24})
|
||||
.css({zIndex: 2})
|
||||
.append(
|
||||
oml.$ui.openButton = oml.ui.openButton()
|
||||
).append(
|
||||
oml.$ui.previewButton = oml.ui.previewButton()
|
||||
).append(
|
||||
oml.$ui.listViewButtons = oml.ui.listViewButtons()
|
||||
).append(
|
||||
oml.$ui.sortElement = oml.ui.sortElement()
|
||||
).append(
|
||||
oml.$ui.findElement = oml.ui.findElement()
|
||||
);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
58
static/js/listView.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listView = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.TableList({
|
||||
columns: oml.config.sortKeys.map(function(key) {
|
||||
var position = ui.listColumns.indexOf(key.id);
|
||||
return {
|
||||
addable: key.id != 'random',
|
||||
align: ['string', 'text'].indexOf(
|
||||
Ox.isArray(key.type) ? key.type[0]: key.type
|
||||
) > -1 ? 'left' : key.type == 'list' ? 'center' : 'right',
|
||||
defaultWidth: key.columnWidth,
|
||||
format: key.format,
|
||||
id: key.id,
|
||||
operator: key.operator,
|
||||
position: position,
|
||||
removable: !key.columnRequired,
|
||||
title: Ox._(key.title),
|
||||
type: key.type,
|
||||
visible: position > -1,
|
||||
width: ui.listColumnWidth[key.id] || key.columnWidth
|
||||
};
|
||||
}),
|
||||
columnsMovable: true,
|
||||
columnsRemovable: true,
|
||||
columnsResizable: true,
|
||||
columnsVisible: true,
|
||||
items: function(data, callback) {
|
||||
oml.api.find(Ox.extend(data, {
|
||||
query: ui.find
|
||||
}), callback);
|
||||
},
|
||||
draggable: true,
|
||||
scrollbarVisible: true,
|
||||
selected: ui.listSelection,
|
||||
sort: ui.listSort,
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
columnchange: function(data) {
|
||||
oml.UI.set({listColumns: data.ids});
|
||||
},
|
||||
columnresize: function(data) {
|
||||
oml.UI.set('listColumnWidth.' + data.id, data.width);
|
||||
},
|
||||
sort: function(data) {
|
||||
oml.UI.set({
|
||||
listSort: [{key: data.key, operator: data.operator}]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
29
static/js/listViewButtons.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.listViewButtons = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.ButtonGroup({
|
||||
buttons: oml.config.listViews.map(function(view) {
|
||||
return Ox.extend({
|
||||
tooltip: Ox._('View as') + ' ' + view.title
|
||||
}, view);
|
||||
}),
|
||||
selectable: true,
|
||||
style: 'squared',
|
||||
type: 'image',
|
||||
value: ui.listView
|
||||
}).css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
oml.UI.set({listView: data.value});
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
63
static/js/loadingIcon.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.loadingIcon = function() {
|
||||
|
||||
var self = {},
|
||||
that = Ox.LoadingIcon({
|
||||
tooltip: Ox._('Reload')
|
||||
}, self)
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL('symbolRedo')
|
||||
})
|
||||
.css(getCSS('stop'))
|
||||
.bindEvent({
|
||||
anyclick: function() {
|
||||
oml.$ui.appPanel.reload();
|
||||
}
|
||||
});
|
||||
|
||||
that.superStart = that.start;
|
||||
that.superStop = that.stop;
|
||||
|
||||
that.start = function() {
|
||||
if (!self.loadingInterval) {
|
||||
that.css(getCSS('start'))
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL('symbolLoading')
|
||||
});
|
||||
that.superStart();
|
||||
}
|
||||
};
|
||||
|
||||
that.stop = function() {
|
||||
if (self.loadingInterval) {
|
||||
that.superStop(function() {
|
||||
that.css(getCSS('stop'))
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL('symbolRedo')
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
that.update = function(requests) {
|
||||
that[requests ? 'start' : 'stop']();
|
||||
};
|
||||
|
||||
function getCSS(action) {
|
||||
return action == 'start' ? {
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
margin: 0,
|
||||
opacity: 0
|
||||
} : {
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '3px',
|
||||
opacity: 1
|
||||
};
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
568
static/js/mainMenu.js
Normal file
|
|
@ -0,0 +1,568 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.mainMenu = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
findState = oml.getFindState(ui.find),
|
||||
appItems = Ox.getObjectById(oml.config.pages, 'app').parts,
|
||||
|
||||
that = Ox.MainMenu({
|
||||
extras: [
|
||||
oml.$ui.connectionButton = oml.ui.connectionButton(),
|
||||
oml.$ui.notificationsButton = oml.ui.notificationsButton(),
|
||||
oml.$ui.loadingIcon = oml.ui.loadingIcon()
|
||||
],
|
||||
menus: [
|
||||
{
|
||||
id: 'appMenu',
|
||||
title: '<img src="/static/png/oml.png" style="width: 14px; height: 14px">',
|
||||
items: [
|
||||
appItems[0],
|
||||
{}
|
||||
].concat(
|
||||
appItems.slice(1, -1)
|
||||
).concat([
|
||||
{},
|
||||
Ox.last(appItems)
|
||||
])
|
||||
},
|
||||
{
|
||||
id: 'userMenu',
|
||||
title: Ox._('User'),
|
||||
items: [
|
||||
{
|
||||
id: 'preferences',
|
||||
title: Ox._('Preferences...'),
|
||||
keyboard: 'control ,'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'users',
|
||||
title: Ox._('Users...')
|
||||
},
|
||||
{
|
||||
id: 'devices',
|
||||
title: Ox._('Devices...')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'listMenu',
|
||||
title: Ox._('List'),
|
||||
items: [
|
||||
{
|
||||
id: 'newlist',
|
||||
title: Ox._('New List'),
|
||||
keyboard: 'control n'
|
||||
},
|
||||
{
|
||||
id: 'newlistfromselection',
|
||||
title: Ox._('New List from Selection'),
|
||||
keyboard: 'shift control n',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'newsmartlist',
|
||||
title: Ox._('New Smart List'),
|
||||
keyboard: 'alt control n'
|
||||
},
|
||||
{
|
||||
id: 'newsmartlistfromresults',
|
||||
title: Ox._('New Smart List from Results'),
|
||||
keyboard: 'shift alt control n',
|
||||
disabled: true
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'duplicatelist',
|
||||
title: Ox._('Duplicate List'),
|
||||
keyboard: 'control d',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'editlist',
|
||||
title: Ox._('Edit List...'),
|
||||
keyboard: 'return',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'deletelist',
|
||||
title: Ox._('Delete List...'),
|
||||
keyboard: 'delete',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'editMenu',
|
||||
title: Ox._('Edit'),
|
||||
items: [
|
||||
{
|
||||
id: 'importitems',
|
||||
title: Ox._('Import Books...')
|
||||
},
|
||||
{
|
||||
id: 'exportitems',
|
||||
title: Ox._('Export Books...')
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'download',
|
||||
title: Ox._('Download Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control d'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'selectall',
|
||||
title: Ox._('Select All'),
|
||||
keyboard: 'control a'
|
||||
},
|
||||
{
|
||||
id: 'selectnone',
|
||||
title: Ox._('Select None'),
|
||||
keyboard: 'shift control a'
|
||||
},
|
||||
{
|
||||
id: 'invertselection',
|
||||
title: Ox._('Invert Selection'),
|
||||
keyboard: 'alt control a'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'cut',
|
||||
title: Ox._('Cut Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control x'
|
||||
},
|
||||
{
|
||||
id: 'cutadd',
|
||||
title: Ox._('Cut and Add to Clipboard'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control x'
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
title: Ox._('Copy Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control c'
|
||||
},
|
||||
{
|
||||
id: 'copyadd',
|
||||
title: Ox._('Copy and Add to Clipboard'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control c'
|
||||
},
|
||||
{
|
||||
id: 'paste',
|
||||
title: Ox._('Paste Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control v'
|
||||
},
|
||||
{
|
||||
id: 'clearclipboard',
|
||||
title: Ox._('Clear Clipboard'),
|
||||
disabled: true
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'delete',
|
||||
title: Ox._('Delete Items from List'),
|
||||
disabled: true,
|
||||
keyboard: 'delete'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'undo',
|
||||
title: Ox._('Undo'),
|
||||
disabled: true,
|
||||
keyboard: 'control z'
|
||||
},
|
||||
{
|
||||
id: 'redo',
|
||||
title: Ox._('Redo'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control z'
|
||||
},
|
||||
{
|
||||
id: 'clearhistory',
|
||||
title: Ox._('Clear History'),
|
||||
disabled: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'viewMenu',
|
||||
title: Ox._('View'),
|
||||
items: [
|
||||
{
|
||||
id: 'section',
|
||||
title: Ox._('Section'),
|
||||
items: [
|
||||
{
|
||||
group: 'section',
|
||||
min: 1,
|
||||
max: 1,
|
||||
items: [
|
||||
{
|
||||
id: 'books',
|
||||
title: Ox._('Books'),
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
id: 'music',
|
||||
title: Ox._('Music'),
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'movies',
|
||||
title: Ox._('Movies'),
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'iconSubmenu',
|
||||
title: 'Icons',
|
||||
items: [
|
||||
{
|
||||
group: 'icons',
|
||||
min: 1,
|
||||
max: 1,
|
||||
items: [
|
||||
{
|
||||
id: 'cover',
|
||||
title: Ox._('Cover'),
|
||||
checked: ui.icons == 'cover'
|
||||
},
|
||||
{
|
||||
id: 'preview',
|
||||
title: Ox._('Preview'),
|
||||
checked: ui.icons == 'preview'
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'showfileinfo',
|
||||
title: 'Show File Info',
|
||||
checked: ui.showFileInfo
|
||||
},
|
||||
{},
|
||||
{
|
||||
group: 'fileinfo',
|
||||
min: 1,
|
||||
max: 1,
|
||||
disabled: !ui.showFileInfo,
|
||||
items: [
|
||||
{
|
||||
id: 'extension',
|
||||
title: Ox._('Show Extension'),
|
||||
checked: ui.fileInfo == 'extension'
|
||||
},
|
||||
{
|
||||
id: 'size',
|
||||
title: Ox._('Show Size'),
|
||||
checked: ui.fileInfo == 'size'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'showsidebar',
|
||||
title: Ox._((ui.showSidebar ? 'Hide' : 'Show') + ' Sidebar'),
|
||||
keyboard: 'shift s'
|
||||
},
|
||||
{
|
||||
id: 'showinfo',
|
||||
title: Ox._((ui.showInfo ? 'Hide' : 'Show') + ' Info'),
|
||||
keyboard: 'shift i',
|
||||
disabled: !ui.showSidebar
|
||||
},
|
||||
{
|
||||
id: 'showfilters',
|
||||
title: Ox._((ui.showFilters ? 'Hide' : 'Show') + ' Filters'),
|
||||
keyboard: 'shift f',
|
||||
disabled: !!ui.item
|
||||
},
|
||||
{
|
||||
id: 'showbrowser',
|
||||
title: Ox._((ui.showBrowser ? 'Hide': 'Show') + ' Browser'),
|
||||
keyboard: 'shift b',
|
||||
disabled: !ui.item
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'notifications',
|
||||
title: Ox._('Notifications...')
|
||||
},
|
||||
{
|
||||
id: 'transfers',
|
||||
title: Ox._('Transfers...')
|
||||
},
|
||||
{
|
||||
id: 'activity',
|
||||
title: Ox._('Activity...')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'sortMenu',
|
||||
title: Ox._('Sort'),
|
||||
items: [
|
||||
{
|
||||
id: 'sortitems',
|
||||
title: Ox._('Sort Books by'),
|
||||
items: [
|
||||
{
|
||||
group: 'sort',
|
||||
title: Ox._('Sort Books by'),
|
||||
min: 1,
|
||||
max: 1,
|
||||
items: oml.config.sortKeys.map(function(key) {
|
||||
return {
|
||||
id: key.id,
|
||||
title: Ox._(key.title),
|
||||
checked: key.id == ui.listSort[0].key
|
||||
};
|
||||
})
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'orderitems',
|
||||
title: Ox._('Order Books'),
|
||||
items: [
|
||||
{
|
||||
group: 'order',
|
||||
min: 1,
|
||||
max: 1,
|
||||
items: [
|
||||
{
|
||||
id: 'ascending',
|
||||
title: Ox._('Ascending'),
|
||||
checked: ui.listSort[0].operator == '+'
|
||||
},
|
||||
{
|
||||
id: 'descending',
|
||||
title: Ox._('Descending'),
|
||||
checked: ui.listSort[0].operator == '-'
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'advancedsort',
|
||||
title: Ox._('Advanced Sort'),
|
||||
keyboard: 'shift control s',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'findMenu',
|
||||
title: Ox._('Find'),
|
||||
items: [
|
||||
{
|
||||
id: 'finditems',
|
||||
title: Ox._('Find'),
|
||||
items: [
|
||||
{
|
||||
group: 'find',
|
||||
title: Ox._('Find'),
|
||||
min: 1,
|
||||
max: 1,
|
||||
items: oml.config.findKeys.map(function(key) {
|
||||
return {
|
||||
id: key.id,
|
||||
checked: key.id == findState.key,
|
||||
title: Ox._(key.title)
|
||||
};
|
||||
})
|
||||
},
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
id: 'advancedfind',
|
||||
title: Ox._('Advanced Find'),
|
||||
keyboard: 'shift control f'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'helpMenu',
|
||||
title: Ox._('Help'),
|
||||
items: [
|
||||
{
|
||||
id: 'gettingstarted',
|
||||
title: 'Getting Started...'
|
||||
},
|
||||
{
|
||||
id: 'help',
|
||||
title: Ox._('{0} Help...', ['Open Media Library']),
|
||||
keyboard: 'control ?'
|
||||
},
|
||||
{
|
||||
id: 'documentation',
|
||||
title: Ox._('Documentation...'),
|
||||
keyboard: 'shift control ?'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'debugMenu',
|
||||
title: Ox._('Debug'),
|
||||
items: [
|
||||
{
|
||||
id: 'debugmode',
|
||||
title: Ox._((
|
||||
oml.localStorage('enableDebugMode')
|
||||
? 'Disable' : 'Enable'
|
||||
) + ' Debug Mode'),
|
||||
},
|
||||
{
|
||||
id: 'eventlogging',
|
||||
title: Ox._((
|
||||
oml.localStorage('enableEventLogging')
|
||||
? 'Disable' : 'Enable'
|
||||
) + ' Event Logging'),
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'changelog',
|
||||
title: Ox._('Change Log...')
|
||||
},
|
||||
{
|
||||
id: 'errorlog',
|
||||
title: Ox._('Error Log...')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
.bindKeyboard()
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var id = data.id,
|
||||
value = Ox.isBoolean(data.checked)
|
||||
? data.checked : data.checked[0].id;
|
||||
if (id == 'icons') {
|
||||
oml.UI.set({icons: value});
|
||||
} else if (id == 'showfileinfo') {
|
||||
oml.UI.set({showFileInfo: value});
|
||||
} else if (id == 'fileinfo') {
|
||||
oml.UI.set({fileInfo: value});
|
||||
} else {
|
||||
Ox.print('MAIN MENU DOES NOT YET HANDLE', id);
|
||||
}
|
||||
},
|
||||
click: function(data) {
|
||||
var id = data.id;
|
||||
if (Ox.contains([
|
||||
'about', 'faq', 'terms',
|
||||
'development', 'contact', 'update'
|
||||
], id)) {
|
||||
oml.UI.set({'part.app': id});
|
||||
oml.UI.set({page: 'app'});
|
||||
} else if (id == 'preferences') {
|
||||
oml.UI.set({page: 'preferences'});
|
||||
} else if (id == 'users') {
|
||||
oml.UI.set({page: 'users'});
|
||||
} else if (Ox.contains([
|
||||
'newlist', 'newlistfromselection',
|
||||
'newsmartlist', 'newsmartlistfromresults'
|
||||
], id)) {
|
||||
oml.addList(Ox.contains(id, 'smart'), Ox.contains(id, 'from'));
|
||||
} else if (id == 'duplicatelist') {
|
||||
oml.addList(ui._list);
|
||||
} else if (id == 'editlist') {
|
||||
oml.ui.listDialog.open();
|
||||
} else if (id == 'deletelist') {
|
||||
oml.ui.deleteListDialog.open();
|
||||
} else if (id == 'showsidebar') {
|
||||
oml.UI.set({showSidebar: !ui.showSidebar});
|
||||
} else if (id == 'showinfo') {
|
||||
oml.UI.set({showInfo: !ui.showInfo});
|
||||
} else if (id == 'showfilters') {
|
||||
oml.UI.set({showFilters: !ui.showFilters});
|
||||
} else if (id == 'showbrowser') {
|
||||
oml.UI.set({showBrowser: !ui.showBrowser});
|
||||
} else if (id == 'transfers') {
|
||||
oml.UI.set({page: 'transfers'});
|
||||
} else {
|
||||
Ox.print('MAIN MENU DOES NOT YET HANDLE', id);
|
||||
}
|
||||
},
|
||||
key_control_comma: function() {
|
||||
if (!oml.hasDialogOrScreen()) {
|
||||
oml.UI.set({page: 'preferences'});
|
||||
}
|
||||
},
|
||||
key_control_f: function() {
|
||||
if (!oml.hasDialogOrScreen()) {
|
||||
if (ui._findState.key != 'advanced') {
|
||||
setTimeout(function() {
|
||||
oml.$ui.findInput.focusInput(true);
|
||||
});
|
||||
} else {
|
||||
oml.$ui.filterDialog = oml.ui.filterDialog().open();
|
||||
}
|
||||
}
|
||||
},
|
||||
key_shift_b: function() {
|
||||
ui.item && oml.UI.set({showBrowser: !ui.showBrowser});
|
||||
},
|
||||
key_shift_f: function() {
|
||||
!ui.item && oml.UI.set({showFilters: !ui.showFilters});
|
||||
},
|
||||
key_shift_i: function() {
|
||||
ui.showSidebar && oml.UI.set({showInfo: !ui.showInfo});
|
||||
},
|
||||
key_shift_s: function() {
|
||||
oml.UI.set({showSidebar: !ui.showSidebar});
|
||||
},
|
||||
oml_find: function() {
|
||||
var action = Ox.startsWith(ui._list, ':') && ui._list != ':'
|
||||
? 'enableItem' : 'disableItem';
|
||||
that[
|
||||
ui._list && !Ox.endsWith(ui._list, ':')
|
||||
? 'enableItem' : 'disableItem'
|
||||
]('duplicatelist');
|
||||
that[action]('editlist');
|
||||
that[action]('deletelist');
|
||||
},
|
||||
oml_item: function(data) {
|
||||
if (!!data.value != !!data.previousValue) {
|
||||
that[data.value ? 'disableItem' : 'enableItem']('showfilters');
|
||||
that[data.value ? 'enableItem' : 'disableItem']('showbrowser');
|
||||
}
|
||||
},
|
||||
oml_showbrowser: function(data) {
|
||||
that.setItemTitle('showbrowser', Ox._((data.value ? 'Hide' : 'Show') + ' Browser'));
|
||||
},
|
||||
oml_showfilters: function(data) {
|
||||
that.setItemTitle('showfilters', Ox._((data.value ? 'Hide' : 'Show') + ' Filters'));
|
||||
},
|
||||
oml_showinfo: function(data) {
|
||||
that.setItemTitle('showinfo', Ox._((data.value ? 'Hide' : 'Show') + ' Info'));
|
||||
},
|
||||
oml_showsidebar: function(data) {
|
||||
that.setItemTitle('showsidebar', Ox._((data.value ? 'Hide' : 'Show') + ' Sidebar'));
|
||||
that[data.value ? 'enableItem' : 'disableItem']('showinfo');
|
||||
},
|
||||
});
|
||||
|
||||
function getItemMenu() {
|
||||
// ...
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
33
static/js/mainPanel.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.mainPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
collapsible: true,
|
||||
collapsed: !ui.showSidebar,
|
||||
element: oml.$ui.leftPanel = oml.ui.leftPanel(),
|
||||
resizable: true,
|
||||
resize: [192, 256, 320, 384],
|
||||
size: ui.sidebarSize,
|
||||
tooltip: Ox._('sidebar') + ' <span class="OxBright">'
|
||||
+ Ox.SYMBOLS.SHIFT + 'S</span>'
|
||||
},
|
||||
{
|
||||
element: oml.$ui.rightPanel = oml.ui.rightPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_showsidebar: function(data) {
|
||||
data.value == that.options('elements')[0].collapsed && that.toggle(0);
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
38
static/js/notificationsButton.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.notificationsButton = function() {
|
||||
|
||||
var that = Ox.Element({
|
||||
tooltip: Ox._('No notifications')
|
||||
})
|
||||
.css({
|
||||
marginRight: '3px'
|
||||
})
|
||||
.bindEvent({
|
||||
// ...
|
||||
});
|
||||
|
||||
Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'playlist',
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
borderRadius: 0
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
Ox.Element()
|
||||
.addClass('OxLight')
|
||||
.css({
|
||||
float: 'left',
|
||||
marginTop: '2px',
|
||||
fontSize: '9px'
|
||||
})
|
||||
.html('0')
|
||||
.appendTo(that);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
255
static/js/oml.js
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
'use strict';
|
||||
|
||||
(function() {
|
||||
|
||||
var animationInterval,
|
||||
enableDebugMode = getLocalStorage('oml.enableDebugMode'),
|
||||
omlVersion = getOMLVersion(),
|
||||
oxjsPath = '/static/oxjs/' + (enableDebugMode ? 'dev' : 'build'),
|
||||
terminal,
|
||||
theme = getLocalStorage('Ox.theme')
|
||||
&& JSON.parse(localStorage['Ox.theme'])
|
||||
|| 'oxlight';
|
||||
|
||||
loadImages(function(images) {
|
||||
loadScreen(images);
|
||||
loadOxJS(loadOML);
|
||||
});
|
||||
|
||||
function getLocalStorage(key) {
|
||||
try {
|
||||
return localStorage[key];
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function getOMLVersion() {
|
||||
var i, path, scripts = document.getElementsByTagName('script');
|
||||
for (i = 0; i < scripts.length; i++) {
|
||||
if(/oml.js/.test(scripts[i].src)) {
|
||||
return scripts[i].src.replace(/.*\?/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initOML(data) {
|
||||
Ox.extend(oml, {
|
||||
$ui: {},
|
||||
config: data.config,
|
||||
user: data.user
|
||||
});
|
||||
// make sure all valid ui settings are present
|
||||
oml.user.ui = Ox.extend(
|
||||
Ox.clone(data.config.user.ui, true),
|
||||
oml.user.ui
|
||||
);
|
||||
// make sure no invalid ui settings are present
|
||||
Object.keys(oml.user.ui).forEach(function(key) {
|
||||
if (Ox.isUndefined(oml.config.user.ui[key])) {
|
||||
delete oml.user.ui[key];
|
||||
}
|
||||
});
|
||||
// TODO: make sure listView, listSort and itemView are valid
|
||||
Ox.extend(oml.config, {
|
||||
filters: oml.config.itemKeys.filter(function(key) {
|
||||
return key.filter;
|
||||
}).map(function(key) {
|
||||
return {
|
||||
id: key.id,
|
||||
title: key.title,
|
||||
type: Ox.isArray(key.type) ? key.type[0] : key.type
|
||||
};
|
||||
}),
|
||||
findKeys: oml.config.itemKeys.filter(function(key) {
|
||||
return key.find;
|
||||
}),
|
||||
sortKeys: oml.config.itemKeys.filter(function(key) {
|
||||
return key.sort && key.columnWidth;
|
||||
}).map(function(key) {
|
||||
return Ox.extend({
|
||||
operator: oml.getSortOperator(key.id)
|
||||
}, key, {
|
||||
format: function(value) {
|
||||
return key.format
|
||||
? Ox['format' + Ox.toTitleCase(key.format.type)].apply(
|
||||
this, [value].concat(key.format.args || [])
|
||||
)
|
||||
: value;
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
oml.config.listSettings = {};
|
||||
Ox.forEach(oml.config.user.ui, function(value, key) {
|
||||
if (/^list[A-Z]/.test(key)) {
|
||||
oml.config.listSettings[key] = key[4].toLowerCase() + key.slice(5);
|
||||
}
|
||||
});
|
||||
oml.URL.init().parse(function() {
|
||||
oml.clipboard = Ox.Clipboard();
|
||||
oml.history = Ox.History();
|
||||
oml.$ui.appPanel = oml.ui.appPanel().appendTo(Ox.$body);
|
||||
oml.$ui.loadingIcon.update(Ox.Request.requests());
|
||||
Ox.Request.bindEvent({
|
||||
error: function(data) {
|
||||
oml.$ui.errorDialog = oml.ui.errorDialog().update(data).open();
|
||||
},
|
||||
request: function(data) {
|
||||
oml.$ui.loadingIcon.update(data.requests);
|
||||
}
|
||||
});
|
||||
if (oml.user.preferences.extensions) {
|
||||
try {
|
||||
eval(oml.user.preferences.extensions)
|
||||
} catch(e) {}
|
||||
}
|
||||
removeScreen();
|
||||
});
|
||||
}
|
||||
|
||||
function loadImages(callback) {
|
||||
var images = {};
|
||||
images.logo = document.createElement('img');
|
||||
images.logo.onload = function() {
|
||||
images.logo.style.position = 'absolute';
|
||||
images.logo.style.left = 0;
|
||||
images.logo.style.top = 0;
|
||||
images.logo.style.right = 0;
|
||||
images.logo.style.bottom = '96px';
|
||||
images.logo.style.width = '256px';
|
||||
images.logo.style.height = '256px';
|
||||
images.logo.style.margin = 'auto';
|
||||
images.logo.style.MozUserSelect = 'none';
|
||||
images.logo.style.MSUserSelect = 'none';
|
||||
images.logo.style.OUserSelect = 'none';
|
||||
images.logo.style.WebkitUserSelect = 'none';
|
||||
images.loadingIcon = document.createElement('img');
|
||||
images.loadingIcon.setAttribute('id', 'loadingIcon');
|
||||
images.loadingIcon.style.position = 'absolute';
|
||||
images.loadingIcon.style.left = 0;
|
||||
images.loadingIcon.style.top = '256px'
|
||||
images.loadingIcon.style.right = 0;
|
||||
images.loadingIcon.style.bottom = 0;
|
||||
images.loadingIcon.style.width = '32px';
|
||||
images.loadingIcon.style.height = '32px';
|
||||
images.loadingIcon.style.margin = 'auto';
|
||||
images.loadingIcon.style.MozUserSelect = 'none';
|
||||
images.loadingIcon.style.MSUserSelect = 'none';
|
||||
images.loadingIcon.style.OUserSelect = 'none';
|
||||
images.loadingIcon.style.WebkitUserSelect = 'none';
|
||||
images.loadingIcon.src = oxjsPath
|
||||
+ '/Ox.UI/themes/' + theme + '/svg/symbolLoading.svg';
|
||||
callback(images);
|
||||
};
|
||||
images.logo.src = '/static/png/oml.png';
|
||||
}
|
||||
|
||||
function loadOML(browserSupported) {
|
||||
window.oml = Ox.App({
|
||||
name: 'oml',
|
||||
socket: 'ws://' + document.location.host + '/ws',
|
||||
url: '/api/'
|
||||
}).bindEvent({
|
||||
load: function(data) {
|
||||
data.browserSupported = browserSupported;
|
||||
oml.ui = {};
|
||||
loadOMLFiles(function() {
|
||||
initOML(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadOMLFiles(callback) {
|
||||
var path = '/static/';
|
||||
if (enableDebugMode) {
|
||||
Ox.getJSON(path + 'json/js.json?' + Ox.random(1000000), function(files) {
|
||||
Ox.getFile(files.map(function(file) {
|
||||
return path + 'js/' + file + '?' + omlVersion;
|
||||
}), callback);
|
||||
});
|
||||
} else {
|
||||
Ox.getScript(path + 'js/oml.min.js?' + omlVersion, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function loadOxJS(callback) {
|
||||
var head = document.head
|
||||
|| document.getElementsByTagName('head')[0]
|
||||
|| document.documentElement,
|
||||
script = document.createElement('script');
|
||||
script.onload = function() {
|
||||
Ox.load({UI: {theme: theme}}, callback);
|
||||
};
|
||||
script.src = oxjsPath + '/Ox.js?' + omlVersion;
|
||||
script.type = 'text/javascript';
|
||||
head.appendChild(script);
|
||||
}
|
||||
|
||||
function loadScreen(images) {
|
||||
var loadingScreen = document.createElement('div');
|
||||
loadingScreen.setAttribute('id', 'loadingScreen');
|
||||
loadingScreen.className = 'OxScreen';
|
||||
loadingScreen.style.position = 'absolute';
|
||||
loadingScreen.style.width = '100%';
|
||||
loadingScreen.style.height = '100%';
|
||||
loadingScreen.style.backgroundColor = theme == 'oxlight' ? 'rgb(224, 224, 224)'
|
||||
: theme == 'oxmedium' ? 'rgb(144, 144, 144)' : 'rgb(32, 32, 32)';
|
||||
loadingScreen.style.zIndex = '1002';
|
||||
loadingScreen.appendChild(images.logo);
|
||||
loadingScreen.appendChild(images.loadingIcon);
|
||||
// FF3.6 document.body can be undefined here
|
||||
window.onload = function() {
|
||||
document.body.style.margin = 0;
|
||||
document.body.appendChild(loadingScreen);
|
||||
startAnimation();
|
||||
};
|
||||
// IE8 does not call onload if already loaded before set
|
||||
document.body && window.onload();
|
||||
}
|
||||
|
||||
function loadTerminal() {
|
||||
terminal = document.createElement('div');
|
||||
terminal.style.display = 'none';
|
||||
terminal.style.position = 'absolute';
|
||||
terminal.style.width = '100%';
|
||||
terminal.style.height = '100%';
|
||||
terminal.style.zIndex = '1003';
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
var line = document.createElement('div');
|
||||
line.innerHTML = message;
|
||||
terminal.appendChild(line);
|
||||
}
|
||||
|
||||
function removeScreen() {
|
||||
var $loadingScreen = $('#loadingScreen');
|
||||
$loadingScreen.animate({
|
||||
opacity: 0
|
||||
}, 1000, function() {
|
||||
$loadingScreen.remove();
|
||||
});
|
||||
}
|
||||
|
||||
function startAnimation() {
|
||||
var css, deg = 0, loadingIcon = document.getElementById('loadingIcon'),
|
||||
previousTime = +new Date();
|
||||
animationInterval = setInterval(function() {
|
||||
var currentTime = +new Date(),
|
||||
delta = (currentTime - previousTime) / 1000;
|
||||
previousTime = currentTime;
|
||||
deg = Math.round((deg + delta * 360) % 360 / 30) * 30;
|
||||
css = 'rotate(' + deg + 'deg)';
|
||||
loadingIcon.style.MozTransform = css;
|
||||
loadingIcon.style.MSTransform = css;
|
||||
loadingIcon.style.OTransform = css;
|
||||
loadingIcon.style.WebkitTransform = css;
|
||||
loadingIcon.style.transform = css;
|
||||
}, 83);
|
||||
}
|
||||
|
||||
function stopAnimation() {
|
||||
clearInterval(animationInterval);
|
||||
}
|
||||
|
||||
}());
|
||||
32
static/js/openButton.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.openButton = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Button({
|
||||
style: 'squared',
|
||||
title: 'arrowRight',
|
||||
tooltip: Ox._('Open Book {0}', [Ox.UI.symbols.return]),
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px 4px 4px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({item: ui.listSelection[0]});
|
||||
},
|
||||
oml_listselection: function() {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function() {
|
||||
return that.options({disabled: ui.listSelection.length == 0});
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
444
static/js/preferencesDialog.js
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.preferencesDialog = function() {
|
||||
|
||||
var preferences = oml.user.preferences,
|
||||
ui = oml.user.ui,
|
||||
|
||||
items = {
|
||||
account: [
|
||||
{
|
||||
id: 'username',
|
||||
title: 'Username',
|
||||
value: preferences.username,
|
||||
placeholder: 'anonymous',
|
||||
help: 'Your username doesn\'t have to be your real name, and you can change it at any time. You can also leave it blank, in which case you will appear as "anonymous". Any characters other than leading, trailing or consecutive spaces are okay.'
|
||||
},
|
||||
{
|
||||
id: 'contact',
|
||||
title: 'Contact',
|
||||
value: preferences.contact,
|
||||
help: 'This is a one-liner, visible to all your peers, that describes how to contact you. It can be an e-mail address, profile URL, IRC channel, postal address or anything else. It\'s also perfectly fine to leave this blank.'
|
||||
}
|
||||
],
|
||||
library: [
|
||||
{
|
||||
id: 'libraryPath',
|
||||
title: 'Library Path',
|
||||
value: preferences.libaryPath,
|
||||
help: 'The directory in which your "Books" folder is located. This is where your media files are stored. It can be on your local machine, but just as well on an external drive or networked volume.'
|
||||
},
|
||||
{
|
||||
id: 'importPath',
|
||||
title: 'Import Path',
|
||||
value: preferences.importPath,
|
||||
help: 'Any media files that you put in this folder will be added to your library. Once added, they will be removed from this folder.'
|
||||
}
|
||||
],
|
||||
peering: [
|
||||
{
|
||||
id: 'sendRequests',
|
||||
title: 'Send Requests',
|
||||
value: preferences.sendRequests,
|
||||
items: [
|
||||
{id: 'manually', title: 'Manually'},
|
||||
{id: 'automatically', title: 'Automatically'}
|
||||
],
|
||||
help: 'By default, sending peering requests (to users that your peers are peered with) is done manually, but you can also automate this.'
|
||||
},
|
||||
{
|
||||
id: 'receivedRequests',
|
||||
title: 'Received Requests',
|
||||
value: preferences.peeringRequests,
|
||||
items: [
|
||||
{id: 'notify', title: 'Notify Me'},
|
||||
{id: 'accept', title: 'Accept All'},
|
||||
{id: 'reject', title: 'Reject All'}
|
||||
],
|
||||
help: 'Here you can set what happens when you receive a peering request. "Notify Me" is a good default, but maybe you think differently.'
|
||||
},
|
||||
{
|
||||
id: 'acceptMessage',
|
||||
title: 'Accept Message',
|
||||
value: preferences.acceptMessage,
|
||||
help: 'This is the default message you send whenever you accept a peering request. You can review it when accepting a request manually, and you can also leave it blank.'
|
||||
},
|
||||
{
|
||||
id: 'rejectMessage',
|
||||
title: 'Reject Message',
|
||||
value: preferences.rejectMessage,
|
||||
help: 'This is the default message you send whenever you reject a peering request. You can review it when rejecting a request manually, and you can also leave this blank.'
|
||||
},
|
||||
],
|
||||
network: [
|
||||
{
|
||||
id: 'downloadRate',
|
||||
title: 'Download Rate',
|
||||
value: preferences.downloadRate,
|
||||
unit: 'KB/s',
|
||||
help: 'Here you can set your bandwidth limits for file transfers. Leaving this blank means no limit.'
|
||||
},
|
||||
{
|
||||
id: 'uploadRate',
|
||||
title: 'Upload Rate',
|
||||
value: preferences.uploadRate,
|
||||
unit: 'KB/s',
|
||||
help: 'Here you can set your bandwidth limits for file transfers. Leaving this blank means no limit.'
|
||||
}
|
||||
],
|
||||
appearance: [
|
||||
{
|
||||
id: 'theme',
|
||||
title: 'Theme',
|
||||
value: ui.theme,
|
||||
items: oml.config.themes.map(function(theme) {
|
||||
return {id: theme, title: Ox.Theme.getThemeData(theme).themeName};
|
||||
}),
|
||||
help: 'More themes are on their way. Also, you can contribute.'
|
||||
},
|
||||
{
|
||||
id: 'locale',
|
||||
title: 'Language',
|
||||
value: ui.locale,
|
||||
items: oml.config.locales.map(function(locale) {
|
||||
return {id: locale, title: Ox.LOCALE_NAMES[locale]}
|
||||
}),
|
||||
help: 'More languages are on their way. Also, you can contribute.'
|
||||
},
|
||||
{
|
||||
id: 'resetUI',
|
||||
title: 'Reset UI Settings',
|
||||
click: function() {
|
||||
oml.$ui.resetUIDialog = oml.ui.resetUIDialog().open();
|
||||
},
|
||||
help: 'This will reset the user interface to its default settings. This affects the size of various elements, your current selection of items, the application\'s theme and language, etc.'
|
||||
}
|
||||
],
|
||||
extensions: [
|
||||
{
|
||||
id: 'extensions',
|
||||
title: 'Extensions',
|
||||
value: preferences.extensions,
|
||||
type: 'textarea',
|
||||
help: 'If you want to write plug-ins to extend the functionality of Open Media Library, this is how. Any JavaScript you enter here will run on load. In case you ever need to temporarily disable extensions, press X once you see the loading screen.'
|
||||
}
|
||||
],
|
||||
advanced: [
|
||||
{
|
||||
id: 'showDebugMenu',
|
||||
title: 'Show Debug Menu',
|
||||
value: ui.showDebugMenu,
|
||||
help: 'This enables the Debug Menu, which provides access to various profiling tools that you\'re probably not going to need, unless you\'re really curious.'
|
||||
},
|
||||
{
|
||||
id: 'sendDiagnostics',
|
||||
title: 'Send Diagnostics Data',
|
||||
value: preferences.sendDiagnostics,
|
||||
help: 'If enabled, this will periodically send a list of JavaScript errors and server errors – and nothing else – to openmedialibrary.com. It still comes with your IP address and your browser\'s user agent string, so if you don\'t want to share that, just leave this turned off.'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
$list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
id: 'title',
|
||||
visible: true,
|
||||
width: 128 - Ox.UI.SCROLLBAR_SIZE
|
||||
}
|
||||
],
|
||||
items: Ox.getObjectById(
|
||||
oml.config.pages, 'preferences'
|
||||
).parts.map(function(part, index) {
|
||||
return {
|
||||
id: part.id,
|
||||
index: index,
|
||||
title: Ox._(part.title)
|
||||
};
|
||||
}),
|
||||
max: 1,
|
||||
min: 1,
|
||||
scrollbarVisible: true,
|
||||
selected: [ui.part.preferences],
|
||||
sort: [{key: 'index', operator: '+'}],
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
select: function(data) {
|
||||
oml.UI.set({'part.preferences': data.ids[0]});
|
||||
}
|
||||
}),
|
||||
|
||||
$main = Ox.Element()
|
||||
.addClass('OxTextPage OxSelectable'),
|
||||
|
||||
$formElement = Ox.Element()
|
||||
.appendTo($main),
|
||||
|
||||
$formLabel = Ox.Label({
|
||||
width: 512
|
||||
})
|
||||
.hide()
|
||||
.appendTo($formElement),
|
||||
|
||||
$helpElement = Ox.Element()
|
||||
.css({
|
||||
position: 'absolute',
|
||||
right: '16px',
|
||||
top: '16px',
|
||||
width: '192px'
|
||||
})
|
||||
.hide()
|
||||
.appendTo($main),
|
||||
|
||||
$helpLabel = Ox.Label({
|
||||
width: 176
|
||||
}),
|
||||
|
||||
$closeButton = Ox.Button({
|
||||
overlap: 'left',
|
||||
title: 'close',
|
||||
tooltip: Ox._('Hide'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
$helpElement.hide();
|
||||
}
|
||||
}),
|
||||
|
||||
$helpTitle = Ox.FormElementGroup({
|
||||
elements: [
|
||||
$helpLabel,
|
||||
$closeButton
|
||||
],
|
||||
float: 'right'
|
||||
})
|
||||
.appendTo($helpElement),
|
||||
|
||||
$help = Ox.Element()
|
||||
.css({
|
||||
position: 'absolute',
|
||||
top: '24px'
|
||||
})
|
||||
.appendTo($helpElement),
|
||||
|
||||
$panel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: $list, size: 128},
|
||||
{element: $main}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
}),
|
||||
|
||||
$usersButton = Ox.Button({
|
||||
id: 'users',
|
||||
title: Ox._('Users...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: 'users'});
|
||||
}
|
||||
}),
|
||||
|
||||
$notificationsButton = Ox.Button({
|
||||
id: 'notifications',
|
||||
title: Ox._('Notifications...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: 'notifications'});
|
||||
}
|
||||
}),
|
||||
|
||||
$transfersButton = Ox.Button({
|
||||
id: 'transfers',
|
||||
title: Ox._('Transfers...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: 'transfers'});
|
||||
}
|
||||
}),
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [
|
||||
$usersButton,
|
||||
$notificationsButton,
|
||||
$transfersButton,
|
||||
{},
|
||||
Ox.Button({
|
||||
id: 'done',
|
||||
title: Ox._('Done')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: $panel,
|
||||
fixedSize: true,
|
||||
height: 384,
|
||||
keys: {escape: 'close'},
|
||||
removeOnClose: true,
|
||||
title: Ox._('Preferences'),
|
||||
width: 768
|
||||
})
|
||||
.bindEvent({
|
||||
open: function() {
|
||||
$list.gainFocus();
|
||||
},
|
||||
close: function() {
|
||||
if (ui.page == 'preferences') {
|
||||
oml.UI.set({page: ''});
|
||||
}
|
||||
},
|
||||
'oml_part.preferences': function() {
|
||||
if (ui.page == 'preferences') {
|
||||
that.update();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function displayHelp(item) {
|
||||
$helpLabel.options({title: item.title});
|
||||
$help.html(Ox._(item.help));
|
||||
$helpElement.show();
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
|
||||
var $form,
|
||||
$formTitle,
|
||||
part = ui.part.preferences,
|
||||
formItems = items[part];
|
||||
|
||||
if (formItems[0].type == 'textarea') {
|
||||
$formTitle = Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Label({
|
||||
title: Ox._(formItems[0].title),
|
||||
width: 384
|
||||
}),
|
||||
Ox.Button({
|
||||
overlap: 'left',
|
||||
title: 'help',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
displayHelp(formItems[0]);
|
||||
}
|
||||
})
|
||||
],
|
||||
float: 'right'
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
top: '16px'
|
||||
});
|
||||
}
|
||||
|
||||
$form = Ox.Form({
|
||||
items: formItems.map(function(item) {
|
||||
return Ox.FormElementGroup({
|
||||
elements: [
|
||||
item.click
|
||||
? Ox.Button({
|
||||
title: Ox._(item.title),
|
||||
width: 256
|
||||
})
|
||||
.bindEvent({
|
||||
click: item.click
|
||||
})
|
||||
: item.items
|
||||
? Ox.Select({
|
||||
items: item.items,
|
||||
label: Ox._(item.title),
|
||||
labelWidth: 128,
|
||||
value: item.value,
|
||||
width: 384 - !!item.unit * 48
|
||||
})
|
||||
: item.type == 'textarea'
|
||||
? Ox.Input({
|
||||
height: 328,
|
||||
type: 'textarea',
|
||||
value: item.value,
|
||||
width: 400
|
||||
})
|
||||
: Ox.isBoolean(item.value)
|
||||
? Ox.Checkbox({
|
||||
title: Ox._(item.title),
|
||||
value: item.value,
|
||||
width: 384
|
||||
})
|
||||
: Ox.Input({
|
||||
label: Ox._(item.title),
|
||||
labelWidth: 128,
|
||||
placeholder: item.placeholder || '',
|
||||
value: item.value || '',
|
||||
width: 384 - !!item.unit * 48
|
||||
})
|
||||
|
||||
].concat(item.unit ? [
|
||||
Ox.Label({
|
||||
overlap: 'left',
|
||||
title: item.unit,
|
||||
width: 48
|
||||
})
|
||||
] : []).concat(item.type != 'textarea' ? [
|
||||
Ox.Button({
|
||||
overlap: 'left',
|
||||
title: 'help',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
displayHelp(item);
|
||||
}
|
||||
})
|
||||
] : []),
|
||||
float: 'right',
|
||||
id: item.id
|
||||
});
|
||||
})
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '16px',
|
||||
top: $formTitle ? '40px' : '16px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var key = data.id,
|
||||
value = data.data.value[0];
|
||||
if (key in oml.config.user.preferences) {
|
||||
oml.Preferences.set(key, value);
|
||||
} else {
|
||||
oml.UI.set(key, value);
|
||||
}
|
||||
if (key == 'theme') {
|
||||
Ox.Theme(value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$formElement.empty();
|
||||
$formTitle && $formElement.append($formTitle);
|
||||
$formElement.append($form);
|
||||
$helpElement.hide();
|
||||
|
||||
$usersButton[part == 'peering' ? 'show' : 'hide']();
|
||||
$notificationsButton[part == 'peering' ? 'show' : 'hide']();
|
||||
$transfersButton[part == 'network' ? 'show' : 'hide']();
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
34
static/js/previewButton.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.previewButton = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Button({
|
||||
selectable: true,
|
||||
style: 'squared',
|
||||
title: 'view',
|
||||
tooltip: Ox._('Preview {0}', [Ox.UI.symbols.space]),
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
oml.$ui.list[data.value ? 'openPreview' : 'closePreview']();
|
||||
},
|
||||
oml_listselection: function() {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
that.update = function() {
|
||||
return that.options({disabled: ui.listSelection.length == 0});
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
|
||||
};
|
||||
92
static/js/previewDialog.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.previewDialog = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
$image,
|
||||
$list = oml.$ui.list,
|
||||
item = Ox.last($list.options('selected')),
|
||||
coverRatio = $list.value(item, 'coverRatio') || oml.config.coverRatio,
|
||||
size = getSize(coverRatio),
|
||||
|
||||
that = Ox.Dialog({
|
||||
closeButton: true,
|
||||
content: Ox.Element(),
|
||||
fixedRatio: true,
|
||||
focus: false,
|
||||
height: size.height,
|
||||
maximizeButton: true,
|
||||
title: Ox._('Loading...'),
|
||||
width: size.width
|
||||
})
|
||||
.bindEvent({
|
||||
resize: function(data) {
|
||||
$image.css({
|
||||
width: data.width,
|
||||
height: data.height
|
||||
});
|
||||
},
|
||||
oml_find: function() {
|
||||
that.close();
|
||||
},
|
||||
oml_item: function() {
|
||||
that.close();
|
||||
},
|
||||
oml_page: function() {
|
||||
that.close();
|
||||
}
|
||||
});
|
||||
|
||||
function getSize(posterRatio) {
|
||||
var windowWidth = window.innerWidth * 0.8,
|
||||
windowHeight = window.innerHeight * 0.8,
|
||||
windowRatio = windowWidth / windowHeight;
|
||||
return {
|
||||
width: Math.round(
|
||||
posterRatio > windowRatio ? windowWidth : windowHeight * posterRatio
|
||||
),
|
||||
height: Math.round(
|
||||
posterRatio < windowRatio ? windowHeight : windowWidth / posterRatio
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
oml.api.get({
|
||||
id: Ox.last($list.options('selected')),
|
||||
keys: ['coverRatio', 'id', 'modified', 'title']
|
||||
}, function(result) {
|
||||
var item = result.data,
|
||||
coverRatio = item.coverRatio,
|
||||
size = getSize(coverRatio),
|
||||
title = Ox.encodeHTMLEntities(item.title);
|
||||
$image = $('<img>')
|
||||
.attr({
|
||||
src: '/' + item.id + '/cover128.jpg?' + item.modified
|
||||
})
|
||||
.css({
|
||||
width: size.width + 'px',
|
||||
height: size.height + 'px'
|
||||
});
|
||||
$('<img>')
|
||||
.load(function() {
|
||||
$image.attr({
|
||||
src: $(this).attr('src')
|
||||
});
|
||||
})
|
||||
.attr({
|
||||
src: '/' + item.id + '/cover1024.jpg?' + item.modified
|
||||
});
|
||||
that.options({
|
||||
content: $image,
|
||||
title: title,
|
||||
})
|
||||
.setSize(size.width, size.height);
|
||||
});
|
||||
return that;
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
35
static/js/resetUIDialog.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.resetUIDialog = function(data) {
|
||||
|
||||
var that = oml.ui.iconDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'cancel',
|
||||
title: Ox._('Don\'t Reset')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
}),
|
||||
Ox.Button({
|
||||
id: 'reset',
|
||||
title: Ox._('Reset')
|
||||
}).bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
oml.$ui.preferencesDialog.close();
|
||||
oml.UI.set({page: ''});
|
||||
oml.UI.reset();
|
||||
}
|
||||
})
|
||||
],
|
||||
content: Ox._('Are you sure you want to reset all UI settings?'),
|
||||
keys: {enter: 'reset', escape: 'cancel'},
|
||||
title: Ox._('Reset UI Settings')
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
37
static/js/rightPanel.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.rightPanel = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.SlidePanel({
|
||||
elements: [
|
||||
{
|
||||
id: 'list',
|
||||
element: oml.$ui.listOuterPanel = oml.ui.listOuterPanel()
|
||||
},
|
||||
{
|
||||
id: 'item',
|
||||
element: oml.$ui.itemOuterPanel = oml.ui.itemOuterPanel()
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal',
|
||||
selected: !ui.item ? 'list' : 'item',
|
||||
size: window.innerWidth - ui.showSidebar * ui.sidebarSize - 1
|
||||
})
|
||||
.bindEvent({
|
||||
resize: function(data) {
|
||||
that.options({size: data.size});
|
||||
oml.$ui.filtersOuterPanel.update();
|
||||
oml.$ui.itemViewPanel.options({size: data.size});
|
||||
},
|
||||
oml_item: function(data) {
|
||||
if (!!data.value != !!data.previousValue) {
|
||||
that.options({selected: !ui.item ? 'list' : 'item'});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
28
static/js/sectionButtons.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.sectionButtons = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.ButtonGroup({
|
||||
buttons: [
|
||||
{id: 'books', title: Ox._('Books')},
|
||||
{id: 'music', title: Ox._('Music'), disabled: true},
|
||||
{id: 'movies', title: Ox._('Movies'), disabled: true}
|
||||
],
|
||||
style: 'squared',
|
||||
selectable: true,
|
||||
value: 'books'
|
||||
}).css({
|
||||
float: 'left',
|
||||
margin: '4px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
14
static/js/sectionbar.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.sectionbar = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Bar({size: 24})
|
||||
.append(
|
||||
oml.$ui.sectionButtons = oml.ui.sectionButtons()
|
||||
);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
80
static/js/sortElement.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.sortElement = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
$sortSelect = Ox.Select({
|
||||
items: oml.config.sortKeys.map(function(key) {
|
||||
return Ox.extend({}, key, {
|
||||
title: Ox._('Sort by {0}', [Ox._(key.title)])
|
||||
});
|
||||
}),
|
||||
style: 'squared',
|
||||
value: ui.listSort[0].key,
|
||||
width: 160
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var key = data.value;
|
||||
oml.UI.set({
|
||||
listSort: [{
|
||||
key: key,
|
||||
operator: oml.getSortOperator(key)
|
||||
}]
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
$orderButton = Ox.Button({
|
||||
overlap: 'left',
|
||||
style: 'squared',
|
||||
title: getButtonTitle(),
|
||||
tooltip: getButtonTooltip(),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({
|
||||
listSort: [{
|
||||
key: ui.listSort[0].key,
|
||||
operator: ui.listSort[0].operator == '+' ? '-' : '+'
|
||||
}]
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
that = Ox.FormElementGroup({
|
||||
elements: [$sortSelect, $orderButton],
|
||||
float: 'right'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px 2px'
|
||||
})
|
||||
.bindEvent({
|
||||
oml_listsort: function() {
|
||||
that.update();
|
||||
}
|
||||
});
|
||||
|
||||
function getButtonTitle() {
|
||||
return ui.listSort[0].operator == '+' ? 'up' : 'down';
|
||||
}
|
||||
|
||||
function getButtonTooltip() {
|
||||
return Ox._(ui.listSort[0].operator == '+' ? 'Ascending' : 'Descending');
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
$sortSelect.value(ui.listSort[0].key);
|
||||
$orderButton.options({
|
||||
title: getButtonTitle(),
|
||||
tooltip: getButtonTooltip()
|
||||
});
|
||||
return that;
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
43
static/js/statusIcon.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.statusIcon = function(status) {
|
||||
|
||||
// FIXME: not only '-webkit'
|
||||
|
||||
var color = {
|
||||
connected: [[64, 255, 64], [0, 192, 0]],
|
||||
disconnected: [[255, 64, 64], [192, 0, 0]],
|
||||
transferring: [[64, 255, 255], [0, 192, 192]],
|
||||
unknown: [[255, 255, 64], [192, 192, 0]]
|
||||
}[status].map(function(rgb) {
|
||||
return 'rgb(' + rgb.join(', ') + ')';
|
||||
}).join(', '),
|
||||
|
||||
that = Ox.Element({
|
||||
tooltip: Ox._({
|
||||
connected: 'Connected',
|
||||
disconnected: 'Disconnected',
|
||||
transferring: 'Transferring'
|
||||
}[status])
|
||||
})
|
||||
.css({
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '3px',
|
||||
background: '-webkit-linear-gradient(bottom, ' + color + ')',
|
||||
borderRadius: '5px'
|
||||
})
|
||||
.append(
|
||||
$('<div>')
|
||||
.css({
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
margin: '1px',
|
||||
background: '-webkit-linear-gradient(top, ' + color + ')',
|
||||
borderRadius: '4px'
|
||||
})
|
||||
);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
68
static/js/statusbar.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.statusbar = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
$text = {
|
||||
titleTotal: Ox.Element('<span>').html(Ox._('Total: ')),
|
||||
total: Ox.Element('<span>'),
|
||||
titleSelected: Ox.Element('<span>').html(' — ' + Ox._('Selected: ')),
|
||||
selected: Ox.Element('<span>'),
|
||||
loading: Ox.Element('<span>').html(Ox._('Loading...'))
|
||||
},
|
||||
|
||||
that = Ox.Bar({size: 16})
|
||||
.css({textAlign: 'center'})
|
||||
.append(
|
||||
Ox.Element()
|
||||
.css({
|
||||
margin: '2px 4px',
|
||||
fontSize: '9px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
})
|
||||
.append($text.loading)
|
||||
.append($text.titleTotal)
|
||||
.append($text.total)
|
||||
.append($text.titleSelected)
|
||||
.append($text.selected)
|
||||
)
|
||||
.bindEvent({
|
||||
oml_listselection: function(data) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
function getText(data) {
|
||||
return Ox.toTitleCase(Ox.formatCount(data.items, 'book'));
|
||||
}
|
||||
|
||||
that.set = function(key, data) {
|
||||
if (key == 'loading') {
|
||||
Ox.forEach($text, function($element, key) {
|
||||
$element[key == 'loading' ? 'show' : 'hide']();
|
||||
});
|
||||
} else {
|
||||
$text.loading.hide();
|
||||
if (key == 'selected') {
|
||||
if (data.items == 0) {
|
||||
$text.titleTotal.hide();
|
||||
$text.titleSelected.hide();
|
||||
$text.selected.hide();
|
||||
} else {
|
||||
$text.titleTotal.show();
|
||||
$text.titleSelected.show();
|
||||
$text.selected.html(getText(data)).show();
|
||||
}
|
||||
} else {
|
||||
$text.total.html(getText(data)).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
that.set('loading');
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
125
static/js/transfersDialog.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.transfersDialog = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
$list = Ox.TableList({
|
||||
columns: [
|
||||
'title', 'extension', 'size',
|
||||
'transferadded', 'transferprogress'
|
||||
].map(function(id) {
|
||||
var key = Ox.getObjectById(oml.config.itemKeys, id);
|
||||
return {
|
||||
align: Ox.contains([
|
||||
'size', 'transferprogress'
|
||||
], id) ? 'right' : 'left',
|
||||
format: key.format,
|
||||
id: id,
|
||||
operator: oml.getSortOperator(id),
|
||||
title: Ox._(key.title),
|
||||
visible: true,
|
||||
width: id == 'title' ? 240
|
||||
: id == 'transferadded' ? 144
|
||||
: id == 'transferprogress' ? 80
|
||||
: key.columnWidth
|
||||
};
|
||||
}),
|
||||
columnsVisible: true,
|
||||
items: function(data, callback) {
|
||||
Ox.Request.clearCache('find'); // FIXME: not ideal - and doesn't work
|
||||
oml.api.find(Ox.extend(data, {
|
||||
query: {
|
||||
conditions: [{
|
||||
key: 'mediastate',
|
||||
operator: '==',
|
||||
value: 'transferring'
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
}), callback);
|
||||
},
|
||||
keys: ['author'],
|
||||
sort: [{key: 'transferprogress', operator: '-'}],
|
||||
unique: 'id'
|
||||
}),
|
||||
|
||||
$statusbar = Ox.Bar({size: 16}),
|
||||
|
||||
$panel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: $list},
|
||||
{element: $statusbar, size: 16}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
}),
|
||||
|
||||
$item = Ox.Element(),
|
||||
|
||||
$cancelButton = Ox.Button({
|
||||
title: 'Cancel Transfer...',
|
||||
width: 128
|
||||
})
|
||||
.css({
|
||||
margin: '8px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
.appendTo($item),
|
||||
|
||||
$content = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: $panel},
|
||||
{element: $item, size: 160}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
}),
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'preferences',
|
||||
title: Ox._('Network Preferences...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: 'transfers'});
|
||||
}
|
||||
}),
|
||||
{},
|
||||
Ox.Button({
|
||||
id: 'done',
|
||||
title: Ox._('Done')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: $content,
|
||||
height: 384,
|
||||
title: Ox._('Transfers'),
|
||||
width: 768
|
||||
})
|
||||
.bindEvent({
|
||||
close: function() {
|
||||
if (ui.page == 'transfers') {
|
||||
oml.UI.set({page: ''});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
oml.bindEvent({
|
||||
transfer: Ox.throttle(function() {
|
||||
$list.reloadList(true);
|
||||
}, 1000)
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
38
static/js/userButton.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.userButton = function() {
|
||||
|
||||
var that = Ox.Element({
|
||||
tooltip: Ox._('Click to open preferences')
|
||||
})
|
||||
.css({
|
||||
marginRight: '3px'
|
||||
})
|
||||
.bindEvent({
|
||||
// ...
|
||||
});
|
||||
|
||||
Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'user',
|
||||
type: 'image'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
borderRadius: 0
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
Ox.Element()
|
||||
.addClass('OxLight')
|
||||
.css({
|
||||
float: 'left',
|
||||
marginTop: '2px',
|
||||
fontSize: '9px'
|
||||
})
|
||||
.html('anonymous')
|
||||
.appendTo(that);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
546
static/js/usersDialog.js
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.usersDialog = function() {
|
||||
|
||||
var preferences = oml.user.preferences,
|
||||
ui = oml.user.ui,
|
||||
|
||||
that = Ox.Dialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'preferences',
|
||||
title: Ox._('Peering Preferences...')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({
|
||||
page: 'preferences',
|
||||
'part.preferences': 'peering'
|
||||
});
|
||||
}
|
||||
}),
|
||||
{},
|
||||
Ox.Button({
|
||||
id: 'done',
|
||||
title: Ox._('Done')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
that.close();
|
||||
}
|
||||
})
|
||||
],
|
||||
closeButton: true,
|
||||
content: Ox.Element(),
|
||||
fixedSize: true,
|
||||
height: 384,
|
||||
title: Ox._('Users'),
|
||||
width: 768
|
||||
})
|
||||
.bindEvent({
|
||||
close: function() {
|
||||
if (ui.page == 'users') {
|
||||
oml.UI.set({page: ''});
|
||||
}
|
||||
},
|
||||
oml_page: function() {
|
||||
// ...
|
||||
}
|
||||
}),
|
||||
|
||||
// FIXME: WRONG!
|
||||
$users = Ox.Element().css({background: 'rgb(240, 240, 240)'}),
|
||||
|
||||
$user = Ox.Element(),
|
||||
|
||||
$panel = Ox.SplitPanel({
|
||||
elements: [
|
||||
{element: $users, size: 256},
|
||||
{element: $user}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
}),
|
||||
|
||||
users,
|
||||
|
||||
buttons = [
|
||||
{id: 'send', title: Ox._('Send')},
|
||||
{id: 'cancel', title: Ox._('Cancel')},
|
||||
{id: 'accept', title: Ox._('Accept')},
|
||||
{id: 'reject', title: Ox._('Reject')},
|
||||
{id: 'remove', title: Ox._('Remove...')}
|
||||
],
|
||||
|
||||
folders = [
|
||||
{
|
||||
id: 'peers',
|
||||
title: Ox._('Your Peers'),
|
||||
itemTitle: Ox._('No peers'),
|
||||
text: Ox._('You don\'t have any peers yet.')
|
||||
},
|
||||
{
|
||||
id: 'received',
|
||||
title: Ox._('Received Requests'),
|
||||
itemTitle: Ox._('No pending requests'),
|
||||
text: '...'
|
||||
},
|
||||
{
|
||||
id: 'sent',
|
||||
title: Ox._('Sent Requests'),
|
||||
itemTitle: Ox._('No pending requests'),
|
||||
text: '...'
|
||||
},
|
||||
{
|
||||
id: 'others',
|
||||
title: Ox._('Other Users'),
|
||||
itemTitle: Ox._('No other users'),
|
||||
text: Ox._('There are no other users in your extended network.')
|
||||
}
|
||||
],
|
||||
|
||||
$lists = [
|
||||
renderSectionList({id: 'invitations'}).appendTo($users)
|
||||
],
|
||||
|
||||
$folders = folders.map(function(folder) {
|
||||
return Ox.CollapsePanel({
|
||||
collapsed: false,
|
||||
title: folder.title
|
||||
})
|
||||
.css({width: '256px'})
|
||||
.appendTo($users);
|
||||
});
|
||||
|
||||
function renderSectionList(folder) {
|
||||
|
||||
var $list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
format: function() {
|
||||
return $('<img>')
|
||||
.attr({
|
||||
src: Ox.UI.getImageURL('symbolUser')
|
||||
})
|
||||
.css({
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
margin: '2px -2px 2px 0',
|
||||
opacity: folder.id == 'invitations' ? 1 : 0.5
|
||||
})
|
||||
},
|
||||
id: 'id',
|
||||
visible: true,
|
||||
width: 16
|
||||
},
|
||||
{
|
||||
format: function(value) {
|
||||
return folder.id == 'invitations'
|
||||
? value
|
||||
: '<span class="OxLight">' + value + '</span>'
|
||||
},
|
||||
id: 'title',
|
||||
visible: true,
|
||||
width: 240
|
||||
}
|
||||
],
|
||||
items: [
|
||||
{
|
||||
id: folder.id,
|
||||
title: folder.id == 'invitations' ? Ox._('Invitations')
|
||||
: Ox.getObjectById(folders, folder.id).itemTitle
|
||||
},
|
||||
],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
unique: 'id'
|
||||
})
|
||||
.css({height: '16px'})
|
||||
.bindEvent({
|
||||
select: function(data) {
|
||||
if (data.ids.length) {
|
||||
selectItem($list);
|
||||
renderUser({section: data.ids[0]});
|
||||
} else {
|
||||
renderUser();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$list.$body.css({height: '16px'}); // FIXME!
|
||||
|
||||
return $list;
|
||||
|
||||
}
|
||||
|
||||
function renderUser(user) {
|
||||
|
||||
Ox.print('renderUSER', user)
|
||||
|
||||
var $user = Ox.Element(),
|
||||
|
||||
$form = Ox.Element()
|
||||
.addClass('OxSelectable OxTextPage')
|
||||
.css({margin: '16px'})
|
||||
.appendTo($user),
|
||||
|
||||
$warning = Ox.Element()
|
||||
.css({margin: '32px 16px'}) // FIXME: WTF!
|
||||
.appendTo($user),
|
||||
|
||||
$id, $buttons = [], $message,
|
||||
|
||||
folder;
|
||||
|
||||
if (user && user.section) {
|
||||
folder = Ox.getObjectById(folders, user.section);
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
|
||||
$('<div>')
|
||||
.html(folder.text)
|
||||
.appendTo($form);
|
||||
|
||||
} else if (user) {
|
||||
|
||||
if (user.section == 'invitations') {
|
||||
|
||||
$('<div>')
|
||||
.html(
|
||||
'To invite someone, just send her your public key – that\'s all she\'ll need to add you as a peer. '
|
||||
+ 'Along with that, you may want to send a download link for Open Media Library, in case she doesn\'t have it yet.'
|
||||
)
|
||||
.appendTo($form);
|
||||
|
||||
Ox.Input({
|
||||
disabled: true,
|
||||
label: Ox._('Your Public Key'),
|
||||
labelWidth: 128,
|
||||
value: oml.user.id,
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
Ox.Input({
|
||||
disabled: true,
|
||||
label: Ox._('Download Link'),
|
||||
labelWidth: 128,
|
||||
value: 'https://openmedialibrary.com/#download',
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
$('<div>')
|
||||
.html(
|
||||
'If someone invites you, or if you know another user\'s public key, you can add her here.'
|
||||
)
|
||||
.css({
|
||||
margin: '32px 0 8px 0'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
}
|
||||
|
||||
Ox.Input({
|
||||
label: Ox._('Nickname'),
|
||||
labelWidth: 128,
|
||||
placeholder: 'anonymous',
|
||||
value: user.nickname || user.username || '',
|
||||
width: 480
|
||||
})
|
||||
.bindEvent({
|
||||
change: function() {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
if (user.section == 'invitations') {
|
||||
|
||||
$id = Ox.Input({
|
||||
label: Ox._('Public Key'),
|
||||
labelWidth: 128,
|
||||
width: 480
|
||||
})
|
||||
.bindEvent({
|
||||
change: function(data) {
|
||||
var isOwn = data.value == oml.user.id,
|
||||
isPeer = Ox.contains(peerIds, data.value),
|
||||
isValid = oml.validatePublicKey(data.value),
|
||||
peer = Ox.getObjectById(users, data.value);
|
||||
$sendButton.options({
|
||||
disabled: isOwn || isPeer || !isValid
|
||||
});
|
||||
if (data.value && $sendButton.options('disabled')) {
|
||||
$warning.html(
|
||||
isOwn ? 'That\'s your own public key.'
|
||||
: isPeer ? 'That\'s '
|
||||
+ Ox.encodeHTMLEntities(peer.nickname || peer.username)
|
||||
+ ' - you\'re already peered.'
|
||||
: 'That\'s not a valid key.'
|
||||
)
|
||||
} else {
|
||||
$warning.empty();
|
||||
if (data.value) {
|
||||
user.id = data.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
} else {
|
||||
|
||||
Ox.Input({
|
||||
disabled: true,
|
||||
label: Ox._('Username'),
|
||||
labelWidth: 128,
|
||||
placeholder: 'anonymous',
|
||||
value: user.username || '',
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
Ox.Input({
|
||||
disabled: true,
|
||||
label: Ox._('Contact'),
|
||||
labelWidth: 128,
|
||||
value: user.contact || '',
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
}
|
||||
|
||||
Ox.Label({
|
||||
textAlign: 'center',
|
||||
title: user.peered ? Ox._('Remove Peer')
|
||||
: user.pending == 'received' ? Ox._('Peering Request')
|
||||
: user.pending == 'sent' ? Ox._('Cancel Request')
|
||||
: Ox._('Send Peering Request'),
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
marginTop: '32px'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
$message = Ox.Input({
|
||||
label: Ox._('Message'),
|
||||
labelWidth: 128,
|
||||
placeholder: Ox._('none'),
|
||||
width: 480
|
||||
})
|
||||
.css({
|
||||
margin: '8px 0'
|
||||
})
|
||||
.appendTo($form);
|
||||
|
||||
$buttons = (
|
||||
user.peered ? ['remove']
|
||||
: user.pending == 'received' ? ['accept', 'reject']
|
||||
: user.pending == 'sent' ? ['cancel']
|
||||
: ['send']
|
||||
).map(function(id, index) {
|
||||
return Ox.Button({
|
||||
title: Ox.getObjectById(buttons, id).title
|
||||
})
|
||||
.css({
|
||||
float: 'right',
|
||||
marginRight: index ? '8px' : 0
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
var data = {
|
||||
id: user.id,
|
||||
message: $message.value()
|
||||
};
|
||||
if (id == 'send') {
|
||||
oml.api.requestPeering(data, function(result) {
|
||||
|
||||
});
|
||||
} else if (id == 'cancel') {
|
||||
oml.api.cancelPeering(data, function(result) {
|
||||
|
||||
});
|
||||
} else if (id == 'accept') {
|
||||
oml.api.acceptPeering(data, function(result) {
|
||||
|
||||
});
|
||||
} else if (id == 'reject') {
|
||||
oml.api.rejectPeering(data, function(result) {
|
||||
|
||||
});
|
||||
} else if (id == 'remove') {
|
||||
oml.ui.confirmDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
title: Ox._('No, Keep Peer')
|
||||
}),
|
||||
Ox.Button({
|
||||
title: Ox._('Yes, Remove Peer')
|
||||
})
|
||||
],
|
||||
title: Ox._('Remove Peering'),
|
||||
content: Ox._('Are you sure you want to remove this peer?')
|
||||
}, function() {
|
||||
oml.api.removePeering(data, function(result) {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.appendTo($form);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$panel.replaceElement(1, $user);
|
||||
|
||||
}
|
||||
|
||||
function renderUserList(folder) {
|
||||
|
||||
var $list = Ox.TableList({
|
||||
columns: [
|
||||
{
|
||||
format: function(value, data) {
|
||||
return oml.ui.statusIcon(
|
||||
value ? 'connected' : 'disconnected'
|
||||
)
|
||||
.css({
|
||||
margin: '2px 3px 3px 0'
|
||||
});
|
||||
},
|
||||
id: 'online',
|
||||
visible: true,
|
||||
width: 16
|
||||
},
|
||||
{
|
||||
format: function(value) {
|
||||
return value
|
||||
? Ox.encodeHTMLEntities(value)
|
||||
: '<span class="OxLight">anonymous</span>';
|
||||
},
|
||||
id: folder.id == 'peers' ? 'nickname' : 'username',
|
||||
visible: true,
|
||||
width: 240
|
||||
}
|
||||
],
|
||||
items: folder.items,
|
||||
max: 1,
|
||||
sort: [{key: 'index', operator: '+'}],
|
||||
sortable: folder.id == 'peers',
|
||||
unique: 'id'
|
||||
})
|
||||
.css({
|
||||
height: folder.items.length * 16 + 'px'
|
||||
})
|
||||
.bindEvent({
|
||||
select: function(data) {
|
||||
$lists.forEach(function($element) {
|
||||
if ($element != $list) {
|
||||
$element.options({selected: []});
|
||||
}
|
||||
});
|
||||
renderUser(Ox.getObjectById(users, data.ids[0]));
|
||||
}
|
||||
});
|
||||
|
||||
return $list;
|
||||
|
||||
}
|
||||
|
||||
function selectItem($list, id) {
|
||||
$lists.forEach(function($element) {
|
||||
if ($element == $list) {
|
||||
$element.gainFocus();
|
||||
} else {
|
||||
$element.options({selected: []});
|
||||
}
|
||||
});
|
||||
if (id) {
|
||||
$list.options({selected: [id]});
|
||||
renderUser(Ox.getObjectById(users, id));
|
||||
}
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
|
||||
that.options({
|
||||
content: Ox.LoadingScreen().start()
|
||||
});
|
||||
|
||||
oml.api.getUsers(function(result) {
|
||||
|
||||
users = result.data.users;
|
||||
folders.forEach(function(folder) {
|
||||
folder.items = [];
|
||||
});
|
||||
users.forEach(function(user, index) {
|
||||
var id = user.peered ? 'peers' : (user.pending || 'others');
|
||||
Ox.getObjectById(folders, id).items.push(
|
||||
Ox.extend({
|
||||
index: index,
|
||||
nickname: '',
|
||||
username: ''
|
||||
}, user)
|
||||
);
|
||||
});
|
||||
|
||||
Ox.print('FOLDERS::', folders)
|
||||
folders.forEach(function(folder, index) {
|
||||
$lists.push(
|
||||
(
|
||||
folder.items.length
|
||||
? renderUserList(folder)
|
||||
: renderSectionList(folder)
|
||||
).appendTo($folders[index].$content)
|
||||
);
|
||||
});
|
||||
|
||||
$lists.forEach(function($list, index) {
|
||||
$list.bindEvent({
|
||||
selectnext: function() {
|
||||
var $list;
|
||||
if (index < $lists.length - 1) {
|
||||
$list = $lists[index + 1];
|
||||
selectItem($list, $list.options('items')[0].id);
|
||||
}
|
||||
},
|
||||
selectprevious: function() {
|
||||
var $list;
|
||||
if (index) {
|
||||
$list = $lists[index - 1];
|
||||
selectItem($list, Ox.last($list.options('items')).id)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
that.options({content: $panel});
|
||||
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
428
static/js/utils.js
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
(function() {
|
||||
|
||||
// Note: getFindState has to run after getListState and getFilterState
|
||||
|
||||
function everyCondition(conditions, key, operator) {
|
||||
// If every condition has the given key and operator
|
||||
// (excluding conditions where all subconditions match)
|
||||
// returns true, otherwise false
|
||||
return Ox.every(conditions, function(condition) {
|
||||
return condition.key == key && condition.operator == operator;
|
||||
});
|
||||
}
|
||||
|
||||
function oneCondition(conditions, key, operator, includeSubconditions) {
|
||||
// If exactly one condition has the given key and operator
|
||||
// (including or excluding conditions where all subconditions match)
|
||||
// returns the corresponding index, otherwise returns -1
|
||||
var indices = Ox.indicesOf(conditions, function(condition) {
|
||||
return (
|
||||
condition.conditions
|
||||
? includeSubconditions && everyCondition(condition.conditions, key, operator)
|
||||
: condition.key == key && condition.operator == operator
|
||||
);
|
||||
});
|
||||
return indices.length == 1 ? indices[0] : -1;
|
||||
}
|
||||
|
||||
|
||||
oml.getFindState = function(find) {
|
||||
// The find element is populated if exactly one condition in an & query
|
||||
// has a findKey as key and "=" as operator (and all other conditions
|
||||
// are either list or filters), or if all conditions in an | query have
|
||||
// the same filter id as key and "==" as operator
|
||||
Ox.Log('Find', 'getFindState', find)
|
||||
// FIXME: this is still incorrect when you select a lot of filter items
|
||||
// and reload the page (will be advanced)
|
||||
var conditions,
|
||||
indices,
|
||||
state = {index: -1, key: '*', value: ''},
|
||||
ui = oml.user.ui;
|
||||
if (find.operator == '&') {
|
||||
// number of conditions that are not list or filters
|
||||
conditions = find.conditions.length
|
||||
- !!ui._list
|
||||
- ui._filterState.filter(function(filter) {
|
||||
return filter.index > -1;
|
||||
}).length;
|
||||
// indices of non-advanced find queries
|
||||
indices = oml.config.findKeys.map(function(findKey) {
|
||||
return oneCondition(find.conditions, findKey.id, '=');
|
||||
}).filter(function(index) {
|
||||
return index > -1;
|
||||
});
|
||||
state = conditions == 1 && indices.length == 1 ? {
|
||||
index: indices[0],
|
||||
key: find.conditions[indices[0]].key,
|
||||
value: Ox.decodeURIComponent(find.conditions[indices[0]].value)
|
||||
} : {
|
||||
index: -1,
|
||||
key: conditions == 0 && indices.length == 0 ? '*' : 'advanced',
|
||||
value: ''
|
||||
};
|
||||
} else {
|
||||
state = {
|
||||
index: -1,
|
||||
key: 'advanced',
|
||||
value: ''
|
||||
};
|
||||
Ox.forEach(ui.filters, function(key) {
|
||||
if (everyCondition(find.conditions, key, '==')) {
|
||||
state.key = '*';
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
oml.getFilterState = function(find) {
|
||||
// A filter is selected if exactly one condition in an & query or every
|
||||
// condition in an | query has the filter id as key and "==" as operator
|
||||
var ui = oml.user.ui;
|
||||
return ui.filters.map(function(filter) {
|
||||
// FIXME: cant index be an empty array, instead of -1?
|
||||
var key = filter.id,
|
||||
state = {index: -1, find: Ox.clone(find, true), selected: []};
|
||||
if (find.operator == '&') {
|
||||
// include conditions where all subconditions match
|
||||
state.index = oneCondition(find.conditions, key, '==', true);
|
||||
if (state.index > -1) {
|
||||
state.selected = find.conditions[state.index].conditions
|
||||
? find.conditions[state.index].conditions.map(function(condition) {
|
||||
return condition.value;
|
||||
})
|
||||
: [find.conditions[state.index].value];
|
||||
}
|
||||
} else {
|
||||
if (everyCondition(find.conditions, key, '==')) {
|
||||
state.index = Ox.range(find.conditions.length);
|
||||
state.selected = find.conditions.map(function(condition) {
|
||||
return condition.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (state.selected.length) {
|
||||
if (Ox.isArray(state.index)) {
|
||||
// every condition in an | query matches this filter
|
||||
state.find = {conditions: [], operator: ''};
|
||||
} else {
|
||||
// one condition in an & query matches this filter
|
||||
state.find.conditions.splice(state.index, 1);
|
||||
if (
|
||||
state.find.conditions.length == 1
|
||||
&& state.find.conditions[0].conditions
|
||||
) {
|
||||
// unwrap single remaining bracketed query
|
||||
state.find = {
|
||||
conditions: state.find.conditions[0].conditions,
|
||||
operator: state.find.conditions[0].operator
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
}
|
||||
|
||||
oml.getListState = function(find) {
|
||||
// A list is selected if exactly one condition in an & query has "list"
|
||||
// as key and "==" as operator
|
||||
var index, state = '';
|
||||
if (find.operator == '&') {
|
||||
index = oneCondition(find.conditions, 'list', '==');
|
||||
if (index > -1) {
|
||||
state = find.conditions[index].value;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
}());
|
||||
|
||||
oml.addList = function() {
|
||||
// addList(isSmart, isFrom) or addList(list) [=dupicate]
|
||||
var args = arguments,
|
||||
isDuplicate = args.length == 1,
|
||||
isSmart, isFrom, list, listData, data;
|
||||
oml.api.getLists(function(result) {
|
||||
var lists = result.data.lists,
|
||||
listNames = lists[oml.user.id].map(function(list) {
|
||||
return list.name;
|
||||
}),
|
||||
query;
|
||||
if (!isDuplicate) {
|
||||
isSmart = args[0];
|
||||
isFrom = args[1];
|
||||
data = {
|
||||
name: oml.validateName(Ox._('Untitled'), listNames),
|
||||
type: !isSmart ? 'static' : 'smart'
|
||||
};
|
||||
if (isFrom) {
|
||||
if (!isSmart) {
|
||||
data.items = ui.listSelection;
|
||||
} else {
|
||||
data.query = ui.find;
|
||||
}
|
||||
}
|
||||
addList();
|
||||
} else {
|
||||
list = args[0];
|
||||
listData = Ox.getObjectById(Ox.flatten(Ox.values(lists)), list);
|
||||
Ox.print('LISTDATA,', listData)
|
||||
data = Ox.extend({
|
||||
name: oml.validateName(listData.name, listNames),
|
||||
type: listData.type
|
||||
}, listData.query ? {
|
||||
query: listData.query
|
||||
} : {});
|
||||
if (!data.query) {
|
||||
var query = {
|
||||
conditions: [{key: 'list', operator: '==', value: list}],
|
||||
operator: '&'
|
||||
};
|
||||
oml.api.find({query: query}, function(result) {
|
||||
if (result.data.items) {
|
||||
oml.api.find({
|
||||
query: query,
|
||||
keys: ['id'],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
range: [0, result.data.items]
|
||||
}, function(result) {
|
||||
data.items = result.data.items.map(function(item) {
|
||||
return item.id;
|
||||
});
|
||||
addList();
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
}
|
||||
});
|
||||
function addList() {
|
||||
Ox.print('DATA, ', data);
|
||||
oml.api.addList(data, function(result) {
|
||||
Ox.print('LIST ADDED', result.data);
|
||||
var list = result.data.id,
|
||||
$folderList = oml.$ui.folderList[0];
|
||||
oml.$ui.folder[0].options({collapsed: false}); // FIXME: SET UI!
|
||||
// FIXME: DOESN'T WORK
|
||||
$folderList
|
||||
.bindEventOnce({
|
||||
load: function() {
|
||||
$folderList
|
||||
.gainFocus()
|
||||
.options({selected: [list]});
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: list
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
oml.clearFilters = function() {
|
||||
var ui = oml.user.ui,
|
||||
find = Ox.clone(ui.find, true),
|
||||
indices = ui._filterState.map(function(filterState) {
|
||||
return filterState.index;
|
||||
}).filter(function(index) {
|
||||
return index > -1;
|
||||
});
|
||||
find.conditions = find.conditions.filter(function(condition, index) {
|
||||
return !Ox.contains(indices, index);
|
||||
});
|
||||
oml.UI.set({find: find});
|
||||
};
|
||||
|
||||
oml.deleteList = function() {
|
||||
var ui = oml.user.ui;
|
||||
oml.ui.confirmDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
title: Ox._('No, Keep List')
|
||||
}),
|
||||
Ox.Button({
|
||||
title: Ox._('Yes, Delete List')
|
||||
})
|
||||
],
|
||||
content: Ox._('Are you sure you want to delete this list?'),
|
||||
title: Ox._('Delete List')
|
||||
}, function() {
|
||||
oml.api.removeList({
|
||||
id: ui._list
|
||||
}, function() {
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: ':'
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
oml.getPageTitle = function(stateOrURL) {
|
||||
var page = Ox.getObjectById(
|
||||
oml.config.pages,
|
||||
Ox.isObject(stateOrURL) ? stateOrURL.page : stateOrURL.slice(1)
|
||||
);
|
||||
return (page ? page.title + ' – ' : '') + 'Open Media Library';
|
||||
};
|
||||
|
||||
oml.getSortOperator = function(key) {
|
||||
var itemKey = Ox.getObjectById(oml.config.itemKeys, key);
|
||||
return itemKey.sortOperator
|
||||
|| Ox.contains(
|
||||
['string', 'text'],
|
||||
Ox.isArray(itemKey.type) ? itemKey.type[0] : itemKey.type
|
||||
) ? '+' : '-';
|
||||
};
|
||||
|
||||
oml.getFileTypeColor = function(data) {
|
||||
return data.extension == 'epub' ? [[0, 128, 0], [128, 255, 128]]
|
||||
: data.extension == 'pdf' ? (
|
||||
data.textsize ? [[192, 0, 0], [255, 192, 192]]
|
||||
: [[192, 96, 0], [255, 192, 128]]
|
||||
)
|
||||
: data.extension == 'txt' ? [[255, 255, 255], [0, 0, 0]]
|
||||
: [[64, 64, 64], [192, 192, 192]];
|
||||
};
|
||||
|
||||
oml.getFilterSizes = function() {
|
||||
var ui = oml.user.ui;
|
||||
return Ox.splitInt(
|
||||
window.innerWidth - ui.showSidebar * ui.sidebarSize - 1,
|
||||
5
|
||||
);
|
||||
};
|
||||
|
||||
oml.getListFoldersHeight = function() {
|
||||
var ui = oml.user.ui;
|
||||
return Object.keys(ui.showFolder).reduce(function(value, id, index) {
|
||||
var items = oml.$ui.folderList[index].options('items').length;
|
||||
Ox.print('REDUCE', value, id, index, '...', items)
|
||||
return value + 16 + ui.showFolder[id] * (1 + items) * 16;
|
||||
}, 16);
|
||||
};
|
||||
|
||||
oml.getListFoldersWidth = function() {
|
||||
var ui = oml.user.ui;
|
||||
Ox.print('HEIGHT::::', oml.getListFoldersHeight(), 'SCROLLBAR????', oml.$ui.appPanel
|
||||
&& oml.getListFoldersHeight()
|
||||
> window.innerHeight - 20 - 24 - 1 - ui.showInfo * ui.sidebarSize)
|
||||
return ui.sidebarSize - (
|
||||
oml.$ui.appPanel
|
||||
&& oml.getListFoldersHeight()
|
||||
> window.innerHeight - 20 - 24 - 1 - ui.showInfo * ui.sidebarSize
|
||||
? Ox.UI.SCROLLBAR_SIZE : 0
|
||||
);
|
||||
};
|
||||
|
||||
oml.hasDialogOrScreen = function() {
|
||||
return !!$('.OxDialog:visible').length
|
||||
|| !!$('.OxFullscreen').length
|
||||
|| !!$('.OxScreen').length;
|
||||
};
|
||||
|
||||
oml.resizeFilters = function() {
|
||||
// ...
|
||||
};
|
||||
|
||||
oml.resizeListFolders = function() {
|
||||
// FIXME: does this have to be here?
|
||||
Ox.print('RESIZING LIST FOLDERS', 'WIDTH', oml.getListFoldersWidth(), 'HEIGHT', oml.getListFoldersHeight())
|
||||
var width = oml.getListFoldersWidth(),
|
||||
columnWidth = width - 58;
|
||||
oml.$ui.librariesList
|
||||
.resizeColumn('title', columnWidth)
|
||||
.css({width: width + 'px'});
|
||||
Ox.forEach(oml.$ui.folder, function($folder, index) {
|
||||
$folder.css({width: width + 'px'});
|
||||
oml.$ui.libraryList[index]
|
||||
.resizeColumn('title', columnWidth)
|
||||
.css({width: width + 'px'});
|
||||
oml.$ui.folderList[index]
|
||||
.resizeColumn('title', columnWidth)
|
||||
.css({width: width + 'px'});
|
||||
});
|
||||
oml.$ui.librariesList
|
||||
.$body.find('.OxContent')
|
||||
.css({width: width + 'px'});
|
||||
Ox.forEach(oml.$ui.folder, function($folder, index) {
|
||||
oml.$ui.libraryList[index]
|
||||
.$body.find('.OxContent')
|
||||
.css({width: width + 'px'});
|
||||
oml.$ui.folderList[index]
|
||||
.$body.find('.OxContent')
|
||||
.css({width: width + 'px'});
|
||||
})
|
||||
};
|
||||
|
||||
oml.updateFilterMenus = function() {
|
||||
// FIXME: does this have to be a utils function?
|
||||
var selected = oml.$ui.filters.map(function($filter) {
|
||||
return !Ox.isEmpty($filter.options('selected'));
|
||||
}),
|
||||
filtersHaveSelection = !!Ox.sum(selected);
|
||||
oml.$ui.filters.forEach(function($filter, index) {
|
||||
$filter[
|
||||
selected[index] ? 'enableMenuItem' : 'disableMenuItem'
|
||||
]('clearFilter');
|
||||
$filter[
|
||||
filtersHaveSelection ? 'enableMenuItem' : 'disableMenuItem'
|
||||
]('clearFilters');
|
||||
});
|
||||
};
|
||||
|
||||
oml.updateLists = function(callback) {
|
||||
// FIXME: can this go somewhere else?
|
||||
Ox.Request.clearCache('getLists');
|
||||
oml.api.getLists(function(result) {
|
||||
var items = result.data.lists[oml.user.id];
|
||||
oml.$ui.folderList[0].options({
|
||||
items: items
|
||||
})
|
||||
.css({height: items.length * 16 + 'px'})
|
||||
.size();
|
||||
oml.$ui.folder[0].$content
|
||||
.css({height: 16 + items.length * 16 + 'px'});
|
||||
callback && callback();
|
||||
});
|
||||
};
|
||||
|
||||
oml.validateName = function(value, names) {
|
||||
var index = 1, length = 256, suffix;
|
||||
value = Ox.clean(Ox.clean(value).slice(0, length));
|
||||
names = names || [];
|
||||
while (Ox.contains(names, value)) {
|
||||
suffix = ' [' + (++index) + ']';
|
||||
value = value.replace(/ \[\d+\]$/, '')
|
||||
.slice(0, length - suffix.length) + suffix;
|
||||
};
|
||||
return value;
|
||||
};
|
||||
|
||||
oml.validatePublicKey = function(value) {
|
||||
return /^[A-Za-z0-9+\/]{43}$/.test(value);
|
||||
};
|
||||
|
||||
35
static/js/viewer.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.viewer = function() {
|
||||
|
||||
var ui = oml.user.ui,
|
||||
|
||||
that = Ox.Element()
|
||||
.bindEvent({
|
||||
oml_item: function(data) {
|
||||
that.update();
|
||||
},
|
||||
oml_itemview: function(data) {
|
||||
that.update();
|
||||
}
|
||||
}),
|
||||
|
||||
$iframe;
|
||||
|
||||
that.update = function() {
|
||||
if (ui.item && ui.itemView == 'book') {
|
||||
$iframe = $iframe || Ox.Element('<iframe>').css({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: 0
|
||||
}).appendTo(that);
|
||||
$iframe.attr({
|
||||
src: '/' + ui.item + '/reader/'
|
||||
});
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
return that.update();
|
||||
|
||||
};
|
||||
25
static/js/welcomeDialog.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
oml.ui.welcomeDialog = function() {
|
||||
|
||||
var that = oml.ui.iconDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
id: 'close',
|
||||
title: Ox._('Close')
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
oml.UI.set({page: ''});
|
||||
}
|
||||
})
|
||||
],
|
||||
content: 'Welcome! To get started, you may want to set your '
|
||||
+ 'library path, import some media, start your node, chose a '
|
||||
+ 'username and add a few peers.',
|
||||
title: Ox._('Welcome to Open Media Library')
|
||||
});
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
61
static/json/js.json
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
[
|
||||
"Preferences.js",
|
||||
"UI.js",
|
||||
"URL.js",
|
||||
"allItems.js",
|
||||
"appDialog.js",
|
||||
"appPanel.js",
|
||||
"backButton.js",
|
||||
"browser.js",
|
||||
"columnView.js",
|
||||
"confirmDialog.js",
|
||||
"connectionButton.js",
|
||||
"errorDialog.js",
|
||||
"filter.js",
|
||||
"filtersInnerPanel.js",
|
||||
"filtersOuterPanel.js",
|
||||
"findElement.js",
|
||||
"folderList.js",
|
||||
"folderPlaceholder.js",
|
||||
"folders.js",
|
||||
"fullscreenButton.js",
|
||||
"gridView.js",
|
||||
"iconDialog.js",
|
||||
"info.js",
|
||||
"infoView.js",
|
||||
"itemInnerPanel.js",
|
||||
"itemMenu.js",
|
||||
"itemOuterPanel.js",
|
||||
"itemToolbar.js",
|
||||
"itemViewButtons.js",
|
||||
"itemViewPanel.js",
|
||||
"leftPanel.js",
|
||||
"list.js",
|
||||
"listDialog.js",
|
||||
"listInnerPanel.js",
|
||||
"listOuterPanel.js",
|
||||
"listToolbar.js",
|
||||
"listView.js",
|
||||
"listViewButtons.js",
|
||||
"loadingIcon.js",
|
||||
"mainMenu.js",
|
||||
"mainPanel.js",
|
||||
"notificationsButton.js",
|
||||
"openButton.js",
|
||||
"preferencesDialog.js",
|
||||
"previewButton.js",
|
||||
"previewDialog.js",
|
||||
"resetUIDialog.js",
|
||||
"rightPanel.js",
|
||||
"sectionButtons.js",
|
||||
"sectionbar.js",
|
||||
"sortElement.js",
|
||||
"statusIcon.js",
|
||||
"statusbar.js",
|
||||
"transfersDialog.js",
|
||||
"userButton.js",
|
||||
"usersDialog.js",
|
||||
"utils.js",
|
||||
"viewer.js",
|
||||
"welcomeDialog.js"
|
||||
]
|
||||
483
static/pdf.js/compatibility.js
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* globals VBArray, PDFJS */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Initializing PDFJS global object here, it case if we need to change/disable
|
||||
// some PDF.js features, e.g. range requests
|
||||
if (typeof PDFJS === 'undefined') {
|
||||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
// Checking if the typed arrays are supported
|
||||
(function checkTypedArrayCompatibility() {
|
||||
if (typeof Uint8Array !== 'undefined') {
|
||||
// some mobile versions do not support subarray (e.g. safari 5 / iOS)
|
||||
if (typeof Uint8Array.prototype.subarray === 'undefined') {
|
||||
Uint8Array.prototype.subarray = function subarray(start, end) {
|
||||
return new Uint8Array(this.slice(start, end));
|
||||
};
|
||||
Float32Array.prototype.subarray = function subarray(start, end) {
|
||||
return new Float32Array(this.slice(start, end));
|
||||
};
|
||||
}
|
||||
|
||||
// some mobile version might not support Float64Array
|
||||
if (typeof Float64Array === 'undefined')
|
||||
window.Float64Array = Float32Array;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function subarray(start, end) {
|
||||
return new TypedArray(this.slice(start, end));
|
||||
}
|
||||
|
||||
function setArrayOffset(array, offset) {
|
||||
if (arguments.length < 2)
|
||||
offset = 0;
|
||||
for (var i = 0, n = array.length; i < n; ++i, ++offset)
|
||||
this[offset] = array[i] & 0xFF;
|
||||
}
|
||||
|
||||
function TypedArray(arg1) {
|
||||
var result;
|
||||
if (typeof arg1 === 'number') {
|
||||
result = [];
|
||||
for (var i = 0; i < arg1; ++i)
|
||||
result[i] = 0;
|
||||
} else if ('slice' in arg1) {
|
||||
result = arg1.slice(0);
|
||||
} else {
|
||||
result = [];
|
||||
for (var i = 0, n = arg1.length; i < n; ++i) {
|
||||
result[i] = arg1[i];
|
||||
}
|
||||
}
|
||||
|
||||
result.subarray = subarray;
|
||||
result.buffer = result;
|
||||
result.byteLength = result.length;
|
||||
result.set = setArrayOffset;
|
||||
|
||||
if (typeof arg1 === 'object' && arg1.buffer)
|
||||
result.buffer = arg1.buffer;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
window.Uint8Array = TypedArray;
|
||||
|
||||
// we don't need support for set, byteLength for 32-bit array
|
||||
// so we can use the TypedArray as well
|
||||
window.Uint32Array = TypedArray;
|
||||
window.Int32Array = TypedArray;
|
||||
window.Uint16Array = TypedArray;
|
||||
window.Float32Array = TypedArray;
|
||||
window.Float64Array = TypedArray;
|
||||
})();
|
||||
|
||||
// URL = URL || webkitURL
|
||||
(function normalizeURLObject() {
|
||||
if (!window.URL) {
|
||||
window.URL = window.webkitURL;
|
||||
}
|
||||
})();
|
||||
|
||||
// Object.create() ?
|
||||
(function checkObjectCreateCompatibility() {
|
||||
if (typeof Object.create !== 'undefined')
|
||||
return;
|
||||
|
||||
Object.create = function objectCreate(proto) {
|
||||
function Constructor() {}
|
||||
Constructor.prototype = proto;
|
||||
return new Constructor();
|
||||
};
|
||||
})();
|
||||
|
||||
// Object.defineProperty() ?
|
||||
(function checkObjectDefinePropertyCompatibility() {
|
||||
if (typeof Object.defineProperty !== 'undefined') {
|
||||
var definePropertyPossible = true;
|
||||
try {
|
||||
// some browsers (e.g. safari) cannot use defineProperty() on DOM objects
|
||||
// and thus the native version is not sufficient
|
||||
Object.defineProperty(new Image(), 'id', { value: 'test' });
|
||||
// ... another test for android gb browser for non-DOM objects
|
||||
var Test = function Test() {};
|
||||
Test.prototype = { get id() { } };
|
||||
Object.defineProperty(new Test(), 'id',
|
||||
{ value: '', configurable: true, enumerable: true, writable: false });
|
||||
} catch (e) {
|
||||
definePropertyPossible = false;
|
||||
}
|
||||
if (definePropertyPossible) return;
|
||||
}
|
||||
|
||||
Object.defineProperty = function objectDefineProperty(obj, name, def) {
|
||||
delete obj[name];
|
||||
if ('get' in def)
|
||||
obj.__defineGetter__(name, def['get']);
|
||||
if ('set' in def)
|
||||
obj.__defineSetter__(name, def['set']);
|
||||
if ('value' in def) {
|
||||
obj.__defineSetter__(name, function objectDefinePropertySetter(value) {
|
||||
this.__defineGetter__(name, function objectDefinePropertyGetter() {
|
||||
return value;
|
||||
});
|
||||
return value;
|
||||
});
|
||||
obj[name] = def.value;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Object.keys() ?
|
||||
(function checkObjectKeysCompatibility() {
|
||||
if (typeof Object.keys !== 'undefined')
|
||||
return;
|
||||
|
||||
Object.keys = function objectKeys(obj) {
|
||||
var result = [];
|
||||
for (var i in obj) {
|
||||
if (obj.hasOwnProperty(i))
|
||||
result.push(i);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
|
||||
// No readAsArrayBuffer ?
|
||||
(function checkFileReaderReadAsArrayBuffer() {
|
||||
if (typeof FileReader === 'undefined')
|
||||
return; // FileReader is not implemented
|
||||
var frPrototype = FileReader.prototype;
|
||||
// Older versions of Firefox might not have readAsArrayBuffer
|
||||
if ('readAsArrayBuffer' in frPrototype)
|
||||
return; // readAsArrayBuffer is implemented
|
||||
Object.defineProperty(frPrototype, 'readAsArrayBuffer', {
|
||||
value: function fileReaderReadAsArrayBuffer(blob) {
|
||||
var fileReader = new FileReader();
|
||||
var originalReader = this;
|
||||
fileReader.onload = function fileReaderOnload(evt) {
|
||||
var data = evt.target.result;
|
||||
var buffer = new ArrayBuffer(data.length);
|
||||
var uint8Array = new Uint8Array(buffer);
|
||||
|
||||
for (var i = 0, ii = data.length; i < ii; i++)
|
||||
uint8Array[i] = data.charCodeAt(i);
|
||||
|
||||
Object.defineProperty(originalReader, 'result', {
|
||||
value: buffer,
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
var event = document.createEvent('HTMLEvents');
|
||||
event.initEvent('load', false, false);
|
||||
originalReader.dispatchEvent(event);
|
||||
};
|
||||
fileReader.readAsBinaryString(blob);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// No XMLHttpRequest.response ?
|
||||
(function checkXMLHttpRequestResponseCompatibility() {
|
||||
var xhrPrototype = XMLHttpRequest.prototype;
|
||||
if (!('overrideMimeType' in xhrPrototype)) {
|
||||
// IE10 might have response, but not overrideMimeType
|
||||
Object.defineProperty(xhrPrototype, 'overrideMimeType', {
|
||||
value: function xmlHttpRequestOverrideMimeType(mimeType) {}
|
||||
});
|
||||
}
|
||||
if ('response' in xhrPrototype ||
|
||||
'mozResponseArrayBuffer' in xhrPrototype ||
|
||||
'mozResponse' in xhrPrototype ||
|
||||
'responseArrayBuffer' in xhrPrototype)
|
||||
return;
|
||||
// IE9 ?
|
||||
if (typeof VBArray !== 'undefined') {
|
||||
Object.defineProperty(xhrPrototype, 'response', {
|
||||
get: function xmlHttpRequestResponseGet() {
|
||||
return new Uint8Array(new VBArray(this.responseBody).toArray());
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// other browsers
|
||||
function responseTypeSetter() {
|
||||
// will be only called to set "arraybuffer"
|
||||
this.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
}
|
||||
if (typeof xhrPrototype.overrideMimeType === 'function') {
|
||||
Object.defineProperty(xhrPrototype, 'responseType',
|
||||
{ set: responseTypeSetter });
|
||||
}
|
||||
function responseGetter() {
|
||||
var text = this.responseText;
|
||||
var i, n = text.length;
|
||||
var result = new Uint8Array(n);
|
||||
for (i = 0; i < n; ++i)
|
||||
result[i] = text.charCodeAt(i) & 0xFF;
|
||||
return result;
|
||||
}
|
||||
Object.defineProperty(xhrPrototype, 'response', { get: responseGetter });
|
||||
})();
|
||||
|
||||
// window.btoa (base64 encode function) ?
|
||||
(function checkWindowBtoaCompatibility() {
|
||||
if ('btoa' in window)
|
||||
return;
|
||||
|
||||
var digits =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
window.btoa = function windowBtoa(chars) {
|
||||
var buffer = '';
|
||||
var i, n;
|
||||
for (i = 0, n = chars.length; i < n; i += 3) {
|
||||
var b1 = chars.charCodeAt(i) & 0xFF;
|
||||
var b2 = chars.charCodeAt(i + 1) & 0xFF;
|
||||
var b3 = chars.charCodeAt(i + 2) & 0xFF;
|
||||
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
||||
var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
|
||||
var d4 = i + 2 < n ? (b3 & 0x3F) : 64;
|
||||
buffer += (digits.charAt(d1) + digits.charAt(d2) +
|
||||
digits.charAt(d3) + digits.charAt(d4));
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
})();
|
||||
|
||||
// window.atob (base64 encode function) ?
|
||||
(function checkWindowAtobCompatibility() {
|
||||
if ('atob' in window)
|
||||
return;
|
||||
|
||||
// https://github.com/davidchambers/Base64.js
|
||||
var digits =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
window.atob = function (input) {
|
||||
input = input.replace(/=+$/, '');
|
||||
if (input.length % 4 == 1) throw new Error('bad atob input');
|
||||
for (
|
||||
// initialize result and counters
|
||||
var bc = 0, bs, buffer, idx = 0, output = '';
|
||||
// get next character
|
||||
buffer = input.charAt(idx++);
|
||||
// character found in table?
|
||||
// initialize bit storage and add its ascii value
|
||||
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
|
||||
// and if not first of each 4 characters,
|
||||
// convert the first 8 bits to one ascii character
|
||||
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
|
||||
) {
|
||||
// try to find character in table (0-63, not found => -1)
|
||||
buffer = digits.indexOf(buffer);
|
||||
}
|
||||
return output;
|
||||
};
|
||||
})();
|
||||
|
||||
// Function.prototype.bind ?
|
||||
(function checkFunctionPrototypeBindCompatibility() {
|
||||
if (typeof Function.prototype.bind !== 'undefined')
|
||||
return;
|
||||
|
||||
Function.prototype.bind = function functionPrototypeBind(obj) {
|
||||
var fn = this, headArgs = Array.prototype.slice.call(arguments, 1);
|
||||
var bound = function functionPrototypeBindBound() {
|
||||
var args = Array.prototype.concat.apply(headArgs, arguments);
|
||||
return fn.apply(obj, args);
|
||||
};
|
||||
return bound;
|
||||
};
|
||||
})();
|
||||
|
||||
// HTMLElement dataset property
|
||||
(function checkDatasetProperty() {
|
||||
var div = document.createElement('div');
|
||||
if ('dataset' in div)
|
||||
return; // dataset property exists
|
||||
|
||||
Object.defineProperty(HTMLElement.prototype, 'dataset', {
|
||||
get: function() {
|
||||
if (this._dataset)
|
||||
return this._dataset;
|
||||
|
||||
var dataset = {};
|
||||
for (var j = 0, jj = this.attributes.length; j < jj; j++) {
|
||||
var attribute = this.attributes[j];
|
||||
if (attribute.name.substring(0, 5) != 'data-')
|
||||
continue;
|
||||
var key = attribute.name.substring(5).replace(/\-([a-z])/g,
|
||||
function(all, ch) { return ch.toUpperCase(); });
|
||||
dataset[key] = attribute.value;
|
||||
}
|
||||
|
||||
Object.defineProperty(this, '_dataset', {
|
||||
value: dataset,
|
||||
writable: false,
|
||||
enumerable: false
|
||||
});
|
||||
return dataset;
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
})();
|
||||
|
||||
// HTMLElement classList property
|
||||
(function checkClassListProperty() {
|
||||
var div = document.createElement('div');
|
||||
if ('classList' in div)
|
||||
return; // classList property exists
|
||||
|
||||
function changeList(element, itemName, add, remove) {
|
||||
var s = element.className || '';
|
||||
var list = s.split(/\s+/g);
|
||||
if (list[0] === '') list.shift();
|
||||
var index = list.indexOf(itemName);
|
||||
if (index < 0 && add)
|
||||
list.push(itemName);
|
||||
if (index >= 0 && remove)
|
||||
list.splice(index, 1);
|
||||
element.className = list.join(' ');
|
||||
return (index >= 0);
|
||||
}
|
||||
|
||||
var classListPrototype = {
|
||||
add: function(name) {
|
||||
changeList(this.element, name, true, false);
|
||||
},
|
||||
contains: function(name) {
|
||||
return changeList(this.element, name, false, false);
|
||||
},
|
||||
remove: function(name) {
|
||||
changeList(this.element, name, false, true);
|
||||
},
|
||||
toggle: function(name) {
|
||||
changeList(this.element, name, true, true);
|
||||
}
|
||||
};
|
||||
|
||||
Object.defineProperty(HTMLElement.prototype, 'classList', {
|
||||
get: function() {
|
||||
if (this._classList)
|
||||
return this._classList;
|
||||
|
||||
var classList = Object.create(classListPrototype, {
|
||||
element: {
|
||||
value: this,
|
||||
writable: false,
|
||||
enumerable: true
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, '_classList', {
|
||||
value: classList,
|
||||
writable: false,
|
||||
enumerable: false
|
||||
});
|
||||
return classList;
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
})();
|
||||
|
||||
// Check console compatibility
|
||||
(function checkConsoleCompatibility() {
|
||||
if (!('console' in window)) {
|
||||
window.console = {
|
||||
log: function() {},
|
||||
error: function() {},
|
||||
warn: function() {}
|
||||
};
|
||||
} else if (!('bind' in console.log)) {
|
||||
// native functions in IE9 might not have bind
|
||||
console.log = (function(fn) {
|
||||
return function(msg) { return fn(msg); };
|
||||
})(console.log);
|
||||
console.error = (function(fn) {
|
||||
return function(msg) { return fn(msg); };
|
||||
})(console.error);
|
||||
console.warn = (function(fn) {
|
||||
return function(msg) { return fn(msg); };
|
||||
})(console.warn);
|
||||
}
|
||||
})();
|
||||
|
||||
// Check onclick compatibility in Opera
|
||||
(function checkOnClickCompatibility() {
|
||||
// workaround for reported Opera bug DSK-354448:
|
||||
// onclick fires on disabled buttons with opaque content
|
||||
function ignoreIfTargetDisabled(event) {
|
||||
if (isDisabled(event.target)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
function isDisabled(node) {
|
||||
return node.disabled || (node.parentNode && isDisabled(node.parentNode));
|
||||
}
|
||||
if (navigator.userAgent.indexOf('Opera') != -1) {
|
||||
// use browser detection since we cannot feature-check this bug
|
||||
document.addEventListener('click', ignoreIfTargetDisabled, true);
|
||||
}
|
||||
})();
|
||||
|
||||
// Checks if navigator.language is supported
|
||||
(function checkNavigatorLanguage() {
|
||||
if ('language' in navigator)
|
||||
return;
|
||||
Object.defineProperty(navigator, 'language', {
|
||||
get: function navigatorLanguage() {
|
||||
var language = navigator.userLanguage || 'en-US';
|
||||
return language.substring(0, 2).toLowerCase() +
|
||||
language.substring(2).toUpperCase();
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
})();
|
||||
|
||||
(function checkRangeRequests() {
|
||||
// Safari has issues with cached range requests see:
|
||||
// https://github.com/mozilla/pdf.js/issues/3260
|
||||
// Last tested with version 6.0.4.
|
||||
var isSafari = Object.prototype.toString.call(
|
||||
window.HTMLElement).indexOf('Constructor') > 0;
|
||||
|
||||
// Older versions of Android (pre 3.0) has issues with range requests, see:
|
||||
// https://github.com/mozilla/pdf.js/issues/3381.
|
||||
// Make sure that we only match webkit-based Android browsers,
|
||||
// since Firefox/Fennec works as expected.
|
||||
var regex = /Android\s[0-2][^\d]/;
|
||||
var isOldAndroid = regex.test(navigator.userAgent);
|
||||
|
||||
if (isSafari || isOldAndroid) {
|
||||
PDFJS.disableRange = true;
|
||||
}
|
||||
})();
|
||||
|
||||
// Check if the browser supports manipulation of the history.
|
||||
(function checkHistoryManipulation() {
|
||||
if (!window.history.pushState) {
|
||||
PDFJS.disableHistory = true;
|
||||
}
|
||||
})();
|
||||
28
static/pdf.js/css/videopdf.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
.button {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding: 4px;
|
||||
border: 2px solid rgb(255, 255, 255);
|
||||
border-radius: 16px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 0 2px rgb(0, 0, 0);
|
||||
cursor: pointer;
|
||||
}
|
||||
.button.playButton {
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.button.editButton {
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
}
|
||||
.interface {
|
||||
position: absolute;
|
||||
}
|
||||
.interface.video {
|
||||
cursor: pointer;
|
||||
}
|
||||
540
static/pdf.js/debugger.js
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* globals PDFJS */
|
||||
|
||||
'use strict';
|
||||
|
||||
var FontInspector = (function FontInspectorClosure() {
|
||||
var fonts;
|
||||
var panelWidth = 300;
|
||||
var active = false;
|
||||
var fontAttribute = 'data-font-name';
|
||||
function removeSelection() {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = '';
|
||||
}
|
||||
}
|
||||
function resetSelection() {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = 'debuggerHideText';
|
||||
}
|
||||
}
|
||||
function selectFont(fontName, show) {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + '=' +
|
||||
fontName + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = show ? 'debuggerShowText' : 'debuggerHideText';
|
||||
}
|
||||
}
|
||||
function textLayerClick(e) {
|
||||
if (!e.target.dataset.fontName || e.target.tagName.toUpperCase() !== 'DIV')
|
||||
return;
|
||||
var fontName = e.target.dataset.fontName;
|
||||
var selects = document.getElementsByTagName('input');
|
||||
for (var i = 0; i < selects.length; ++i) {
|
||||
var select = selects[i];
|
||||
if (select.dataset.fontName != fontName) continue;
|
||||
select.checked = !select.checked;
|
||||
selectFont(fontName, select.checked);
|
||||
select.scrollIntoView();
|
||||
}
|
||||
}
|
||||
return {
|
||||
// Properties/functions needed by PDFBug.
|
||||
id: 'FontInspector',
|
||||
name: 'Font Inspector',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
var panel = this.panel;
|
||||
panel.setAttribute('style', 'padding: 5px;');
|
||||
var tmp = document.createElement('button');
|
||||
tmp.addEventListener('click', resetSelection);
|
||||
tmp.textContent = 'Refresh';
|
||||
panel.appendChild(tmp);
|
||||
|
||||
fonts = document.createElement('div');
|
||||
panel.appendChild(fonts);
|
||||
},
|
||||
enabled: false,
|
||||
get active() {
|
||||
return active;
|
||||
},
|
||||
set active(value) {
|
||||
active = value;
|
||||
if (active) {
|
||||
document.body.addEventListener('click', textLayerClick, true);
|
||||
resetSelection();
|
||||
} else {
|
||||
document.body.removeEventListener('click', textLayerClick, true);
|
||||
removeSelection();
|
||||
}
|
||||
},
|
||||
// FontInspector specific functions.
|
||||
fontAdded: function fontAdded(fontObj, url) {
|
||||
function properties(obj, list) {
|
||||
var moreInfo = document.createElement('table');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var tr = document.createElement('tr');
|
||||
var td1 = document.createElement('td');
|
||||
td1.textContent = list[i];
|
||||
tr.appendChild(td1);
|
||||
var td2 = document.createElement('td');
|
||||
td2.textContent = obj[list[i]].toString();
|
||||
tr.appendChild(td2);
|
||||
moreInfo.appendChild(tr);
|
||||
}
|
||||
return moreInfo;
|
||||
}
|
||||
var moreInfo = properties(fontObj, ['name', 'type']);
|
||||
var m = /url\(['"]?([^\)"']+)/.exec(url);
|
||||
var fontName = fontObj.loadedName;
|
||||
var font = document.createElement('div');
|
||||
var name = document.createElement('span');
|
||||
name.textContent = fontName;
|
||||
var download = document.createElement('a');
|
||||
download.href = m[1];
|
||||
download.textContent = 'Download';
|
||||
var logIt = document.createElement('a');
|
||||
logIt.href = '';
|
||||
logIt.textContent = 'Log';
|
||||
logIt.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
console.log(fontObj);
|
||||
});
|
||||
var select = document.createElement('input');
|
||||
select.setAttribute('type', 'checkbox');
|
||||
select.dataset.fontName = fontName;
|
||||
select.addEventListener('click', (function(select, fontName) {
|
||||
return (function() {
|
||||
selectFont(fontName, select.checked);
|
||||
});
|
||||
})(select, fontName));
|
||||
font.appendChild(select);
|
||||
font.appendChild(name);
|
||||
font.appendChild(document.createTextNode(' '));
|
||||
font.appendChild(download);
|
||||
font.appendChild(document.createTextNode(' '));
|
||||
font.appendChild(logIt);
|
||||
font.appendChild(moreInfo);
|
||||
fonts.appendChild(font);
|
||||
// Somewhat of a hack, should probably add a hook for when the text layer
|
||||
// is done rendering.
|
||||
setTimeout(function() {
|
||||
if (this.active)
|
||||
resetSelection();
|
||||
}.bind(this), 2000);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Manages all the page steppers.
|
||||
var StepperManager = (function StepperManagerClosure() {
|
||||
var steppers = [];
|
||||
var stepperDiv = null;
|
||||
var stepperControls = null;
|
||||
var stepperChooser = null;
|
||||
var breakPoints = {};
|
||||
return {
|
||||
// Properties/functions needed by PDFBug.
|
||||
id: 'Stepper',
|
||||
name: 'Stepper',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
var self = this;
|
||||
this.panel.setAttribute('style', 'padding: 5px;');
|
||||
stepperControls = document.createElement('div');
|
||||
stepperChooser = document.createElement('select');
|
||||
stepperChooser.addEventListener('change', function(event) {
|
||||
self.selectStepper(this.value);
|
||||
});
|
||||
stepperControls.appendChild(stepperChooser);
|
||||
stepperDiv = document.createElement('div');
|
||||
this.panel.appendChild(stepperControls);
|
||||
this.panel.appendChild(stepperDiv);
|
||||
if (sessionStorage.getItem('pdfjsBreakPoints'))
|
||||
breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
// Stepper specific functions.
|
||||
create: function create(pageIndex) {
|
||||
var debug = document.createElement('div');
|
||||
debug.id = 'stepper' + pageIndex;
|
||||
debug.setAttribute('hidden', true);
|
||||
debug.className = 'stepper';
|
||||
stepperDiv.appendChild(debug);
|
||||
var b = document.createElement('option');
|
||||
b.textContent = 'Page ' + (pageIndex + 1);
|
||||
b.value = pageIndex;
|
||||
stepperChooser.appendChild(b);
|
||||
var initBreakPoints = breakPoints[pageIndex] || [];
|
||||
var stepper = new Stepper(debug, pageIndex, initBreakPoints);
|
||||
steppers.push(stepper);
|
||||
if (steppers.length === 1)
|
||||
this.selectStepper(pageIndex, false);
|
||||
return stepper;
|
||||
},
|
||||
selectStepper: function selectStepper(pageIndex, selectPanel) {
|
||||
if (selectPanel)
|
||||
this.manager.selectPanel(1);
|
||||
for (var i = 0; i < steppers.length; ++i) {
|
||||
var stepper = steppers[i];
|
||||
if (stepper.pageIndex == pageIndex)
|
||||
stepper.panel.removeAttribute('hidden');
|
||||
else
|
||||
stepper.panel.setAttribute('hidden', true);
|
||||
}
|
||||
var options = stepperChooser.options;
|
||||
for (var i = 0; i < options.length; ++i) {
|
||||
var option = options[i];
|
||||
option.selected = option.value == pageIndex;
|
||||
}
|
||||
},
|
||||
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
|
||||
breakPoints[pageIndex] = bps;
|
||||
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// The stepper for each page's IRQueue.
|
||||
var Stepper = (function StepperClosure() {
|
||||
// Shorter way to create element and optionally set textContent.
|
||||
function c(tag, textContent) {
|
||||
var d = document.createElement(tag);
|
||||
if (textContent)
|
||||
d.textContent = textContent;
|
||||
return d;
|
||||
}
|
||||
|
||||
function glyphsToString(glyphs) {
|
||||
var out = '';
|
||||
for (var i = 0; i < glyphs.length; i++) {
|
||||
if (glyphs[i] === null) {
|
||||
out += ' ';
|
||||
} else {
|
||||
out += glyphs[i].fontChar;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
var opMap = null;
|
||||
|
||||
var glyphCommands = {
|
||||
'showText': 0,
|
||||
'showSpacedText': 0,
|
||||
'nextLineShowText': 0,
|
||||
'nextLineSetSpacingShowText': 2
|
||||
};
|
||||
|
||||
function Stepper(panel, pageIndex, initialBreakPoints) {
|
||||
this.panel = panel;
|
||||
this.breakPoint = 0;
|
||||
this.nextBreakPoint = null;
|
||||
this.pageIndex = pageIndex;
|
||||
this.breakPoints = initialBreakPoints;
|
||||
this.currentIdx = -1;
|
||||
this.operatorListIdx = 0;
|
||||
}
|
||||
Stepper.prototype = {
|
||||
init: function init() {
|
||||
var panel = this.panel;
|
||||
var content = c('div', 'c=continue, s=step');
|
||||
var table = c('table');
|
||||
content.appendChild(table);
|
||||
table.cellSpacing = 0;
|
||||
var headerRow = c('tr');
|
||||
table.appendChild(headerRow);
|
||||
headerRow.appendChild(c('th', 'Break'));
|
||||
headerRow.appendChild(c('th', 'Idx'));
|
||||
headerRow.appendChild(c('th', 'fn'));
|
||||
headerRow.appendChild(c('th', 'args'));
|
||||
panel.appendChild(content);
|
||||
this.table = table;
|
||||
if (!opMap) {
|
||||
opMap = Object.create(null);
|
||||
for (var key in PDFJS.OPS) {
|
||||
opMap[PDFJS.OPS[key]] = key;
|
||||
}
|
||||
}
|
||||
},
|
||||
updateOperatorList: function updateOperatorList(operatorList) {
|
||||
var self = this;
|
||||
for (var i = this.operatorListIdx; i < operatorList.fnArray.length; i++) {
|
||||
var line = c('tr');
|
||||
line.className = 'line';
|
||||
line.dataset.idx = i;
|
||||
this.table.appendChild(line);
|
||||
var checked = this.breakPoints.indexOf(i) != -1;
|
||||
var args = operatorList.argsArray[i] ? operatorList.argsArray[i] : [];
|
||||
|
||||
var breakCell = c('td');
|
||||
var cbox = c('input');
|
||||
cbox.type = 'checkbox';
|
||||
cbox.className = 'points';
|
||||
cbox.checked = checked;
|
||||
cbox.onclick = (function(x) {
|
||||
return function() {
|
||||
if (this.checked)
|
||||
self.breakPoints.push(x);
|
||||
else
|
||||
self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
|
||||
StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
|
||||
};
|
||||
})(i);
|
||||
|
||||
breakCell.appendChild(cbox);
|
||||
line.appendChild(breakCell);
|
||||
line.appendChild(c('td', i.toString()));
|
||||
var fn = opMap[operatorList.fnArray[i]];
|
||||
var decArgs = args;
|
||||
if (fn in glyphCommands) {
|
||||
var glyphIndex = glyphCommands[fn];
|
||||
var glyphs = args[glyphIndex];
|
||||
var decArgs = args.slice();
|
||||
var newArg;
|
||||
if (fn === 'showSpacedText') {
|
||||
newArg = [];
|
||||
for (var j = 0; j < glyphs.length; j++) {
|
||||
if (typeof glyphs[j] === 'number') {
|
||||
newArg.push(glyphs[j]);
|
||||
} else {
|
||||
newArg.push(glyphsToString(glyphs[j]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newArg = glyphsToString(glyphs);
|
||||
}
|
||||
decArgs[glyphIndex] = newArg;
|
||||
}
|
||||
line.appendChild(c('td', fn));
|
||||
line.appendChild(c('td', JSON.stringify(decArgs)));
|
||||
}
|
||||
},
|
||||
getNextBreakPoint: function getNextBreakPoint() {
|
||||
this.breakPoints.sort(function(a, b) { return a - b; });
|
||||
for (var i = 0; i < this.breakPoints.length; i++) {
|
||||
if (this.breakPoints[i] > this.currentIdx)
|
||||
return this.breakPoints[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
breakIt: function breakIt(idx, callback) {
|
||||
StepperManager.selectStepper(this.pageIndex, true);
|
||||
var self = this;
|
||||
var dom = document;
|
||||
self.currentIdx = idx;
|
||||
var listener = function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 83: // step
|
||||
dom.removeEventListener('keydown', listener, false);
|
||||
self.nextBreakPoint = self.currentIdx + 1;
|
||||
self.goTo(-1);
|
||||
callback();
|
||||
break;
|
||||
case 67: // continue
|
||||
dom.removeEventListener('keydown', listener, false);
|
||||
var breakPoint = self.getNextBreakPoint();
|
||||
self.nextBreakPoint = breakPoint;
|
||||
self.goTo(-1);
|
||||
callback();
|
||||
break;
|
||||
}
|
||||
};
|
||||
dom.addEventListener('keydown', listener, false);
|
||||
self.goTo(idx);
|
||||
},
|
||||
goTo: function goTo(idx) {
|
||||
var allRows = this.panel.getElementsByClassName('line');
|
||||
for (var x = 0, xx = allRows.length; x < xx; ++x) {
|
||||
var row = allRows[x];
|
||||
if (row.dataset.idx == idx) {
|
||||
row.style.backgroundColor = 'rgb(251,250,207)';
|
||||
row.scrollIntoView();
|
||||
} else {
|
||||
row.style.backgroundColor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return Stepper;
|
||||
})();
|
||||
|
||||
var Stats = (function Stats() {
|
||||
var stats = [];
|
||||
function clear(node) {
|
||||
while (node.hasChildNodes())
|
||||
node.removeChild(node.lastChild);
|
||||
}
|
||||
function getStatIndex(pageNumber) {
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
if (stats[i].pageNumber === pageNumber)
|
||||
return i;
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
// Properties/functions needed by PDFBug.
|
||||
id: 'Stats',
|
||||
name: 'Stats',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
this.panel.setAttribute('style', 'padding: 5px;');
|
||||
PDFJS.enableStats = true;
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
// Stats specific functions.
|
||||
add: function(pageNumber, stat) {
|
||||
if (!stat)
|
||||
return;
|
||||
var statsIndex = getStatIndex(pageNumber);
|
||||
if (statsIndex !== false) {
|
||||
var b = stats[statsIndex];
|
||||
this.panel.removeChild(b.div);
|
||||
stats.splice(statsIndex, 1);
|
||||
}
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'stats';
|
||||
var title = document.createElement('div');
|
||||
title.className = 'title';
|
||||
title.textContent = 'Page: ' + pageNumber;
|
||||
var statsDiv = document.createElement('div');
|
||||
statsDiv.textContent = stat.toString();
|
||||
wrapper.appendChild(title);
|
||||
wrapper.appendChild(statsDiv);
|
||||
stats.push({ pageNumber: pageNumber, div: wrapper });
|
||||
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber; });
|
||||
clear(this.panel);
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
this.panel.appendChild(stats[i].div);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Manages all the debugging tools.
|
||||
var PDFBug = (function PDFBugClosure() {
|
||||
var panelWidth = 300;
|
||||
var buttons = [];
|
||||
var activePanel = null;
|
||||
|
||||
return {
|
||||
tools: [
|
||||
FontInspector,
|
||||
StepperManager,
|
||||
Stats
|
||||
],
|
||||
enable: function(ids) {
|
||||
var all = false, tools = this.tools;
|
||||
if (ids.length === 1 && ids[0] === 'all')
|
||||
all = true;
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
if (all || ids.indexOf(tool.id) !== -1)
|
||||
tool.enabled = true;
|
||||
}
|
||||
if (!all) {
|
||||
// Sort the tools by the order they are enabled.
|
||||
tools.sort(function(a, b) {
|
||||
var indexA = ids.indexOf(a.id);
|
||||
indexA = indexA < 0 ? tools.length : indexA;
|
||||
var indexB = ids.indexOf(b.id);
|
||||
indexB = indexB < 0 ? tools.length : indexB;
|
||||
return indexA - indexB;
|
||||
});
|
||||
}
|
||||
},
|
||||
init: function init() {
|
||||
/*
|
||||
* Basic Layout:
|
||||
* PDFBug
|
||||
* Controls
|
||||
* Panels
|
||||
* Panel
|
||||
* Panel
|
||||
* ...
|
||||
*/
|
||||
var ui = document.createElement('div');
|
||||
ui.id = 'PDFBug';
|
||||
|
||||
var controls = document.createElement('div');
|
||||
controls.setAttribute('class', 'controls');
|
||||
ui.appendChild(controls);
|
||||
|
||||
var panels = document.createElement('div');
|
||||
panels.setAttribute('class', 'panels');
|
||||
ui.appendChild(panels);
|
||||
|
||||
var container = document.getElementById('viewerContainer');
|
||||
container.appendChild(ui);
|
||||
container.style.right = panelWidth + 'px';
|
||||
|
||||
// Initialize all the debugging tools.
|
||||
var tools = this.tools;
|
||||
var self = this;
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
var panel = document.createElement('div');
|
||||
var panelButton = document.createElement('button');
|
||||
panelButton.textContent = tool.name;
|
||||
panelButton.addEventListener('click', (function(selected) {
|
||||
return function(event) {
|
||||
event.preventDefault();
|
||||
self.selectPanel(selected);
|
||||
};
|
||||
})(i));
|
||||
controls.appendChild(panelButton);
|
||||
panels.appendChild(panel);
|
||||
tool.panel = panel;
|
||||
tool.manager = this;
|
||||
if (tool.enabled)
|
||||
tool.init();
|
||||
else
|
||||
panel.textContent = tool.name + ' is disabled. To enable add ' +
|
||||
' "' + tool.id + '" to the pdfBug parameter ' +
|
||||
'and refresh (seperate multiple by commas).';
|
||||
buttons.push(panelButton);
|
||||
}
|
||||
this.selectPanel(0);
|
||||
},
|
||||
selectPanel: function selectPanel(index) {
|
||||
if (index === activePanel)
|
||||
return;
|
||||
activePanel = index;
|
||||
var tools = this.tools;
|
||||
for (var j = 0; j < tools.length; ++j) {
|
||||
if (j == index) {
|
||||
buttons[j].setAttribute('class', 'active');
|
||||
tools[j].active = true;
|
||||
tools[j].panel.removeAttribute('hidden');
|
||||
} else {
|
||||
buttons[j].setAttribute('class', '');
|
||||
tools[j].active = false;
|
||||
tools[j].panel.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
155
static/pdf.js/embeds.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
Ox.load(function() {
|
||||
var currentPage = PDFView.page;
|
||||
window.addEventListener('pagechange', function (evt) {
|
||||
var page = evt.pageNumber;
|
||||
if (page && page != currentPage) {
|
||||
currentPage = page;
|
||||
Ox.$parent.postMessage('page', {
|
||||
page: Math.round(page)
|
||||
});
|
||||
}
|
||||
});
|
||||
Ox.$parent.onMessage(function(event, data, oxid) {
|
||||
if (event == 'page' && Ox.isUndefined(oxid)) {
|
||||
if (data.page != PDFView.page) {
|
||||
PDFView.page = data.page;
|
||||
}
|
||||
}
|
||||
if (event == 'pdf' && Ox.isUndefined(oxid)) {
|
||||
if (PDFView.url != data.pdf) {
|
||||
PDFView.open(data.pdf);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ox.$parent.postMessage('init', {});
|
||||
});
|
||||
|
||||
function getVideoOverlay(page) {
|
||||
var links = (window.embeds || []).filter(function(embed) {
|
||||
return embed.page == page && embed.type =='inline';
|
||||
});
|
||||
return (window.editable || links.length) ? {
|
||||
beginLayout: function() {
|
||||
this.counter = 0;
|
||||
},
|
||||
endLayout: function() {
|
||||
},
|
||||
appendImage: function(image) {
|
||||
var id = ++this.counter,
|
||||
video = links.filter(function(embed) {
|
||||
return embed.id == id;
|
||||
})[0],
|
||||
$interface, $playButton, $editButton;
|
||||
if (editable || video) {
|
||||
$interface = Ox.$('<div>')
|
||||
.addClass('interface')
|
||||
.css({
|
||||
left: image.left + 'px',
|
||||
top: image.top + 'px',
|
||||
width: image.width + 'px',
|
||||
height: image.height + 'px'
|
||||
});
|
||||
$playButton = Ox.$('<img>')
|
||||
.addClass('button playButton')
|
||||
.attr({
|
||||
src: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2Ij48cG9seWdvbiBwb2ludHM9IjU2LDMyIDI0OCwxMjggNTYsMjI0IiBmaWxsPSIjRkZGRkZGIi8+PC9zdmc+PCEtLXsiY29sb3IiOiJ2aWRlbyIsIm5hbWUiOiJzeW1ib2xQbGF5IiwidGhlbWUiOiJveGRhcmsifS0tPg=='
|
||||
})
|
||||
.hide()
|
||||
.appendTo($interface);
|
||||
$editButton = Ox.$('<img>')
|
||||
.addClass('button editButton')
|
||||
.attr({
|
||||
src: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2Ij48cG9seWdvbiBwb2ludHM9IjMyLDIyNCA2NCwxNjAgOTYsMTkyIiBmaWxsPSIjRkZGRkZGIi8+PGxpbmUgeDE9Ijg4IiB5MT0iMTY4IiB4Mj0iMTg0IiB5Mj0iNzIiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSI0OCIvPjxsaW5lIHgxPSIxOTIiIHkxPSI2NCIgeDI9IjIwOCIgeTI9IjQ4IiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iNDgiLz48bGluZSB4MT0iMTEyIiB5MT0iMjIwIiB4Mj0iMjI0IiB5Mj0iMjIwIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iOCIvPjxsaW5lIHgxPSIxMjgiIHkxPSIyMDQiIHgyPSIyMjQiIHkyPSIyMDQiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSI4Ii8+PGxpbmUgeDE9IjE0NCIgeTE9IjE4OCIgeDI9IjIyNCIgeTI9IjE4OCIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjgiLz48L3N2Zz4=',
|
||||
title: 'Click to add video'
|
||||
})
|
||||
.on({click: edit})
|
||||
.hide()
|
||||
.appendTo($interface);
|
||||
if (editable) {
|
||||
$editButton.show();
|
||||
}
|
||||
if (video) {
|
||||
enableVideoUI();
|
||||
}
|
||||
this.div.appendChild($interface[0]);
|
||||
Ox.Message.bind(function(event, data, oxid) {
|
||||
if (event == 'update') {
|
||||
if(Ox.isUndefined(oxid)
|
||||
&& video
|
||||
&& data.id == video.id
|
||||
&& data.page == video.page) {
|
||||
video.src = data.src;
|
||||
video.src !== '' ? enableVideoUI() : disableVideoUI();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function play(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var videoId = 'video' + page + id + Ox.uid(),
|
||||
$iframe = Ox.$('<iframe>')
|
||||
.attr({
|
||||
id: videoId,
|
||||
src: video.src
|
||||
+ (video.src.indexOf('?') == -1 ? '?' : '&')
|
||||
+ '&showCloseButton=true&fullscreen=false&paused=false',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
frameborder: 0
|
||||
})
|
||||
.appendTo($interface),
|
||||
closed = false;
|
||||
$iframe.postMessage = function(event, data) {
|
||||
Ox.Message.post($iframe, event, data);
|
||||
return $iframe;
|
||||
};
|
||||
Ox.Message.bind(function(event, data, oxid) {
|
||||
if(!closed && event == 'loaded') {
|
||||
$iframe.postMessage('init', {id: videoId});
|
||||
} else if(event == 'close') {
|
||||
if(!closed && !Ox.isUndefined(oxid) && videoId == oxid) {
|
||||
closed = true;
|
||||
$iframe.remove();
|
||||
delete $iframe;
|
||||
$playButton.show();
|
||||
$editButton.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
$playButton.hide();
|
||||
$editButton.hide();
|
||||
return false;
|
||||
}
|
||||
function edit(e) {
|
||||
var url;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
video = video || {
|
||||
id: id,
|
||||
page: page,
|
||||
src: '',
|
||||
type: 'inline'
|
||||
};
|
||||
Ox.$parent.postMessage('edit', video);
|
||||
return false;
|
||||
}
|
||||
function enableVideoUI() {
|
||||
$interface
|
||||
.addClass('video')
|
||||
.attr({title: 'Click to play video'})
|
||||
.on({click: play});
|
||||
$playButton.show();
|
||||
$editButton.attr({title: 'Click to edit or remove video'});
|
||||
}
|
||||
function disableVideoUI() {
|
||||
$interface
|
||||
.removeClass('video')
|
||||
.attr({title: ''})
|
||||
.off({click: play});
|
||||
$playButton.hide();
|
||||
$editButton.attr({title: 'Click to add video'});
|
||||
}
|
||||
}
|
||||
} : null;
|
||||
}
|
||||
11
static/pdf.js/images/annotation-check.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<path
|
||||
d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
|
||||
id="path4"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 415 B |
16
static/pdf.js/images/annotation-comment.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="40"
|
||||
width="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915" />
|
||||
<path
|
||||
d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 883 B |
26
static/pdf.js/images/annotation-help.svg
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<g
|
||||
transform="translate(0,-60)"
|
||||
id="layer1">
|
||||
<rect
|
||||
width="36.460953"
|
||||
height="34.805603"
|
||||
x="1.7695236"
|
||||
y="62.597198"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.30826771;stroke-opacity:1" />
|
||||
<g
|
||||
transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)">
|
||||
<path
|
||||
d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
10
static/pdf.js/images/annotation-insert.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 408 B |
11
static/pdf.js/images/annotation-key.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
|
||||
id="path604"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
11
static/pdf.js/images/annotation-newparagraph.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
|
||||
id="path2985"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 426 B |
42
static/pdf.js/images/annotation-note.svg
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="36.075428"
|
||||
height="31.096582"
|
||||
x="1.962286"
|
||||
y="4.4517088"
|
||||
id="rect4"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="1.5012145"
|
||||
x="6.0157046"
|
||||
y="10.285"
|
||||
id="rect6"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157056"
|
||||
y="23.21689"
|
||||
id="rect8"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="5.8130345"
|
||||
y="28.964394"
|
||||
id="rect10"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157046"
|
||||
y="17.426493"
|
||||
id="rect12"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
16
static/pdf.js/images/annotation-paragraph.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
|
||||
style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/pdf.js/images/findbarButton-next-rtl.png
Normal file
|
After Width: | Height: | Size: 272 B |
BIN
static/pdf.js/images/findbarButton-next.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
static/pdf.js/images/findbarButton-previous-rtl.png
Normal file
|
After Width: | Height: | Size: 268 B |
BIN
static/pdf.js/images/findbarButton-previous.png
Normal file
|
After Width: | Height: | Size: 272 B |
BIN
static/pdf.js/images/grab.cur
Normal file
|
After Width: | Height: | Size: 326 B |
BIN
static/pdf.js/images/grabbing.cur
Normal file
|
After Width: | Height: | Size: 326 B |
BIN
static/pdf.js/images/loading-icon.gif
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
static/pdf.js/images/loading-small.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
static/pdf.js/images/secondaryToolbarButton-firstPage.png
Normal file
|
After Width: | Height: | Size: 340 B |