Merge pull request #1959 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-07-20 10:04:48 -04:00 committed by GitHub
commit c36348f193
77 changed files with 642 additions and 3168 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
base64@1.0.3
binary-heap@1.0.3
blaze@2.1.2
blaze-tools@1.0.3
callback-hook@1.0.3
check@1.0.5
dburles:mongo-collection-instances@0.3.4
ddp@1.1.0
deps@1.0.7
ejson@1.0.6
geojson-utils@1.0.3
html-tools@1.0.4
htmljs@1.0.4
id-map@1.0.3
jquery@1.11.3_2
json@1.0.3
lai:collection-extensions@0.1.4
local-test:rubaxa:sortable@1.2.1
logging@1.0.7
meteor@1.1.6
minifiers@1.1.5
minimongo@1.0.8
mongo@1.1.0
observe-sequence@1.0.6
ordered-dict@1.0.3
random@1.0.3
reactive-var@1.0.5
retry@1.0.3
rubaxa:sortable@1.2.1
spacebars-compiler@1.0.6
templating@1.1.1
tinytest@1.0.5
tracker@1.0.7
underscore@1.0.3

View file

@ -1,8 +0,0 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2

View file

@ -1,7 +0,0 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
ir0jg2douy3yo5mehw

View file

@ -1,10 +0,0 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-platform
autopublish
insecure
rubaxa:sortable
fezvrasta:bootstrap-material-design

View file

@ -1,2 +0,0 @@
browser
server

View file

@ -1 +0,0 @@
METEOR@1.1.0.3

View file

@ -1,53 +0,0 @@
autopublish@1.0.3
autoupdate@1.2.1
base64@1.0.3
binary-heap@1.0.3
blaze@2.1.2
blaze-tools@1.0.3
boilerplate-generator@1.0.3
callback-hook@1.0.3
check@1.0.5
dburles:mongo-collection-instances@0.3.4
ddp@1.1.0
deps@1.0.7
ejson@1.0.6
fastclick@1.0.3
fezvrasta:bootstrap-material-design@0.3.0
geojson-utils@1.0.3
html-tools@1.0.4
htmljs@1.0.4
http@1.1.0
id-map@1.0.3
insecure@1.0.3
jquery@1.11.3_2
json@1.0.3
lai:collection-extensions@0.1.4
launch-screen@1.0.2
livedata@1.0.13
logging@1.0.7
meteor@1.1.6
meteor-platform@1.2.2
minifiers@1.1.5
minimongo@1.0.8
mobile-status-bar@1.0.3
mongo@1.1.0
observe-sequence@1.0.6
ordered-dict@1.0.3
random@1.0.3
reactive-dict@1.1.0
reactive-var@1.0.5
reload@1.1.3
retry@1.0.3
routepolicy@1.0.5
rubaxa:sortable@1.2.1
session@1.1.0
spacebars@1.0.6
spacebars-compiler@1.0.6
templating@1.1.1
tracker@1.0.7
twbs:bootstrap@3.3.5
ui@1.0.6
underscore@1.0.3
url@1.0.4
webapp@1.2.0
webapp-hashing@1.0.3

View file

@ -1,57 +0,0 @@
.glyphicon {
vertical-align: baseline;
font-size: 80%;
margin-right: 0.5em;
}
[class^="mdi-"], [class*=" mdi-"] {
vertical-align: baseline;
font-size: 90%;
margin-right: 0.4em;
}
.list-pair {
display: flex; /* use the flexbox model */
flex-direction: row;
}
.sortable {
/* font-size: 2em;*/
}
.sortable.source {
/*background: #9FA8DA;*/
flex: 0 0 auto;
margin-right: 1em;
cursor: move;
cursor: -webkit-grabbing;
}
.sortable.target {
/*background: #3F51B5;*/
flex: 1 1 auto;
margin-left: 1em;
}
.target .well {
}
.sortable-handle {
cursor: move;
cursor: -webkit-grabbing;
}
.sortable-handle.pull-right {
margin-top: 0.3em;
}
.sortable-ghost {
opacity: 0.6;
}
/* show the remove button on hover */
.removable .close {
display: none;
}
.removable:hover .close {
display: block;
}

View file

@ -1,94 +0,0 @@
<head>
<title>Reactive RubaXa:Sortable for Meteor</title>
</head>
<body>
{{> navbar}}
<div class="container">
<div class="page-header">
<h1>RubaXa:Sortable - reactive reorderable lists for Meteor</h1>
<h2>Drag attribute types from the left to define an object type on the right</h2>
<h3>Drag the <i class="sortable-handle mdi-action-view-headline"></i> handle to reorder elements</h3>
</div>
{{> typeDefinition}}
</div>
</body>
<template name="typeDefinition">
<div class="row">
<div class="list-pair col-sm-12">
<div class="sortable source list-group" id="types">
{{#sortable items=types options=typesOptions}}
<div class="list-group-item well well-sm">
{{{icon}}} {{name}}
</div>
{{/sortable}}
</div>
<div class="sortable target" id="object">
{{#sortable items=attributes animation="100" handle=".sortable-handle" ghostClass="sortable-ghost" options=attributesOptions}}
{{> sortableItemTarget}}
{{/sortable}}
</div>
</div>
</div>
</template>
<template name="sortableItemTarget">
<div data-id="{{order}}" class="sortable-item removable well well-sm">
{{{icon}}}
<i class="sortable-handle mdi-action-view-headline pull-right"></i>
<span class="name">{{name}}</span>
<span class="badge">{{order}}</span>
<button type="button" class="close" data-dismiss="alert">
<span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
</button>
</div>
</template>
<template name="navbar">
<div class="navbar navbar-inverse">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-inverse-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="https://atmospherejs.com/rubaxa/sortable">RubaXa:Sortable</a>
</div>
<div class="navbar-collapse collapse navbar-inverse-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="http://rubaxa-sortable.meteor.com">Meteor Demo</a></li>
<li><a href="https://rubaxa.github.io/Sortable/" target="_blank">Sortable standalone demo</a></li>
<li class="dropdown">
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">Source <b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="dropdown-header">GitHub</li>
<li><a href="https://github.com/RubaXa/Sortable/tree/dev/meteor/example" target="_blank">This demo</a></li>
<li><a href="https://github.com/RubaXa/Sortable/tree/dev/meteor" target="_blank">Meteor integration</a></li>
<li><a href="https://github.com/RubaXa/Sortable" target="_blank">rubaxa/sortable</a></li>
<li class="divider"></li>
<li><a href="https://atmospherejs.com/rubaxa/sortable">Star this package on Atmosphere!</a></li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">Resources <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="http://www.meteorpedia.com/read/Packaging_existing_Libraries">Packaging 3rd party libraries for Meteor</a></li>
<li><a href="https://twitter.com/dandv">Author: @dandv</a></li>
</ul>
</li>
</ul>
</div>
</div>
</template>

View file

@ -1,101 +0,0 @@
// Define an object type by dragging together attributes
Template.typeDefinition.helpers({
types: function () {
return Types.find({}, { sort: { order: 1 } });
},
typesOptions: {
sortField: 'order', // defaults to 'order' anyway
group: {
name: 'typeDefinition',
pull: 'clone',
put: false
},
sort: false // don't allow reordering the types, just the attributes below
},
attributes: function () {
return Attributes.find({}, {
sort: { order: 1 },
transform: function (doc) {
doc.icon = Types.findOne({name: doc.type}).icon;
return doc;
}
});
},
attributesOptions: {
group: {
name: 'typeDefinition',
put: true
},
onAdd: function (event) {
delete event.data._id; // Generate a new id when inserting in the Attributes collection. Otherwise, if we add the same type twice, we'll get an error that the ids are not unique.
delete event.data.icon;
event.data.type = event.data.name;
event.data.name = 'Rename me (double click)'
},
// event handler for reordering attributes
onSort: function (event) {
console.log('Item %s went from #%d to #%d',
event.data.name, event.oldIndex, event.newIndex
);
}
}
});
Template.sortableItemTarget.events({
'dblclick .name': function (event, template) {
// Make the name editable. We should use an existing component, but it's
// in a sorry state - https://github.com/arillo/meteor-x-editable/issues/1
var name = template.$('.name');
var input = template.$('input');
if (input.length) { // jQuery never returns null - http://stackoverflow.com/questions/920236/how-can-i-detect-if-a-selector-returns-null
input.show();
} else {
input = $('<input class="form-control" type="text" placeholder="' + this.name + '" style="display: inline">');
name.after(input);
}
name.hide();
input.focus();
},
'blur input[type=text]': function (event, template) {
// commit the change to the name, if any
var input = template.$('input');
input.hide();
template.$('.name').show();
// TODO - what is the collection here? We'll hard-code for now.
// https://github.com/meteor/meteor/issues/3303
if (this.name !== input.val() && this.name !== '')
Attributes.update(this._id, {$set: {name: input.val()}});
},
'keydown input[type=text]': function (event, template) {
if (event.which === 27) {
// ESC - discard edits and keep existing value
template.$('input').val(this.name);
event.preventDefault();
event.target.blur();
} else if (event.which === 13) {
// ENTER
event.preventDefault();
event.target.blur();
}
}
});
// you can add events to all Sortable template instances
Template.sortable.events({
'click .close': function (event, template) {
// `this` is the data context set by the enclosing block helper (#each, here)
template.collection.remove(this._id);
// custom code, working on a specific collection
if (Attributes.find().count() === 0) {
Meteor.setTimeout(function () {
Attributes.insert({
name: 'Not nice to delete the entire list! Add some attributes instead.',
type: 'String',
order: 0
})
}, 1000);
}
}
});

View file

@ -1,2 +0,0 @@
Types = new Mongo.Collection('types');
Attributes = new Mongo.Collection('attributes');

View file

@ -1,7 +0,0 @@
@echo off
REM Sanity check: make sure we're in the directory of the script
set DIR=%~dp0
cd %DIR%
set PACKAGE_DIRS=..\..\
meteor run %*

View file

@ -1,5 +0,0 @@
# sanity check: make sure we're in the root directory of the example
cd "$( dirname "$0" )"
# let Meteor find the local package
PACKAGE_DIRS=../../ meteor run "$@"

View file

@ -1,75 +0,0 @@
Meteor.startup(function () {
if (Types.find().count() === 0) {
[
{
name: 'String',
icon: '<span class="glyphicon glyphicon-tag" aria-hidden="true"></span>'
},
{
name: 'Text, multi-line',
icon: '<i class="mdi-communication-message" aria-hidden="true"></i>'
},
{
name: 'Category',
icon: '<span class="glyphicon glyphicon-list" aria-hidden="true"></span>'
},
{
name: 'Number',
icon: '<i class="mdi-image-looks-one" aria-hidden="true"></i>'
},
{
name: 'Date',
icon: '<span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>'
},
{
name: 'Hyperlink',
icon: '<span class="glyphicon glyphicon-link" aria-hidden="true"></span>'
},
{
name: 'Image',
icon: '<span class="glyphicon glyphicon-picture" aria-hidden="true"></span>'
},
{
name: 'Progress',
icon: '<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>'
},
{
name: 'Duration',
icon: '<span class="glyphicon glyphicon-time" aria-hidden="true"></span>'
},
{
name: 'Map address',
icon: '<i class="mdi-maps-place" aria-hidden="true"></i>'
},
{
name: 'Relationship',
icon: '<span class="glyphicon glyphicon-flash" aria-hidden="true"></span>'
}
].forEach(function (type, i) {
Types.insert({
name: type.name,
icon: type.icon,
order: i
});
}
);
console.log('Initialized attribute types.');
}
if (Attributes.find().count() === 0) {
[
{ name: 'Name', type: 'String' },
{ name: 'Created at', type: 'Date' },
{ name: 'Link', type: 'Hyperlink' },
{ name: 'Owner', type: 'Relationship' }
].forEach(function (attribute, i) {
Attributes.insert({
name: attribute.name,
type: attribute.type,
order: i
});
}
);
console.log('Created sample object type.');
}
});

View file

@ -1,3 +0,0 @@
'use strict';
Sortable.collections = ['attributes'];

View file

@ -1,16 +0,0 @@
'use strict';
Meteor.methods({
/**
* Update the sortField of documents with given ids in a collection, incrementing it by incDec
* @param {String} collectionName - name of the collection to update
* @param {String[]} ids - array of document ids
* @param {String} orderField - the name of the order field, usually "order"
* @param {Number} incDec - pass 1 or -1
*/
'rubaxa:sortable/collection-update': function (collectionName, ids, sortField, incDec) {
var selector = {_id: {$in: ids}}, modifier = {$inc: {}};
modifier.$inc[sortField] = incDec;
Mongo.Collection.get(collectionName).update(selector, modifier, {multi: true});
}
});

View file

@ -1,31 +0,0 @@
'use strict';
Sortable = {};
Sortable.collections = []; // array of collection names that the client is allowed to reorder
Meteor.methods({
/**
* Update the sortField of documents with given ids in a collection, incrementing it by incDec
* @param {String} collectionName - name of the collection to update
* @param {String[]} ids - array of document ids
* @param {String} orderField - the name of the order field, usually "order"
* @param {Number} incDec - pass 1 or -1
*/
'rubaxa:sortable/collection-update': function (collectionName, ids, sortField, incDec) {
check(collectionName, String);
// don't allow the client to modify just any collection
if (!Sortable || !Array.isArray(Sortable.collections)) {
throw new Meteor.Error(500, 'Please define Sortable.collections');
}
if (Sortable.collections.indexOf(collectionName) === -1) {
throw new Meteor.Error(403, 'Collection <' + collectionName + '> is not Sortable. Please add it to Sortable.collections in server code.');
}
check(ids, [String]);
check(sortField, String);
check(incDec, Number);
var selector = {_id: {$in: ids}}, modifier = {$inc: {}};
modifier.$inc[sortField] = incDec;
Mongo.Collection.get(collectionName).update(selector, modifier, {multi: true});
}
});

View file

@ -1,85 +0,0 @@
// Package metadata file for Meteor.js
'use strict';
var packageName = 'rubaxa:sortable'; // https://atmospherejs.com/rubaxa/sortable
var gitHubPath = 'RubaXa/Sortable'; // https://github.com/RubaXa/Sortable
var npmPackageName = 'sortablejs'; // https://www.npmjs.com/package/sortablejs - optional but recommended; used as fallback if GitHub fails
/* All of the below is just to get the version number of the 3rd party library.
* First we'll try to read it from package.json. This works when publishing or testing the package
* but not when running an example app that uses a local copy of the package because the current
* directory will be that of the app, and it won't have package.json. Finding the path of a file is hard:
* http://stackoverflow.com/questions/27435797/how-do-i-obtain-the-path-of-a-file-in-a-meteor-package
* Therefore, we'll fall back to GitHub (which is more frequently updated), and then to NPMJS.
* We also don't have the HTTP package at this stage, and if we use Package.* in the request() callback,
* it will error that it must be run in a Fiber. So we'll use Node futures.
*/
var request = Npm.require('request');
var Future = Npm.require('fibers/future');
var fut = new Future;
var version;
if (!version) try {
var packageJson = JSON.parse(Npm.require('fs').readFileSync('../package.json'));
version = packageJson.version;
} catch (e) {
// if the file was not found, fall back to GitHub
console.warn('Could not find ../package.json to read version number from; trying GitHub...');
var url = 'https://api.github.com/repos/' + gitHubPath + '/tags';
request.get({
url: url,
headers: {
'User-Agent': 'request' // GitHub requires it
}
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var versions = JSON.parse(body).map(function (version) {
return version['name'].replace(/^\D+/, '') // trim leading non-digits from e.g. "v4.3.0"
}).sort();
fut.return(versions[versions.length -1]);
} else {
// GitHub API rate limit reached? Fall back to npmjs.
console.warn('GitHub request to', url, 'failed:\n ', response && response.statusCode, response && response.body, error || '', '\nTrying NPMJS...');
url = 'http://registry.npmjs.org/' + npmPackageName + '/latest';
request.get(url, function (error, response, body) {
if (!error && response.statusCode === 200)
fut.return(JSON.parse(body).version);
else
fut.throw('Could not get version information from ' + url + ' either (incorrect package name?):\n' + (response && response.statusCode || '') + (response && response.body || '') + (error || ''));
});
}
});
version = fut.wait();
}
// Now that we finally have an accurate version number...
Package.describe({
name: packageName,
summary: 'Sortable: reactive minimalist reorderable drag-and-drop lists on modern browsers and touch devices',
version: version,
git: 'https://github.com/RubaXa/Sortable.git',
documentation: 'README.md'
});
Package.onUse(function (api) {
api.versionsFrom(['METEOR@0.9.0', 'METEOR@1.0']);
api.use('templating', 'client');
api.use('dburles:mongo-collection-instances@0.3.4'); // to watch collections getting created
api.export('Sortable'); // exported on the server too, as a global to hold the array of sortable collections (for security)
api.addFiles([
'../Sortable.js',
'template.html', // the HTML comes first, so reactivize.js can refer to the template in it
'reactivize.js'
], 'client');
api.addFiles('methods-client.js', 'client');
api.addFiles('methods-server.js', 'server');
});
Package.onTest(function (api) {
api.use(packageName, 'client');
api.use('tinytest', 'client');
api.addFiles('test.js', 'client');
});

View file

@ -1,26 +0,0 @@
#!/bin/bash
# Publish package to Meteor's repository, Atmospherejs.com
# Make sure Meteor is installed, per https://www.meteor.com/install.
# The curl'ed script is totally safe; takes 2 minutes to read its source and check.
type meteor >/dev/null 2>&1 || { curl https://install.meteor.com/ | sh; }
# sanity check: make sure we're in the directory of the script
cd "$( dirname "$0" )"
# publish package, creating it if it's the first time we're publishing
PACKAGE_NAME=$(grep -i name package.js | head -1 | cut -d "'" -f 2)
echo "Publishing $PACKAGE_NAME..."
# Attempt to re-publish the package - the most common operation once the initial release has
# been made. If the package name was changed (rare), you'll have to pass the --create flag.
meteor publish "$@"; EXIT_CODE=$?
if (( $EXIT_CODE == 0 )); then
echo "Thanks for releasing a new version. You can see it at"
echo "https://atmospherejs.com/${PACKAGE_NAME/://}"
else
echo "We have an error. Please post it at https://github.com/RubaXa/Sortable/issues"
fi
exit $EXIT_CODE

View file

@ -1,201 +0,0 @@
/*
Make a Sortable reactive by binding it to a Mongo.Collection.
Calls `rubaxa:sortable/collection-update` on the server to update the sortField of affected records.
TODO:
* supply consecutive values if the `order` field doesn't have any
* .get(DOMElement) - return the Sortable object of a DOMElement
* create a new _id automatically onAdd if the event.from list had pull: 'clone'
* support arrays
* sparse arrays
* tests
* drop onto existing empty lists
* insert back into lists emptied by dropping
* performance on dragging into long list at the beginning
* handle failures on Collection operations, e.g. add callback to .insert
* when adding elements, update ranks just for the half closer to the start/end of the list
* revisit http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible
* reproduce the insidious bug where the list isn't always sorted (fiddle with dragging #1 over #2, then back, then #N before #1)
*/
'use strict';
Template.sortable.created = function () {
var templateInstance = this;
// `this` is a template instance that can store properties of our choice - http://docs.meteor.com/#/full/template_inst
if (templateInstance.setupDone) return; // paranoid: only run setup once
// this.data is the data context - http://docs.meteor.com/#/full/template_data
// normalize all options into templateInstance.options, and remove them from .data
templateInstance.options = templateInstance.data.options || {};
Object.keys(templateInstance.data).forEach(function (key) {
if (key === 'options' || key === 'items') return;
templateInstance.options[key] = templateInstance.data[key];
delete templateInstance.data[key];
});
templateInstance.options.sortField = templateInstance.options.sortField || 'order';
// We can get the collection via the .collection property of the cursor, but changes made that way
// will NOT be sent to the server - https://github.com/meteor/meteor/issues/3271#issuecomment-66656257
// Thus we need to use dburles:mongo-collection-instances to get a *real* collection
if (templateInstance.data.items && templateInstance.data.items.collection) {
// cursor passed via items=; its .collection works client-only and has a .name property
templateInstance.collectionName = templateInstance.data.items.collection.name;
templateInstance.collection = Mongo.Collection.get(templateInstance.collectionName);
} else if (templateInstance.data.items) {
// collection passed via items=; does NOT have a .name property, but _name
templateInstance.collection = templateInstance.data.items;
templateInstance.collectionName = templateInstance.collection._name;
} else if (templateInstance.data.collection) {
// cursor passed directly
templateInstance.collectionName = templateInstance.data.collection.name;
templateInstance.collection = Mongo.Collection.get(templateInstance.collectionName);
} else {
templateInstance.collection = templateInstance.data; // collection passed directly
templateInstance.collectionName = templateInstance.collection._name;
}
// TODO if (Array.isArray(templateInstance.collection))
// What if user filters some of the items in the cursor, instead of ordering the entire collection?
// Use case: reorder by preference movies of a given genre, a filter within all movies.
// A: Modify all intervening items **that are on the client**, to preserve the overall order
// TODO: update *all* orders via a server method that takes not ids, but start & end elements - mild security risk
delete templateInstance.data.options;
/**
* When an element was moved, adjust its orders and possibly the order of
* other elements, so as to maintain a consistent and correct order.
*
* There are three approaches to this:
* 1) Using arbitrary precision arithmetic and setting only the order of the moved
* element to the average of the orders of the elements around it -
* http://programmers.stackexchange.com/questions/266451/maintain-ordered-collection-by-updating-as-few-order-fields-as-possible
* The downside is that the order field in the DB will increase by one byte every
* time an element is reordered.
* 2) Adjust the orders of the intervening items. This keeps the orders sane (integers)
* but is slower because we have to modify multiple documents.
* TODO: we may be able to update fewer records by only altering the
* order of the records between the newIndex/oldIndex and the start/end of the list.
* 3) Use regular precision arithmetic, but when the difference between the orders of the
* moved item and the one before/after it falls below a certain threshold, adjust
* the order of that other item, and cascade doing so up or down the list.
* This will keep the `order` field constant in size, and will only occasionally
* require updating the `order` of other records.
*
* For now, we use approach #2.
*
* @param {String} itemId - the _id of the item that was moved
* @param {Number} orderPrevItem - the order of the item before it, or null
* @param {Number} orderNextItem - the order of the item after it, or null
*/
templateInstance.adjustOrders = function adjustOrders(itemId, orderPrevItem, orderNextItem) {
var orderField = templateInstance.options.sortField;
var selector = templateInstance.options.selector || {}, modifier = {$set: {}};
var ids = [];
var startOrder = templateInstance.collection.findOne(itemId)[orderField];
if (orderPrevItem !== null) {
// Element has a previous sibling, therefore it was moved down in the list.
// Decrease the order of intervening elements.
selector[orderField] = {$lte: orderPrevItem, $gt: startOrder};
ids = _.pluck(templateInstance.collection.find(selector, {fields: {_id: 1}}).fetch(), '_id');
Meteor.call('rubaxa:sortable/collection-update', templateInstance.collectionName, ids, orderField, -1);
// Set the order of the dropped element to the order of its predecessor, whose order was decreased
modifier.$set[orderField] = orderPrevItem;
} else {
// element moved up the list, increase order of intervening elements
selector[orderField] = {$gte: orderNextItem, $lt: startOrder};
ids = _.pluck(templateInstance.collection.find(selector, {fields: {_id: 1}}).fetch(), '_id');
Meteor.call('rubaxa:sortable/collection-update', templateInstance.collectionName, ids, orderField, 1);
// Set the order of the dropped element to the order of its successor, whose order was increased
modifier.$set[orderField] = orderNextItem;
}
templateInstance.collection.update(itemId, modifier);
};
templateInstance.setupDone = true;
};
Template.sortable.rendered = function () {
var templateInstance = this;
var orderField = templateInstance.options.sortField;
// sorting was changed within the list
var optionsOnUpdate = templateInstance.options.onUpdate;
templateInstance.options.onUpdate = function sortableUpdate(/**Event*/event) {
var itemEl = event.item; // dragged HTMLElement
event.data = Blaze.getData(itemEl);
if (event.newIndex < event.oldIndex) {
// Element moved up in the list. The dropped element has a next sibling for sure.
var orderNextItem = Blaze.getData(itemEl.nextElementSibling)[orderField];
templateInstance.adjustOrders(event.data._id, null, orderNextItem);
} else if (event.newIndex > event.oldIndex) {
// Element moved down in the list. The dropped element has a previous sibling for sure.
var orderPrevItem = Blaze.getData(itemEl.previousElementSibling)[orderField];
templateInstance.adjustOrders(event.data._id, orderPrevItem, null);
} else {
// do nothing - drag and drop in the same location
}
if (optionsOnUpdate) optionsOnUpdate(event);
};
// element was added from another list
var optionsOnAdd = templateInstance.options.onAdd;
templateInstance.options.onAdd = function sortableAdd(/**Event*/event) {
var itemEl = event.item; // dragged HTMLElement
event.data = Blaze.getData(itemEl);
// let the user decorate the object with additional properties before insertion
if (optionsOnAdd) optionsOnAdd(event);
// Insert the new element at the end of the list and move it where it was dropped.
// We could insert it at the beginning, but that would lead to negative orders.
var sortSpecifier = {}; sortSpecifier[orderField] = -1;
event.data.order = templateInstance.collection.findOne({}, { sort: sortSpecifier, limit: 1 }).order + 1;
// TODO: this can obviously be optimized by setting the order directly as the arithmetic average, with the caveats described above
var newElementId = templateInstance.collection.insert(event.data);
event.data._id = newElementId;
if (itemEl.nextElementSibling) {
var orderNextItem = Blaze.getData(itemEl.nextElementSibling)[orderField];
templateInstance.adjustOrders(newElementId, null, orderNextItem);
} else {
// do nothing - inserted after the last element
}
// remove the dropped HTMLElement from the list because we have inserted it in the collection, which will update the template
itemEl.parentElement.removeChild(itemEl);
};
// element was removed by dragging into another list
var optionsOnRemove = templateInstance.options.onRemove;
templateInstance.options.onRemove = function sortableRemove(/**Event*/event) {
var itemEl = event.item; // dragged HTMLElement
event.data = Blaze.getData(itemEl);
// don't remove from the collection if group.pull is clone or false
if (typeof templateInstance.options.group === 'undefined'
|| typeof templateInstance.options.group.pull === 'undefined'
|| templateInstance.options.group.pull === true
) templateInstance.collection.remove(event.data._id);
if (optionsOnRemove) optionsOnRemove(event);
};
// just compute the `data` context
['onStart', 'onEnd', 'onSort', 'onFilter'].forEach(function (eventHandler) {
if (templateInstance.options[eventHandler]) {
var userEventHandler = templateInstance.options[eventHandler];
templateInstance.options[eventHandler] = function (/**Event*/event) {
var itemEl = event.item; // dragged HTMLElement
event.data = Blaze.getData(itemEl);
userEventHandler(event);
};
}
});
templateInstance.sortable = Sortable.create(templateInstance.firstNode.parentElement, templateInstance.options);
// TODO make the object accessible, e.g. via Sortable.getSortableById() or some such
};
Template.sortable.destroyed = function () {
if(this.sortable) this.sortable.destroy();
};

View file

@ -1,8 +0,0 @@
@echo off
REM Test Meteor package before publishing to Atmospherejs.com
REM Sanity check: make sure we're in the directory of the script
set DIR=%~dp0
cd %DIR%
meteor test-packages ./ %*

View file

@ -1,35 +0,0 @@
#!/bin/sh
# Test Meteor package before publishing to Atmospherejs.com
# Make sure Meteor is installed, per https://www.meteor.com/install.
# The curl'ed script is totally safe; takes 2 minutes to read its source and check.
type meteor >/dev/null 2>&1 || { curl https://install.meteor.com/ | sh; }
# sanity check: make sure we're in the directory of the script
cd "$( dirname "$0" )"
# delete the temporary files even if Ctrl+C is pressed
int_trap() {
printf "\nTests interrupted. Cleaning up...\n\n"
}
trap int_trap INT
EXIT_CODE=0
PACKAGE_NAME=$(grep -i name package.js | head -1 | cut -d "'" -f 2)
echo "### Testing $PACKAGE_NAME..."
# provide an invalid MONGO_URL so Meteor doesn't bog us down with an empty Mongo database
if [ $# -gt 0 ]; then
# interpret any parameter to mean we want an interactive test
MONGO_URL=mongodb:// meteor test-packages ./
else
# automated/CI test with phantomjs
./node_modules/.bin/spacejam --mongo-url mongodb:// test-packages ./
EXIT_CODE=$(( $EXIT_CODE + $? ))
fi
exit $EXIT_CODE

View file

@ -1,5 +0,0 @@
<template name="sortable">
{{#each items}}
{{> Template.contentBlock this}}
{{/each}}
</template>

View file

@ -1,9 +0,0 @@
'use strict';
Tinytest.add('Sortable.is', function (test) {
var items = document.createElement('ul');
items.innerHTML = '<li data-id="one">item 1</li><li data-id="two">item 2</li><li data-id="three">item 3</li>';
var sortable = new Sortable(items);
test.instanceOf(sortable, Sortable, 'Instantiation OK');
test.length(sortable.toArray(), 3, 'Three elements');
});

View file

@ -1,239 +0,0 @@
html {
background-image: -webkit-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: -ms-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: linear-gradient(to bottom, #F4E2C9 20%, #F4D7C9 100%);
}
html, body {
margin: 0;
padding: 0;
position: relative;
color: #464637;
min-height: 100%;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
}
h1 {
color: #FF3F00;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.container {
width: 80%;
margin: auto;
min-width: 1100px;
max-width: 1300px;
position: relative;
}
@media (min-width: 750px) and (max-width: 970px){
.container {
width: 100%;
min-width: 750px;
}
}
.sortable-ghost {
opacity: .2;
}
img {
border: 0;
vertical-align: middle;
}
.logo {
top: 55px;
left: 30px;
position: absolute;
}
.title {
color: #fff;
padding: 3px 10px;
display: inline-block;
position: relative;
background-color: #FF7373;
z-index: 1000;
}
.title_xl {
padding: 3px 15px;
font-size: 40px;
}
.tile {
width: 22%;
min-width: 245px;
color: #FF7270;
padding: 10px 30px;
text-align: center;
margin-top: 15px;
margin-left: 5px;
margin-right: 30px;
background-color: #fff;
display: inline-block;
vertical-align: top;
}
.tile__name {
cursor: move;
padding-bottom: 10px;
border-bottom: 1px solid #FF7373;
}
.tile__list {
margin-top: 10px;
}
.tile__list:last-child {
margin-right: 0;
min-height: 80px;
}
.tile__list img {
cursor: move;
margin: 10px;
border-radius: 100%;
}
.block {
opacity: 1;
position: absolute;
}
.block__list {
padding: 20px 0;
max-width: 360px;
margin-top: -8px;
margin-left: 5px;
background-color: #fff;
}
.block__list-title {
margin: -20px 0 0;
padding: 10px;
text-align: center;
background: #5F9EDF;
}
.block__list li { cursor: move; }
.block__list_words li {
background-color: #fff;
padding: 10px 40px;
}
.block__list_words .sortable-ghost {
opacity: 0.4;
background-color: #F4E2C9;
}
.block__list_words li:first-letter {
text-transform: uppercase;
}
.block__list_tags {
padding-left: 30px;
}
.block__list_tags:after {
clear: both;
content: '';
display: block;
}
.block__list_tags li {
color: #fff;
float: left;
margin: 8px 20px 10px 0;
padding: 5px 10px;
min-width: 10px;
background-color: #5F9EDF;
text-align: center;
}
.block__list_tags li:first-child:first-letter {
text-transform: uppercase;
}
#editable {}
#editable li {
position: relative;
}
#editable i {
-webkit-transition: opacity .2s;
transition: opacity .2s;
opacity: 0;
display: block;
cursor: pointer;
color: #c00;
top: 10px;
right: 40px;
position: absolute;
font-style: normal;
}
#editable li:hover i {
opacity: 1;
}
#filter {}
#filter button {
color: #fff;
width: 100%;
border: none;
outline: 0;
opacity: .5;
margin: 10px 0 0;
transition: opacity .1s ease;
cursor: pointer;
background: #5F9EDF;
padding: 10px 0;
font-size: 20px;
}
#filter button:hover {
opacity: 1;
}
#filter .block__list {
padding-bottom: 0;
}
.drag-handle {
margin-right: 10px;
font: bold 20px Sans-Serif;
color: #5F9EDF;
display: inline-block;
cursor: move;
cursor: -webkit-grabbing; /* overrides 'move' */
}
#todos input {
padding: 5px;
font-size: 14px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
}
#nested ul li {
background-color: rgba(0,0,0,.05);
}

View file

@ -1,226 +0,0 @@
(function () {
'use strict';
var byId = function (id) { return document.getElementById(id); },
loadScripts = function (desc, callback) {
var deps = [], key, idx = 0;
for (key in desc) {
deps.push(key);
}
(function _next() {
var pid,
name = deps[idx],
script = document.createElement('script');
script.type = 'text/javascript';
script.src = desc[deps[idx]];
pid = setInterval(function () {
if (window[name]) {
clearTimeout(pid);
deps[idx++] = window[name];
if (deps[idx]) {
_next();
} else {
callback.apply(null, deps);
}
}
}, 30);
document.getElementsByTagName('head')[0].appendChild(script);
})()
},
console = window.console;
if (!console.log) {
console.log = function () {
alert([].join.apply(arguments, ' '));
};
}
Sortable.create(byId('foo'), {
group: "words",
animation: 150,
store: {
get: function (sortable) {
var order = localStorage.getItem(sortable.options.group);
return order ? order.split('|') : [];
},
set: function (sortable) {
var order = sortable.toArray();
localStorage.setItem(sortable.options.group, order.join('|'));
}
},
onAdd: function (evt){ console.log('onAdd.foo:', [evt.item, evt.from]); },
onUpdate: function (evt){ console.log('onUpdate.foo:', [evt.item, evt.from]); },
onRemove: function (evt){ console.log('onRemove.foo:', [evt.item, evt.from]); },
onStart:function(evt){ console.log('onStart.foo:', [evt.item, evt.from]);},
onSort:function(evt){ console.log('onStart.foo:', [evt.item, evt.from]);},
onEnd: function(evt){ console.log('onEnd.foo:', [evt.item, evt.from]);}
});
Sortable.create(byId('bar'), {
group: "words",
animation: 150,
onAdd: function (evt){ console.log('onAdd.bar:', evt.item); },
onUpdate: function (evt){ console.log('onUpdate.bar:', evt.item); },
onRemove: function (evt){ console.log('onRemove.bar:', evt.item); },
onStart:function(evt){ console.log('onStart.foo:', evt.item);},
onEnd: function(evt){ console.log('onEnd.foo:', evt.item);}
});
// Multi groups
Sortable.create(byId('multi'), {
animation: 150,
draggable: '.tile',
handle: '.tile__name'
});
[].forEach.call(byId('multi').getElementsByClassName('tile__list'), function (el){
Sortable.create(el, {
group: 'photo',
animation: 150
});
});
// Editable list
var editableList = Sortable.create(byId('editable'), {
animation: 150,
filter: '.js-remove',
onFilter: function (evt) {
evt.item.parentNode.removeChild(evt.item);
}
});
byId('addUser').onclick = function () {
Ply.dialog('prompt', {
title: 'Add',
form: { name: 'name' }
}).done(function (ui) {
var el = document.createElement('li');
el.innerHTML = ui.data.name + '<i class="js-remove">✖</i>';
editableList.el.appendChild(el);
});
};
// Advanced groups
[{
name: 'advanced',
pull: true,
put: true
},
{
name: 'advanced',
pull: 'clone',
put: false
}, {
name: 'advanced',
pull: false,
put: true
}].forEach(function (groupOpts, i) {
Sortable.create(byId('advanced-' + (i + 1)), {
sort: (i != 1),
group: groupOpts,
animation: 150
});
});
// 'handle' option
Sortable.create(byId('handle-1'), {
handle: '.drag-handle',
animation: 150
});
// Angular example
angular.module('todoApp', ['ng-sortable'])
.constant('ngSortableConfig', {onEnd: function() {
console.log('default onEnd()');
}})
.controller('TodoController', ['$scope', function ($scope) {
$scope.todos = [
{text: 'learn angular', done: true},
{text: 'build an angular app', done: false}
];
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.archive = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
}])
.controller('TodoControllerNext', ['$scope', function ($scope) {
$scope.todos = [
{text: 'learn Sortable', done: true},
{text: 'use ng-sortable', done: false},
{text: 'Enjoy', done: false}
];
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.sortableConfig = { group: 'todo', animation: 150 };
'Start End Add Update Remove Sort'.split(' ').forEach(function (name) {
$scope.sortableConfig['on' + name] = console.log.bind(console, name);
});
}]);
})();
// Background
document.addEventListener("DOMContentLoaded", function () {
function setNoiseBackground(el, width, height, opacity) {
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
for (var i = 0; i < width; i++) {
for (var j = 0; j < height; j++) {
var val = Math.floor(Math.random() * 255);
context.fillStyle = "rgba(" + val + "," + val + "," + val + "," + opacity + ")";
context.fillRect(i, j, 1, 1);
}
}
el.style.background = "url(" + canvas.toDataURL("image/png") + ")";
}
setNoiseBackground(document.getElementsByTagName('body')[0], 50, 50, 0.02);
}, false);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
<!-- List with handle -->
<div id="listWithHandle" class="list-group">
<div class="list-group-item">
<span class="badge">14</span>
<span class="glyphicon glyphicon-move" aria-hidden="true"></span>
Drag me by the handle
</div>
<div class="list-group-item">
<span class="badge">2</span>
<span class="glyphicon glyphicon-move" aria-hidden="true"></span>
You can also select text
</div>
<div class="list-group-item">
<span class="badge">1</span>
<span class="glyphicon glyphicon-move" aria-hidden="true"></span>
Best of both worlds!
</div>
</div>
</body>
</html>

View file

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>IFrame playground</title>
</head>
<body>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
<!-- Latest Sortable -->
<script src="../../Sortable.js"></script>
<!-- Simple List -->
<div id="simpleList" class="list-group">
<div class="list-group-item">This is <a href="http://rubaxa.github.io/Sortable/">Sortable</a></div>
<div class="list-group-item">It works with Bootstrap...</div>
<div class="list-group-item">...out of the box.</div>
<div class="list-group-item">It has support for touch devices.</div>
<div class="list-group-item">Just drag some elements around.</div>
</div>
<script>
(function () {
Sortable.create(simpleList, {group: 'shared'});
var iframe = document.createElement('iframe');
iframe.src = 'frame.html';
iframe.width = '100%';
iframe.onload = function () {
var doc = iframe.contentDocument,
list = doc.getElementById('listWithHandle');
Sortable.create(list, {group: 'shared'});
};
document.body.appendChild(iframe);
})();
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -15,12 +15,12 @@
},
"devDependencies": {},
"ignore": [],
"version": "1.4.101",
"_release": "1.4.101",
"version": "1.4.108",
"_release": "1.4.108",
"_resolution": {
"type": "version",
"tag": "1.4.101",
"commit": "c6aa6b09f9c705ca83e31728ebbf0c4a2cda2454"
"tag": "1.4.108",
"commit": "42932aca23d729123c468fa71c73d84483025aca"
},
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "^1.2.0",

View file

@ -217,16 +217,30 @@
});
var timeout;
if (options.timeout) {
timeout = setTimeout(function () {
dialogHelper.close(dlg);
}, options.timeout);
}
return new Promise(function (resolve, reject) {
dlg.addEventListener('close', function () {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
if (selectedId != null) {
if (options.callback) {
options.callback(selectedId);
}
resolve(selectedId);
} else {
reject();
}
});

View file

@ -1,95 +1,32 @@
define(['dialogHelper', 'layoutManager', 'globalize', 'material-icons', 'css!./../prompt/style.css', 'emby-button', 'paper-icon-button-light'], function (dialogHelper, layoutManager, globalize) {
define(['dialog', 'globalize'], function (dialog, globalize) {
function getIcon(icon, cssClass, canFocus, autoFocus) {
return function (text, title) {
var tabIndex = canFocus ? '' : ' tabindex="-1"';
autoFocus = autoFocus ? ' autofocus' : '';
return '<button is="paper-icon-button-light" class="autoSize ' + cssClass + '"' + tabIndex + autoFocus + '><i class="md-icon">' + icon + '</i></button>';
}
return function (options) {
if (typeof options === 'string') {
var options;
if (typeof text === 'string') {
options = {
title: '',
text: options
title: title,
text: text
};
}
var dialogOptions = {
removeOnClose: true
};
var backButton = false;
var raisedButtons = false;
var isFullscreen = false;
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
backButton = true;
raisedButtons = true;
isFullscreen = true;
} else {
dialogOptions.modal = false;
dialogOptions.entryAnimationDuration = 160;
dialogOptions.exitAnimationDuration = 200;
options = text;
}
var dlg = dialogHelper.createDialog(dialogOptions);
var items = [];
dlg.classList.add('promptDialog');
var html = '';
html += '<div class="promptDialogContent">';
if (backButton) {
html += getIcon('&#xE5C4;', 'btnPromptExit', false);
}
if (options.title) {
html += '<h2>';
html += options.title;
html += '</h2>';
} else if (!isFullscreen) {
// Add a little space so it's not hugging the border
html += '<br/>';
}
var text = options.html || options.text;
if (text) {
if (options.title) {
html += '<p style="margin-top:2em;">';
} else {
html += '<p>';
}
html += text;
html += '</p>';
}
var buttonText = options.type == 'error' ? 'sharedcomponents#ButtonOk' : 'sharedcomponents#ButtonGotIt';
if (raisedButtons) {
html += '<button is="emby-button" type="button" class="raised btnSubmit"><i class="md-icon">check</i><span>' + globalize.translate(buttonText) + '</span></button>';
} else {
html += '<div class="buttons" style="text-align:right;">';
html += '<button is="emby-button" type="button" class="btnSubmit">' + globalize.translate(buttonText) + '</button>';
html += '</div>';
}
html += '</div>';
dlg.innerHTML = html;
document.body.appendChild(dlg);
dlg.querySelector('.btnSubmit').addEventListener('click', function (e) {
dialogHelper.close(dlg);
items.push({
name: globalize.translate('sharedcomponents#ButtonOk'),
id: 'ok'
});
return dialogHelper.open(dlg);
options.buttons = items;
return dialog(options).then(function (result) {
if (result == 'ok') {
return Promise.resolve();
}
return Promise.reject();
});
};
});

View file

@ -137,7 +137,7 @@
var html = '';
html += '<div class="dialogContent smoothScrollY">';
html += '<div class="dialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner centeredContent">';
html += '<form class="newCollectionForm" style="margin:auto;">';
@ -240,9 +240,9 @@
dlg.classList.add('formDialog');
var html = '';
var title = items.length ? globalize.translate('sharedcomponents#AddToCollection') : globalize.translate('sharedcomponents#NewCollection');
var title = items.length ? globalize.translate('sharedcomponents#HeaderAddToCollection') : globalize.translate('sharedcomponents#NewCollection');
html += '<div class="dialogHeader" style="margin:0 0 2em;">';
html += '<div class="dialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>';
html += '<div class="dialogHeaderTitle">';
html += title;

View file

@ -1,116 +1,4 @@
define(['layoutManager', 'globalize'], function (layoutManager, globalize) {
function showTvConfirm(options) {
return new Promise(function (resolve, reject) {
require(['actionsheet'], function (actionSheet) {
var items = [];
items.push({
name: globalize.translate('sharedcomponents#ButtonOk'),
id: 'ok'
});
items.push({
name: globalize.translate('sharedcomponents#ButtonCancel'),
id: 'cancel'
});
actionSheet.show({
title: options.text,
items: items
}).then(function (id) {
switch (id) {
case 'ok':
resolve();
break;
default:
reject();
break;
}
}, reject);
});
});
}
function showConfirmInternal(options, dialogHelper, resolve, reject) {
var dialogOptions = {
removeOnClose: true
};
var backButton = false;
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
backButton = true;
dialogOptions.autoFocus = true;
} else {
dialogOptions.modal = false;
dialogOptions.entryAnimationDuration = 160;
dialogOptions.exitAnimationDuration = 160;
dialogOptions.autoFocus = false;
}
var dlg = dialogHelper.createDialog(dialogOptions);
var html = '';
if (options.title) {
html += '<h2>' + options.title + '</h2>';
}
var text = options.html || options.text;
if (text) {
html += '<div>' + text + '</div>';
}
html += '<div class="buttons">';
html += '<button is="emby-button" type="button" class="btnConfirm" autofocus>' + globalize.translate('sharedcomponents#ButtonOk') + '</button>';
html += '<button is="emby-button" type="button" class="btnCancel">' + globalize.translate('sharedcomponents#ButtonCancel') + '</button>';
html += '</div>';
dlg.innerHTML = html;
document.body.appendChild(dlg);
var confirmed = false;
dlg.querySelector('.btnConfirm').addEventListener('click', function () {
confirmed = true;
dialogHelper.close(dlg);
});
dlg.querySelector('.btnCancel').addEventListener('click', function () {
confirmed = false;
dialogHelper.close(dlg);
});
dialogHelper.open(dlg).then(function () {
if (confirmed) {
resolve();
} else {
reject();
}
});
}
function showConfirm(options) {
return new Promise(function (resolve, reject) {
require(['dialogHelper', 'emby-button'], function (dialogHelper) {
showConfirmInternal(options, dialogHelper, resolve, reject);
});
});
}
define(['dialog', 'globalize'], function (dialog, globalize) {
return function (text, title) {
@ -124,10 +12,26 @@ define(['layoutManager', 'globalize'], function (layoutManager, globalize) {
options = text;
}
if (layoutManager.tv) {
return showTvConfirm(options);
var items = [];
items.push({
name: globalize.translate('sharedcomponents#ButtonOk'),
id: 'ok'
});
items.push({
name: globalize.translate('sharedcomponents#ButtonCancel'),
id: 'cancel'
});
options.buttons = items;
return dialog(options).then(function (result) {
if (result == 'ok') {
return Promise.resolve();
}
return showConfirm(options);
return Promise.reject();
});
};
});

View file

@ -0,0 +1,126 @@
define(['layoutManager', 'globalize'], function (layoutManager, globalize) {
function showTvDialog(options) {
return new Promise(function (resolve, reject) {
require(['actionsheet'], function (actionSheet) {
var items = [];
items.push({
name: globalize.translate('sharedcomponents#ButtonOk'),
id: 'ok'
});
items.push({
name: globalize.translate('sharedcomponents#ButtonCancel'),
id: 'cancel'
});
actionSheet.show({
title: options.text,
items: options.buttons
}).then(resolve, reject);
});
});
}
function showDialogInternal(options, dialogHelper, resolve, reject) {
var dialogOptions = {
removeOnClose: true
};
var backButton = false;
if (layoutManager.tv) {
dialogOptions.size = 'fullscreen';
backButton = true;
dialogOptions.autoFocus = true;
} else {
dialogOptions.modal = false;
dialogOptions.entryAnimationDuration = 160;
dialogOptions.exitAnimationDuration = 160;
dialogOptions.autoFocus = true;
}
var dlg = dialogHelper.createDialog(dialogOptions);
var html = '';
if (options.title) {
html += '<h2>' + options.title + '</h2>';
}
var text = options.html || options.text;
if (text) {
html += '<div>' + text + '</div>';
}
html += '<div class="buttons">';
var i, length;
for (i = 0, length = options.buttons.length; i < length; i++) {
var item = options.buttons[i];
var autoFocus = i == 0 ? ' autofocus' : '';
html += '<button is="emby-button" type="button" class="btnOption" data-id="' + item.id + '"' + autoFocus + '>' + item.name + '</button>';
}
html += '</div>';
dlg.innerHTML = html;
document.body.appendChild(dlg);
var dialogResult;
function onButtonClick() {
dialogResult = this.getAttribute('data-id');
dialogHelper.close(dlg);
}
var buttons = dlg.querySelectorAll('.btnOption');
for (i = 0, length = options.buttons.length; i < length; i++) {
buttons[i].addEventListener('click', onButtonClick);
}
dialogHelper.open(dlg).then(function () {
if (dialogResult) {
resolve(dialogResult);
} else {
reject();
}
});
}
function showDialog(options) {
return new Promise(function (resolve, reject) {
require(['dialogHelper', 'emby-button'], function (dialogHelper) {
showDialogInternal(options, dialogHelper, resolve, reject);
});
});
}
return function (text, title) {
var options;
if (typeof text === 'string') {
options = {
title: title,
text: text
};
} else {
options = text;
}
if (layoutManager.tv) {
return showTvDialog(options);
}
return showDialog(options);
};
});

View file

@ -1,4 +1,4 @@
define(['itemShortcuts', 'connectionManager', 'layoutManager', 'browser', 'dom', 'registerElement'], function (itemShortcuts, connectionManager, layoutManager, browser, dom) {
define(['itemShortcuts', 'connectionManager', 'layoutManager', 'browser', 'dom', 'loading', 'registerElement'], function (itemShortcuts, connectionManager, layoutManager, browser, dom, loading) {
var ItemsContainerProtoType = Object.create(HTMLDivElement.prototype);
@ -62,9 +62,11 @@
var current = this.hoverMenu;
if (!enabled && current) {
if (!enabled) {
if (current) {
current.destroy();
this.hoverMenu = null;
}
return;
}
@ -82,9 +84,11 @@
var current = this.multiSelect;
if (!enabled && current) {
if (!enabled) {
if (current) {
current.destroy();
this.multiSelect = null;
}
return;
}
@ -101,6 +105,76 @@
});
};
function onDrop(evt, itemsContainer) {
var playlistId = itemsContainer.getAttribute('data-playlistid');
loading.show();
var el = evt.item;
var newIndex = evt.newIndex;
var itemId = el.getAttribute('data-playlistitemid');
var serverId = el.getAttribute('data-serverid');
var apiClient = connectionManager.getApiClient(serverId);
apiClient.ajax({
url: apiClient.getUrl('Playlists/' + playlistId + '/Items/' + itemId + '/Move/' + newIndex),
type: 'POST'
}).then(function () {
el.setAttribute('data-index', newIndex);
loading.hide();
}, function () {
loading.hide();
itemsContainer.dispatchEvent(new CustomEvent('needsrefresh', {
detail: {},
cancelable: false,
bubbles: true
}));
});
}
ItemsContainerProtoType.enableDragReordering = function (enabled) {
var current = this.sortable;
if (!enabled) {
if (current) {
current.destroy();
this.sortable = null;
}
return;
}
if (current) {
return;
}
var self = this;
require(['sortable'], function (Sortable) {
self.sortable = new Sortable(self, {
draggable: ".listItem",
handle: '.listViewDragHandle',
// dragging ended
onEnd: function (/**Event*/evt) {
onDrop(evt, self);
}
});
});
};
ItemsContainerProtoType.attachedCallback = function () {
this.addEventListener('click', onClick);
@ -126,6 +200,7 @@
this.enableHoverMenu(false);
this.enableMultiSelect(false);
this.enableDragReordering(false);
this.removeEventListener('click', onClick);
this.removeEventListener('contextmenu', onContextMenu);
this.removeEventListener('contextmenu', disableEvent);

View file

@ -61,7 +61,7 @@ define(['css!./indicators.css', 'material-icons'], function () {
}
if (userData.PlayedPercentage && userData.PlayedPercentage >= 100 || (userData.Played)) {
return '<div class="playedIndicator indicator"><i class="md-icon">check</i></div>';
return '<div class="playedIndicator indicator"><i class="md-icon">&#xE5CA;</i></div>';
}
}

View file

@ -1,4 +1,4 @@
define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter', 'playbackManager'], function (appHost, globalize, connectionManager, itemHelper, embyRouter, playbackManager) {
define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter', 'playbackManager', 'loading'], function (appHost, globalize, connectionManager, itemHelper, embyRouter, playbackManager, loading) {
var isTheater = true;
appHost.appInfo().then(function (result) {
@ -32,6 +32,13 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter',
});
}
if (item.Type == 'Timer' && user.Policy.EnableLiveTvManagement) {
commands.push({
name: globalize.translate('sharedcomponents#ButtonCancel'),
id: 'canceltimer'
});
}
if (item.CanDelete) {
commands.push({
name: globalize.translate('sharedcomponents#Delete'),
@ -154,7 +161,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter',
if (user.Policy.IsAdministrator) {
if (item.Type != 'Timer') {
if (item.Type != 'Timer' && item.Type != 'Program') {
commands.push({
name: globalize.translate('sharedcomponents#Refresh'),
id: 'refresh'
@ -445,7 +452,7 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter',
break;
case 'canceltimer':
deleteTimer(itemId, parentWithClass(card, 'itemsContainer'));
deleteTimer(apiClient, item, resolve, id);
break;
default:
reject();
@ -454,6 +461,27 @@ define(['apphost', 'globalize', 'connectionManager', 'itemHelper', 'embyRouter',
});
}
function deleteTimer(apiClient, item, resolve, command) {
require(['confirm'], function (confirm) {
confirm(globalize.translate('sharedcomponents#MessageConfirmRecordingCancellation'), globalize.translate('sharedcomponents#HeaderConfirmRecordingCancellation')).then(function () {
loading.show();
apiClient.cancelLiveTvTimer(item.Id).then(function () {
require(['toast'], function (toast) {
toast(globalize.translate('sharedcomponents#RecordingCancelled'));
});
loading.hide();
getResolveFunction(resolve, command, true)();
});
});
});
}
function play(item, resume, queue) {
var method = queue ? 'queue' : 'play';

View file

@ -75,6 +75,10 @@ define(['apphost'], function (appHost) {
return false;
}
if (itemType == 'Program') {
return false;
}
if (user.Policy.IsAdministrator) {
return true;

View file

@ -1,7 +1,7 @@
button.listItem {
background: transparent;
border: 0 !important;
border-bottom: 1px solid #2a2a2a !important;
border: 0;
border-bottom: 1px solid #2a2a2a;
cursor: pointer;
outline: none !important;
color: inherit;
@ -138,6 +138,10 @@ div.listItem {
transform: scale(1.025, 1.025);
}
.listItem > .fab:first-child, .listItem > i:first-child {
margin-left: .75em;
}
.paperList {
padding: .5em 0;
margin: 1em auto;
@ -149,6 +153,10 @@ div.listItem {
background-color: transparent !important;
}
.paperList .listItem {
border-bottom: 0;
}
.listItemMediaInfo {
align-items: center;
}

View file

@ -137,7 +137,7 @@
var html = '';
html += '<div class="dialogContent smoothScrollY">';
html += '<div class="dialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner centeredContent">';
html += '<form style="margin:auto;">';
@ -221,9 +221,9 @@
dlg.classList.add('formDialog');
var html = '';
var title = globalize.translate('sharedcomponents#AddToPlaylist');
var title = globalize.translate('sharedcomponents#HeaderAddToPlaylist');
html += '<div class="dialogHeader" style="margin:0 0 2em;">';
html += '<div class="dialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>';
html += '<div class="dialogHeaderTitle">';
html += title;

View file

@ -17,7 +17,7 @@
var html = '';
html += '<div class="dialogContent smoothScrollY">';
html += '<div class="dialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner centeredContent">';
html += '<form style="margin:auto;">';
@ -114,7 +114,7 @@
var html = '';
var title = globalize.translate('sharedcomponents#RefreshMetadata');
html += '<div class="dialogHeader" style="margin:0 0 2em;">';
html += '<div class="dialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>';
html += '<div class="dialogHeaderTitle">';
html += title;

View file

@ -911,9 +911,9 @@ define(['browser', 'layoutManager', 'scrollStyles'], function (browser, layoutMa
} else {
if (o.horizontal) {
slideeElement.scrollLeft += o.scrollBy * delta;
slideeElement.scrollLeft += 12 * delta;
} else {
slideeElement.scrollTop += o.scrollBy * delta;
slideeElement.scrollTop += 12 * delta;
}
}
}

View file

@ -146,6 +146,16 @@ define(['playbackManager', 'inputManager', 'connectionManager', 'embyRouter', 'g
}));
}
}
else if (result.command == 'canceltimer') {
if (itemsContainer) {
itemsContainer.dispatchEvent(new CustomEvent('timercancelled', {
detail: {},
cancelable: false,
bubbles: true
}));
}
}
});
});
});

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Special - {0}",
"Share": "Del",
"ServerUpdateNeeded": "Denne Emby server b\u00f8r opdateres. For at downloade den nyeste version bes\u00f8g venligst {0}",
@ -103,7 +99,6 @@
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard.",
"Open": "Open",
"Play": "Play",
@ -121,5 +116,13 @@
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Special - {0}",
"Share": "Teilen",
"ServerUpdateNeeded": "Dieser Emby Server sollte aktualisiert werden. Um die neueste Version zu laden, besuche bitte {0}",
@ -103,7 +99,6 @@
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard.",
"Open": "Open",
"Play": "Play",
@ -121,5 +116,13 @@
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -57,7 +57,8 @@
"ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?",
"Refresh": "Refresh",
"RefreshQueued": "Refresh queued.",
"AddToCollection": "Add to Collection",
"AddToCollection": "Add to collection",
"HeaderAddToCollection": "Add to Collection",
"NewCollection": "New Collection",
"LabelCollection": "Collection:",
"Help": "Help",
@ -68,7 +69,8 @@
"MessageItemsAdded": "Items added.",
"OptionNew": "New...",
"LabelPlaylist": "Playlist:",
"AddToPlaylist": "Add to Playlist",
"AddToPlaylist": "Add to playlist",
"HeaderAddToPlaylist": "Add to Playlist",
"Subtitles": "Subtitles",
"SearchForSubtitles": "Search for Subtitles",
"LabelLanguage": "Language:",
@ -79,7 +81,7 @@
"ConfirmDeletion": "Confirm Deletion",
"MySubtitles": "My Subtitles",
"MessageDownloadQueued": "Download queued.",
"EditSubtitles": "Edit Subtitles",
"EditSubtitles": "Edit subtitles",
"UnlockGuide": "Unlock Guide",
"RefreshMetadata": "Refresh Metadata",
"ReplaceExistingImages": "Replace existing images",
@ -103,17 +105,18 @@
"Queue": "Queue",
"Shuffle": "Shuffle",
"Identify": "Identify",
"EditImages": "Edit Images",
"EditInfo": "Edit Info",
"EditImages": "Edit images",
"EditInfo": "Edit info",
"Sync": "Sync",
"InstantMix": "Instant Mix",
"ViewAlbum": "View Album",
"ViewArtist": "View Artist",
"QueueAllFromHere": "Queue All from Here",
"PlayAllFromHere": "Play All from Here",
"InstantMix": "Instant mix",
"ViewAlbum": "View album",
"ViewArtist": "View artist",
"QueueAllFromHere": "Queue all from here",
"PlayAllFromHere": "Play all from here",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"RemoveFromPlaylist": "Remove from playlist",
"RemoveFromCollection": "Remove from collection",
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
@ -122,5 +125,8 @@
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"Error": "Error",
"VoiceInput": "Voice Input"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Especial - {0}",
"Share": "Compartir",
"ServerUpdateNeeded": "Este Servidor Emby necesita ser actualizado. Para descargar la ultima versi\u00f3n, por favor visite {0}",
@ -98,28 +94,35 @@
"HeaderYouSaid": "Ha Dicho...",
"MessageWeDidntRecognizeCommand": "Lo sentimos, no reconocimos ese comando.",
"MessageIfYouBlockedVoice": "Si ha negado el acceso por voz a la aplicaci\u00f3n necesitara reconfigurar antes de intentarlo de nuevo.",
"ValueDiscNumber": "Disc {0}",
"Unrated": "Unrated",
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"ValueDiscNumber": "Disco {0}",
"Unrated": "Sin clasificar",
"Favorite": "Favorito",
"Like": "Me gusta",
"Dislike": "No me gusta",
"RefreshDialogHelp": "Los metadatos son actualizados bas\u00e1ndose en las configuraciones y servicios de internet que que est\u00e9n activados en el panel de control de su Servidor de Emby.",
"Open": "Open",
"Play": "Play",
"Queue": "Queue",
"Shuffle": "Shuffle",
"Identify": "Identify",
"EditImages": "Edit Images",
"EditInfo": "Edit Info",
"Sync": "Sync",
"InstantMix": "Instant Mix",
"ViewAlbum": "View Album",
"ViewArtist": "View Artist",
"QueueAllFromHere": "Queue All from Here",
"PlayAllFromHere": "Play All from Here",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Open": "Abrir",
"Play": "Reproducir",
"Queue": "Cola",
"Shuffle": "Aleatorio",
"Identify": "Identificar",
"EditImages": "Editar im\u00e1genes",
"EditInfo": "Editar Informaci\u00f3n",
"Sync": "Sinc.",
"InstantMix": "Mix instant\u00e1neo",
"ViewAlbum": "Ver \u00c1lbum",
"ViewArtist": "Ver Artista",
"QueueAllFromHere": "Encolar todos desde aqu\u00ed",
"PlayAllFromHere": "Reproducir todos desde aqu\u00ed",
"PlayFromBeginning": "Reproducir desde el inicio",
"ResumeAt": "Reanudar desde {0}",
"RemoveFromPlaylist": "Eliminar de la lista de reproducci\u00f3n",
"Trailer": "Tr\u00e1iler",
"MarkPlayed": "Marcar como Reproducido",
"MarkUnplayed": "Marcar como No Reproducido",
"GroupVersions": "Agrupar Versiones",
"PleaseSelectTwoItems": "Por favor seleccione al menos dos \u00edtems.",
"TheSelectedItemsWillBeGrouped": "Los videos seleccionados se agruparan en un solo \u00edtem virtual. Las aplicaciones Emby elegir\u00e1n autom\u00e1ticamente cual versi\u00f3n reproducir dependiendo del dispositivo y el rendimiento de la red. \u00bfEsta seguro de que desea continuar?",
"TryMultiSelect": "Intente Multi-Selecci\u00f3n",
"TryMultiSelectMessage": "Para editar m\u00faltiples medios, solo de clic sostenido sobre cualquier p\u00f3ster y elija los items que desea administrar. \u00a1int\u00e9ntelo!",
"Error": "Error"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Sp\u00e9cial - {0}",
"Share": "Partager",
"ServerUpdateNeeded": "Le serveur Emby doit \u00eatre mis \u00e0 jour. Pour t\u00e9l\u00e9charger la derni\u00e8re version, veuillez visiter {0}",
@ -103,7 +99,6 @@
"Favorite": "Favoris",
"Like": "J'aime",
"Dislike": "Je n'aime pas",
"Played": "Lu",
"RefreshDialogHelp": "Les m\u00e9tadonn\u00e9es sont actualis\u00e9es en fonction des param\u00e8tres et des services internet qui sont activ\u00e9s dans le tableau de bord Emby Server.",
"Open": "Ouvrir",
"Play": "Lire",
@ -118,8 +113,16 @@
"ViewArtist": "Voir l'artiste",
"QueueAllFromHere": "Tout mette en file d'attente \u00e0 partir d'ici",
"PlayAllFromHere": "Tout lire \u00e0 partir d'ici",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"PlayFromBeginning": "Lire depuis le d\u00e9but",
"ResumeAt": "Reprendre \u00e0 {0}",
"RemoveFromPlaylist": "Supprimer de la liste de lecture",
"Trailer": "Trailer"
"Trailer": "Bande-annonce",
"MarkPlayed": "Marquer comme lu",
"MarkUnplayed": "Marquer comme non lu",
"GroupVersions": "Versions des groupes",
"PleaseSelectTwoItems": "Veuillez s\u00e9lectionner au moins deux \u00e9l\u00e9ments.",
"TheSelectedItemsWillBeGrouped": "Les vid\u00e9os s\u00e9lectionn\u00e9es seront regroup\u00e9es dans un objet virtuel. L'application Emby choisra automatiquement quelle version jouer d'apr\u00e8s le p\u00e9riph\u00e9rique et la performance du r\u00e9seau. \u00cates vous s\u00fbre de vouloir continuer ?",
"TryMultiSelect": "Essayer la s\u00e9lection multiple",
"TryMultiSelectMessage": "Pour modifier plusieurs \u00e9l\u00e9ments m\u00e9dias, il suffit de cliquer et maintenir le clic sur n'importe quel poster et de s\u00e9lectionner les \u00e9l\u00e9ments que vous voulez g\u00e9rer. Essayer le !",
"Error": "Erreur"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "\u0410\u0440\u043d\u0430\u0439\u044b - {0}",
"Share": "\u041e\u0440\u0442\u0430\u049b\u0442\u0430\u0441\u0443",
"ServerUpdateNeeded": "\u041e\u0441\u044b Emby Server \u0436\u0430\u04a3\u0430\u0440\u0442\u044b\u043b\u0443\u044b \u049b\u0430\u0436\u0435\u0442. \u0421\u043e\u04a3\u0493\u044b \u043d\u04b1\u0441\u049b\u0430\u0441\u044b\u043d \u0436\u04af\u043a\u0442\u0435\u043f \u0430\u043b\u0443 \u04af\u0448\u0456\u043d, {0} \u043a\u0456\u0440\u0456\u04a3\u0456\u0437",
@ -103,7 +99,6 @@
"Favorite": "\u0422\u0430\u04a3\u0434\u0430\u0443\u043b\u044b",
"Like": "\u04b0\u043d\u0430\u0439\u0434\u044b",
"Dislike": "\u04b0\u043d\u0430\u043c\u0430\u0439\u0434\u044b",
"Played": "\u041e\u0439\u043d\u0430\u0442\u044b\u043b\u0493\u0430\u043d",
"RefreshDialogHelp": "\u041c\u0435\u0442\u0430\u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043b\u0435\u0440 \u043c\u0435\u043d Emby Server \u0431\u0430\u049b\u044b\u043b\u0430\u0443 \u0442\u0430\u049b\u0442\u0430\u0441\u044b\u043d\u0434\u0430 \u049b\u043e\u0441\u044b\u043b\u0493\u0430\u043d \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u049b\u044b\u0437\u043c\u0435\u0442\u0442\u0435\u0440\u0456 \u043d\u0435\u0433\u0456\u0437\u0456\u043d\u0434\u0435 \u0436\u0430\u04a3\u0493\u044b\u0440\u0442\u044b\u043b\u0430\u0434\u044b.",
"Open": "\u0410\u0448\u0443",
"Play": "\u041e\u0439\u043d\u0430\u0442\u0443",
@ -111,15 +106,23 @@
"Shuffle": "\u0410\u0440\u0430\u043b\u0430\u0441\u0442\u044b\u0440\u0443",
"Identify": "\u0410\u043d\u044b\u049b\u0442\u0430\u0443",
"EditImages": "\u0421\u0443\u0440\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u04e9\u04a3\u0434\u0435\u0443",
"EditInfo": "Edit Info",
"EditInfo": "\u041c\u04d9\u043b\u0456\u043c\u0435\u0442\u0442\u0435\u0440\u0434\u0456 \u04e9\u04a3\u0434\u0435\u0443",
"Sync": "\u04ae\u043d\u0434\u0435\u0441\u0442\u0456\u0440\u0443",
"InstantMix": "\u041b\u0435\u0437\u0434\u0456\u043a \u049b\u043e\u0441\u043f\u0430",
"ViewAlbum": "\u0410\u043b\u044c\u0431\u043e\u043c\u0434\u044b \u049b\u0430\u0440\u0430\u0443",
"ViewArtist": "\u041e\u0440\u044b\u043d\u0434\u0430\u0443\u0448\u044b\u043d\u044b \u049b\u0430\u0440\u0430\u0443",
"QueueAllFromHere": "\u0411\u04b1\u043b \u0430\u0440\u0430\u0434\u0430\u043d \u0431\u04d9\u0440\u0456\u043d \u043a\u0435\u0437\u0435\u043a\u043a\u0435",
"PlayAllFromHere": "\u0411\u04b1\u043b \u0430\u0440\u0430\u0434\u0430\u043d \u0431\u04d9\u0440\u0456\u043d \u043e\u0439\u043d\u0430\u0442\u0443",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"PlayFromBeginning": "\u0411\u0430\u0441\u044b\u043d\u0430\u043d \u043e\u0439\u043d\u0430\u0442\u0443",
"ResumeAt": "{0} \u0431\u0430\u0441\u0442\u0430\u043f \u0436\u0430\u043b\u0493\u0430\u0441\u0442\u044b\u0440\u0443",
"RemoveFromPlaylist": "\u041e\u0439\u043d\u0430\u0442\u0443 \u0442\u0456\u0437\u0456\u043c\u0456\u043d\u0435\u043d \u0430\u043b\u0430\u0441\u0442\u0430\u0443",
"Trailer": "\u0422\u0440\u0435\u0439\u043b\u0435\u0440",
"MarkPlayed": "\u041e\u0439\u043d\u0430\u0442\u044b\u043b\u0493\u0430\u043d \u0434\u0435\u043f \u0431\u0435\u043b\u0433\u0456\u043b\u0435\u0443",
"MarkUnplayed": "\u041e\u0439\u043d\u0430\u0442\u044b\u043b\u043c\u0430\u0493\u0430\u043d \u0434\u0435\u043f \u0431\u0435\u043b\u0433\u0456\u043b\u0435\u0443",
"GroupVersions": "\u041d\u04b1\u0441\u049b\u0430\u043b\u0430\u0440\u0434\u044b \u0442\u043e\u043f\u0442\u0430\u0441\u0442\u044b\u0440\u0443",
"PleaseSelectTwoItems": "\u0415\u04a3 \u043a\u0435\u043c\u0456\u043d\u0434\u0435 \u0435\u043a\u0456 \u0442\u0430\u0440\u043c\u0430\u049b\u0442\u044b \u0442\u0430\u04a3\u0434\u0430\u04a3\u044b\u0437.",
"TheSelectedItemsWillBeGrouped": "\u0411\u04e9\u043b\u0435\u043a\u0442\u0435\u043b\u0433\u0435\u043d \u0431\u0435\u0439\u043d\u0435\u043b\u0435\u0440 \u0431\u0456\u0440 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u0434\u044b \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043a\u0435 \u0442\u043e\u043f\u0442\u0430\u0441\u0442\u044b\u0440\u044b\u043b\u0430\u0434\u044b. Emby \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u043b\u0430\u0440\u044b \u049b\u04b1\u0440\u044b\u043b\u0493\u044b \u043c\u0435\u043d \u0436\u0435\u043b\u0456 \u04e9\u043d\u0456\u043c\u0434\u0456\u043b\u0456\u0433\u0456 \u043d\u0435\u0433\u0456\u0437\u0456\u043d\u0434\u0435 \u043e\u0439\u043d\u0430\u0442\u044b\u043b\u0430\u0442\u044b\u043d \u043d\u04b1\u0441\u049b\u0430\u0441\u044b\u043d \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u0442\u0430\u04a3\u0434\u0430\u0439\u0434\u044b. \u0428\u044b\u043d\u044b\u043c\u0435\u043d \u0436\u0430\u043b\u0493\u0430\u0441\u0442\u044b\u0440\u0443 \u049b\u0430\u0436\u0435\u0442 \u043f\u0435?",
"TryMultiSelect": "\u04ae\u043d\u0434\u0435\u0441\u043a\u0435\u043d \u0431\u04e9\u043b\u0435\u043a\u0442\u0435\u0443\u0434\u0456 \u0441\u044b\u043d\u0430\u043f \u043a\u04e9\u0440\u0443",
"TryMultiSelectMessage": "\u0411\u0456\u0440\u043d\u0435\u0448\u0435 \u0442\u0430\u0441\u044b\u0493\u044b\u0448 \u0434\u0435\u0440\u0435\u043a\u0442\u0435\u0440 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0442\u0435\u0440\u0456\u043d \u04e9\u04a3\u0434\u0435\u0443 \u04af\u0448\u0456\u043d, \u043a\u0435\u0437 \u043a\u0435\u043b\u0433\u0435\u043d \u043f\u043e\u0441\u0442\u0435\u0440\u0434\u0456 \u0436\u0430\u0439 \u0493\u0430\u043d\u0430 \u0442\u0456\u043d\u0442\u0443\u0456\u0440 \u0431\u0430\u0442\u044b\u0440\u043c\u0430\u0493\u0430 \u0431\u0430\u0441\u044b\u043f \u0442\u04b1\u0440\u044b\u043f \u043d\u04b1\u049b\u044b\u04a3\u044b\u0437 \u0436\u04d9\u043d\u0435 \u0431\u0430\u0441\u049b\u0430\u0440\u0443\u044b\u043d \u049b\u0430\u043b\u0430\u0493\u0430\u043d \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0442\u0435\u0440\u0434\u0456 \u0431\u04e9\u043b\u0435\u043a\u0442\u0435\u04a3\u0456\u0437. \u0421\u044b\u043d\u0430\u043f \u043a\u04e9\u0440\u0456\u04a3\u0456\u0437!",
"Error": "\u049a\u0430\u0442\u0435"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Spesial - {0}",
"Share": "Del",
"ServerUpdateNeeded": "Denne Emby serveren trenger en oppdatering. For \u00e5 laste ned nyeste versjon, vennligst bes\u00f8k: {0}",
@ -29,9 +25,9 @@
"ButtonOk": "Ok",
"ButtonCancel": "Avbryt",
"ButtonGotIt": "Skj\u00f8nner",
"RecordingCancelled": "Recording cancelled.",
"RecordingCancelled": "Opptatt avbrutt.",
"RecordingScheduled": "Planlagte opptak.",
"SeriesRecordingScheduled": "Series recording scheduled.",
"SeriesRecordingScheduled": "Serie opptak planlagt.",
"HeaderNewRecording": "Tar opp n\u00e5",
"Sunday": "S\u00f8ndag",
"Monday": "Mandag",
@ -75,51 +71,58 @@
"OptionNew": "Ny",
"LabelPlaylist": "Spilleliste:",
"AddToPlaylist": "Legg til i Spilleliste",
"Subtitles": "Subtitles",
"SearchForSubtitles": "Search for Subtitles",
"LabelLanguage": "Language:",
"Search": "Search",
"NoSubtitleSearchResultsFound": "No results found.",
"File": "File",
"MessageAreYouSureDeleteSubtitles": "Are you sure you wish to delete this subtitle file?",
"ConfirmDeletion": "Confirm Deletion",
"MySubtitles": "My Subtitles",
"MessageDownloadQueued": "Download queued.",
"EditSubtitles": "Edit Subtitles",
"UnlockGuide": "Unlock Guide",
"RefreshMetadata": "Refresh Metadata",
"ReplaceExistingImages": "Replace existing images",
"ReplaceAllMetadata": "Replace all metadata",
"SearchForMissingMetadata": "Search for missing metadata",
"LabelRefreshMode": "Refresh mode:",
"NoItemsFound": "No items found.",
"HeaderSaySomethingLike": "Say Something Like...",
"ButtonTryAgain": "Try Again",
"HeaderYouSaid": "You Said...",
"MessageWeDidntRecognizeCommand": "We're sorry, we didn't recognize that command.",
"MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.",
"ValueDiscNumber": "Disc {0}",
"Unrated": "Unrated",
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard.",
"Open": "Open",
"Play": "Play",
"Queue": "Queue",
"Shuffle": "Shuffle",
"Identify": "Identify",
"EditImages": "Edit Images",
"EditInfo": "Edit Info",
"Sync": "Sync",
"InstantMix": "Instant Mix",
"ViewAlbum": "View Album",
"ViewArtist": "View Artist",
"QueueAllFromHere": "Queue All from Here",
"PlayAllFromHere": "Play All from Here",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Subtitles": "Undertekster",
"SearchForSubtitles": "S\u00f8k etter undertekster",
"LabelLanguage": "Spr\u00e5k:",
"Search": "S\u00f8k:",
"NoSubtitleSearchResultsFound": "Ingen resultater funnet.",
"File": "Fil:",
"MessageAreYouSureDeleteSubtitles": "Er du sikker p\u00e5 at du vil slette denne undertekst filen?",
"ConfirmDeletion": "Bekreft sletting",
"MySubtitles": "Mine undertekster",
"MessageDownloadQueued": "Nedlasting satt til i k\u00f8",
"EditSubtitles": "Endre undertekster",
"UnlockGuide": "L\u00e5s opp guide",
"RefreshMetadata": "Oppdater metadata",
"ReplaceExistingImages": "Erstatt eksisterende bilde",
"ReplaceAllMetadata": "Erstatt all metadata",
"SearchForMissingMetadata": "S\u00f8k etter manglende metadata",
"LabelRefreshMode": "Oppdatering modus:",
"NoItemsFound": "Ingen elementer funnet",
"HeaderSaySomethingLike": "Si noe slik som...",
"ButtonTryAgain": "Pr\u00f8v igjen",
"HeaderYouSaid": "Du Sa...",
"MessageWeDidntRecognizeCommand": "Beklager vi kunne ikke forst\u00e5 den kommandoen.",
"MessageIfYouBlockedVoice": "Hvis du nektet stemmegjenkjenning tilgang til applikasjonen m\u00e5 du rekonfigurere dette f\u00f8r du pr\u00f8ver igjen.",
"ValueDiscNumber": "Disk {0}",
"Unrated": "Uten sensur",
"Favorite": "Favoritt",
"Like": "Liker",
"Dislike": "Liker ikke",
"RefreshDialogHelp": "Metadata er oppdatert basert p\u00e5 innstillinger og internett-tjenester som er aktivert i Emby Server dashbordet",
"Open": "\u00c5pne",
"Play": "Spill",
"Queue": "K\u00f8",
"Shuffle": "Tilfeldig",
"Identify": "Identifisere",
"EditImages": "Endre bilder",
"EditInfo": "Endre informasjon",
"Sync": "Synkronisere",
"InstantMix": "Umiddelbar Blanding",
"ViewAlbum": "Vis album",
"ViewArtist": "Vis artister",
"QueueAllFromHere": "Sett alt i k\u00f8en fra her",
"PlayAllFromHere": "Spill av alle fra her",
"PlayFromBeginning": "Start fra starten",
"ResumeAt": "Forsett fra {0}",
"RemoveFromPlaylist": "Fjern fra spilleliste",
"Trailer": "Trailer",
"MarkPlayed": "Merker som sett",
"MarkUnplayed": "Merker som usett",
"GroupVersions": "Gruppering av versjoner",
"PleaseSelectTwoItems": "Vennligst velg minst to elementer",
"TheSelectedItemsWillBeGrouped": "De valgte videoene blir gruppert i en virtuell element. Emby app vil automatisk velge hvilken versjon du vil spille basert p\u00e5 enheten og nettverksytelse . Er du sikker p\u00e5 at du vil fortsette?",
"TryMultiSelect": "Pr\u00f8v flervalg.",
"TryMultiSelectMessage": "Hvis du vil redigere flere medier elementer, klikker du bare og hold en plakat og velg elementene du \u00f8nsker \u00e5 administrere. Pr\u00f8v det!",
"Error": "Feil"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Speciaal - {0}",
"Share": "Delen",
"ServerUpdateNeeded": "Deze Emby Server moet worden bijgewerkt. Om de laatste versie te downloaden, gaat u naar {0}",
@ -103,7 +99,6 @@
"Favorite": "Favoriet",
"Like": "Leuk",
"Dislike": "Niet leuk",
"Played": "Afgespeeld",
"RefreshDialogHelp": "Metadata wordt vernieuwd op basis van de instellingen en internet diensten die zijn ingeschakeld in het dashboard van de Emby Server.",
"Open": "Openen",
"Play": "Afspelen",
@ -118,8 +113,16 @@
"ViewArtist": "Bekijk artiest",
"QueueAllFromHere": "Plaats alles in de wachtrij vanaf hier",
"PlayAllFromHere": "Speel alles vanaf hier",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"PlayFromBeginning": "Afspelen vanaf begin",
"ResumeAt": "Hervatten vanaf {0}",
"RemoveFromPlaylist": "Verwijderen uit afspeellijst",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Markeren als Afgespeeld",
"MarkUnplayed": "Markeren als Niet Afgespeeld",
"GroupVersions": "Groepeer Versies",
"PleaseSelectTwoItems": "Selecteer ten minste twee items.",
"TheSelectedItemsWillBeGrouped": "De geselecteerde videos worden in 1 item gegroepeerd. Emby apps zullen automatisch de juiste versie selecteren op basis van het apparaat en netwerk prestaties. Weet u zeker dat u door wilt gaan?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Fout"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Especial - {0}",
"Share": "Compartilhar",
"ServerUpdateNeeded": "Este servidor Emby precisa ser atualizado. Para baixar a \u00faltima vers\u00e3o, por favor visite {0}",
@ -103,7 +99,6 @@
"Favorite": "Favorito",
"Like": "Gostei",
"Dislike": "N\u00e3o Gostei",
"Played": "Reproduzido",
"RefreshDialogHelp": "Os metadados s\u00e3o atualizados com bases nas defini\u00e7\u00f5es e nos servi\u00e7os de internet que est\u00e3o ativos no painel do Servidor Emby.",
"Open": "Abrir",
"Play": "Reproduzir",
@ -121,5 +116,13 @@
"PlayFromBeginning": "Reproduzir do in\u00edcio",
"ResumeAt": "Retomar de {0}",
"RemoveFromPlaylist": "Remover da Lista de Reprodu\u00e7\u00e3o",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Especial - {0}",
"Share": "Partilhar",
"ServerUpdateNeeded": "Este Servidor Emby precisa ser atualizado. Para fazer download da vers\u00e3o mais recente, por favor visite {0}",
@ -103,7 +99,6 @@
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard.",
"Open": "Open",
"Play": "Play",
@ -121,5 +116,13 @@
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u044d\u043f\u0438\u0437\u043e\u0434 - {0}",
"Share": "\u041f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f",
"ServerUpdateNeeded": "\u0414\u0430\u043d\u043d\u044b\u0439 Emby Server \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c. \u0427\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e, \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u0435 {0}",
@ -103,7 +99,6 @@
"Favorite": "\u0418\u0437\u0431\u0440\u0430\u043d\u043d\u043e\u0435",
"Like": "\u041d\u0440\u0430\u0432\u0438\u0442\u0441\u044f",
"Dislike": "\u041d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f",
"Played": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u0435",
"RefreshDialogHelp": "\u041f\u043e\u0434\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u0438\u043d\u0444\u043e\u043f\u0430\u043d\u0435\u043b\u0438 Emby Server \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0443\u0441\u043b\u0443\u0433\u0430\u043c\u0438.",
"Open": "\u041e\u0442\u043a\u0440\u044b\u0442\u044c",
"Play": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438",
@ -111,15 +106,23 @@
"Shuffle": "\u041f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0442\u044c",
"Identify": "\u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0442\u044c",
"EditImages": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c \u0440\u0438\u0441\u0443\u043d\u043a\u0438",
"EditInfo": "Edit Info",
"EditInfo": "\u041f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u0435\u0434\u0435\u043d\u0438\u044f",
"Sync": "\u0421\u0438\u043d\u0445\u0440\u043e",
"InstantMix": "\u0410\u0432\u0442\u043e\u043c\u0438\u043a\u0441",
"ViewAlbum": "\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0430\u043b\u044c\u0431\u043e\u043c",
"ViewArtist": "\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044f",
"QueueAllFromHere": "\u0412 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0432\u0441\u0435 \u043e\u0442\u0441\u044e\u0434\u0430",
"PlayAllFromHere": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u043e\u0442\u0441\u044e\u0434\u0430",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"PlayFromBeginning": "\u0412\u043e\u0441\u043f\u0440. \u0441 \u043d\u0430\u0447\u0430\u043b\u0430",
"ResumeAt": "\u0412\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441 {0}",
"RemoveFromPlaylist": "\u0418\u0437\u044a\u044f\u0442\u044c \u0438\u0437 \u043f\u043b\u0435\u0439-\u043b\u0438\u0441\u0442\u0430",
"Trailer": "\u0422\u0440\u0435\u0439\u043b\u0435\u0440",
"MarkPlayed": "\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043a\u0430\u043a \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u0435",
"MarkUnplayed": "\u041e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043a\u0430\u043a \u043d\u0435\u0432\u043e\u0441\u043f\u0440-\u043e\u0435",
"GroupVersions": "\u0421\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u0438",
"PleaseSelectTwoItems": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0445\u043e\u0442\u044f \u0431\u044b \u0434\u0432\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430.",
"TheSelectedItemsWillBeGrouped": "\u0412\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0432\u0438\u0434\u0435\u043e \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0432 \u043e\u0434\u0438\u043d \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442. \u0412 Emby-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043b\u044f \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c\u0430\u044f \u043f\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0442\u0438. \u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c?",
"TryMultiSelect": "\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043d\u0435\u0441\u0432\u044f\u0437\u043d\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435",
"TryMultiSelectMessage": "\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u0440\u043e\u0441\u0442\u043e \u0449\u0451\u043b\u043a\u043d\u0438\u0442\u0435 \u0438 \u0443\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0439\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u043c\u044b\u0448\u0438 \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u043f\u043e\u0441\u0442\u0435\u0440\u0435 \u0438 \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u0435 \u0442\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u044d\u0442\u043e!",
"Error": "\u041e\u0448\u0438\u0431\u043a\u0430"
}

View file

@ -1,8 +1,6 @@
{
"EditInfo": "Edit Info",
"RemoveFromPlaylist": "Remove from Playlist",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Specialavsnitt - {0}",
"Share": "Dela",
"ServerUpdateNeeded": "Den h\u00e4r Emby servern beh\u00f6ver uppdateras. F\u00f6r att ladda ner senaste versionen, g\u00e5 till {0}",
@ -101,7 +99,6 @@
"Favorite": "Favorit",
"Like": "Gilla",
"Dislike": "Ogilla",
"Played": "Visad",
"RefreshDialogHelp": "Metadata uppdateras baserat p\u00e5 inst\u00e4llningar och internettj\u00e4nster som har aktiverats under Emby servers kontrollpanel.",
"Open": "\u00d6ppna",
"Play": "Spela",
@ -109,10 +106,23 @@
"Shuffle": "Blanda",
"Identify": "Identifiera",
"EditImages": "\u00c4ndra bilder",
"EditInfo": "Edit Info",
"Sync": "Synk",
"InstantMix": "Instant mix",
"ViewAlbum": "Bl\u00e4ddra album",
"ViewArtist": "Bl\u00e4ddra artist",
"QueueAllFromHere": "K\u00f6a alla h\u00e4rifr\u00e5n",
"PlayAllFromHere": "Spela upp alla h\u00e4rifr\u00e5n"
"PlayAllFromHere": "Spela upp alla h\u00e4rifr\u00e5n",
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -1,10 +1,6 @@
{
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"Error": "Error",
"HeaderConfirmRecordingCancellation": "Confirm Recording Cancellation",
"MessageConfirmRecordingCancellation": "Are you sure you wish to cancel this recording?",
"ValueSpecialEpisodeName": "Special - {0}",
"Share": "\u5206\u4eab",
"ServerUpdateNeeded": "\u6b64Emby\u4f3a\u670d\u5668\u9700\u8981\u66f4\u65b0\uff0c\u8acb\u81f3{0}\u53d6\u5f97\u6700\u65b0\u7248\u672c",
@ -103,7 +99,6 @@
"Favorite": "Favorite",
"Like": "Like",
"Dislike": "Dislike",
"Played": "Played",
"RefreshDialogHelp": "\u8a73\u7d30\u8cc7\u6599\u7684\u66f4\u65b0\u65b9\u5f0f\u6703\u4f9d\u64daEmby\u7684\u8a2d\u5b9a\u53ca\u5df2\u7d93\u555f\u7528\u7684\u7db2\u8def\u670d\u52d9\u4f86\u9032\u884c",
"Open": "Open",
"Play": "Play",
@ -121,5 +116,13 @@
"PlayFromBeginning": "Play from beginning",
"ResumeAt": "Resume from {0}",
"RemoveFromPlaylist": "Remove from Playlist",
"Trailer": "Trailer"
"Trailer": "Trailer",
"MarkPlayed": "Mark Played",
"MarkUnplayed": "Mark Unplayed",
"GroupVersions": "Group Versions",
"PleaseSelectTwoItems": "Please select at least two items.",
"TheSelectedItemsWillBeGrouped": "The selected videos will be grouped into one virtual item. Emby apps will automatically choose which version to play based on device and network performance. Are you sure you wish to continue?",
"TryMultiSelect": "Try Multi-Select",
"TryMultiSelectMessage": "To edit multiple media items, just click and hold any poster and select the items you want to manage. Try it!",
"Error": "Error"
}

View file

@ -10,13 +10,15 @@
items = shuffleArray(items);
}
if (items.length) {
var serverId = items[0].ServerId;
items = items.map(function (i) {
return i.Id;
});
if (items.length) {
playbackManager.play({
ids: items
ids: items,
serverId: serverId
});
}
else {
@ -63,6 +65,7 @@
}
var apiClient = connectionManager.currentApiClient();
if (result.item.sourceid === 'nextup') {
apiClient.getNextUpEpisodes(query).then(function (queryResult) {
@ -90,7 +93,6 @@
query.Filters = 'IsFavorite';
}
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle);

View file

@ -85,7 +85,6 @@ define([], function () {
},
command: null,
text: text,
userId: Dashboard.getCurrentUserId(),
success: false
};

View file

@ -1,4 +1,4 @@
define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emby-button', 'css!./voice.css', 'material-icons', 'css!./../formdialog'], function (dialogHelper, voicereceiver, voiceprocessor, globalize) {
define(['dialogHelper', 'voiceReceiver', 'voiceProcessor', 'globalize', 'emby-button', 'css!./voice.css', 'material-icons', 'css!./../formdialog'], function (dialogHelper, voicereceiver, voiceprocessor, globalize) {
var lang = 'en-US';
@ -111,16 +111,16 @@ define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emb
dlg.classList.add('formDialog');
var html = '';
html += '<div class="dialogHeader" style="margin:0 0 2em;">';
html += '<div class="dialogHeader">';
html += '<button is="paper-icon-button-light" class="btnCancelVoiceInput autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>';
html += '<div class="dialogHeaderTitle">';
//html += title;
html += globalize.translate('sharedcomponents#VoiceInput');
html += '</div>';
html += '</div>';
html += '<div>';
html += '<div class="dialogContent smoothScrollY">';
html += '<div class="dialogContent smoothScrollY" style="padding-top:2em;">';
html += '<div class="dialogContentInner centeredContent">';
html += '<div class="voiceHelpContent">';
@ -244,7 +244,11 @@ define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emb
listen();
}
function listen() {
voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (result) {
voicereceiver.listen({
lang: lang || "en-US"
}).then(processInput).then(function (result) {
closeDialog();

View file

@ -23,6 +23,8 @@
/// <returns> . </returns>
function processTranscript(text) {
if (text) {
return getCommandGroups().then(function (commandgroups) {
var processor = grammarprocessor(commandgroups, text);
if (processor && processor.command) {
console.log("Command from Grammar Processor", processor);
@ -41,6 +43,7 @@
} else {
return Promise.reject({ error: "unrecognized-command", text: text });
}
});
} else {
return Promise.reject({ error: "empty" });

View file

@ -1,10 +1,15 @@
define([], function () {
var currentRecognition = null;
define(['events'], function (events) {
var receiver = {
};
var currentRecognition = null;
/// <summary> Starts listening for voice commands </summary>
/// <returns> . </returns>
function listenForCommand(lang) {
function listen(options) {
return new Promise(function (resolve, reject) {
cancelListener();
@ -13,14 +18,29 @@
window.mozSpeechRecognition ||
window.oSpeechRecognition ||
window.msSpeechRecognition)();
recognition.lang = lang;
recognition.lang = options.lang;
recognition.continuous = options.continuous || false;
var resultCount = 0;
recognition.onresult = function (event) {
console.log(event);
if (event.results.length > 0) {
var resultInput = event.results[0][0].transcript || '';
var resultInput = event.results[resultCount][0].transcript || '';
resultCount++;
if (options.continuous) {
events.trigger(receiver, 'input', [
{
text: resultInput
}
]);
} else {
resolve(resultInput);
}
}
};
recognition.onerror = function () {
@ -36,7 +56,6 @@
});
}
/// <summary> Cancel listener. </summary>
/// <returns> . </returns>
function cancelListener() {
@ -48,10 +67,9 @@
}
/// <summary> An enum constant representing the window. voice input manager option. </summary>
return {
listenForCommand: listenForCommand,
cancel: cancelListener
};
receiver.listen = listen;
receiver.cancel = cancelListener;
/// <summary> An enum constant representing the window. voice input manager option. </summary>
return receiver;
});

View file

@ -70,6 +70,7 @@
queue: false,
playAllFromHere: false,
queueAllFromHere: false,
sync: false,
positionTo: button
};
}

View file

@ -76,22 +76,6 @@
elem.setAttribute('data-playlistid', item.Id);
elem.innerHTML = html;
var listParent = elem;
require(['sortable'], function (Sortable) {
var sortable = new Sortable(listParent, {
draggable: ".listItem",
handle: '.listViewDragHandle',
// dragging ended
onEnd: function (/**Event*/evt) {
onDrop(evt, page, item);
}
});
});
ImageLoader.lazyChildren(elem);
$('.btnNextPage', elem).on('click', function () {
@ -108,37 +92,12 @@
});
}
function onDrop(evt, page, item) {
Dashboard.showLoadingMsg();
var el = evt.item;
var newIndex = evt.newIndex;
var itemId = el.getAttribute('data-playlistitemid');
ApiClient.ajax({
url: ApiClient.getUrl('Playlists/' + item.Id + '/Items/' + itemId + '/Move/' + newIndex),
type: 'POST'
}).then(function () {
el.setAttribute('data-index', newIndex);
Dashboard.hideLoadingMsg();
}, function () {
Dashboard.hideLoadingMsg();
reloadItems(page, item);
});
}
function init(page, item) {
var elem = page.querySelector('#childrenContent .itemsContainer');
elem.enableDragReordering(true);
elem.addEventListener('needsrefresh', function () {
reloadItems(page, item);

View file

@ -1830,6 +1830,8 @@ var AppInfo = {};
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide", 'embyRouter'], returnFirstDependency);
define("voiceDialog", [embyWebComponentsBowerPath + "/voice/voicedialog"], returnFirstDependency);
define("voiceReceiver", [embyWebComponentsBowerPath + "/voice/voicereceiver"], returnFirstDependency);
define("voiceProcessor", [embyWebComponentsBowerPath + "/voice/voiceprocessor"], returnFirstDependency);
define("viewManager", [embyWebComponentsBowerPath + "/viewmanager/viewmanager"], function (viewManager) {
window.ViewManager = viewManager;
@ -2175,6 +2177,8 @@ var AppInfo = {};
define("alert", [embyWebComponentsBowerPath + "/alert/alert"], returnFirstDependency);
}
define("dialog", [embyWebComponentsBowerPath + "/dialog/dialog"], returnFirstDependency);
if (preferNativeAlerts && window.confirm) {
define("confirm", [embyWebComponentsBowerPath + "/confirm/nativeconfirm"], returnFirstDependency);
} else {