1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

update script loading

This commit is contained in:
Luke Pulverenti 2015-12-03 22:59:48 -05:00
parent 31b3061157
commit 22f689e089
65 changed files with 732 additions and 6138 deletions

View file

@ -1,736 +0,0 @@
(function (name, context, definition) {
if (typeof module != 'undefined' && module.exports) module.exports = definition()
else if (typeof define == 'function' && define.amd) define(definition)
else context[name] = definition()
})('bean', this, function (name, context) {
name = name || 'bean'
context = context || this
var win = window
, old = context[name]
, namespaceRegex = /[^\.]*(?=\..*)\.|.*/
, nameRegex = /\..*/
, addEvent = 'addEventListener'
, removeEvent = 'removeEventListener'
, doc = document || {}
, root = doc.documentElement || {}
, W3C_MODEL = root[addEvent]
, eventSupport = W3C_MODEL ? addEvent : 'attachEvent'
, ONE = {} // singleton for quick matching making add() do one()
, slice = Array.prototype.slice
, str2arr = function (s, d) { return s.split(d || ' ') }
, isString = function (o) { return typeof o == 'string' }
, isFunction = function (o) { return typeof o == 'function' }
// events that we consider to be 'native', anything not in this list will
// be treated as a custom event
, standardNativeEvents =
'click dblclick mouseup mousedown contextmenu ' + // mouse buttons
'mousewheel mousemultiwheel DOMMouseScroll ' + // mouse wheel
'mouseover mouseout mousemove selectstart selectend ' + // mouse movement
'keydown keypress keyup ' + // keyboard
'orientationchange ' + // mobile
'focus blur change reset select submit ' + // form elements
'load unload beforeunload resize move DOMContentLoaded ' + // window
'readystatechange message ' + // window
'error abort scroll ' // misc
// element.fireEvent('onXYZ'... is not forgiving if we try to fire an event
// that doesn't actually exist, so make sure we only do these on newer browsers
, w3cNativeEvents =
'show ' + // mouse buttons
'input invalid ' + // form elements
'touchstart touchmove touchend touchcancel ' + // touch
'gesturestart gesturechange gestureend ' + // gesture
'textinput ' + // TextEvent
'readystatechange pageshow pagehide popstate ' + // window
'hashchange offline online ' + // window
'afterprint beforeprint ' + // printing
'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd
'loadstart progress suspend emptied stalled loadmetadata ' + // media
'loadeddata canplay canplaythrough playing waiting seeking ' + // media
'seeked ended durationchange timeupdate play pause ratechange ' + // media
'volumechange cuechange ' + // media
'checking noupdate downloading cached updateready obsolete ' // appcache
// convert to a hash for quick lookups
, nativeEvents = (function (hash, events, i) {
for (i = 0; i < events.length; i++) events[i] && (hash[events[i]] = 1)
return hash
}({}, str2arr(standardNativeEvents + (W3C_MODEL ? w3cNativeEvents : ''))))
// custom events are events that we *fake*, they are not provided natively but
// we can use native events to generate them
, customEvents = (function () {
var isAncestor = 'compareDocumentPosition' in root
? function (element, container) {
return container.compareDocumentPosition && (container.compareDocumentPosition(element) & 16) === 16
}
: 'contains' in root
? function (element, container) {
container = container.nodeType === 9 || container === window ? root : container
return container !== element && container.contains(element)
}
: function (element, container) {
while (element = element.parentNode) if (element === container) return 1
return 0
}
, check = function (event) {
var related = event.relatedTarget
return !related
? related == null
: (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString())
&& !isAncestor(related, this))
}
return {
mouseenter: { base: 'mouseover', condition: check }
, mouseleave: { base: 'mouseout', condition: check }
, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' }
}
}())
// we provide a consistent Event object across browsers by taking the actual DOM
// event object and generating a new one from its properties.
, Event = (function () {
// a whitelist of properties (for different event types) tells us what to check for and copy
var commonProps = str2arr('altKey attrChange attrName bubbles cancelable ctrlKey currentTarget ' +
'detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey ' +
'srcElement target timeStamp type view which propertyName path')
, mouseProps = commonProps.concat(str2arr('button buttons clientX clientY dataTransfer ' +
'fromElement offsetX offsetY pageX pageY screenX screenY toElement movementX movementY region'))
, mouseWheelProps = mouseProps.concat(str2arr('wheelDelta wheelDeltaX wheelDeltaY wheelDeltaZ ' +
'axis')) // 'axis' is FF specific
, keyProps = commonProps.concat(str2arr('char charCode key keyCode keyIdentifier ' +
'keyLocation location isComposing code'))
, textProps = commonProps.concat(str2arr('data'))
, touchProps = commonProps.concat(str2arr('touches targetTouches changedTouches scale rotation'))
, messageProps = commonProps.concat(str2arr('data origin source'))
, stateProps = commonProps.concat(str2arr('state'))
, overOutRegex = /over|out/
// some event types need special handling and some need special properties, do that all here
, typeFixers = [
{ // key events
reg: /key/i
, fix: function (event, newEvent) {
newEvent.keyCode = event.keyCode || event.which
return keyProps
}
}
, { // mouse events
reg: /click|mouse(?!(.*wheel|scroll))|menu|drag|drop/i
, fix: function (event, newEvent, type) {
newEvent.rightClick = event.which === 3 || event.button === 2
newEvent.pos = { x: 0, y: 0 }
if (event.pageX || event.pageY) {
newEvent.clientX = event.pageX
newEvent.clientY = event.pageY
} else if (event.clientX || event.clientY) {
newEvent.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft
newEvent.clientY = event.clientY + doc.body.scrollTop + root.scrollTop
}
if (overOutRegex.test(type)) {
newEvent.relatedTarget = event.relatedTarget
|| event[(type == 'mouseover' ? 'from' : 'to') + 'Element']
}
return mouseProps
}
}
, { // mouse wheel events
reg: /mouse.*(wheel|scroll)/i
, fix: function () { return mouseWheelProps }
}
, { // TextEvent
reg: /^text/i
, fix: function () { return textProps }
}
, { // touch and gesture events
reg: /^touch|^gesture/i
, fix: function () { return touchProps }
}
, { // message events
reg: /^message$/i
, fix: function () { return messageProps }
}
, { // popstate events
reg: /^popstate$/i
, fix: function () { return stateProps }
}
, { // everything else
reg: /.*/
, fix: function () { return commonProps }
}
]
, typeFixerMap = {} // used to map event types to fixer functions (above), a basic cache mechanism
, Event = function (event, element, isNative) {
if (!arguments.length) return
event = event || ((element.ownerDocument || element.document || element).parentWindow || win).event
this.originalEvent = event
this.isNative = isNative
this.isBean = true
if (!event) return
var type = event.type
, target = event.target || event.srcElement
, i, l, p, props, fixer
this.target = target && target.nodeType === 3 ? target.parentNode : target
if (isNative) { // we only need basic augmentation on custom events, the rest expensive & pointless
fixer = typeFixerMap[type]
if (!fixer) { // haven't encountered this event type before, map a fixer function for it
for (i = 0, l = typeFixers.length; i < l; i++) {
if (typeFixers[i].reg.test(type)) { // guaranteed to match at least one, last is .*
typeFixerMap[type] = fixer = typeFixers[i].fix
break
}
}
}
props = fixer(event, this, type)
for (i = props.length; i--;) {
if (!((p = props[i]) in this) && p in event) this[p] = event[p]
}
}
}
// preventDefault() and stopPropagation() are a consistent interface to those functions
// on the DOM, stop() is an alias for both of them together
Event.prototype.preventDefault = function () {
if (this.originalEvent.preventDefault) this.originalEvent.preventDefault()
else this.originalEvent.returnValue = false
}
Event.prototype.stopPropagation = function () {
if (this.originalEvent.stopPropagation) this.originalEvent.stopPropagation()
else this.originalEvent.cancelBubble = true
}
Event.prototype.stop = function () {
this.preventDefault()
this.stopPropagation()
this.stopped = true
}
// stopImmediatePropagation() has to be handled internally because we manage the event list for
// each element
// note that originalElement may be a Bean#Event object in some situations
Event.prototype.stopImmediatePropagation = function () {
if (this.originalEvent.stopImmediatePropagation) this.originalEvent.stopImmediatePropagation()
this.isImmediatePropagationStopped = function () { return true }
}
Event.prototype.isImmediatePropagationStopped = function () {
return this.originalEvent.isImmediatePropagationStopped && this.originalEvent.isImmediatePropagationStopped()
}
Event.prototype.clone = function (currentTarget) {
//TODO: this is ripe for optimisation, new events are *expensive*
// improving this will speed up delegated events
var ne = new Event(this, this.element, this.isNative)
ne.currentTarget = currentTarget
return ne
}
return Event
}())
// if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both
, targetElement = function (element, isNative) {
return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element
}
/**
* Bean maintains an internal registry for event listeners. We don't touch elements, objects
* or functions to identify them, instead we store everything in the registry.
* Each event listener has a RegEntry object, we have one 'registry' for the whole instance.
*/
, RegEntry = (function () {
// each handler is wrapped so we can handle delegation and custom events
var wrappedHandler = function (element, fn, condition, args) {
var call = function (event, eargs) {
return fn.apply(element, args ? slice.call(eargs, event ? 0 : 1).concat(args) : eargs)
}
, findTarget = function (event, eventElement) {
return fn.__beanDel ? fn.__beanDel.ft(event.target, element) : eventElement
}
, handler = condition
? function (event) {
var target = findTarget(event, this) // deleated event
if (condition.apply(target, arguments)) {
if (event) event.currentTarget = target
return call(event, arguments)
}
}
: function (event) {
if (fn.__beanDel) event = event.clone(findTarget(event)) // delegated event, fix the fix
return call(event, arguments)
}
handler.__beanDel = fn.__beanDel
return handler
}
, RegEntry = function (element, type, handler, original, namespaces, args, root) {
var customType = customEvents[type]
, isNative
if (type == 'unload') {
// self clean-up
handler = once(removeListener, element, type, handler, original)
}
if (customType) {
if (customType.condition) {
handler = wrappedHandler(element, handler, customType.condition, args)
}
type = customType.base || type
}
this.isNative = isNative = nativeEvents[type] && !!element[eventSupport]
this.customType = !W3C_MODEL && !isNative && type
this.element = element
this.type = type
this.original = original
this.namespaces = namespaces
this.eventType = W3C_MODEL || isNative ? type : 'propertychange'
this.target = targetElement(element, isNative)
this[eventSupport] = !!this.target[eventSupport]
this.root = root
this.handler = wrappedHandler(element, handler, null, args)
}
// given a list of namespaces, is our entry in any of them?
RegEntry.prototype.inNamespaces = function (checkNamespaces) {
var i, j, c = 0
if (!checkNamespaces) return true
if (!this.namespaces) return false
for (i = checkNamespaces.length; i--;) {
for (j = this.namespaces.length; j--;) {
if (checkNamespaces[i] == this.namespaces[j]) c++
}
}
return checkNamespaces.length === c
}
// match by element, original fn (opt), handler fn (opt)
RegEntry.prototype.matches = function (checkElement, checkOriginal, checkHandler) {
return this.element === checkElement &&
(!checkOriginal || this.original === checkOriginal) &&
(!checkHandler || this.handler === checkHandler)
}
return RegEntry
}())
, registry = (function () {
// our map stores arrays by event type, just because it's better than storing
// everything in a single array.
// uses '$' as a prefix for the keys for safety and 'r' as a special prefix for
// rootListeners so we can look them up fast
var map = {}
// generic functional search of our registry for matching listeners,
// `fn` returns false to break out of the loop
, forAll = function (element, type, original, handler, root, fn) {
var pfx = root ? 'r' : '$'
if (!type || type == '*') {
// search the whole registry
for (var t in map) {
if (t.charAt(0) == pfx) {
forAll(element, t.substr(1), original, handler, root, fn)
}
}
} else {
var i = 0, l, list = map[pfx + type], all = element == '*'
if (!list) return
for (l = list.length; i < l; i++) {
if ((all || list[i].matches(element, original, handler)) && !fn(list[i], list, i, type)) return
}
}
}
, has = function (element, type, original, root) {
// we're not using forAll here simply because it's a bit slower and this
// needs to be fast
var i, list = map[(root ? 'r' : '$') + type]
if (list) {
for (i = list.length; i--;) {
if (!list[i].root && list[i].matches(element, original, null)) return true
}
}
return false
}
, get = function (element, type, original, root) {
var entries = []
forAll(element, type, original, null, root, function (entry) {
return entries.push(entry)
})
return entries
}
, put = function (entry) {
var has = !entry.root && !this.has(entry.element, entry.type, null, false)
, key = (entry.root ? 'r' : '$') + entry.type
;(map[key] || (map[key] = [])).push(entry)
return has
}
, del = function (entry) {
forAll(entry.element, entry.type, null, entry.handler, entry.root, function (entry, list, i) {
list.splice(i, 1)
entry.removed = true
if (list.length === 0) delete map[(entry.root ? 'r' : '$') + entry.type]
return false
})
}
// dump all entries, used for onunload
, entries = function () {
var t, entries = []
for (t in map) {
if (t.charAt(0) == '$') entries = entries.concat(map[t])
}
return entries
}
return { has: has, get: get, put: put, del: del, entries: entries }
}())
// we need a selector engine for delegated events, use querySelectorAll if it exists
// but for older browsers we need Qwery, Sizzle or similar
, selectorEngine
, setSelectorEngine = function (e) {
if (!arguments.length) {
selectorEngine = doc.querySelectorAll
? function (s, r) {
return r.querySelectorAll(s)
}
: function () {
throw new Error('Bean: No selector engine installed') // eeek
}
} else {
selectorEngine = e
}
}
// we attach this listener to each DOM event that we need to listen to, only once
// per event type per DOM element
, rootListener = function (event, type) {
if (!W3C_MODEL && type && event && event.propertyName != '_on' + type) return
var listeners = registry.get(this, type || event.type, null, false)
, l = listeners.length
, i = 0
event = new Event(event, this, true)
if (type) event.type = type
// iterate through all handlers registered for this type, calling them unless they have
// been removed by a previous handler or stopImmediatePropagation() has been called
for (; i < l && !event.isImmediatePropagationStopped(); i++) {
if (!listeners[i].removed) listeners[i].handler.call(this, event)
}
}
// add and remove listeners to DOM elements
, listener = W3C_MODEL
? function (element, type, add) {
// new browsers
element[add ? addEvent : removeEvent](type, rootListener, false)
}
: function (element, type, add, custom) {
// IE8 and below, use attachEvent/detachEvent and we have to piggy-back propertychange events
// to simulate event bubbling etc.
var entry
if (add) {
registry.put(entry = new RegEntry(
element
, custom || type
, function (event) { // handler
rootListener.call(element, event, custom)
}
, rootListener
, null
, null
, true // is root
))
if (custom && element['_on' + custom] == null) element['_on' + custom] = 0
entry.target.attachEvent('on' + entry.eventType, entry.handler)
} else {
entry = registry.get(element, custom || type, rootListener, true)[0]
if (entry) {
entry.target.detachEvent('on' + entry.eventType, entry.handler)
registry.del(entry)
}
}
}
, once = function (rm, element, type, fn, originalFn) {
// wrap the handler in a handler that does a remove as well
return function () {
fn.apply(this, arguments)
rm(element, type, originalFn)
}
}
, removeListener = function (element, orgType, handler, namespaces) {
var type = orgType && orgType.replace(nameRegex, '')
, handlers = registry.get(element, type, null, false)
, removed = {}
, i, l
for (i = 0, l = handlers.length; i < l; i++) {
if ((!handler || handlers[i].original === handler) && handlers[i].inNamespaces(namespaces)) {
// TODO: this is problematic, we have a registry.get() and registry.del() that
// both do registry searches so we waste cycles doing this. Needs to be rolled into
// a single registry.forAll(fn) that removes while finding, but the catch is that
// we'll be splicing the arrays that we're iterating over. Needs extra tests to
// make sure we don't screw it up. @rvagg
registry.del(handlers[i])
if (!removed[handlers[i].eventType] && handlers[i][eventSupport])
removed[handlers[i].eventType] = { t: handlers[i].eventType, c: handlers[i].type }
}
}
// check each type/element for removed listeners and remove the rootListener where it's no longer needed
for (i in removed) {
if (!registry.has(element, removed[i].t, null, false)) {
// last listener of this type, remove the rootListener
listener(element, removed[i].t, false, removed[i].c)
}
}
}
// set up a delegate helper using the given selector, wrap the handler function
, delegate = function (selector, fn) {
//TODO: findTarget (therefore $) is called twice, once for match and once for
// setting e.currentTarget, fix this so it's only needed once
var findTarget = function (target, root) {
var i, array = isString(selector) ? selectorEngine(selector, root) : selector
for (; target && target !== root; target = target.parentNode) {
for (i = array.length; i--;) {
if (array[i] === target) return target
}
}
}
, handler = function (e) {
var match = findTarget(e.target, this)
if (match) fn.apply(match, arguments)
}
// __beanDel isn't pleasant but it's a private function, not exposed outside of Bean
handler.__beanDel = {
ft : findTarget // attach it here for customEvents to use too
, selector : selector
}
return handler
}
, fireListener = W3C_MODEL ? function (isNative, type, element) {
// modern browsers, do a proper dispatchEvent()
var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents')
evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1)
element.dispatchEvent(evt)
} : function (isNative, type, element) {
// old browser use onpropertychange, just increment a custom property to trigger the event
element = targetElement(element, isNative)
isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++
}
/**
* Public API: off(), on(), add(), (remove()), one(), fire(), clone()
*/
/**
* off(element[, eventType(s)[, handler ]])
*/
, off = function (element, typeSpec, fn) {
var isTypeStr = isString(typeSpec)
, k, type, namespaces, i
if (isTypeStr && typeSpec.indexOf(' ') > 0) {
// off(el, 't1 t2 t3', fn) or off(el, 't1 t2 t3')
typeSpec = str2arr(typeSpec)
for (i = typeSpec.length; i--;)
off(element, typeSpec[i], fn)
return element
}
type = isTypeStr && typeSpec.replace(nameRegex, '')
if (type && customEvents[type]) type = customEvents[type].base
if (!typeSpec || isTypeStr) {
// off(el) or off(el, t1.ns) or off(el, .ns) or off(el, .ns1.ns2.ns3)
if (namespaces = isTypeStr && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.')
removeListener(element, type, fn, namespaces)
} else if (isFunction(typeSpec)) {
// off(el, fn)
removeListener(element, null, typeSpec)
} else {
// off(el, { t1: fn1, t2, fn2 })
for (k in typeSpec) {
if (typeSpec.hasOwnProperty(k)) off(element, k, typeSpec[k])
}
}
return element
}
/**
* on(element, eventType(s)[, selector], handler[, args ])
*/
, on = function(element, events, selector, fn) {
var originalFn, type, types, i, args, entry, first
//TODO: the undefined check means you can't pass an 'args' argument, fix this perhaps?
if (selector === undefined && typeof events == 'object') {
//TODO: this can't handle delegated events
for (type in events) {
if (events.hasOwnProperty(type)) {
on.call(this, element, type, events[type])
}
}
return
}
if (!isFunction(selector)) {
// delegated event
originalFn = fn
args = slice.call(arguments, 4)
fn = delegate(selector, originalFn, selectorEngine)
} else {
args = slice.call(arguments, 3)
fn = originalFn = selector
}
types = str2arr(events)
// special case for one(), wrap in a self-removing handler
if (this === ONE) {
fn = once(off, element, events, fn, originalFn)
}
for (i = types.length; i--;) {
// add new handler to the registry and check if it's the first for this element/type
first = registry.put(entry = new RegEntry(
element
, types[i].replace(nameRegex, '') // event type
, fn
, originalFn
, str2arr(types[i].replace(namespaceRegex, ''), '.') // namespaces
, args
, false // not root
))
if (entry[eventSupport] && first) {
// first event of this type on this element, add root listener
listener(element, entry.eventType, true, entry.customType)
}
}
return element
}
/**
* add(element[, selector], eventType(s), handler[, args ])
*
* Deprecated: kept (for now) for backward-compatibility
*/
, add = function (element, events, fn, delfn) {
return on.apply(
null
, !isString(fn)
? slice.call(arguments)
: [ element, fn, events, delfn ].concat(arguments.length > 3 ? slice.call(arguments, 5) : [])
)
}
/**
* one(element, eventType(s)[, selector], handler[, args ])
*/
, one = function () {
return on.apply(ONE, arguments)
}
/**
* fire(element, eventType(s)[, args ])
*
* The optional 'args' argument must be an array, if no 'args' argument is provided
* then we can use the browser's DOM event system, otherwise we trigger handlers manually
*/
, fire = function (element, type, args) {
var types = str2arr(type)
, i, j, l, names, handlers
for (i = types.length; i--;) {
type = types[i].replace(nameRegex, '')
if (names = types[i].replace(namespaceRegex, '')) names = str2arr(names, '.')
if (!names && !args && element[eventSupport]) {
fireListener(nativeEvents[type], type, element)
} else {
// non-native event, either because of a namespace, arguments or a non DOM element
// iterate over all listeners and manually 'fire'
handlers = registry.get(element, type, null, false)
args = [false].concat(args)
for (j = 0, l = handlers.length; j < l; j++) {
if (handlers[j].inNamespaces(names)) {
handlers[j].handler.apply(element, args)
}
}
}
}
return element
}
/**
* clone(dstElement, srcElement[, eventType ])
*
* TODO: perhaps for consistency we should allow the same flexibility in type specifiers?
*/
, clone = function (element, from, type) {
var handlers = registry.get(from, type, null, false)
, l = handlers.length
, i = 0
, args, beanDel
for (; i < l; i++) {
if (handlers[i].original) {
args = [ element, handlers[i].type ]
if (beanDel = handlers[i].handler.__beanDel) args.push(beanDel.selector)
args.push(handlers[i].original)
on.apply(null, args)
}
}
return element
}
, bean = {
'on' : on
, 'add' : add
, 'one' : one
, 'off' : off
, 'remove' : off
, 'clone' : clone
, 'fire' : fire
, 'Event' : Event
, 'setSelectorEngine' : setSelectorEngine
, 'noConflict' : function () {
context[name] = old
return this
}
}
// for IE, clean up on unload to avoid leaks
if (win.attachEvent) {
var cleanup = function () {
var i, entries = registry.entries()
for (i in entries) {
if (entries[i].type && entries[i].type !== 'unload') off(entries[i].element, entries[i].type)
}
win.detachEvent('onunload', cleanup)
win.CollectGarbage && win.CollectGarbage()
}
win.attachEvent('onunload', cleanup)
}
// initialize selector engine to internal default (qSA or throw Error)
setSelectorEngine()
return bean
});

View file

@ -1,31 +0,0 @@
(function (globalScope) {
globalScope.Events = {
on: function (obj, eventName, selector, fn) {
bean.on(obj, eventName, selector, fn);
},
off: function (obj, eventName, selector, fn) {
bean.off(obj, eventName, selector);
},
trigger: function (obj, eventName, params) {
// Need to push an extra param to make the argument order consistent with jquery
var newParams = [];
newParams.push({});
if (params && params.length) {
for (var i = 0, length = params.length; i < length; i++) {
newParams.push(params[i]);
}
}
bean.fire(obj, eventName, newParams);
}
};
})(window);

View file

@ -39,6 +39,6 @@
"commit": "cec8e49744a1e18b14a711eea77e201bb70de544"
},
"_source": "git://github.com/desandro/doc-ready.git",
"_target": "~1.0.4",
"_target": "1.0.x",
"_originalSource": "doc-ready"
}

View file

@ -31,6 +31,6 @@
"commit": "34fc5e4a0f252964ed2790138b8d7d30d04b55c1"
},
"_source": "git://github.com/desandro/get-style-property.git",
"_target": "~1.0.4",
"_target": "1.x",
"_originalSource": "get-style-property"
}

View file

@ -1,6 +1,6 @@
{
"name": "iron-media-query",
"version": "1.0.7",
"version": "1.0.8",
"description": "Lets you bind to a CSS media query",
"authors": [
"The Polymer Authors"
@ -29,11 +29,11 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"main": "iron-media-query.html",
"_release": "1.0.7",
"_release": "1.0.8",
"_resolution": {
"type": "version",
"tag": "v1.0.7",
"commit": "a78cdbadb6de48857d176b964ab906a12872f2d3"
"tag": "v1.0.8",
"commit": "3f916be171af7a3e03eb019acdfea71055d3c744"
},
"_source": "git://github.com/PolymerElements/iron-media-query.git",
"_target": "^1.0.0",

View file

@ -11,7 +11,7 @@ env:
- secure: LgnZP4BNGBkTZhf8Vr7r9LdrOwq2/58TqqYkFFloEGBRT6HmumNSRwNbIwOh1U9jSTVkqjC2rn4G27u4XlEIs+QTD2PVSSEKy7Vbn0KxSNCvCGaOB1ZaxWTwZa7nkg09ZFRCHGh+WIbuV+BxyzsjOqlN82GSzFNSb3rxhqDM6dU=
node_js: 4
addons:
firefox: '42.0'
firefox: latest
apt:
sources:
- google-chrome

View file

@ -1,6 +1,6 @@
{
"name": "iron-media-query",
"version": "1.0.7",
"version": "1.0.8",
"description": "Lets you bind to a CSS media query",
"authors": [
"The Polymer Authors"

View file

@ -1,11 +1,11 @@
<!doctype html>
<!--
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE
The complete set of authors may be found at http://polymer.github.io/AUTHORS
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>

View file

@ -77,6 +77,7 @@ Example:
},
attached: function() {
this.style.display = 'none';
this.queryChanged();
},

View file

@ -1,15 +1,11 @@
<!doctype html>
<!--
<!DOCTYPE html><!--
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
--><html><head>
<meta charset="utf-8">
<title>Tests</title>
@ -19,12 +15,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<body>
<script>
WCT.loadSuites([
'basic.html'
'basic.html',
'basic.html?dom=shadow'
]);
</script>
</body>
</html>
</body></html>

View file

@ -36,7 +36,7 @@
"tag": "v1.0.8",
"commit": "e9a66727f3da0446f04956d4e4f1dcd51cdec2ff"
},
"_source": "git://github.com/polymerelements/iron-selector.git",
"_source": "git://github.com/PolymerElements/iron-selector.git",
"_target": "^1.0.0",
"_originalSource": "polymerelements/iron-selector"
"_originalSource": "PolymerElements/iron-selector"
}

View file

@ -0,0 +1,46 @@
{
"name": "paper-collapse-item",
"version": "1.0.5",
"authors": [
"collaborne"
],
"description": "A Material Design item with header and collapsible content (Polymer 1.x)",
"main": "paper-collapse-item.html",
"keywords": [
"web-components",
"polymer",
"paper",
"material design",
"lists",
"item"
],
"license": "Apache 2",
"homepage": "http://www.apache.org/licenses/LICENSE-2.0",
"ignore": [
"**/.*"
],
"dependencies": {
"polymer": "Polymer/polymer#^1.0.0",
"iron-icon": "PolymerElements/iron-icon#^1.0.0",
"iron-icons": "PolymerElements/iron-icons#^1.0.0",
"iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"paper-item": "PolymerElements/paper-item#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0"
},
"devDependencies": {
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "*",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"_release": "1.0.5",
"_resolution": {
"type": "version",
"tag": "1.0.5",
"commit": "26aef07fcfd1c09265916f6a384cb2adfbb34727"
},
"_source": "git://github.com/Collaborne/paper-collapse-item.git",
"_target": "~1.0.5",
"_originalSource": "paper-collapse-item",
"_direct": true
}

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,36 @@
{
"name": "paper-collapse-item",
"version": "1.0.5",
"authors": [
"collaborne"
],
"description": "A Material Design item with header and collapsible content (Polymer 1.x)",
"main": "paper-collapse-item.html",
"keywords": [
"web-components",
"polymer",
"paper",
"material design",
"lists",
"item"
],
"license": "Apache 2",
"homepage": "http://www.apache.org/licenses/LICENSE-2.0",
"ignore": [
"**/.*"
],
"dependencies": {
"polymer": "Polymer/polymer#^1.0.0",
"iron-icon": "PolymerElements/iron-icon#^1.0.0",
"iron-icons": "PolymerElements/iron-icons#^1.0.0",
"iron-collapse": "PolymerElements/iron-collapse#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"paper-item": "PolymerElements/paper-item#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0"
},
"devDependencies": {
"test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "*",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
}
}

View file

@ -0,0 +1,19 @@
<html>
<head>
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="../../iron-icons/iron-icons.html">
<link rel="import" href="../paper-collapse-item.html">
</head>
<body>
<paper-collapse-item icon="icons:favorite" header="Item 1" opened>
Lots of very interesting content.
</paper-collapse-item>
<paper-collapse-item icon="icons:info" header="Item 2">
Lots of very interesting content.
</paper-collapse-item>
<paper-collapse-item icon="icons:help" header="Item 3">
Lots of very interesting content.
</paper-collapse-item>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,83 @@
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<link rel="import" href="../iron-collapse/iron-collapse.html">
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
<link rel="import" href="../paper-item/paper-item.html">
<link rel="import" href="../paper-item/paper-item-body.html">
<link rel="import" href="../paper-styles/paper-styles.html">
<dom-module id='paper-collapse-item'>
<template>
<style>
.header {
min-height: 48px;
opacity: var(--dark-primary-opacity);
@apply(--layout-horizontal);
@apply(--layout-center-center);
@apply(--paper-font-subhead);
}
.icon {
margin-right: 24px;
--iron-icon-height: 32px;
--iron-icon-width: 32px;
}
.icon,.toogle {
opacity: var(--dark-disabled-opacity);
}
.content {
opacity: var(--dark-primary-opacity);
@apply(--paper-font-body1);
}
</style>
<paper-item on-tap="_toggleOpened">
<paper-item-body>
<div class="header">
<iron-icon class='icon' icon=[[icon]]></iron-icon>
<div class="flex">[[header]]</div>
<paper-icon-button class='toogle' icon=[[_toggleIcon]]></paper-icon-button>
</div>
<iron-collapse class="content" opened={{opened}}>
<content></content>
</iron-collapse>
</paper-item-body>
</paper-item>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'paper-collapse-item',
properties: {
header: String,
icon: String,
opened: Boolean,
_toggleIcon: {
type: String,
computed: '_computeToggleIcon(opened)'
}
},
// Private methods
_toggleOpened: function(e) {
this.opened = !this.opened;
},
_computeToggleIcon: function(opened) {
return opened ? "icons:expand-less" : "icons:expand-more";
}
});
})();
</script>

View file

@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Tests</title>
<script src="../../web-component-tester/browser.js"></script>
</head>
<body>
<script>
WCT.loadSuites([ 'paper-collapse-item.html' ]);
</script>
</body>
</html>

View file

@ -0,0 +1,50 @@
<!doctype html>
<html>
<head>
<title>paper-collapse-item</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../web-component-tester/browser.js"></script>
<script src="../../test-fixture/test-fixture-mocha.js"></script>
<link rel="import" href="../paper-collapse-item.html">
<link rel="import" href="../../test-fixture/test-fixture.html">
</head>
<body>
<test-fixture id="TrivialElement">
<template>
<paper-collapse-item></paper-collapse-item>
</template>
</test-fixture>
<script>
suite('<paper-collapse-item>', function() {
suite('open/close behavior', function() {
var element;
setup(function() {
element = fixture('TrivialElement');
});
test('defaults to closed', function() {
expect(element.opened).to.be.eql(false);
});
test('shows open toggle icon when closed', function() {
element.opened = false;
expect(element._toggleIcon).to.be.eql('icons:expand-more');
});
test('shows open toggle icon when opened', function() {
element.opened = true;
expect(element._toggleIcon).to.be.eql('icons:expand-less');
});
});
});
</script>
</body>
</html>

View file

@ -1,6 +1,6 @@
{
"name": "paper-input",
"version": "1.1.1",
"version": "1.1.2",
"description": "Material design text fields",
"authors": [
"The Polymer Authors"
@ -32,7 +32,8 @@
"iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
"iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
"iron-input": "PolymerElements/iron-input#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0"
"paper-styles": "PolymerElements/paper-styles#^1.0.0",
"iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
},
"devDependencies": {
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
@ -44,11 +45,11 @@
"web-component-tester": "Polymer/web-component-tester#^3.3.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"_release": "1.1.1",
"_release": "1.1.2",
"_resolution": {
"type": "version",
"tag": "v1.1.1",
"commit": "1bbce220b027dc030b294163f7da6f3e9052ab13"
"tag": "v1.1.2",
"commit": "6c6ba4b5e3e4b18ee387d2b922e00e3edfe0e347"
},
"_source": "git://github.com/polymerelements/paper-input.git",
"_target": "^1.0.9",

View file

@ -11,7 +11,7 @@ env:
- secure: nh65tvhnhOrK05qKvDJKMV7Jm9yiCoG1wFkP3ZnqOHix9Ny+KmcTa41Bl6NXQdvYaMTFtzS7lMZX5cqIziyKyGWHVN30LzGMHJNz12fhcMi3nJ84trhQGcu/9qR9yDv16q9ouGlcz1VxnDOHaRAHnIKjLIbhN3aJtMtZBbnWihA=
node_js: 4
addons:
firefox: '42.0'
firefox: latest
apt:
sources:
- google-chrome

View file

@ -1,6 +1,6 @@
{
"name": "paper-input",
"version": "1.1.1",
"version": "1.1.2",
"description": "Material design text fields",
"authors": [
"The Polymer Authors"
@ -32,7 +32,8 @@
"iron-behaviors": "PolymerElements/iron-behaviors#^1.0.0",
"iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#^1.0.0",
"iron-input": "PolymerElements/iron-input#^1.0.0",
"paper-styles": "PolymerElements/paper-styles#^1.0.0"
"paper-styles": "PolymerElements/paper-styles#^1.0.0",
"iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#^1.0.0"
},
"devDependencies": {
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",

View file

@ -56,6 +56,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<div class="vertical center-justified layout">
<h4>Text input</h4>
<div class="vertical-section">
<paper-input label="label"></paper-input>
@ -90,7 +91,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<paper-textarea label="textarea" char-counter></paper-textarea>
<paper-textarea label="textarea with maxlength" char-counter maxlength="10"></paper-textarea>
<paper-textarea label="textarea with maxlength" maxlength="10" char-counter></paper-textarea>
<paper-textarea label="text area with rows and max-rows" rows="3" max-rows="4"></paper-textarea>
</div>
@ -108,7 +109,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<paper-input class="short" label="icons and buttons" id="inputWithButton">
<iron-icon icon="search" prefix></iron-icon>
<paper-icon-button suffix onclick="clearInput()"
icon="clear" alt="clear" title="clear" tabindex="0">
icon="clear" alt="clear" title="clear">
</paper-icon-button>
</paper-input>
</div>

View file

@ -9,6 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
-->
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-behaviors/iron-control-state.html">
<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<script>
@ -32,7 +33,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
*/
/**
* The label for this input. Bind this to `<paper-input-container>`'s `label` property.
* The label for this input. Bind this to `<label>`'s content and `hidden` property, e.g.
* `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
*/
label: {
type: String
@ -287,21 +289,21 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
/**
* Bind this to the `<input is="iron-input">`'s `results` property, , used with type=search.
* Bind this to the `<input is="iron-input">`'s `results` property, used with type=search.
*/
results: {
type: Number
},
/**
* Bind this to the `<input is="iron-input">`'s `accept` property, , used with type=file.
* Bind this to the `<input is="iron-input">`'s `accept` property, used with type=file.
*/
accept: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `multiple` property, , used with type=file.
* Bind this to the `<input is="iron-input">`'s `multiple` property, used with type=file.
*/
multiple: {
type: Boolean
@ -320,13 +322,22 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
listeners: {
'addon-attached': '_onAddonAttached'
'addon-attached': '_onAddonAttached',
'focus': '_onFocus'
},
observers: [
'_focusedControlStateChanged(focused)'
],
keyBindings: {
'shift+tab:keydown': '_onShiftTabDown'
},
hostAttributes: {
tabindex: 0
},
/**
* Returns a reference to the input element.
*/
@ -334,6 +345,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return this.$.input;
},
/**
* Returns a reference to the focusable element.
*/
get _focusableElement() {
return this.inputElement;
},
attached: function() {
this._updateAriaLabelledBy();
},
@ -367,6 +385,29 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return this.inputElement.validate();
},
/**
* Forward focus to inputElement
*/
_onFocus: function() {
if (!this._shiftTabPressed) {
this._focusableElement.focus();
}
},
/**
* Handler that is called when a shift+tab keypress is detected by the menu.
*
* @param {CustomEvent} event A key combination event.
*/
_onShiftTabDown: function(event) {
var oldTabIndex = this.getAttribute('tabindex');
this._shiftTabPressed = true;
this.setAttribute('tabindex', '-1');
this.async(function() {
this.setAttribute('tabindex', oldTabIndex);
this._shiftTabPressed = false;
}, 1);
},
/**
* If `autoValidate` is true, then validates the element.
*/
@ -451,6 +492,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
};
/** @polymerBehavior */
Polymer.PaperInputBehavior = [Polymer.IronControlState, Polymer.PaperInputBehaviorImpl];
Polymer.PaperInputBehavior = [
Polymer.IronControlState,
Polymer.IronA11yKeysBehavior,
Polymer.PaperInputBehaviorImpl
];
</script>

View file

@ -83,7 +83,7 @@ Custom property | Description | Default
`--paper-input-container-label-focus` | Mixin applied to the label when the input is focused | `{}`
`--paper-input-container-input` | Mixin applied to the input | `{}`
`--paper-input-container-underline` | Mixin applied to the underline | `{}`
`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focued | `{}`
`--paper-input-container-underline-focus` | Mixin applied to the underline when the input is focused | `{}`
`--paper-input-container-underline-disabled` | Mixin applied to the underline when the input is disabled | `{}`
`--paper-input-prefix` | Mixin applied to the input prefix | `{}`
`--paper-input-suffix` | Mixin applied to the input suffix | `{}`
@ -179,7 +179,7 @@ This element is `display:block` by default, but you can set the `inline` attribu
.input-content {
position: relative;
@apply(--layout-horizontal);
@apply(--layout-end);
@apply(--layout-center);
}
.input-content ::content label,

View file

@ -48,11 +48,17 @@ as to not overlap the native UI (search icon, file button, etc.).
See `Polymer.PaperInputBehavior` for more API docs.
### Focus
To focus a paper-input, you can call the native `focus()` method as long as the
paper input has a tab index.
### Styling
See `Polymer.PaperInputContainer` for a list of custom properties used to
style this element.
@group Paper Elements
@element paper-input
@hero hero.svg
@ -118,6 +124,7 @@ style this element.
autocapitalize$="[[autocapitalize]]"
autocorrect$="[[autocorrect]]"
on-change="_onChange"
tabindex$="[[tabindex]]"
autosave$="[[autosave]]"
results$="[[results]]"
accept$="[[accept]]"
@ -144,8 +151,7 @@ style this element.
behaviors: [
Polymer.IronFormElementBehavior,
Polymer.PaperInputBehavior,
Polymer.IronControlState
Polymer.PaperInputBehavior
]
});
</script>

View file

@ -123,6 +123,10 @@ style this element.
_ariaDescribedByChanged: function(ariaDescribedBy) {
this.$.input.textarea.setAttribute('aria-describedby', ariaDescribedBy);
}
},
get _focusableElement() {
return this.$.input.textarea;
},
});
</script>

View file

@ -1,14 +1,11 @@
<!doctype html>
<!--
<!DOCTYPE html><!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
--><html><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>paper-input tests</title>
@ -21,8 +18,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
'paper-textarea.html',
'paper-input-container.html',
'paper-input-error.html',
'paper-input-char-counter.html'
'paper-input-char-counter.html',
'paper-input.html?dom=shadow',
'paper-textarea.html?dom=shadow',
'paper-input-container.html?dom=shadow',
'paper-input-error.html?dom=shadow',
'paper-input-char-counter.html?dom=shadow'
]);
</script>
</body>
</html>
</body></html>

View file

@ -37,6 +37,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template>
</test-fixture>
<test-fixture id="has-tabindex">
<template>
<paper-input tabindex="0"></paper-input>
</template>
</test-fixture>
<test-fixture id="label">
<template>
<paper-input label="foo"></paper-input>
@ -211,7 +217,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
});
suite('validation', function() {
test('invalid attribute updated after calling validate()', function() {
@ -255,6 +260,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.equal(input.inputElement.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id + ' ' + Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the error message and character counter');
});
test('focus an input with tabindex', function(done) {
var input = fixture('has-tabindex');
flush(function() {
MockInteractions.focus(input);
flush(function() {
assert.equal(input.shadowRoot ? input.shadowRoot.activeElement :
document.activeElement, input._focusableElement);
done();
});
});
});
});
</script>

View file

@ -36,6 +36,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template>
</test-fixture>
<test-fixture id="has-tabindex">
<template>
<paper-textarea tabindex="0"></paper-input>
</template>
</test-fixture>
<test-fixture id="label">
<template>
<paper-textarea label="foo"></paper-textarea>
@ -128,7 +134,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var inputContent = Polymer.dom(container.root).querySelector('.input-content');
assert.isTrue(inputContent.classList.contains('label-is-floating'), 'label is floating');
});
});
suite('focus/blur events', function() {
@ -155,6 +160,17 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.isTrue(nBlurEvents >= 1, 'blur event fired')
});
test('focus a textarea with tabindex', function(done) {
var input = fixture('has-tabindex');
flush(function() {
assert.notEqual(document.activeElement, input._focusableElement);
MockInteractions.focus(input);
setTimeout(function() {
assert.equal(document.activeElement, input.shadowRoot ? input : input._focusableElement);
done();
}, 1);
})
});
});
suite('a11y', function() {
@ -186,7 +202,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.equal(input.inputElement.textarea.getAttribute('aria-describedby'), Polymer.dom(input.root).querySelector('paper-input-error').id + ' ' + Polymer.dom(input.root).querySelector('paper-input-char-counter').id, 'aria-describedby points to the error message and character counter');
});
});

View file

@ -32,14 +32,14 @@
"iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0"
},
"ignore": [],
"homepage": "https://github.com/PolymerElements/paper-ripple",
"homepage": "https://github.com/polymerelements/paper-ripple",
"_release": "1.0.5",
"_resolution": {
"type": "version",
"tag": "v1.0.5",
"commit": "d72e7a9a8ab518b901ed18dde492df3b87a93be5"
},
"_source": "git://github.com/PolymerElements/paper-ripple.git",
"_source": "git://github.com/polymerelements/paper-ripple.git",
"_target": "^1.0.0",
"_originalSource": "PolymerElements/paper-ripple"
"_originalSource": "polymerelements/paper-ripple"
}

View file

@ -0,0 +1,46 @@
define(function () {
var cssAPI = {};
cssAPI.normalize = function (name, normalize) {
if (name.substr(name.length - 5, 5) == '.html')
name = name.substr(0, name.length - 5);
return normalize(name);
}
var importedFiles = [];
cssAPI.load = function (cssId, req, load, config) {
// Somehow if the url starts with /css, require will get all screwed up since this extension is also called css
cssId = cssId.replace('js/requirehtml', 'html');
var url = cssId + '.html';
if (url.indexOf('http') != 0 && url.indexOf('file:') != 0) {
url = config.baseUrl + url;
}
if (importedFiles.indexOf(url) == -1) {
importedFiles.push(url);
var link = document.createElement('link');
link.rel = 'import';
if (url.toLowerCase().indexOf('bower_') == -1) {
url = url + "?" + config.urlArgs;
}
link.onload = load;
link.href = url;
document.head.appendChild(link);
return;
}
load();
}
return cssAPI;
});

View file

@ -1,63 +0,0 @@
(function () {
function show(options) {
// items
// positionTo
// showCancel
// title
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
var renderIcon = options.items.filter(function (o) {
return o.ironIcon == 'check';
}).length;
if (renderIcon) {
for (var i = 0, length = options.items.length; i < length; i++) {
var option = options.items[i];
switch (option.ironIcon) {
case 'check':
option.name = '• ' + option.name;
break;
default:
option.name = ' ' + option.name;
break;
}
}
}
var innerOptions = {
'title': options.title,
'buttonLabels': options.items.map(function (i) {
return i.name;
})
};
// Show cancel unless the caller explicitly set it to false
if (options.showCancel !== false) {
innerOptions.addCancelButtonWithLabel = Globalize.translate('ButtonCancel');
}
// Depending on the buttonIndex, you can now call shareViaFacebook or shareViaTwitter
// of the SocialSharing plugin (https://github.com/EddyVerbruggen/SocialSharing-PhoneGap-Plugin)
window.plugins.actionsheet.show(innerOptions, function (index) {
if (options.callback) {
// Results are 1-based
if (index >= 1 && options.items.length >= index) {
options.callback(options.items[index - 1].id);
}
}
});
}
window.ActionSheetElement = {
show: show
};
})();

View file

@ -1,95 +0,0 @@
(function () {
function updateCredentials() {
Logger.log('sending updated credentials to ApiClientBridge');
var json = JSON.stringify(ConnectionManager.credentialProvider().credentials());
var credentials = JSON.parse(json);
for (var i = 0, length = credentials.Servers.length; i < length; i++) {
var server = credentials.Servers[i];
if (server.DateLastAccessed != null) {
server.DateLastAccessed = new Date(server.DateLastAccessed).toISOString();
}
}
json = JSON.stringify(credentials);
ApiClientBridge.updateCredentials(json);
}
function initNativeConnectionManager() {
Logger.log('initNativeConnectionManager');
var capabilities = ConnectionManager.capabilities();
ApiClientBridge.init(AppInfo.appName, AppInfo.appVersion, AppInfo.deviceId, AppInfo.deviceName, JSON.stringify(capabilities));
}
function getDownloadSpeed(bytes, url) {
var deferred = DeferredBuilder.Deferred();
ApiClientBridge.getDownloadSpeed(bytes, url);
Events.on(AndroidAjax, 'downloadspeedresponse', function (e, response) {
Events.off(AndroidAjax, 'downloadspeedresponse');
if (response) {
deferred.resolveWith(null, [response]);
}
else {
// Need to mimic the jquery ajax error response
deferred.reject();
}
});
return deferred.promise();
}
function initApiClient(newApiClient) {
newApiClient.getDownloadSpeed = function (bytes) {
return getDownloadSpeed(bytes, newApiClient.getUrl('Playback/BitrateTest', {
api_key: newApiClient.accessToken(),
Size: bytes
}));
};
}
Events.on(ConnectionManager, 'apiclientcreated', function (e, newApiClient) {
initApiClient(newApiClient);
});
Events.on(ConnectionManager.credentialProvider(), 'credentialsupdated', updateCredentials);
updateCredentials();
initNativeConnectionManager();
if (window.ApiClient) {
initApiClient(window.ApiClient);
}
window.AndroidAjax = {
onResponse: function (id, response) {
Events.trigger(AndroidAjax, 'response' + id, [true, response]);
},
onError: function (id, response) {
Events.trigger(AndroidAjax, 'response' + id, [false, response]);
},
onDownloadSpeedResponse: function (response) {
Events.trigger(AndroidAjax, 'downloadspeedresponse', [response]);
}
};
})();

View file

@ -1,70 +0,0 @@
(function (globalScope, localStorage, sessionStorage) {
function myStore(defaultObject) {
var self = this;
self.localData = {};
var isDefaultAvailable;
if (defaultObject) {
try {
defaultObject.setItem('_test', '0');
isDefaultAvailable = true;
} catch (e) {
}
}
self.setItem = function (name, value) {
if (isDefaultAvailable) {
defaultObject.setItem(name, value);
} else {
self.localData[name] = value;
}
};
self.getItem = function (name) {
if (isDefaultAvailable) {
return defaultObject.getItem(name);
}
return self.localData[name];
};
self.removeItem = function (name) {
if (isDefaultAvailable) {
defaultObject.removeItem(name);
} else {
self.localData[name] = null;
}
};
}
function preferencesStore() {
var self = this;
self.setItem = function (name, value) {
AndroidSharedPreferences.set(name, value);
};
self.getItem = function (name) {
return AndroidSharedPreferences.get(name);
};
self.removeItem = function (name) {
AndroidSharedPreferences.remove(name);
};
}
globalScope.appStorage = new preferencesStore();
globalScope.sessionStore = new myStore(sessionStorage);
})(window, window.localStorage, window.sessionStorage);

View file

@ -1,195 +0,0 @@
(function () {
var updatedProducts = [];
function updateProductInfo(id, owned, price) {
updatedProducts = updatedProducts.filter(function (r) {
return r.id != id;
});
var product = {
id: id,
owned: owned,
price: price
};
updatedProducts.push(product);
Events.trigger(IapManager, 'productupdated', [product]);
}
function getProduct(feature) {
var id;
if (feature == 'embypremieremonthly') {
id = NativeIapManager.getPremiereMonthlySku();
} else {
id = NativeIapManager.getUnlockProductSku();
}
var products = updatedProducts.filter(function (r) {
return r.id == id;
});
return products.length ? products[0] : null;
}
var storeReady = false;
function onStoreReady() {
storeReady = true;
refreshPurchases();
}
function isPurchaseAvailable() {
return storeReady;
}
function beginPurchase(feature, email) {
if (feature == 'embypremieremonthly') {
return MainActivity.purchasePremiereMonthly(email);
}
return MainActivity.purchaseUnlock();
}
function onPurchaseComplete(result) {
if (result === true) {
refreshPurchases();
}
else if (result) {
ApiClient.ajax({
type: "POST",
url: ApiClient.getUrl("Appstore/Register"),
data: {
Parameters: JSON.stringify(result)
}
}).then(function () {
refreshPurchases();
}, function (e) {
refreshPurchases();
});
}
}
function refreshPurchases() {
NativeIapManager.getPurchaseInfos("window.IapManager.updateProduct");
}
function getSubscriptionOptions() {
var deferred = DeferredBuilder.Deferred();
var options = [];
options.push({
feature: 'embypremieremonthly',
buttonText: 'EmbyPremiereMonthly'
});
options = options.filter(function (o) {
return getProduct(o.feature) != null;
}).map(function (o) {
var prod = getProduct(o.feature);
o.buttonText = Globalize.translate(o.buttonText, prod.price);
o.owned = prod.owned;
return o;
});
deferred.resolveWith(null, [options]);
return deferred.promise();
}
function isUnlockedOverride(feature) {
if (feature == 'playback') {
return isPlaybackUnlockedViaOldApp();
} else {
return new Promise(function (resolve, reject) {
resolve(false);
});
}
}
function isPlaybackUnlockedViaOldApp() {
return testDeviceId(ConnectionManager.deviceId()).then(function (isUnlocked) {
if (isUnlocked) {
return true;
}
return testDeviceId(device.uuid).then(function (isUnlocked) {
if (isUnlocked) {
return true;
}
var legacyDeviceId = MainActivity.getLegacyDeviceId();
if (legacyDeviceId) {
return testDeviceId(legacyDeviceId);
}
return false;
});
});
}
function testDeviceId(deviceId) {
var cacheKey = 'oldapp-' + deviceId;
var cacheValue = appStorage.getItem(cacheKey);
if (cacheValue) {
return new Promise(function (resolve, reject) {
resolve(cacheValue == 'true');
});
} else {
return fetch('https://mb3admin.com/admin/service/statistics/appAccess?application=AndroidV1&deviceId=' + deviceId, {
method: 'GET'
}).then(function (response) {
if (response.status == 404) {
appStorage.setItem(cacheKey, 'false');
} else if (response.status < 400) {
appStorage.setItem(cacheKey, 'true');
return true;
}
return false;
}, function (e) {
return false;
});
}
}
window.IapManager = {
isPurchaseAvailable: isPurchaseAvailable,
getProductInfo: getProduct,
updateProduct: updateProductInfo,
beginPurchase: beginPurchase,
onPurchaseComplete: onPurchaseComplete,
getSubscriptionOptions: getSubscriptionOptions,
onStoreReady: onStoreReady,
isUnlockedOverride: isUnlockedOverride
};
NativeIapManager.initStore();
})();

View file

@ -1,18 +0,0 @@
(function () {
window.LocalSync = {
isSupported: function () {
return true;
},
sync: function () {
AndroidSync.startSync();
},
getSyncStatus: function () {
return AndroidSync.getSyncStatus();
}
};
})();

View file

@ -1,6 +0,0 @@
window.Logger = {
log: function (str) {
LoggingBridge.log(str);
}
};

View file

@ -1,183 +0,0 @@
(function () {
// Reports media playback to the device for lock screen control
var currentPlayer;
var lastUpdateTime = 0;
function allowLocalPlayer() {
return false;
}
function updatePlayerState(state, eventName) {
if (!state.NowPlayingItem) {
hideMediaControls();
return;
}
var isLocalPlayer = MediaController.getPlayerInfo().isLocalPlayer || false;
// Local players do their own notifications
if (isLocalPlayer && !allowLocalPlayer()) {
return;
}
// dummy this up
if (eventName == 'init') {
eventName = 'positionchange';
}
var playState = state.PlayState || {};
var nameHtml = MediaController.getNowPlayingNameHtml(state.NowPlayingItem) || '';
var parts = nameHtml.split('<br/>');
var artist = parts.length == 1 ? '' : parts[0];
var title = parts[parts.length - 1];
// Switch these two around for video
if (state.NowPlayingItem.MediaType == 'Video' && parts.length > 1) {
var temp = artist;
artist = title;
title = temp;
}
var album = state.NowPlayingItem.Album || '';
var itemId = state.NowPlayingItem.Id;
// Convert to ms
var duration = state.NowPlayingItem.RunTimeTicks ? (state.NowPlayingItem.RunTimeTicks / 10000) : 0;
var position = playState.PositionTicks ? (playState.PositionTicks / 10000) : 0;
var isPaused = playState.IsPaused || false;
var canSeek = playState.CanSeek || false;
var url = '';
var imgHeight = 400;
var nowPlayingItem = state.NowPlayingItem;
if (nowPlayingItem.PrimaryImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.PrimaryImageItemId, {
type: "Primary",
height: imgHeight,
tag: nowPlayingItem.PrimaryImageTag
});
} else if (nowPlayingItem.ThumbImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.ThumbImageItemId, {
type: "Thumb",
height: imgHeight,
tag: nowPlayingItem.ThumbImageTag
});
}
else if (nowPlayingItem.BackdropImageTag) {
url = ApiClient.getScaledImageUrl(nowPlayingItem.BackdropItemId, {
type: "Backdrop",
height: imgHeight,
tag: nowPlayingItem.BackdropImageTag,
index: 0
});
}
// Don't go crazy reporting position changes
if (eventName == 'positionchange') {
if (lastUpdateTime) {
// Only report if this item hasn't been reported yet, or if there's an actual playback change.
// Don't report on simple time updates
return;
}
}
MainActivity.updateMediaSession(eventName, isLocalPlayer, itemId, title, artist, album, parseInt(duration), parseInt(position), url, canSeek, isPaused);
lastUpdateTime = new Date().getTime();
}
function onStateChanged(e, state) {
updatePlayerState(state, e.type);
}
function onPlaybackStart(e, state) {
Logger.log('nowplaying event: ' + e.type);
var player = this;
player.beginPlayerUpdates();
onStateChanged.call(player, e, state);
}
function onPlaybackStopped(e, state) {
Logger.log('nowplaying event: ' + e.type);
var player = this;
player.endPlayerUpdates();
hideMediaControls();
}
function releaseCurrentPlayer() {
if (currentPlayer) {
$(currentPlayer).off('playbackstart', onPlaybackStart)
.off('playbackstop', onPlaybackStopped)
.off('playstatechange', onStateChanged)
.off('positionchange', onStateChanged);
currentPlayer.endPlayerUpdates();
currentPlayer = null;
hideMediaControls();
}
}
function hideMediaControls() {
MainActivity.hideMediaSession();
lastUpdateTime = 0;
}
function bindToPlayer(player) {
releaseCurrentPlayer();
if (player.isLocalPlayer && !allowLocalPlayer()) {
return;
}
currentPlayer = player;
Logger.log('binding remotecontrols to ' + player.name);
player.getPlayerState().then(function (state) {
if (state.NowPlayingItem) {
player.beginPlayerUpdates();
}
onStateChanged.call(player, { type: 'init' }, state);
});
$(player).on('playbackstart', onPlaybackStart)
.on('playbackstop', onPlaybackStopped)
.on('playstatechange', onStateChanged)
.on('positionchange', onStateChanged);
}
Logger.log('binding remotecontrols to MediaController');
$(MediaController).on('playerchange', function () {
bindToPlayer(MediaController.getCurrentPlayer());
});
bindToPlayer(MediaController.getCurrentPlayer());
})();

View file

@ -1,31 +0,0 @@
(function () {
var currentDeferred;
function chooseDirectory() {
var deferred = DeferredBuilder.Deferred();
AndroidDirectoryChooser.chooseDirectory();
currentDeferred = deferred;
return deferred.promise();
}
function onChosen(path) {
var deferred = currentDeferred;
if (deferred) {
if (path) {
deferred.resolveWith(null, [path]);
} else {
deferred.reject();
}
currentDeferred = null;
}
}
window.NativeDirectoryChooser = {
chooseDirectory: chooseDirectory,
onChosen: onChosen
};
})();

View file

@ -1,291 +0,0 @@
(function () {
function vlcRenderer(options) {
var self = this;
self.enableProgressReporting = options.type == 'audio';
function onEnded() {
Events.trigger(self, 'ended');
}
function onTimeUpdate() {
Events.trigger(self, 'timeupdate');
}
function onVolumeChange() {
Events.trigger(self, 'volumechange');
}
function onPlaying() {
Events.trigger(self, 'playing');
}
function onPlay() {
Events.trigger(self, 'play');
}
function onPause() {
Events.trigger(self, 'pause');
}
function onClick() {
Events.trigger(self, 'click');
}
function onDblClick() {
Events.trigger(self, 'dblclick');
}
function onError() {
var errorCode = this.error ? this.error.code : '';
Logger.log('Media element error code: ' + errorCode);
Events.trigger(self, 'error');
}
var playerState = {};
self.currentTime = function (val) {
if (val != null) {
AndroidVlcPlayer.sendVlcCommand("setposition", val.toString());
return;
}
return playerState.currentTime;
};
self.duration = function (val) {
if (playerState) {
return playerState.duration;
}
return null;
};
self.stop = function () {
AndroidVlcPlayer.sendVlcCommand("stop", null);
};
self.pause = function () {
AndroidVlcPlayer.sendVlcCommand("pause", null);
};
self.unpause = function () {
AndroidVlcPlayer.sendVlcCommand("unpause", null);
};
self.volume = function (val) {
if (playerState) {
if (val != null) {
AndroidVlcPlayer.sendVlcCommand("setvolume", (val * 100).toString());
return;
}
return playerState.volume;
}
};
function getPlaybackStartInfoForVideoActivity(streamInfo, mediaSource, item) {
var playbackStartInfo = {
QueueableMediaTypes: item.MediaType,
ItemId: item.Id,
NowPlayingItem: {},
MediaSourceId: mediaSource.Id
};
if (mediaSource.RunTimeTicks) {
playbackStartInfo.NowPlayingItem.RunTimeTicks = mediaSource.RunTimeTicks;
}
var videoUrl = streamInfo.url;
var audioStreamIndex = getParameterByName('AudioStreamIndex', videoUrl);
if (audioStreamIndex) {
playbackStartInfo.AudioStreamIndex = parseInt(audioStreamIndex);
}
// TODO: This should be passed in rather than going out to get it
if (MediaPlayer.currentSubtitleStreamIndex != null) {
playbackStartInfo.SubtitleStreamIndex = MediaPlayer.currentSubtitleStreamIndex;
}
playbackStartInfo.PlayMethod = streamInfo.playMethod;
playbackStartInfo.LiveStreamId = mediaSource.LiveStreamId;
playbackStartInfo.PlaySessionId = getParameterByName('PlaySessionId', videoUrl);
// Seeing some deserialization errors around this property
if (mediaSource.RunTimeTicks && mediaSource.RunTimeTicks > 0) {
playbackStartInfo.CanSeek = true;
}
return playbackStartInfo;
}
self.setCurrentSrc = function (streamInfo, item, mediaSource, tracks) {
if (!streamInfo) {
self.destroy();
return;
}
var val = streamInfo.url;
var tIndex = val.indexOf('#t=');
var startPosMs = 0;
if (tIndex != -1) {
startPosMs = val.substring(tIndex + 3);
startPosMs = parseFloat(startPosMs) * 1000;
val = val.split('#')[0];
}
if (options.type == 'audio') {
AndroidVlcPlayer.playAudioVlc(val, JSON.stringify(item), JSON.stringify(mediaSource), options.poster);
} else {
var playbackStartInfo = getPlaybackStartInfoForVideoActivity(streamInfo, mediaSource, item);
var serverUrl = ApiClient.serverAddress();
var videoStream = mediaSource.MediaStreams.filter(function (stream) {
return stream.Type == "Video";
})[0];
var videoWidth = videoStream ? videoStream.Width : null;
var videoHeight = videoStream ? videoStream.Height : null;
var videoQualityOptions = MediaPlayer.getVideoQualityOptions(videoWidth, videoHeight).map(function (o) {
return {
Name: o.name,
Value: o.bitrate + "-" + o.maxHeight
};
});
var deviceProfile = MediaPlayer.getDeviceProfile();
var timeLimitMs = MediaController.playbackTimeLimitMs || 0;
AndroidVlcPlayer.playVideoVlc(val,
startPosMs,
item.Name,
JSON.stringify(item),
JSON.stringify(mediaSource),
JSON.stringify(playbackStartInfo),
ApiClient.serverInfo().Id,
serverUrl,
ApiClient.appName(),
ApiClient.appVersion(),
ApiClient.deviceId(),
ApiClient.deviceName(),
ApiClient.getCurrentUserId(),
ApiClient.accessToken(),
JSON.stringify(deviceProfile),
JSON.stringify(videoQualityOptions),
timeLimitMs);
playerState.currentSrc = val;
self.report('playing', null, startPosMs, false, 100);
}
};
self.currentSrc = function () {
if (playerState) {
return playerState.currentSrc;
}
};
self.paused = function () {
if (playerState) {
return playerState.paused;
}
return false;
};
self.cleanup = function (destroyRenderer) {
if (destroyRenderer !== false) {
AndroidVlcPlayer.destroyVlc();
}
playerState = {};
};
self.enableCustomVideoControls = function () {
return false;
};
self.report = function (eventName, duration, position, isPaused, volume) {
var state = playerState;
state.duration = duration;
state.currentTime = position;
state.paused = isPaused;
state.volume = (volume || 0) / 100;
if (eventName == 'playbackstop') {
onEnded();
}
else if (eventName == 'volumechange') {
onVolumeChange();
}
else if (eventName == 'positionchange') {
onTimeUpdate();
}
else if (eventName == 'paused') {
onPause();
}
else if (eventName == 'unpaused') {
onPlaying();
}
else if (eventName == 'playing') {
onPlaying();
}
};
self.init = function () {
var deferred = DeferredBuilder.Deferred();
deferred.resolve();
return deferred.promise();
};
self.onActivityClosed = function (wasStopped, hasError, endPositionMs) {
playerState.currentTime = endPositionMs;
if (wasStopped) {
MediaPlayer.stop(false);
}
self.report('playbackstop', playerState.duration, endPositionMs, false, 100);
};
window.AudioRenderer.Current = self;
window.VideoRenderer.Current = self;
}
window.AudioRenderer = function (options) {
options = options || {};
options.type = 'audio';
return new vlcRenderer(options);
};
window.VideoRenderer = function (options) {
options = options || {};
options.type = 'video';
return new vlcRenderer(options);
};
})();

View file

@ -1,24 +0,0 @@
(function () {
Dashboard.exit = function () {
if (navigator.app && navigator.app.exitApp) {
navigator.app.exitApp();
} else {
Dashboard.logout();
}
};
function onBackKeyDown(e) {
if (Dashboard.exitOnBack()) {
e.preventDefault();
Dashboard.exit();
}
else {
history.back();
}
}
document.addEventListener("backbutton", onBackKeyDown, false);
})();

View file

@ -1,709 +0,0 @@
(function () {
function chromecastPlayer() {
var self = this;
var PlayerName = "Chromecast";
var ApplicationID = "2D4B1DA3";
var currentWebAppSession;
var currentDevice;
var currentDeviceId;
// MediaController needs this
self.name = PlayerName;
self.getItemsForPlayback = function (query) {
var userId = Dashboard.getCurrentUserId();
if (query.Ids && query.Ids.split(',').length == 1) {
return new Promise(function (resolve, reject) {
ApiClient.getItem(userId, query.Ids.split(',')).then(function (item) {
resolve({
Items: [item],
TotalRecordCount: 1
});
});
});
}
else {
query.Limit = query.Limit || 100;
query.ExcludeLocationTypes = "Virtual";
return ApiClient.getItems(userId, query);
}
};
var castPlayer = {};
$(castPlayer).on("playbackstart", function (e, data) {
Logger.log('cc: playbackstart');
var state = self.getPlayerStateInternal(data);
$(self).trigger("playbackstart", [state]);
});
$(castPlayer).on("playbackstop", function (e, data) {
Logger.log('cc: playbackstop');
var state = self.getPlayerStateInternal(data);
$(self).trigger("playbackstop", [state]);
// Reset this so the next query doesn't make it appear like content is playing.
self.lastPlayerData = {};
});
$(castPlayer).on("playbackprogress", function (e, data) {
Logger.log('cc: positionchange');
var state = self.getPlayerStateInternal(data);
$(self).trigger("positionchange", [state]);
});
var endpointInfo;
function getEndpointInfo() {
if (endpointInfo) {
var deferred = $.Deferred();
deferred.resolveWith(null, [endpointInfo]);
return deferred.promise();
}
return ApiClient.getJSON(ApiClient.getUrl('System/Endpoint')).then(function (info) {
endpointInfo = info;
});
}
function sendMessageToDevice(message) {
var bitrateSetting = AppSettings.maxChromecastBitrate();
message = $.extend(message, {
userId: Dashboard.getCurrentUserId(),
deviceId: ApiClient.deviceId(),
accessToken: ApiClient.accessToken(),
serverAddress: ApiClient.serverAddress(),
maxBitrate: bitrateSetting,
receiverName: currentDevice.getFriendlyName(),
supportsAc3: AppSettings.enableChromecastAc3()
});
getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
ApiClient.getPublicSystemInfo().then(function (info) {
message.serverAddress = info.LocalAddress;
sendMessageInternal(message);
});
} else {
sendMessageInternal(message);
}
});
}
function sendMessageInternal(message) {
currentWebAppSession.sendText(JSON.stringify(message));
}
self.play = function (options) {
Dashboard.getCurrentUser().then(function (user) {
if (options.items) {
self.playWithCommand(options, 'PlayNow');
} else {
self.getItemsForPlayback({
Ids: options.ids.join(',')
}).then(function (result) {
options.items = result.Items;
self.playWithCommand(options, 'PlayNow');
});
}
});
};
self.playWithCommand = function (options, command) {
if (!options.items) {
ApiClient.getItem(Dashboard.getCurrentUserId(), options.ids[0]).then(function (item) {
options.items = [item];
self.playWithCommand(options, command);
});
return;
}
// Convert the items to smaller stubs to send the minimal amount of information
options.items = options.items.map(function (i) {
return {
Id: i.Id,
Name: i.Name,
Type: i.Type,
MediaType: i.MediaType,
IsFolder: i.IsFolder
};
});
sendMessageToDevice({
options: options,
command: command
});
};
self.unpause = function () {
sendMessageToDevice({
command: 'Unpause'
});
};
self.pause = function () {
sendMessageToDevice({
command: 'Pause'
});
};
self.shuffle = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'Shuffle');
});
};
self.instantMix = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'InstantMix');
});
};
self.canQueueMediaType = function (mediaType) {
return mediaType == "Audio";
};
self.queue = function (options) {
self.playWithCommnd(options, 'PlayLast');
};
self.queueNext = function (options) {
self.playWithCommand(options, 'PlayNext');
};
self.stop = function () {
sendMessageToDevice({
command: 'Stop'
});
};
self.displayContent = function (options) {
sendMessageToDevice({
options: options,
command: 'DisplayContent'
});
};
self.mute = function () {
sendMessageToDevice({
command: 'Mute'
});
};
self.unMute = function () {
self.setVolume(getCurrentVolume() + 2);
};
self.toggleMute = function () {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
if (state.IsMuted) {
self.unMute();
} else {
self.mute();
}
};
function getBaseTargetInfo() {
var target = {};
target.playerName = PlayerName;
target.playableMediaTypes = ["Audio", "Video"];
target.isLocalPlayer = false;
target.appName = PlayerName;
target.supportedCommands = [
"VolumeUp",
"VolumeDown",
"Mute",
"Unmute",
"ToggleMute",
"SetVolume",
"SetAudioStreamIndex",
"SetSubtitleStreamIndex",
"DisplayContent",
"SetRepeatMode",
"EndSession"
];
return target;
}
function convertDeviceToTarget(device) {
var target = getBaseTargetInfo();
target.name = target.deviceName = device.getFriendlyName();
target.id = device.getId();
return target;
}
function isChromecastName(name) {
name = (name || '').toLowerCase();
var validTokens = ['nexusplayer'];
validTokens.push('chromecast');
validTokens.push('eurekadongle');
return validTokens.filter(function (t) {
return name.replace(' ', '').indexOf(t) != -1;
}).length > 0;
}
self.getTargets = function () {
return ConnectSDKHelper.getDeviceList().filter(function (d) {
return d.hasService('Chromecast') || d.hasService('ChromeCast') || isChromecastName(d.getModelName()) || isChromecastName(d.getFriendlyName());
}).map(convertDeviceToTarget);
};
self.seek = function (position) {
position = parseInt(position);
position = position / 10000000;
sendMessageToDevice({
options: {
position: position
},
command: 'Seek'
});
};
self.setAudioStreamIndex = function (index) {
sendMessageToDevice({
options: {
index: index
},
command: 'SetAudioStreamIndex'
});
};
self.setSubtitleStreamIndex = function (index) {
sendMessageToDevice({
options: {
index: index
},
command: 'SetSubtitleStreamIndex'
});
};
self.nextTrack = function () {
sendMessageToDevice({
options: {},
command: 'NextTrack'
});
};
self.previousTrack = function () {
sendMessageToDevice({
options: {},
command: 'PreviousTrack'
});
};
self.beginPlayerUpdates = function () {
// Setup polling here
};
self.endPlayerUpdates = function () {
// Stop polling here
};
function getCurrentVolume() {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
return state.VolumeLevel == null ? 100 : state.VolumeLevel;
}
self.volumeDown = function () {
sendMessageToDevice({
options: {},
command: 'VolumeDown'
});
};
self.setRepeatMode = function (mode) {
sendMessageToDevice({
options: {
RepeatMode: mode
},
command: 'SetRepeatMode'
});
};
self.volumeUp = function () {
sendMessageToDevice({
options: {},
command: 'VolumeUp'
});
};
self.setVolume = function (vol) {
vol = Math.min(vol, 100);
vol = Math.max(vol, 0);
sendMessageToDevice({
options: {
volume: vol
},
command: 'SetVolume'
});
};
self.getPlayerState = function () {
var deferred = $.Deferred();
var result = self.getPlayerStateInternal();
deferred.resolveWith(null, [result]);
return deferred.promise();
};
self.lastPlayerData = {};
self.getPlayerStateInternal = function (data) {
data = data || self.lastPlayerData;
self.lastPlayerData = data;
Logger.log(JSON.stringify(data));
return data;
};
function onMessage(message) {
if (message.type == 'playbackerror') {
var errorCode = message.data;
setTimeout(function () {
Dashboard.alert({
message: Globalize.translate('MessagePlaybackError' + errorCode),
title: Globalize.translate('HeaderPlaybackError')
});
}, 300);
}
else if (message.type == 'connectionerror') {
setTimeout(function () {
Dashboard.alert({
message: Globalize.translate('MessageChromecastConnectionError'),
title: Globalize.translate('HeaderError')
});
}, 300);
}
else if (message.type && message.type.indexOf('playback') == 0) {
$(castPlayer).trigger(message.type, [message.data]);
}
}
function handleMessage(message) {
// message could be either a string or an object
if (typeof message === 'string') {
onMessage(JSON.parse(message));
} else {
onMessage(message);
}
}
function handleSessionDisconnect() {
Logger.log("session disconnected");
// We can't trust this because we might receive events of other devices disconnecting
//cleanupSession();
//MediaController.removeActivePlayer(PlayerName);
}
function onWebAppSessionConnect(webAppSession, device) {
currentWebAppSession = webAppSession;
Logger.log('session.connect succeeded');
webAppSession.setWebAppSessionListener();
currentDevice = device;
currentDeviceId = device.getId();
self.lastPlayerData = {};
MediaController.setActivePlayer(PlayerName, convertDeviceToTarget(device));
sendIdentifyMessage();
}
function setupWebAppSession(device, session, connectToSession) {
// hold on to a reference
var currentSession = session.acquire();
currentSession.on('message', handleMessage);
currentSession.on('disconnect', handleSessionDisconnect);
if (connectToSession || browserInfo.safari) {
currentSession.connect().success(function () {
onWebAppSessionConnect(currentSession, device);
}).error(handleSessionError);
} else {
onWebAppSessionConnect(currentSession, device);
}
}
function sendIdentifyMessage() {
sendMessageToDevice({
options: {},
command: 'Identify'
});
}
function handleSessionError() {
Logger.log('chromecast session connect error');
cleanupSession();
}
function cleanupSession() {
var session = currentWebAppSession;
if (session) {
// Clean up listeners
session.off("message");
session.off("disconnect");
// Release session to free up memory
session.disconnect();
session.release();
}
self.lastPlayerData = {};
currentWebAppSession = null;
}
function tryLaunchWebSession(device) {
Logger.log('calling launchWebApp');
device.getWebAppLauncher().launchWebApp(ApplicationID).success(function (session) {
Logger.log('launchWebApp success. calling onSessionConnected');
setupWebAppSession(device, session, true);
}).error(function (err1) {
Logger.log('launchWebApp error:' + JSON.stringify(err1));
});
}
function tryJoinWebSession(device, enableRetry, enableLaunch) {
// First try to join existing session. If it fails, launch a new one
Logger.log('calling joinWebApp');
device.getWebAppLauncher().joinWebApp(ApplicationID).success(function (session) {
Logger.log('joinWebApp success. calling onSessionConnected');
setupWebAppSession(device, session, false);
}).error(function (err) {
Logger.log('joinWebApp error: ' + JSON.stringify(err));
if (enableRetry) {
tryJoinWebSession(device, false, true);
return;
}
if (enableLaunch) {
Logger.log('calling launchWebApp');
tryLaunchWebSession(device);
}
});
}
function launchWebApp(device) {
if (currentWebAppSession) {
cleanupSession();
}
tryJoinWebSession(device, true, true);
}
function onDeviceReady(device) {
device.off("ready");
Logger.log('creating webAppSession');
self.lastPlayerData = {};
launchWebApp(device);
}
self.tryPair = function (target) {
var deferred = $.Deferred();
var device = ConnectSDKHelper.getDeviceList().filter(function (d) {
return d.getId() == target.id;
})[0];
if (device) {
self.tryPairWithDevice(device, deferred);
} else {
deferred.reject();
}
return deferred.promise();
};
self.tryPairWithDevice = function (device, deferred) {
Logger.log('Will attempt to connect to Chromecast');
device.on("disconnect", function () {
device.off("ready");
device.off("disconnect");
});
if (device.isReady()) {
Logger.log('Device is already ready, calling onDeviceReady');
onDeviceReady(device);
} else {
Logger.log('Binding device ready handler');
device.on("ready", function () {
Logger.log('device.ready fired');
onDeviceReady(device);
});
Logger.log('Calling device.connect');
device.connect();
}
};
self.endSession = function () {
var session = currentWebAppSession;
if (session) {
session.close();
}
if (currentDevice) {
currentDevice.disconnect();
}
cleanupSession();
currentDevice = null;
currentDeviceId = null;
};
$(MediaController).on('playerchange', function (e, newPlayer, newTarget) {
if (newTarget.id != currentDeviceId) {
if (currentWebAppSession) {
Logger.log('Disconnecting from chromecast');
Logger.log('New target info: ' + JSON.stringify(newTarget));
Logger.log('currentDeviceId: ' + currentDeviceId);
//currentDevice.disconnect();
cleanupSession();
currentDevice = null;
currentDeviceId = null;
}
}
});
function onResume() {
var deviceId = currentDeviceId;
if (deviceId) {
self.tryPair({
id: deviceId
});
}
}
document.addEventListener("resume", onResume, false);
}
MediaController.registerPlayer(new chromecastPlayer());
})();

View file

@ -1,36 +0,0 @@
(function () {
function initSdk() {
var manager = ConnectSDK.discoveryManager;
//manager.setPairingLevel(ConnectSDK.PairingLevel.OFF);
manager.setAirPlayServiceMode(ConnectSDK.AirPlayServiceMode.Media);
// Show devices that support playing videos and pausing
//manager.setCapabilityFilters([
// new ConnectSDK.CapabilityFilter(["MediaPlayer.Display.Video", "MediaControl.Pause"])
//]);
manager.on('devicelistchanged', onDeviceListChanged);
manager.startDiscovery();
requirejs(['cordova/chromecast', 'cordova/generaldevice']);
}
function onDeviceListChanged(list) {
}
function getDeviceList() {
return ConnectSDK.discoveryManager.getDeviceList();
}
window.ConnectSDKHelper = {
getDeviceList: getDeviceList
};
initSdk();
})();

View file

@ -1,23 +0,0 @@
(function () {
function showPlayerSelectionMenu(item, url, mimeType) {
window.plugins.launcher.launch({
uri: url,
dataType: mimeType
}, function () {
Logger.log('plugin launch success');
ExternalPlayer.onPlaybackStart();
}, function () {
Logger.log('plugin launch error');
ExternalPlayer.onPlaybackStart();
});
}
window.ExternalPlayer.showPlayerSelectionMenu = showPlayerSelectionMenu;
})();

View file

@ -1,68 +0,0 @@
(function (globalScope) {
function fileUpload() {
var self = this;
self.upload = function (path, name, url) {
return new Promise(function (resolve, reject) {
resolveLocalFileSystemURL(path, function (fileEntry) {
fileEntry.file(function (file) {
var mimeType = (file.type || '');
if (mimeType.indexOf('image/') != 0) {
Logger.log('Skipping upload because file is not an image. path: ' + path + ' mimeType: ' + mimeType);
reject();
return;
}
Logger.log('mimeType for file ' + path + ' is ' + file);
var onSuccess = function (r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
resolve();
}
var onFail = function (error) {
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
reject();
}
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = name;
options.mimeType = mimeType;
var params = {};
options.params = params;
new FileTransfer().upload(path, url, onSuccess, onFail, options);
}, function () {
Logger.log('File upload failed. fileEntry.file returned an error');
reject();
});
}, function () {
Logger.log('File upload failed. resolveLocalFileSystemURL returned an error');
reject();
});
});
};
}
if (!globalScope.MediaBrowser) {
globalScope.MediaBrowser = {};
}
globalScope.MediaBrowser.FileUpload = fileUpload;
})(this);

View file

@ -1,675 +0,0 @@
(function () {
function connectSDKPlayer() {
var self = this;
var PlayerName = "ConnectSDK";
var currentDevice;
var currentDeviceId;
var currentMediaControl;
// MediaController needs this
self.name = PlayerName;
self.getItemsForPlayback = function (query) {
var userId = Dashboard.getCurrentUserId();
query.Limit = query.Limit || 100;
query.ExcludeLocationTypes = "Virtual";
return ApiClient.getItems(userId, query);
};
var castPlayer = {};
$(castPlayer).on("playbackstart", function (e, data) {
Logger.log('cc: playbackstart');
var state = self.getPlayerStateInternal(data);
$(self).trigger("playbackstart", [state]);
});
$(castPlayer).on("playbackstop", function (e, data) {
Logger.log('cc: playbackstop');
var state = self.getPlayerStateInternal(data);
$(self).trigger("playbackstop", [state]);
// Reset this so the next query doesn't make it appear like content is playing.
self.lastPlayerData = {};
});
$(castPlayer).on("playbackprogress", function (e, data) {
Logger.log('cc: positionchange');
var state = self.getPlayerStateInternal(data);
$(self).trigger("positionchange", [state]);
});
self.play = function (options) {
Dashboard.getCurrentUser().then(function (user) {
if (options.items) {
self.playWithCommand(options, 'PlayNow');
} else {
self.getItemsForPlayback({
Ids: options.ids.join(',')
}).then(function (result) {
options.items = result.Items;
self.playWithCommand(options, 'PlayNow');
});
}
});
};
self.playWithCommand = function (options, command) {
if (!options.items) {
ApiClient.getItem(Dashboard.getCurrentUserId(), options.ids[0]).then(function (item) {
options.items = [item];
self.playWithCommand(options, command);
});
return;
}
playItemInternal(items[0], null, serverAddress);
};
function validatePlaybackInfoResult(result) {
if (result.ErrorCode) {
MediaController.showPlaybackInfoErrorMessage(result.ErrorCode);
return false;
}
return true;
}
function getOptimalMediaSource(mediaType, versions) {
var optimalVersion = versions.filter(function (v) {
v.enableDirectPlay = MediaController.supportsDirectPlay(v);
return v.enableDirectPlay;
})[0];
if (!optimalVersion) {
optimalVersion = versions.filter(function (v) {
return v.SupportsDirectStream;
})[0];
}
return optimalVersion || versions.filter(function (s) {
return s.SupportsTranscoding;
})[0];
}
function playItemInternal(item, startPosition) {
if (item == null) {
throw new Error("item cannot be null");
}
if (item.MediaType !== 'Audio' && item.MediaType !== 'Video') {
throw new Error("Unrecognized media type");
}
if (item.IsPlaceHolder) {
MediaController.showPlaybackInfoErrorMessage('PlaceHolder');
return;
}
var deviceProfile = self.getDeviceProfile();
if (item.MediaType === "Video") {
Dashboard.showModalLoadingMsg();
}
MediaController.getPlaybackInfo(item.Id, deviceProfile, startPosition).then(function (playbackInfoResult) {
if (validatePlaybackInfoResult(playbackInfoResult)) {
var mediaSource = getOptimalMediaSource(item.MediaType, playbackInfoResult.MediaSources);
if (mediaSource) {
if (mediaSource.RequiresOpening) {
getLiveStream(item.Id, playbackInfoResult.PlaySessionId, deviceProfile, startPosition, mediaSource, null, null).then(function (openLiveStreamResult) {
openLiveStreamResult.MediaSource.enableDirectPlay = supportsDirectPlay(openLiveStreamResult.MediaSource);
playInternalPostMediaSourceSelection(item, openLiveStreamResult.MediaSource, startPosition, callback);
});
} else {
playInternalPostMediaSourceSelection(item, mediaSource, startPosition, callback);
}
} else {
Dashboard.hideModalLoadingMsg();
MediaController.showPlaybackInfoErrorMessage('NoCompatibleStream');
}
}
});
}
function playInternalPostMediaSourceSelection(item, mediaSource, startPosition, deferred) {
Dashboard.hideModalLoadingMsg();
var streamInfo = MediaPlayer.createStreamInfo('Video', item, mediaSource, startPosition);
currentDevice.getMediaPlayer().playMedia(
streamInfo.url,
streamInfo.MimeType,
{
title: item.Name,
description: item.Overview || '',
shouldLoop: false
}
).success(function (launchSession, mediaControl) {
Logger.log("Video launch successful");
currentMediaControl = mediaControl && mediaControl.acquire();
}).error(function (err) {
Logger.log("error: " + err.message);
});
deferred.resolveWith(null, [streamInfo]);
}
self.unpause = function () {
if (currentMediaControl) {
currentMediaControl.pause();
}
};
self.pause = function () {
if (currentMediaControl) {
currentMediaControl.pause();
}
};
self.shuffle = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'Shuffle');
});
};
self.instantMix = function (id) {
var userId = Dashboard.getCurrentUserId();
ApiClient.getItem(userId, id).then(function (item) {
self.playWithCommand({
items: [item]
}, 'InstantMix');
});
};
self.canQueueMediaType = function (mediaType) {
return mediaType == "Audio";
};
self.queue = function (options) {
self.playWithCommnd(options, 'PlayLast');
};
self.queueNext = function (options) {
self.playWithCommand(options, 'PlayNext');
};
self.stop = function () {
if (currentMediaControl) {
currentMediaControl.stop();
}
};
self.displayContent = function (options) {
// TODO
};
self.mute = function () {
if (currentDevice) {
currentDevice.getVolumeControl().setMute(true);
}
};
self.unMute = function () {
self.setVolume(getCurrentVolume() + 2);
};
self.toggleMute = function () {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
if (state.IsMuted) {
self.unMute();
} else {
self.mute();
}
};
self.getDeviceProfile = function () {
var qualityOption = self.getVideoQualityOptions().filter(function (q) {
return q.selected;
})[0];
var bitrateSetting = AppSettings.maxStreamingBitrate();
var profile = {};
profile.MaxStreamingBitrate = bitrateSetting;
profile.MaxStaticBitrate = 40000000;
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
profile.DirectPlayProfiles = [];
profile.DirectPlayProfiles.push({
Container: 'mp4,m4v',
Type: 'Video',
VideoCodec: 'h264',
AudioCodec: 'aac,mp3,ac3'
});
profile.DirectPlayProfiles.push({
Container: 'mov',
Type: 'Video'
});
profile.DirectPlayProfiles.push({
Container: 'mp3',
Type: 'Audio'
});
profile.DirectPlayProfiles.push({
Container: 'aac',
Type: 'Audio'
});
profile.TranscodingProfiles = [];
profile.TranscodingProfiles.push({
Container: 'mp3',
Type: 'Audio',
AudioCodec: 'mp3',
Context: 'Streaming',
Protocol: 'http'
});
if (self.canPlayHls()) {
profile.TranscodingProfiles.push({
Container: 'ts',
Type: 'Video',
AudioCodec: 'aac',
VideoCodec: 'h264',
Context: 'Streaming',
Protocol: 'hls'
});
}
profile.TranscodingProfiles.push({
Container: 'mp4',
Type: 'Video',
AudioCodec: 'aac',
VideoCodec: 'h264',
Context: 'Streaming',
Protocol: 'http'
});
profile.ContainerProfiles = [];
profile.CodecProfiles = [];
profile.CodecProfiles.push({
Type: 'Audio',
Conditions: [{
Condition: 'LessThanEqual',
Property: 'AudioChannels',
Value: '2'
}]
});
profile.CodecProfiles.push({
Type: 'VideoAudio',
Codec: 'aac,mp3',
Conditions: [
{
Condition: 'LessThanEqual',
Property: 'AudioChannels',
Value: '6'
}
]
});
profile.CodecProfiles.push({
Type: 'Video',
Codec: 'h264',
Conditions: [
{
Condition: 'NotEquals',
Property: 'IsAnamorphic',
Value: 'true',
IsRequired: false
},
{
Condition: 'EqualsAny',
Property: 'VideoProfile',
Value: 'high|main|baseline|constrained baseline'
},
{
Condition: 'LessThanEqual',
Property: 'VideoLevel',
Value: '41'
},
{
Condition: 'LessThanEqual',
Property: 'Width',
Value: qualityOption.maxWidth
}]
});
// Subtitle profiles
profile.SubtitleProfiles = [];
profile.ResponseProfiles = [];
profile.ResponseProfiles.push({
Type: 'Video',
Container: 'm4v',
MimeType: 'video/mp4'
});
//profile.ResponseProfiles.push({
// Type: 'Video',
// Container: 'mkv',
// MimeType: 'video/webm'
//});
return profile;
};
function getBaseTargetInfo() {
var target = {};
target.playableMediaTypes = ["Audio", "Video"];
target.isLocalPlayer = false;
target.supportedCommands = [
"VolumeUp",
"VolumeDown",
"Mute",
"Unmute",
"ToggleMute",
"SetVolume"
];
return target;
}
function convertDeviceToTarget(device) {
var target = getBaseTargetInfo();
target.appName = target.name = target.deviceName = device.getFriendlyName();
target.playerName = PlayerName;
target.id = device.getId();
return target;
}
function isValid(device) {
var validTokens = ['AirPlay', 'Airplay', 'airplay'];
return validTokens.filter(function (t) {
return device.hasService(t);
}).length > 0;
}
self.getTargets = function () {
return ConnectSDKHelper.getDeviceList().filter(function (d) {
return isValid(d);
}).map(convertDeviceToTarget);
};
self.seek = function (position) {
position = parseInt(position);
position = position / 10000000;
// TODO
};
self.setAudioStreamIndex = function (index) {
// TODO
};
self.setSubtitleStreamIndex = function (index) {
// TODO
};
self.nextTrack = function () {
// TODO
};
self.previousTrack = function () {
// TODO
};
self.beginPlayerUpdates = function () {
// Setup polling here
};
self.endPlayerUpdates = function () {
// Stop polling here
};
function getCurrentVolume() {
var state = self.lastPlayerData || {};
state = state.PlayState || {};
return state.VolumeLevel == null ? 100 : state.VolumeLevel;
}
self.volumeDown = function () {
if (currentDevice) {
currentDevice.getVolumeControl().volumeDown();
}
};
self.volumeUp = function () {
if (currentDevice) {
currentDevice.getVolumeControl().volumeUp();
}
};
self.setVolume = function (vol) {
vol = Math.min(vol, 100);
vol = Math.max(vol, 0);
if (currentDevice) {
currentDevice.getVolumeControl().setVolume(vol / 100);
}
};
self.getPlayerState = function () {
var deferred = $.Deferred();
var result = self.getPlayerStateInternal();
deferred.resolveWith(null, [result]);
return deferred.promise();
};
self.lastPlayerData = {};
self.getPlayerStateInternal = function (data) {
data = data || self.lastPlayerData;
self.lastPlayerData = data;
Logger.log(JSON.stringify(data));
return data;
};
function handleSessionDisconnect() {
Logger.log("session disconnected");
cleanupSession();
MediaController.removeActivePlayer(PlayerName);
}
function cleanupSession() {
}
function launchWebApp(device) {
if (currentDevice) {
cleanupSession();
}
Logger.log('session.connect succeeded');
MediaController.setActivePlayer(PlayerName, convertDeviceToTarget(device));
currentDevice = device;
currentDeviceId = device.getId();
}
function onDeviceReady(device) {
device.off("ready");
Logger.log('creating webAppSession');
launchWebApp(device);
}
self.tryPair = function (target) {
var deferred = $.Deferred();
var device = ConnectSDKHelper.getDeviceList().filter(function (d) {
return d.getId() == target.id;
})[0];
if (device) {
self.tryPairWithDevice(device, deferred);
} else {
deferred.reject();
}
return deferred.promise();
};
self.tryPairWithDevice = function (device, deferred) {
Logger.log('Will attempt to connect to Connect Device');
device.on("disconnect", function () {
device.off("ready");
device.off("disconnect");
});
if (device.isReady()) {
Logger.log('Device is already ready, calling onDeviceReady');
onDeviceReady(device);
} else {
Logger.log('Binding device ready handler');
device.on("ready", function () {
Logger.log('device.ready fired');
onDeviceReady(device);
});
Logger.log('Calling device.connect');
device.connect();
}
};
$(MediaController).on('playerchange', function (e, newPlayer, newTarget) {
if (currentDevice) {
if (newTarget.id != currentDeviceId) {
if (currentDevice) {
Logger.log('Disconnecting from connect device');
//currentDevice.disconnect();
cleanupSession();
currentDevice = null;
currentDeviceId = null;
currentMediaControl = null;
}
}
}
});
function onResume() {
var deviceId = currentDeviceId;
if (deviceId) {
self.tryPair({
id: deviceId
});
}
}
document.addEventListener("resume", onResume, false);
}
MediaController.registerPlayer(new connectSDKPlayer());
})();

View file

@ -1,297 +0,0 @@
(function () {
var updatedProducts = [];
var enteredEmail;
function getStoreFeatureId(feature) {
if (feature == 'embypremieremonthly') {
return 'emby.subscription.monthly';
}
return 'appunlock';
}
function updateProductInfo(product) {
//if (product.id == 'appunlock') {
// product.owned = false;
//}
updatedProducts = updatedProducts.filter(function (r) {
return r.id != product.id;
});
updatedProducts.push(product);
Events.trigger(IapManager, 'productupdated', [product]);
}
function getProduct(feature) {
var id = getStoreFeatureId(feature);
var products = updatedProducts.filter(function (r) {
return r.id == id;
});
return products.length ? products[0] : null;
}
function isPurchaseAvailable(feature) {
var product = getProduct(feature);
return product != null && product.valid /*&& product.canPurchase*/;
}
function beginPurchase(feature, email) {
if (email) {
enteredEmail = email;
}
validationCache = {};
var id = getStoreFeatureId(feature);
store.order(id);
}
function restorePurchase(id) {
validationCache = {};
store.refresh();
}
var validationCache = {};
function validateProduct(product, callback) {
var productId = product.id;
// We should never get in here with the unlock, but in case we do
if ((productId || '').toLowerCase().indexOf('appunlock') != -1) {
callback(true, product);
return;
}
var cacheKey = productId + (product.transaction.id || '');
var cachedResult = validationCache[cacheKey];
if (cachedResult && (new Date().getTime() - cachedResult.date) < 60000) {
if (cachedResult.result) {
callback(true, product);
} else {
callback(false, {
code: cachedResult.errorCode,
error: {
message: cachedResult.errorMessage
}
});
}
return;
}
// product attributes:
// https://github.com/j3k0/cordova-plugin-purchase/blob/master/doc/api.md#validation-error-codes
var receipt = product.transaction.appStoreReceipt;
var price = product.price;
var postData = {
store: "Apple",
application: "com.emby.mobile",
product: productId,
type: "Subscription",
storeToken: receipt,
amt: price
};
var promise;
if (enteredEmail) {
postData.email = enteredEmail;
postData.storeId = enteredEmail;
postData.feature = "MBSClubMonthly";
promise = ApiClient.ajax({
type: "POST",
url: ApiClient.getUrl("Appstore/Register"),
data: {
Parameters: JSON.stringify(postData)
}
});
} else {
promise = fetch("http://mb3admin.com/admin/service/appstore/register", {
method: 'POST',
body: JSON.stringify(postData),
headers: {
"X-Emby-Token": "EMBY-APPLE-VALIDATE",
"Content-Type": "application/json"
}
});
}
promise.then(function () {
setCachedResult(cacheKey, true);
callback(true, product);
}, function (e) {
if (e.status == 402) {
setCachedResult(cacheKey, false, store.PURCHASE_EXPIRED, 'Subscription Expired');
callback(false, {
code: store.PURCHASE_EXPIRED,
error: {
message: "Subscription Expired"
}
});
} else {
//alert('validate fail - other ' + e.status);
validationCache = {};
callback(false, {
code: store.CONNECTION_FAILED,
error: {
message: "Connection Failure"
}
});
}
});
}
function setCachedResult(key, result, code, message) {
validationCache[key] = {
date: new Date().getTime(),
result: result,
errorCode: code,
errorMessage: message
};
}
function initProduct(id, requiresVerification, type) {
store.register({
id: id,
alias: id,
type: type
});
// When purchase of the full version is approved,
// show some logs and finish the transaction.
store.when(id).approved(function (product) {
//product.finish();
if (requiresVerification) {
product.verify();
} else {
product.finish();
}
});
if (requiresVerification) {
store.when(id).verified(function (p) {
//alert('verified');
updateProductInfo(p);
p.finish();
});
}
// The play button can only be accessed when the user
// owns the full version.
store.when(id).updated(function (product) {
if (product.loaded && product.valid && product.state == store.APPROVED) {
Logger.log('finishing previously created transaction');
if (requiresVerification) {
//product.verify();
if (product.owned) {
//alert('sub owned!');
}
} else {
product.finish();
}
}
updateProductInfo(product);
});
}
function initializeStore() {
// Let's set a pretty high verbosity level, so that we see a lot of stuff
// in the console (reassuring us that something is happening).
store.verbosity = store.INFO;
store.validator = validateProduct;
initProduct(getStoreFeatureId(""), false, store.NON_CONSUMABLE);
initProduct(getStoreFeatureId("embypremieremonthly"), true, store.PAID_SUBSCRIPTION);
// When every goes as expected, it's time to celebrate!
// The "ready" event should be welcomed with music and fireworks,
// go ask your boss about it! (just in case)
store.ready(function () {
Logger.log("Store ready");
});
// After we've done our setup, we tell the store to do
// it's first refresh. Nothing will happen if we do not call store.refresh()
store.refresh();
}
function getSubscriptionOptions() {
return new Promise(function (resolve, reject) {
var options = [];
options.push({
feature: 'embypremieremonthly',
buttonText: 'EmbyPremiereMonthlyWithPrice'
});
options = options.filter(function (o) {
return getProduct(o.feature) != null;
}).map(function (o) {
o.id = getStoreFeatureId(o.feature);
o.buttonText = Globalize.translate(o.buttonText, getProduct(o.feature).price);
o.owned = getProduct(o.feature).owned;
return o;
});
resolve(options);
});
}
function isUnlockedOverride(feature) {
return new Promise(function (resolve, reject) {
resolve(false);
});
}
window.IapManager = {
isPurchaseAvailable: isPurchaseAvailable,
getProductInfo: getProduct,
beginPurchase: beginPurchase,
restorePurchase: restorePurchase,
getSubscriptionOptions: getSubscriptionOptions,
isUnlockedOverride: isUnlockedOverride
};
initializeStore();
})();

View file

@ -1,156 +0,0 @@
(function () {
function setImageIntoElement(elem, url) {
if (elem.tagName !== "IMG") {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute("src", url);
}
}
var fileSystem;
function getFileSystem() {
var deferred = DeferredBuilder.Deferred();
if (fileSystem) {
deferred.resolveWith(null, [fileSystem]);
} else {
requestFileSystem(PERSISTENT, 0, function (fs) {
fileSystem = fs;
deferred.resolveWith(null, [fileSystem]);
});
}
return deferred.promise();
}
function indexedDbBlobImageStore() {
var self = this;
function getCacheKey(url) {
// Try to strip off the domain to share the cache between local and remote connections
var index = url.indexOf('://');
if (index != -1) {
url = url.substring(index + 3);
index = url.indexOf('/');
if (index != -1) {
url = url.substring(index + 1);
}
}
return CryptoJS.MD5(url).toString();
}
function normalizeReturnUrl(url) {
if (browserInfo.safari) {
// Use the embedded server for iOS8, and also if we don't know the iOS version, just to be safe
var index = url.indexOf('/Documents');
if (index != -1) {
return url.substring(index);
}
else {
return url.replace('file://', '');
}
}
return url;
}
self.getImageUrl = function (originalUrl) {
if (browserInfo.android && originalUrl.indexOf('tag=') != -1) {
originalUrl += "&accept=webp";
}
var deferred = DeferredBuilder.Deferred();
var key = getCacheKey(originalUrl);
//Logger.log('getImageUrl:' + originalUrl);
getFileSystem().then(function (fileSystem) {
var path = fileSystem.root.toURL() + "/emby/cache/" + key;
resolveLocalFileSystemURL(path, function (fileEntry) {
var localUrl = normalizeReturnUrl(fileEntry.toURL());
//Logger.log('returning cached file: ' + localUrl);
deferred.resolveWith(null, [localUrl]);
}, function () {
//Logger.log('downloading: ' + originalUrl);
var ft = new FileTransfer();
ft.download(originalUrl, path, function (entry) {
var localUrl = normalizeReturnUrl(entry.toURL());
//Logger.log(localUrl);
deferred.resolveWith(null, [localUrl]);
});
});
});
return deferred.promise();
};
self.setImageInto = function (elem, url) {
function onFail() {
setImageIntoElement(elem, url);
}
//if (browserInfo.safari) {
// setImageWithSdWebImage(elem, url);
// return;
//}
self.getImageUrl(url).then(function (localUrl) {
setImageIntoElement(elem, localUrl);
}, onFail);
};
var imageIdIndex = 1;
function setImageWithSdWebImage(elem, url) {
var rect = elem.getBoundingClientRect();
var options = {
data: url,
index: imageIdIndex,
quality: 0,
scale: Math.round(rect.width) + 'x' + Math.round(rect.height),
downloadOptions: window.CollectionRepeatImageOptions.SDWebImageRetryFailed | window.CollectionRepeatImageOptions.SDWebImageLowPriority | window.CollectionRepeatImageOptions.SDWebImageAllowInvalidSSLCertificates
};
if (elem.classList.contains('coveredCardImage')) {
options.scale += '!';
}
imageIdIndex++;
window.CollectionRepeatImage.getImage(options, function (data) {
var dataUrl = 'data:image/jpeg;base64,' + data;
elem.style.backgroundImage = "url('" + dataUrl + "')";
});
}
window.ImageStore = self;
}
require(['cryptojs-sha1'], function () {
new indexedDbBlobImageStore();
});
})();

View file

@ -1,103 +0,0 @@
(function () {
var lastStart = 0;
function onDeviceReady() {
//var fetcher = window.BackgroundFetch;
//fetcher.configure(onBackgroundFetch, onBackgroundFetchFailed, {
// stopOnTerminate: false // <-- false is default
//});
}
function onSyncFinish() {
Logger.log('BackgroundFetch completed');
var fetcher = window.BackgroundFetch;
fetcher.finish(); // <-- N.B. You MUST called #finish so that native-side can signal completion of the background-thread to the os.
}
function onSyncFail() {
Logger.log('BackgroundFetch completed - sync failed');
var fetcher = window.BackgroundFetch;
fetcher.finish(); // <-- N.B. You MUST called #finish so that native-side can signal completion of the background-thread to the os.
}
function startSync(reportToFetcher, syncOptions) {
lastStart = new Date().getTime();
require(['localsync'], function () {
if (LocalSync.getSyncStatus() == 'Syncing') {
onSyncFinish();
return;
}
var promise = LocalSync.sync(syncOptions);
if (reportToFetcher) {
promise.then(onSyncFinish, onSyncFail);
}
});
}
function onBackgroundFetch() {
Logger.log('BackgroundFetch initiated');
startSync(true, {
uploadPhotos: false,
enableNewDownloads: true
});
}
function onBackgroundFetchFailed() {
Logger.log('- BackgroundFetch failed');
}
var syncInterval = 900000;
var photoUploadInterval = 21600000;
var offlineUserSyncInterval = 43200000;
function startIntervalSync() {
startSync(false, {
uploadPhotos: true,
enableNewDownloads: true
});
}
function normalizeSyncOptions(options) {
options.enableBackgroundTransfer = true;
options.uploadPhotos = (new Date().getTime() - lastStart) >= photoUploadInterval;
options.syncOfflineUsers = (new Date().getTime() - lastStart) >= offlineUserSyncInterval;
}
require(['localsync'], function () {
LocalSync.normalizeSyncOptions = normalizeSyncOptions;
});
pageClassOn('pageshow', "libraryPage", function () {
if (!Dashboard.getCurrentUserId()) {
return;
}
if ((new Date().getTime() - lastStart) >= syncInterval) {
setTimeout(function () {
startIntervalSync();
}, 10000);
}
});
onDeviceReady();
})();

View file

@ -1,23 +0,0 @@
(function () {
function forceScroll() {
var doc = $(document);
// Try to make it react quicker to the orientation change
doc.scrollTop(doc.scrollTop() + 1);
//$('paper-tabs').filter(':visible').hide().show();
}
function onOrientationChange() {
forceScroll();
for (var i = 0; i <= 500; i += 100) {
setTimeout(forceScroll, i);
}
}
window.addEventListener('orientationchange', onOrientationChange);
})();

View file

@ -1,205 +0,0 @@
(function () {
var initComplete = false;
var ignoreNextSelection = false;
function onTabSelected(name) {
if (ignoreNextSelection) {
ignoreNextSelection = false;
return;
}
switch (name) {
case 'Favorites':
Dashboard.navigate('favorites.html');
break;
case 'Library':
Dashboard.navigate('index.html');
break;
case 'Search':
Dashboard.navigate('search.html');
break;
case 'NowPlaying':
Dashboard.navigate('nowplaying.html');
break;
case 'Sync':
Dashboard.navigate('mysync.html');
break;
case 'LiveTv':
Dashboard.navigate('livetv.html');
break;
case 'Settings':
Dashboard.navigate('mypreferencesmenu.html?userId=' + Dashboard.getCurrentUserId());
break;
default:
break;
}
}
function init() {
// Use system defined items for this demo
// If an image is passed, label is not used
/**
* Create a new tab bar item for use on a previously created tab bar. Use ::showTabBarItems to show the new item on the tab bar.
*
* If the supplied image name is one of the labels listed below, then this method will construct a tab button
* using the standard system buttons. Note that if you use one of the system images, that the \c title you supply will be ignored.
* - <b>Tab Buttons</b>
* - tabButton:More
* - tabButton:Favorites
* - tabButton:Featured
* - tabButton:TopRated
* - tabButton:Recents
* - tabButton:Contacts
* - tabButton:History
* - tabButton:Bookmarks
* - tabButton:Search
* - tabButton:Downloads
* - tabButton:MostRecent
* - tabButton:MostViewed
* @brief create a tab bar item
* @param arguments Parameters used to create the tab bar
* -# \c name internal name to refer to this tab by
* -# \c title title text to show on the tab, or null if no text should be shown
* -# \c image image filename or internal identifier to show, or null if now image should be shown
* -# \c tag unique number to be used as an internal reference to this button
* @param options Options for customizing the individual tab item
* - \c badge value to display in the optional circular badge on the item; if nil or unspecified, the badge will be hidden
*/
var items = [
{ name: 'Library', label: Globalize.translate('ButtonLibrary'), image: 'tabbar/tab-library.png', options: {} },
{ name: 'LiveTv', label: Globalize.translate('HeaderLiveTV'), image: 'tabbar/tab-livetv.png', options: {} },
{ name: 'Favorites', label: Globalize.translate('ButtonFavorites'), image: 'tabButton:Favorites', options: {} },
{ name: 'Search', label: Globalize.translate('ButtonSearch'), image: 'tabButton:Search', options: {} },
{ name: 'NowPlaying', label: Globalize.translate('ButtonNowPlaying'), image: 'tabbar/tab-nowplaying.png', options: {} },
{ name: 'Sync', label: Globalize.translate('ButtonSync'), image: 'tabbar/tab-sync.png', options: {} },
{ name: 'Settings', label: Globalize.translate('ButtonSettings'), image: 'tabbar/tab-settings.png', options: {} }
];
for (var i = 0; i < items.length; i++) {
var item = items[i];
TabBar.createItem(item.name, item.label, item.image, onTabSelected, item.options);
};
TabBar.showItems();
initComplete = true;
ignoreNextSelection = true;
TabBar.selectItem('Library');
}
function showTabs() {
if (!initComplete) {
return;
}
TabBar.show();
}
function showUserTabs(user) {
if (!window.ApiClient) {
onUserViewResponse(user, []);
return;
}
ApiClient.getUserViews({}, user.Id).then(function (result) {
onUserViewResponse(user, result.Items);
}, function (result) {
onUserViewResponse(user, []);
});
}
function onUserViewResponse(user, views) {
var tabs = ['Library'];
if (views.filter(function (v) {
return v.CollectionType == 'livetv';
}).length) {
tabs.push('LiveTv');
}
tabs.push('Favorites');
tabs.push('Search');
tabs.push('NowPlaying');
if (user.Policy.EnableSync && Dashboard.capabilities().SupportsSync) {
tabs.push('Sync');
}
tabs.push('Settings');
TabBar.showNamedItems(tabs);
// We need to make sure the above completes first
setTimeout(showTabs, 500);
}
function showCurrentUserTabs() {
if (!Dashboard.getCurrentUserId()) {
return;
}
Dashboard.getCurrentUser().then(showUserTabs);
}
var isFirstHide = true;
function hideTabs() {
if (!initComplete) {
return;
}
var hide = function () { TabBar.hide(); };
if (isFirstHide) {
isFirstHide = false;
setTimeout(hide, 1000);
} else {
hide();
}
}
Dashboard.ready(function () {
init();
Events.on(ConnectionManager, 'localusersignedin', function (e, user) {
showUserTabs(user);
});
Events.on(ConnectionManager, 'localusersignedout', hideTabs);
Events.on(MediaController, 'beforeplaybackstart', onPlaybackStart);
Events.on(MediaController, 'playbackstop', onPlaybackStop);
showCurrentUserTabs();
});
function onPlaybackStart(e, state, player) {
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video' && player.isLocalPlayer) {
hideTabs();
}
}
function onPlaybackStop(e, state, player) {
if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video' && player.isLocalPlayer) {
showTabs();
}
}
})();

View file

@ -1,272 +0,0 @@
(function () {
function vlcRenderer(options) {
var self = this;
// Need to use this to fire events because the iOS vlc callbacks always go to the first instance
window.AudioRenderer.Current = self;
self.enableProgressReporting = options.type == 'audio';
function onEnded() {
Events.trigger(window.AudioRenderer.Current, 'ended');
}
function onTimeUpdate() {
Events.trigger(window.AudioRenderer.Current, 'timeupdate');
}
function onVolumeChange() {
Events.trigger(window.AudioRenderer.Current, 'volumechange');
}
function onPlaying() {
Events.trigger(window.AudioRenderer.Current, 'playing');
}
function onPlay() {
Events.trigger(window.AudioRenderer.Current, 'play');
}
function onPause() {
Events.trigger(window.AudioRenderer.Current, 'pause');
}
function onClick() {
Events.trigger(window.AudioRenderer.Current, 'click');
}
function onDblClick() {
Events.trigger(window.AudioRenderer.Current, 'dblclick');
}
function onError() {
var errorCode = this.error ? this.error.code : '';
Logger.log('Media element error code: ' + errorCode);
Events.trigger(window.AudioRenderer.Current, 'error');
}
self.playerState = {};
self.currentTime = function (val) {
if (val != null) {
window.audioplayer.seekto(function () {
Logger.log('set currentTime succeeded!');
}, function () {
Logger.log('set currentTime failed!');
}, val / 1000);
return;
}
return self.playerState.currentTime;
};
self.duration = function (val) {
// TODO
// This value doesn't seem to be getting reported properly
// Right now it's only used to determine if the player can seek, so for now we can mock it
return 1;
};
self.stop = function () {
window.audioplayer.stop(function (result) {
Logger.log('Stop succeeded!');
reportEvent('playbackstop', result);
}, function () {
Logger.log('Stop failed!');
});
};
self.pause = function () {
window.audioplayer.pause(function (result) {
Logger.log('Pause succeeded!');
reportEvent('sepaused', result);
}, function () {
Logger.log('Pause failed!');
});
};
self.unpause = function () {
window.audioplayer.pause(function (result) {
Logger.log('Unpause succeeded!');
reportEvent('playing', result);
}, function () {
Logger.log('Unpause failed!');
});
};
self.volume = function (val) {
if (val != null) {
// TODO
return;
}
return self.playerState.volume;
};
self.setCurrentSrc = function (streamInfo, item, mediaSource, tracks) {
if (!streamInfo) {
return;
}
var val = streamInfo.url;
var tIndex = val.indexOf('#t=');
var startPosMs = 0;
if (tIndex != -1) {
startPosMs = val.substring(tIndex + 3);
startPosMs = parseFloat(startPosMs) * 1000;
val = val.split('#')[0];
}
if (options.type == 'audio') {
//AndroidVlcPlayer.playAudioVlc(val, JSON.stringify(item), JSON.stringify(mediaSource), options.poster);
var artist = item.ArtistItems && item.ArtistItems.length ? item.ArtistItems[0].Name : null;
var metadata = {};
if (item.Name) {
metadata.title = item.Name;
}
if (artist) {
metadata.artist = artist;
}
if (item.Overview) {
metadata.description = item.Overview;
}
if (options.poster) {
metadata.image = {
url: options.poster
};
metadata.imageThumbnail = {
url: options.poster
};
}
window.audioplayer.playstream(successHandler, function () {
Logger.log('playstream failed!');
//onError();
},
{
ios: val
},
// metadata used for iOS lock screen, Android 'Now Playing' notification
metadata
);
} else {
}
AudioRenderer.Current.playerState.currentSrc = val;
reportEvent('playing', {});
};
self.currentSrc = function () {
return self.playerState.currentSrc;
};
self.paused = function () {
return self.playerState.paused;
};
self.cleanup = function (destroyRenderer) {
self.playerState = {};
};
function reportEvent(eventName, result) {
var duration = result.duration || 0;
var position = result.progress || 0;
var state = AudioRenderer.Current.playerState;
state.duration = duration;
state.currentTime = position;
state.paused = result.state == 3 || eventName == 'paused';
state.volume = 0;
if (eventName == 'positionchange') {
onTimeUpdate();
return;
}
Logger.log('eventName: ' + eventName + '. position: ' + position);
if (eventName == 'playbackstop') {
onEnded();
}
else if (eventName == 'paused') {
onPause();
}
else if (eventName == 'unpaused') {
onPlaying();
}
else if (eventName == 'playing') {
onPlaying();
}
}
function successHandler(result) {
if (!result) {
return;
}
if (result.type === 'next') {
console.log('skip to next audio track'); // typically fired by remote control/lock screen controls
MediaPlayer.nextTrack();
} else if (result.type === 'previous') {
console.log('skip to previous track'); // typically fired by remote/control/lock screen controls
MediaPlayer.previousTrack();
}
else if (result.state == 4 || result.state == 6) {
reportEvent('playbackstop', result);
}
else {
var eventName = 'positionchange';
reportEvent(eventName, result);
}
}
function errorHandler() {
onError();
}
self.init = function () {
var deferred = DeferredBuilder.Deferred();
window.audioplayer.configure(successHandler, errorHandler);
setTimeout(function () {
deferred.resolve();
}, 500);
return deferred.promise();
};
}
window.AudioRenderer = function (options) {
options = options || {};
options.type = 'audio';
return new vlcRenderer(options);
};
})();

View file

@ -1,929 +0,0 @@
(function () {
function getLocalMediaSource(serverId, itemId) {
var deferred = DeferredBuilder.Deferred();
// android
if (window.ApiClientBridge) {
var json = ApiClientBridge.getLocalMediaSource(serverId, itemId);
if (json) {
deferred.resolveWith(null, [JSON.parse(json)]);
}
else {
deferred.resolveWith(null, [null]);
}
return deferred.promise();
}
getLocalItem(itemId, serverId).then(function (localItem) {
if (localItem && localItem.Item.MediaSources.length) {
var mediaSource = localItem.Item.MediaSources[0];
fileExists(mediaSource.Path).then(function (exists) {
if (exists) {
deferred.resolveWith(null, [mediaSource]);
}
else {
deferred.resolveWith(null, [null]);
}
}, getOnFail(deferred));
return;
}
deferred.resolveWith(null, [null]);
}, getOnFail(deferred));
return deferred.promise();
}
function getCameraPhotos() {
var deferred = DeferredBuilder.Deferred();
if (window.CameraRoll) {
var photos = [];
CameraRoll.getPhotos(function (result) {
photos.push(result);
});
setTimeout(function () {
// clone the array in case the callback is still getting called
Logger.log('Found ' + photos.length + ' in camera roll');
deferred.resolveWith(null, [photos]);
}, 2000);
} else {
deferred.resolveWith(null, [[]]);
}
return deferred.promise();
}
var offlineUserDatabase;
function getOfflineUserdb(callback) {
if (offlineUserDatabase) {
callback(offlineUserDatabase);
return;
}
// Create/open database
offlineUserDatabase = window.sqlitePlugin.openDatabase({ name: "offlineusers.db" });
offlineUserDatabase.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS users (id text primary key, data text)');
tx.executeSql('create index if not exists idx_users on users(id)');
callback(offlineUserDatabase);
});
}
function saveOfflineUser(user) {
var deferred = DeferredBuilder.Deferred();
getOfflineUserdb(function (db) {
db.transaction(function (tx) {
tx.executeSql("REPLACE INTO offlineusers (id, data) VALUES (?,?)", [user.Id, JSON.stringify(user)], function (tx, res) {
deferred.resolve();
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
function deleteOfflineUser(id) {
var deferred = DeferredBuilder.Deferred();
getOfflineUserdb(function (db) {
db.transaction(function (tx) {
tx.executeSql("DELETE from offlineusers where id=?", [user.Id], function (tx, res) {
deferred.resolve();
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
var offlineActionsDatabase;
function getOfflineActionsDb(callback) {
if (offlineActionsDatabase) {
callback(offlineActionsDatabase);
return;
}
// Create/open database
offlineActionsDatabase = window.sqlitePlugin.openDatabase({ name: "offlineactions.db" });
offlineActionsDatabase.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS offlineactions (Id text primary key, ServerId text not null, Json text not null)');
tx.executeSql('create index if not exists idx_offlineactions on offlineactions(id)');
callback(offlineActionsDatabase);
});
}
function getOfflineActions(serverId) {
var deferred = DeferredBuilder.Deferred();
getOfflineActionsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("SELECT Json from offlineactions where ServerId=?", [serverId], function (tx, res) {
var actions = [];
for (var i = 0, length = res.rows.length; i < length; i++) {
actions.push(JSON.parse(res.rows.item(i).Json));
}
deferred.resolveWith(null, [actions]);
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
function deleteOfflineActions(actions) {
var ids = actions.map(function (a) { return "'" + a.Id + "'"; }).join(',');
var deferred = DeferredBuilder.Deferred();
getOfflineActionsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("DELETE from offlineactions where Id in (" + ids + ")", [], function (tx, res) {
deferred.resolve();
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
var offlineItemsDatabase;
function getOfflineItemsDb(callback) {
if (offlineItemsDatabase) {
callback(offlineItemsDatabase);
return;
}
// Create/open database
offlineItemsDatabase = window.sqlitePlugin.openDatabase({ name: "offlineitems.db" });
offlineItemsDatabase.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS Items ( Id text primary key, ItemId text not null, ItemType text not null, MediaType text, ServerId text not null, LocalPath text not null, UserIdsWithAccess text, AlbumId text, AlbumName text, SeriesId text, SeriesName text, Json text not null)');
tx.executeSql('create index if not exists idx_items on Items(Id)');
tx.executeSql('CREATE TABLE IF NOT EXISTS AlbumArtists ( Id text not null, Name text not null, ItemId text not null)');
tx.executeSql('create index if not exists idx_AlbumArtists on AlbumArtists(id)');
callback(offlineItemsDatabase);
});
}
function getServerItemIds(serverId) {
var deferred = DeferredBuilder.Deferred();
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("SELECT ItemId from Items where ServerId=?", [serverId], function (tx, res) {
var itemIds = [];
for (var i = 0, length = res.rows.length; i < length; i++) {
itemIds.push(res.rows.item(i).ItemId);
}
deferred.resolveWith(null, [itemIds]);
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
function getUserIdsWithAccess(itemId, serverId) {
var deferred = DeferredBuilder.Deferred();
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("SELECT UserIdsWithAccess from Items where ItemId=? AND ServerId=?", [itemId, serverId], function (tx, res) {
var itemIds = [];
if (res.rows.length) {
itemIds = res.rows.item(0).UserIdsWithAccess;
itemIds = itemIds ? itemIds.split(',') : [];
}
deferred.resolveWith(null, [itemIds]);
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
function saveUserIdsWithAccess(itemId, serverId, userIds) {
Logger.log('saveUserIdsWithAccess');
var deferred = DeferredBuilder.Deferred();
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
var values = [userIds.join(','), itemId, serverId];
tx.executeSql("Update Items set UserIdsWithAccess=? where ItemId=? and ServerId=?", values);
deferred.resolve();
});
});
return deferred.promise();
}
function getLocalItem(itemId, serverId) {
var deferred = DeferredBuilder.Deferred();
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("SELECT Json from Items where itemId=? AND serverId=?", [itemId, serverId], function (tx, res) {
if (res.rows.length) {
var localItem = JSON.parse(res.rows.item(0).Json);
deferred.resolveWith(null, [localItem]);
}
else {
deferred.resolveWith(null, [null]);
}
}, function (e) {
deferred.reject();
});
});
});
return deferred.promise();
}
function addOrUpdateLocalItem(item) {
Logger.log('addOrUpdateLocalItem');
var deferred = DeferredBuilder.Deferred();
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
var libraryItem = item.Item || {};
var values = [item.Id, item.ItemId, libraryItem.Type, libraryItem.MediaType, item.ServerId, item.LocalPath, (item.UserIdsWithAccess || []).join(','), libraryItem.AlbumId, libraryItem.AlbumName, libraryItem.SeriesId, libraryItem.SeriesName, JSON.stringify(item)];
tx.executeSql("REPLACE INTO Items (Id, ItemId, ItemType, MediaType, ServerId, LocalPath, UserIdsWithAccess, AlbumId, AlbumName, SeriesId, SeriesName, Json) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", values);
deferred.resolve();
});
});
return deferred.promise();
}
function removeLocalItem(itemId, serverId) {
var deferred = DeferredBuilder.Deferred();
getLocalItem(itemId, serverId).then(function (item) {
getOfflineItemsDb(function (db) {
db.transaction(function (tx) {
tx.executeSql("DELETE from Items where itemId=? AND serverId=?", [itemId, serverId]);
var files = item.AdditionalFiles || [];
files.push(item.LocalPath);
deleteFiles(files).then(function () {
deferred.resolve();
}, getOnFail(deferred));
});
});
}, getOnFail(deferred));
return deferred.promise();
}
function deleteFiles(files) {
var deferred = DeferredBuilder.Deferred();
deleteNextFile(files, 0, deferred);
return deferred.promise();
}
function deleteNextFile(files, index, deferred) {
if (index >= files.length) {
deferred.resolve();
return;
}
deleteFile(file).then(function () {
deleteNextFile(files, index + 1, deferred);
}, function () {
deleteNextFile(files, index + 1, deferred);
});
}
function deleteFile(path) {
var deferred = DeferredBuilder.Deferred();
Logger.log('Deleting ' + path);
resolveFile(path, null, function (fileEntry) {
fileEntry.remove(function () {
Logger.log('Deleted ' + path);
deferred.resolve();
}, function () {
Logger.log('Error deleting ' + path);
deferred.reject();
});
}, function () {
Logger.log('Skipping deletion because file does not exist: ' + path);
deferred.resolve();
});
return deferred.promise();
}
function resolveFile(path, options, success, fail) {
getFileSystem().then(function (fileSystem) {
fileSystem.root.getFile(path, options || { create: false }, success, fail);
});
}
function createLocalItem(libraryItem, serverInfo, originalFileName) {
var enableFriendlyPath = false;
var path = getDirectoryPath(libraryItem, serverInfo, enableFriendlyPath);
if (enableFriendlyPath) {
path.push(getLocalFileName(libraryItem, originalFileName));
} else {
var nameParts = originalFileName.split('.');
var ext = nameParts.length > 1 ? ('.' + nameParts[nameParts.length - 1]) : '';
path.push('media' + ext);
}
var item = {};
var deferred = DeferredBuilder.Deferred();
var localPath = path.join('/');
item.LocalPath = localPath;
for (var i = 0, length = libraryItem.MediaSources.length; i < length; i++) {
var mediaSource = libraryItem.MediaSources[i];
mediaSource.Path = localPath;
mediaSource.Protocol = 'File';
}
item.ServerId = serverInfo.Id;
item.Item = libraryItem;
item.ItemId = libraryItem.Id;
item.Id = getLocalId(item.ServerId, item.ItemId);
deferred.resolveWith(null, [item]);
return deferred.promise();
}
function getDirectoryPath(item, server, enableFriendlyPath) {
if (!enableFriendlyPath) {
return ['sync', item.Id];
}
var parts = [];
parts.push("sync");
parts.push(server.Name);
if (item.Type == "Episode") {
parts.push("TV");
parts.push(item.SeriesName);
if (item.SeasonName) {
parts.push(item.SeasonName);
}
}
else if (item.MediaType == 'Video') {
parts.push("Videos");
parts.push(item.Name);
}
else if (item.MediaType == 'Audio') {
parts.push("Music");
if (item.AlbumArtist) {
parts.push(item.AlbumArtist);
}
if (item.Album) {
parts.push(item.Album);
}
}
else if (item.MediaType == 'Photo') {
parts.push("Photos");
if (item.Album) {
parts.push(item.Album);
}
}
return parts.map(getValidFileName);
}
function getLocalFileName(libraryItem, originalFileName) {
var filename = originalFileName || libraryItem.Name;
return getValidFileName(filename);
}
function getValidFileName(filename) {
// TODO
return filename;
}
function downloadFile(url, localPath, enableBackground, enableNewDownloads) {
if (!enableBackground) {
return downloadWithFileTransfer(url, localPath, enableBackground);
}
var deferred = DeferredBuilder.Deferred();
if (localStorage.getItem('sync-' + url) == '1') {
Logger.log('file was downloaded previously');
deferred.resolveWith(null, [localPath, false]);
return deferred.promise();
}
if (enableNewDownloads === false) {
deferred.resolveWith(null, [localPath, true]);
return deferred.promise();
}
Logger.log('downloading: ' + url + ' to ' + localPath);
createDirectory(getParentDirectoryPath(localPath)).then(function () {
resolveFile(localPath, { create: true }, function (targetFile) {
var downloader = new BackgroundTransfer.BackgroundDownloader();
// Create a new download operation.
var download = downloader.createDownload(url, targetFile);
var isResolved = false;
// Give it a short period of time to see if it has already been completed before. Either way, move on and resolve it.
var timeoutHandle = setTimeout(function () {
isResolved = true;
// true indicates that it's queued
deferred.resolveWith(null, [localPath, true]);
}, 500);
// Start the download and persist the promise to be able to cancel the download.
download.startAsync().then(function () {
clearTimeout(timeoutHandle);
// on success
Logger.log('Downloaded local url: ' + localPath);
// If we've already moved on, set this property so that we'll see it later
localStorage.setItem('sync-' + url, '1');
if (!isResolved) {
// true indicates that it's queued
deferred.resolveWith(null, [localPath, false]);
}
}, function () {
clearTimeout(timeoutHandle);
// on error
Logger.log('Error downloading url: ' + url);
if (!isResolved) {
deferred.reject();
}
}, function (value) {
// on progress
//Logger.log('download progress: ' + value);
});
});
}, getOnFail(deferred));
return deferred.promise();
}
var activeDownloads = [];
function removeDownload(key) {
for (var i = 0, length = activeDownloads.length; i < length; i++) {
if (key == activeDownloads[i]) {
activeDownloads[i] = "";
}
}
}
function downloadWithFileTransfer(url, localPath, enableBackground) {
var deferred = DeferredBuilder.Deferred();
if (localStorage.getItem('sync-' + url) == '1') {
Logger.log('file was downloaded previously');
deferred.resolveWith(null, [localPath]);
return deferred.promise();
}
var downloadKey = url + localPath;
if (activeDownloads.indexOf(downloadKey) != -1) {
deferred.resolveWith(null, [localPath, true]);
}
Logger.log('downloading: ' + url + ' to ' + localPath);
createDirectory(getParentDirectoryPath(localPath)).then(function () {
resolveFile(localPath, { create: true }, function (targetFile) {
var isQueued = enableBackground;
var isError = false;
var ft = new FileTransfer();
activeDownloads.push(downloadKey);
ft.download(url, targetFile.toURL(), function (entry) {
removeDownload(downloadKey);
if (enableBackground) {
Logger.log('Downloaded local url: ' + localPath);
localStorage.setItem('sync-' + url, '1');
isQueued = false;
} else {
deferred.resolveWith(null, [localPath]);
}
}, function () {
removeDownload(downloadKey);
Logger.log('Error downloading url: ' + url);
if (enableBackground) {
isError = true;
} else {
deferred.reject();
}
});
if (enableBackground) {
// Give it a short period of time to see if it has already been completed before. Either way, move on and resolve it.
setTimeout(function () {
if (isError) {
deferred.reject();
} else {
// true indicates that it's queued
deferred.resolveWith(null, [localPath, isQueued]);
}
}, 500);
}
}, function () {
Logger.log('getFile failed for ' + localPath);
deferred.reject();
});
}, getOnFail(deferred));
return deferred.promise();
}
function createDirectory(path) {
var deferred = DeferredBuilder.Deferred();
createDirectoryPart(path, 0, deferred);
return deferred.promise();
}
function createDirectoryPart(path, index, deferred) {
var parts = path.split('/');
if (index >= parts.length) {
deferred.resolve();
return;
}
parts.length = index + 1;
var pathToCreate = parts.join('/');
createDirectoryInternal(pathToCreate).then(function () {
createDirectoryPart(path, index + 1, deferred);
}, getOnFail(deferred));
}
function createDirectoryInternal(path) {
Logger.log('creating directory: ' + path);
var deferred = DeferredBuilder.Deferred();
getFileSystem().then(function (fileSystem) {
fileSystem.root.getDirectory(path, { create: true, exclusive: false }, function (targetFile) {
Logger.log('createDirectory succeeded');
deferred.resolve();
}, function () {
Logger.log('createDirectory failed');
deferred.reject();
});
}, getOnFail(deferred));
return deferred.promise();
}
function downloadSubtitles(url, localItem, subtitleStream) {
var path = item.LocalPath;
var filename = getSubtitleSaveFileName(item, subtitleStream.Language, subtitleStream.IsForced) + "." + subtitleStream.Codec.toLowerCase();
var parentPath = getParentDirectoryPath(path);
path = combinePaths(parentPath, filename);
return downloadFile(url, path);
}
function getSubtitleSaveFileName(item, language, isForced) {
var path = item.LocalPath;
var name = getNameWithoutExtension(path);
if (language) {
name += "." + language.toLowerCase();
}
if (isForced) {
name += ".foreign";
}
return name;
}
function getNameWithoutExtension(path) {
var parts = path.split('/');
var name = parts[parts.length - 1];
var index = name.lastIndexOf('.');
if (index != -1) {
name = name.substring(0, index);
}
return name;
}
function getParentDirectoryPath(path) {
var parts = path.split('/');
parts.length--;
return parts.join('/');
}
function combinePaths(path1, path2) {
return path1 + path2;
}
function getLocalId(serverId, itemId) {
return serverId + '_' + itemId;
}
function hasImage(serverId, itemId, imageTag) {
var deferred = DeferredBuilder.Deferred();
getImageLocalPath(serverId, itemId, imageTag).then(function (localPath) {
fileExists(localPath).then(function (exists) {
deferred.resolveWith(null, [exists]);
}, getOnFail(deferred));
}, getOnFail(deferred));
return deferred.promise();
}
function downloadImage(url, serverId, itemId, imageTag) {
var deferred = DeferredBuilder.Deferred();
getImageLocalPath(serverId, itemId, imageTag).then(function (localPath) {
downloadFile(url, localPath).then(function () {
deferred.resolve();
}, getOnFail(deferred));
}, getOnFail(deferred));
return deferred.promise();
}
function getImageLocalPath(serverId, itemId, imageTag) {
var deferred = DeferredBuilder.Deferred();
var path = "images/" + serverId + "/" + itemId + "/" + imageTag;
deferred.resolveWith(null, [path]);
return deferred.promise();
}
function fileExists(path) {
var deferred = DeferredBuilder.Deferred();
if (window.NativeFileSystem) {
var exists = NativeFileSystem.fileExists(path);
Logger.log('fileExists: ' + exists + ' - path: ' + path);
deferred.resolveWith(null, [exists]);
return deferred.promise();
}
resolveFile(path, null, function (fileEntry) {
Logger.log('fileExists: true - path: ' + path);
deferred.resolveWith(null, [true]);
}, function () {
Logger.log('fileExists: false - path: ' + path);
deferred.resolveWith(null, [false]);
});
return deferred.promise();
}
var fileSystem;
function getFileSystem() {
var deferred = DeferredBuilder.Deferred();
if (fileSystem) {
deferred.resolveWith(null, [fileSystem]);
} else {
requestFileSystem(PERSISTENT, 0, function (fs) {
fileSystem = fs;
deferred.resolveWith(null, [fileSystem]);
});
}
return deferred.promise();
}
function getOnFail(deferred) {
return function () {
deferred.reject();
};
}
function translateFilePath(path) {
var deferred = DeferredBuilder.Deferred();
if (browserInfo.android) {
deferred.resolveWith(null, ['file://' + path]);
return deferred.promise();
}
resolveFile(path, null, function (fileEntry) {
Logger.log('translateFilePath fileExists: true - path: ' + path);
Logger.log('translateFilePath resolving with: ' + fileEntry.toURL());
deferred.resolveWith(null, [fileEntry.toURL()]);
}, function () {
Logger.log('translateFilePath fileExists: false - path: ' + path);
deferred.resolveWith(null, [path]);
});
return deferred.promise();
}
window.LocalAssetManager = {
getLocalMediaSource: getLocalMediaSource,
saveOfflineUser: saveOfflineUser,
deleteOfflineUser: deleteOfflineUser,
getCameraPhotos: getCameraPhotos,
getOfflineActions: getOfflineActions,
deleteOfflineActions: deleteOfflineActions,
getServerItemIds: getServerItemIds,
removeLocalItem: removeLocalItem,
getLocalItem: getLocalItem,
addOrUpdateLocalItem: addOrUpdateLocalItem,
createLocalItem: createLocalItem,
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
hasImage: hasImage,
downloadImage: downloadImage,
fileExists: fileExists,
translateFilePath: translateFilePath,
getUserIdsWithAccess: getUserIdsWithAccess,
saveUserIdsWithAccess: saveUserIdsWithAccess
};
})();

View file

@ -1,17 +0,0 @@
define([], function () {
return function (options) {
var callback = function (result) {
if (result.buttonIndex == 1) {
options.callback(result.input1);
} else {
options.callback(null);
}
};
var buttonLabels = [Globalize.translate('ButtonOk'), Globalize.translate('ButtonCancel')];
navigator.notification.prompt(options.text, callback, options.title, buttonLabels, options.defaultText || '');
};
});

View file

@ -1,439 +0,0 @@
(function () {
function getRegistrationInfo(feature) {
return ConnectionManager.getRegistrationInfo(feature, ApiClient);
}
function validateFeature(feature, deferred) {
var unlockableProduct = IapManager.getProductInfo(feature) || {};
if (unlockableProduct.owned) {
deferred.resolve();
return;
}
var unlockableProductInfo = IapManager.isPurchaseAvailable(feature) ? {
enableAppUnlock: true,
id: unlockableProduct.id,
price: unlockableProduct.price,
feature: feature
} : null;
var prefix = browserInfo.android ? 'android' : 'ios';
IapManager.isUnlockedOverride(feature).then(function (isUnlocked) {
if (isUnlocked) {
deferred.resolve();
return;
}
function onRegistrationInfoResponse(registrationInfo) {
if (registrationInfo.IsRegistered) {
deferred.resolve();
return;
}
IapManager.getSubscriptionOptions().then(function (subscriptionOptions) {
if (subscriptionOptions.filter(function (p) {
return p.owned;
}).length > 0) {
deferred.resolve();
return;
}
var dialogOptions = {
title: Globalize.translate('HeaderUnlockApp'),
enablePlayMinute: feature == 'playback',
feature: feature
};
showInAppPurchaseInfo(subscriptionOptions, unlockableProductInfo, dialogOptions, deferred);
});
}
// Get supporter status
getRegistrationInfo(prefix + 'appunlock').then(onRegistrationInfoResponse, function () {
onRegistrationInfoResponse({});
});
});
}
function cancelInAppPurchase() {
var elem = document.querySelector('.inAppPurchaseOverlay');
if (elem) {
PaperDialogHelper.close(elem);
}
}
var isCancelled = true;
var currentDisplayingProductInfos = [];
var currentDisplayingDeferred = null;
function clearCurrentDisplayingInfo() {
currentDisplayingProductInfos = [];
currentDisplayingDeferred = null;
}
function showInAppPurchaseElement(subscriptionOptions, unlockableProductInfo, dialogOptions, deferred) {
cancelInAppPurchase();
// clone
currentDisplayingProductInfos = subscriptionOptions.slice(0);
if (unlockableProductInfo) {
currentDisplayingProductInfos.push(unlockableProductInfo);
}
var dlg = PaperDialogHelper.createDialog();
var html = '';
html += '<h2 class="dialogHeader">';
html += '<paper-fab icon="arrow-back" mini class="btnCloseDialog"></paper-fab>';
html += '<div style="display:inline-block;margin-left:.6em;vertical-align:middle;">' + dialogOptions.title + '</div>';
html += '</h2>';
html += '<div class="editorContent">';
html += '<form style="max-width: 800px;margin:auto;">';
html += '<p style="margin:2em 0;">';
if (unlockableProductInfo) {
html += Globalize.translate('MessageUnlockAppWithPurchaseOrSupporter');
}
else {
html += Globalize.translate('MessageUnlockAppWithSupporter');
}
html += '</p>';
html += '<p style="margin:2em 0;">';
html += Globalize.translate('MessageToValidateSupporter');
html += '</p>';
var hasProduct = false;
if (unlockableProductInfo) {
hasProduct = true;
var unlockText = Globalize.translate('ButtonUnlockWithPurchase');
if (unlockableProductInfo.price) {
unlockText = Globalize.translate('ButtonUnlockPrice', unlockableProductInfo.price);
}
html += '<p>';
html += '<paper-button raised class="secondary block btnPurchase" data-feature="' + unlockableProductInfo.feature + '"><iron-icon icon="check"></iron-icon><span>' + unlockText + '</span></paper-button>';
html += '</p>';
}
for (var i = 0, length = subscriptionOptions.length; i < length; i++) {
hasProduct = true;
html += '<p>';
html += '<paper-button raised class="submit block btnPurchase" data-email="true" data-feature="' + subscriptionOptions[i].feature + '"><iron-icon icon="check"></iron-icon><span>';
html += subscriptionOptions[i].buttonText;
html += '</span></paper-button>';
html += '</p>';
}
if (hasProduct && IapManager.restorePurchase) {
html += '<p>';
html += '<paper-button raised class="secondary block btnRestorePurchase" style="background-color: #673AB7;"><iron-icon icon="check"></iron-icon><span>' + Globalize.translate('ButtonRestorePreviousPurchase') + '</span></paper-button>';
html += '</p>';
}
if (subscriptionOptions.length) {
html += '<br/>';
html += '<h1>' + Globalize.translate('HeaderBenefitsEmbyPremiere') + '</h1>';
html += '<div class="paperList" style="margin-bottom:1em;">';
html += getSubscriptionBenefits().map(getSubscriptionBenefitHtml).join('');
html += '</div>';
}
if (dialogOptions.enablePlayMinute) {
html += '<p>';
html += '<paper-button raised class="secondary block btnCloseDialog subdued"><iron-icon icon="play-arrow"></iron-icon><span>' + Globalize.translate('ButtonPlayOneMinute') + '</span></paper-button>';
html += '</p>';
}
html += '</form>';
html += '</div>';
dlg.innerHTML = html;
document.body.appendChild(dlg);
initInAppPurchaseElementEvents(dlg, dialogOptions.feature, deferred);
PaperDialogHelper.openWithHash(dlg, 'iap');
$('.btnCloseDialog', dlg).on('click', function () {
PaperDialogHelper.close(dlg);
});
$(dlg).on('iron-overlay-closed', function () {
if (window.TabBar) {
TabBar.show();
}
});
dlg.classList.add('inAppPurchaseOverlay');
}
function getSubscriptionBenefits() {
var list = [];
list.push({
name: Globalize.translate('CoverArt'),
icon: 'photo',
text: Globalize.translate('CoverArtFeatureDescription')
});
list.push({
name: Globalize.translate('HeaderFreeApps'),
icon: 'check',
text: Globalize.translate('FreeAppsFeatureDescription')
});
if (Dashboard.capabilities().SupportsSync) {
list.push({
name: Globalize.translate('HeaderMobileSync'),
icon: 'sync',
text: Globalize.translate('MobileSyncFeatureDescription')
});
}
else if (AppInfo.isNativeApp) {
list.push({
name: Globalize.translate('HeaderCloudSync'),
icon: 'sync',
text: Globalize.translate('CloudSyncFeatureDescription')
});
}
else {
list.push({
name: Globalize.translate('HeaderCinemaMode'),
icon: 'movie',
text: Globalize.translate('CinemaModeFeatureDescription')
});
}
return list;
}
function getSubscriptionBenefitHtml(item) {
var html = '';
html += '<paper-icon-item>';
html += '<paper-fab mini style="background-color:#52B54B;" icon="' + item.icon + '" item-icon></paper-fab>';
html += '<paper-item-body three-line>';
html += '<a class="clearLink" href="https://emby.media/premiere" target="_blank">';
html += '<div>';
html += item.name;
html += '</div>';
html += '<div secondary style="white-space:normal;">';
html += item.text;
html += '</div>';
html += '</a>';
html += '</paper-item-body>';
html += '</paper-icon-item>';
return html;
}
function initInAppPurchaseElementEvents(elem, feature, deferred) {
isCancelled = true;
$('.btnPurchase', elem).on('click', function () {
isCancelled = false;
if (this.getAttribute('data-email') == 'true') {
acquireEmail(this.getAttribute('data-feature'));
} else {
IapManager.beginPurchase(this.getAttribute('data-feature'));
}
});
$('.btnRestorePurchase', elem).on('click', function () {
isCancelled = false;
IapManager.restorePurchase();
});
$(elem).on('iron-overlay-closed', function () {
clearCurrentDisplayingInfo();
var overlay = this;
if (isCancelled) {
if (feature == 'playback') {
Dashboard.alert({
message: Globalize.translate('ThankYouForTryingEnjoyOneMinute'),
title: Globalize.translate('HeaderTryPlayback'),
callback: function () {
deferred.reject();
$(overlay).remove();
}
});
} else {
deferred.reject();
$(overlay).remove();
}
} else {
$(this).remove();
}
});
}
function showInAppPurchaseInfo(subscriptionOptions, unlockableProductInfo, dialogOptions, deferred) {
require(['components/paperdialoghelper'], function () {
if (window.TabBar) {
TabBar.hide();
}
showInAppPurchaseElement(subscriptionOptions, unlockableProductInfo, dialogOptions, deferred);
currentDisplayingDeferred = deferred;
});
}
function acquireEmail(feature) {
if (ConnectionManager.isLoggedIntoConnect()) {
var connectUser = ConnectionManager.connectUser();
if (connectUser && connectUser.Email) {
IapManager.beginPurchase(feature, connectUser.Email);
return;
}
}
promptForEmail(feature);
}
function promptForEmail(feature) {
require(['prompt'], function (prompt) {
prompt({
text: Globalize.translate('TextPleaseEnterYourEmailAddressForSubscription'),
title: Globalize.translate('HeaderEmailAddress'),
callback: function (email) {
if (email) {
IapManager.beginPurchase(feature, email);
}
}
});
});
}
function onProductUpdated(e, product) {
if (product.owned) {
var deferred = currentDisplayingDeferred;
if (deferred && currentDisplayingProductInfos.filter(function (p) {
return product.id == p.id;
}).length) {
isCancelled = false;
cancelInAppPurchase();
deferred.resolve();
}
}
}
function validateSync(deferred) {
Dashboard.getPluginSecurityInfo().then(function (pluginSecurityInfo) {
if (pluginSecurityInfo.IsMBSupporter) {
deferred.resolve();
return;
}
function onRegistrationInfoResponse(registrationInfo) {
if (registrationInfo.IsRegistered) {
deferred.resolve();
return;
}
IapManager.getSubscriptionOptions().then(function (subscriptionOptions) {
var dialogOptions = {
title: Globalize.translate('HeaderUnlockSync'),
feature: 'sync'
};
showInAppPurchaseInfo(subscriptionOptions, null, dialogOptions, deferred);
});
}
// Get supporter status
getRegistrationInfo('Sync').then(onRegistrationInfoResponse, function () {
onRegistrationInfoResponse({});
});
});
}
window.RegistrationServices = {
renderPluginInfo: function (page, pkg, pluginSecurityInfo) {
},
validateFeature: function (name) {
var deferred = DeferredBuilder.Deferred();
if (name == 'playback') {
validateFeature(name, deferred);
} else if (name == 'livetv') {
validateFeature(name, deferred);
} else if (name == 'sync') {
validateSync(deferred);
} else {
deferred.resolve();
}
return deferred.promise();
}
};
function onIapManagerLoaded() {
Events.on(IapManager, 'productupdated', onProductUpdated);
}
if (browserInfo.android) {
requirejs(['cordova/android/iap'], onIapManagerLoaded);
} else {
requirejs(['cordova/iap'], onIapManagerLoaded);
}
})();

View file

@ -1,32 +0,0 @@
(function () {
function searchMenu() {
var self = this;
self.show = function () {
cordova.searchbar.show();
};
self.hide = function () {
cordova.searchbar.hide();
};
document.addEventListener('searchEvent', function (data) {
Events.trigger(self, 'change', [data.text || '']);
}, true);
document.addEventListener('searchClosed', function (data) {
Events.trigger(self, 'closed');
}, true);
}
window.SearchMenu = new searchMenu();
})();

View file

@ -1,180 +0,0 @@
(function (globalScope) {
function stringToArrayBuffer(string) {
// UTF-16LE
var buf = new ArrayBuffer(string.length * 2);
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = string.length; i < strLen; i++) {
bufView[i] = string.charCodeAt(i);
}
return buf;
}
function arrayBufferToString(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function getResultCode(result) {
if (result != null && result.resultCode != null) {
return result.resultCode;
}
return result;
}
function closeSocket(socketId) {
try {
chrome.sockets.udp.close(socketId);
} catch (err) {
}
}
function findServersInternal(timeoutMs) {
var deferred = DeferredBuilder.Deferred();
var servers = [];
// Expected server properties
// Name, Id, Address, EndpointAddress (optional)
var chrome = globalScope.chrome;
if (!chrome) {
deferred.resolveWith(null, [servers]);
return deferred.promise();
}
var timeout;
var socketId;
function onTimerExpired() {
deferred.resolveWith(null, [servers]);
if (socketId) {
chrome.sockets.udp.onReceive.removeListener(onReceive);
closeSocket(socketId);
}
}
function startTimer() {
Logger.log('starting udp receive timer with timeout ms: ' + timeoutMs);
timeout = setTimeout(onTimerExpired, timeoutMs);
}
function onReceive(info) {
try {
Logger.log('ServerDiscovery message received');
Logger.log(info);
if (info != null && info.socketId == socketId) {
var json = arrayBufferToString(info.data);
Logger.log('Server discovery json: ' + json);
var server = JSON.parse(json);
server.RemoteAddress = info.remoteAddress;
if (info.remotePort) {
server.RemoteAddress += ':' + info.remotePort;
}
servers.push(server);
}
} catch (err) {
Logger.log('Error receiving server info: ' + err);
}
}
var port = 7359;
Logger.log('chrome.sockets.udp.create');
startTimer();
chrome.sockets.udp.create(function (createInfo) {
if (!createInfo) {
Logger.log('create fail');
return;
}
if (!createInfo.socketId) {
Logger.log('create fail');
return;
}
socketId = createInfo.socketId;
Logger.log('chrome.sockets.udp.bind');
chrome.sockets.udp.bind(createInfo.socketId, '0.0.0.0', 0, function (bindResult) {
if (getResultCode(bindResult) != 0) {
Logger.log('bind fail: ' + bindResult);
return;
}
var data = stringToArrayBuffer('who is EmbyServer?');
Logger.log('chrome.sockets.udp.send');
chrome.sockets.udp.send(createInfo.socketId, data, '255.255.255.255', port, function (sendResult) {
if (getResultCode(sendResult) != 0) {
Logger.log('send fail: ' + sendResult);
} else {
chrome.sockets.udp.onReceive.addListener(onReceive);
Logger.log('sendTo: success ' + port);
}
});
});
});
return deferred.promise();
}
globalScope.ServerDiscovery = {
findServers: function (timeoutMs) {
var deferred = DeferredBuilder.Deferred();
deviceReadyPromise.then(function () {
try {
findServersInternal(timeoutMs).then(function (result) {
deferred.resolveWith(null, [result]);
}, function () {
deferred.resolveWith(null, [[]]);
});
} catch (err) {
deferred.resolveWith(null, [[]]);
}
});
return deferred.promise();
}
};
var deviceReadyDeferred = DeferredBuilder.Deferred();
var deviceReadyPromise = deviceReadyDeferred.promise();
document.addEventListener("deviceready", function () {
deviceReadyDeferred.resolve();
}, false);
})(window);

View file

@ -1,22 +0,0 @@
(function () {
function showMenu(options, successCallback, cancelCallback) {
var shareInfo = options.share;
window.plugins.socialsharing.share(shareInfo.Overview, shareInfo.Name, shareInfo.ImageUrl, shareInfo.Url, function () {
successCallback(options);
}, function () {
cancelCallback(options);
});
}
window.SharingWidget = {
showMenu: showMenu
};
})();

View file

@ -1,28 +0,0 @@
(function () {
// Handle the volume down button
//
function onVolumeDownKeyDown() {
MediaController.volumeDown();
}
// Handle the volume up button
//
function onVolumeUpKeyDown() {
MediaController.volumeUp();
}
$(MediaController).on('playerchange', function (e, newPlayer, newTarget) {
document.removeEventListener("volumedownbutton", onVolumeDownKeyDown, false);
document.removeEventListener("volumeupbutton", onVolumeUpKeyDown, false);
if (!newPlayer.localPlayer) {
document.addEventListener("volumedownbutton", onVolumeDownKeyDown, false);
document.addEventListener("volumeupbutton", onVolumeUpKeyDown, false);
}
});
})();

View file

@ -1,87 +0,0 @@
(function (globalScope) {
function getResultCode(result) {
if (result != null && result.resultCode != null) {
return result.resultCode;
}
return result;
}
function closeSocket(socketId) {
try {
chrome.sockets.udp.close(socketId);
} catch (err) {
}
}
function stringToArrayBuffer(string) {
// UTF-16LE
var buf = new ArrayBuffer(string.length * 2);
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = string.length; i < strLen; i++) {
bufView[i] = string.charCodeAt(i);
}
return buf;
}
// https://github.com/agnat/node_wake_on_lan/blob/master/wake_on_lan.js
globalScope.WakeOnLan = {
send: function (info) {
var deferred = DeferredBuilder.Deferred();
var chrome = globalScope.chrome;
if (!chrome) {
deferred.resolve();
return deferred.promise();
}
var port = info.Port || 9;
//chrome.sockets.udp.create(function (createInfo) {
// if (!createInfo) {
// console.log('create fail');
// return;
// }
// if (!createInfo.socketId) {
// console.log('create fail');
// return;
// }
// var socketId = createInfo.socketId;
// console.log('chrome.sockets.udp.bind');
// chrome.sockets.udp.bind(createInfo.socketId, '0.0.0.0', 0, function (bindResult) {
// if (getResultCode(bindResult) != 0) {
// console.log('bind fail: ' + bindResult);
// deferred.resolve();
// closeSocket(socketId);
// }
// var data = stringToArrayBuffer('who is EmbyServer?');
// console.log('chrome.sockets.udp.send');
// chrome.sockets.udp.send(createInfo.socketId, data, '255.255.255.255', port, function (sendResult) {
// deferred.resolve();
// closeSocket(socketId);
// });
// });
//});
deferred.resolve();
return deferred.promise();
}
};
})(window);

View file

@ -4,7 +4,7 @@
<title>${TitleSync}</title>
</head>
<body>
<div id="mySyncActivityPage" data-role="page" class="page libraryPage syncActivityPage mySyncPage noSecondaryNavPage" data-contextname="${TitleSync}" data-require="scripts/syncactivity,scripts/taskbutton,scripts/mysync">
<div id="mySyncActivityPage" data-role="page" class="page libraryPage syncActivityPage mySyncPage noSecondaryNavPage" data-contextname="${TitleSync}" data-require="scripts/syncactivity,scripts/taskbutton,scripts/mysync,paper-spinner">
<div data-role="content">

View file

@ -459,11 +459,13 @@ var Dashboard = {
} else {
require(['paper-spinner'], function () {
elem = document.createElement("paper-spinner");
elem.classList.add('docspinner');
document.body.appendChild(elem);
elem.active = true;
});
}
},
@ -1842,7 +1844,8 @@ var AppInfo = {};
requirejs.config({
map: {
'*': {
'css': 'components/requirecss'
'css': 'components/requirecss',
'html': 'components/requirehtml'
}
},
urlArgs: urlArgs,
@ -1852,6 +1855,8 @@ var AppInfo = {};
define("cryptojs-sha1", ["apiclient/sha1"]);
define("cryptojs-md5", ["apiclient/md5"]);
define("paper-spinner", []);
}
function init(promiseResolve, hostingAppInfo) {
@ -2137,12 +2142,14 @@ var AppInfo = {};
}
if (Dashboard.isRunningInCordova()) {
deps.push('scripts/registrationservices');
deps.push('cordova/registrationservices');
deps.push('cordova/back');
if (browserInfo.android) {
deps.push('cordova/android/androidcredentials');
}
} else {
deps.push('scripts/registrationservices');
}
if (browserInfo.msie) {
@ -2399,17 +2406,11 @@ var AppInfo = {};
function onWebComponentsReady() {
var link = document.createElement('link');
link.rel = 'import';
link.onload = function () {
require(['html!vulcanize-out.html'], function () {
getHostingAppInfo().then(function (hostingAppInfo) {
init(resolve, hostingAppInfo);
});
};
link.href = "vulcanize-out.html?v=" + window.dashboardVersion;
document.head.appendChild(link);
});
}
setBrowserInfo(isMobile);

View file

@ -141,6 +141,8 @@ See [iron-iconset](#iron-iconset) and [iron-iconset-svg](#iron-iconset-svg) for
<g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z" /></g>
<g id="notifications-active"><path d="M6.58 3.58L5.15 2.15C2.76 3.97 1.18 6.8 1.03 10h2c.15-2.65 1.51-4.97 3.55-6.42zM19.97 10h2c-.15-3.2-1.73-6.03-4.13-7.85l-1.43 1.43c2.05 1.45 3.41 3.77 3.56 6.42zm-1.97.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2v-5.5zM11.5 22c.14 0 .27-.01.4-.04.65-.13 1.19-.58 1.44-1.18.1-.24.16-.5.16-.78h-4c0 1.1.9 2 2 2z" /></g>
<g id="notifications-off"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z" /></g>
<g id="expand-less"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z" /></g>
<g id="expand-more"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z" /></g>
</defs>
</svg>
</iron-iconset-svg>

View file

@ -12312,6 +12312,7 @@ The `aria-labelledby` attribute will be set to the header element, if one exists
},
attached: function() {
this.style.display = 'none';
this.queryChanged();
},
@ -13627,7 +13628,8 @@ is separate from validation, and `allowed-pattern` does not affect how the input
*/
/**
* The label for this input. Bind this to `<paper-input-container>`'s `label` property.
* The label for this input. Bind this to `<label>`'s content and `hidden` property, e.g.
* `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
*/
label: {
type: String
@ -13882,21 +13884,21 @@ is separate from validation, and `allowed-pattern` does not affect how the input
},
/**
* Bind this to the `<input is="iron-input">`'s `results` property, , used with type=search.
* Bind this to the `<input is="iron-input">`'s `results` property, used with type=search.
*/
results: {
type: Number
},
/**
* Bind this to the `<input is="iron-input">`'s `accept` property, , used with type=file.
* Bind this to the `<input is="iron-input">`'s `accept` property, used with type=file.
*/
accept: {
type: String
},
/**
* Bind this to the `<input is="iron-input">`'s `multiple` property, , used with type=file.
* Bind this to the `<input is="iron-input">`'s `multiple` property, used with type=file.
*/
multiple: {
type: Boolean
@ -13915,13 +13917,22 @@ is separate from validation, and `allowed-pattern` does not affect how the input
},
listeners: {
'addon-attached': '_onAddonAttached'
'addon-attached': '_onAddonAttached',
'focus': '_onFocus'
},
observers: [
'_focusedControlStateChanged(focused)'
],
keyBindings: {
'shift+tab:keydown': '_onShiftTabDown'
},
hostAttributes: {
tabindex: 0
},
/**
* Returns a reference to the input element.
*/
@ -13929,6 +13940,13 @@ is separate from validation, and `allowed-pattern` does not affect how the input
return this.$.input;
},
/**
* Returns a reference to the focusable element.
*/
get _focusableElement() {
return this.inputElement;
},
attached: function() {
this._updateAriaLabelledBy();
},
@ -13962,6 +13980,29 @@ is separate from validation, and `allowed-pattern` does not affect how the input
return this.inputElement.validate();
},
/**
* Forward focus to inputElement
*/
_onFocus: function() {
if (!this._shiftTabPressed) {
this._focusableElement.focus();
}
},
/**
* Handler that is called when a shift+tab keypress is detected by the menu.
*
* @param {CustomEvent} event A key combination event.
*/
_onShiftTabDown: function(event) {
var oldTabIndex = this.getAttribute('tabindex');
this._shiftTabPressed = true;
this.setAttribute('tabindex', '-1');
this.async(function() {
this.setAttribute('tabindex', oldTabIndex);
this._shiftTabPressed = false;
}, 1);
},
/**
* If `autoValidate` is true, then validates the element.
*/
@ -14046,7 +14087,11 @@ is separate from validation, and `allowed-pattern` does not affect how the input
};
/** @polymerBehavior */
Polymer.PaperInputBehavior = [Polymer.IronControlState, Polymer.PaperInputBehaviorImpl];
Polymer.PaperInputBehavior = [
Polymer.IronControlState,
Polymer.IronA11yKeysBehavior,
Polymer.PaperInputBehaviorImpl
];
</script>
<script>
@ -18239,7 +18284,7 @@ is separate from validation, and `allowed-pattern` does not affect how the input
.input-content {
position: relative;
@apply(--layout-horizontal);
@apply(--layout-end);
@apply(--layout-center);
}
.input-content ::content label,
@ -18738,7 +18783,7 @@ is separate from validation, and `allowed-pattern` does not affect how the input
<label hidden$="[[!label]]">[[label]]</label>
<input is="iron-input" id="input" aria-labelledby$="[[_ariaLabelledBy]]" aria-describedby$="[[_ariaDescribedBy]]" disabled$="[[disabled]]" bind-value="{{value}}" invalid="{{invalid}}" prevent-invalid-input="[[preventInvalidInput]]" allowed-pattern="[[allowedPattern]]" validator="[[validator]]" type$="[[type]]" pattern$="[[pattern]]" required$="[[required]]" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" minlength$="[[minlength]]" maxlength$="[[maxlength]]" min$="[[min]]" max$="[[max]]" step$="[[step]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" list$="[[list]]" size$="[[size]]" autocapitalize$="[[autocapitalize]]" autocorrect$="[[autocorrect]]" on-change="_onChange" autosave$="[[autosave]]" results$="[[results]]" accept$="[[accept]]" multiple$="[[multiple]]">
<input is="iron-input" id="input" aria-labelledby$="[[_ariaLabelledBy]]" aria-describedby$="[[_ariaDescribedBy]]" disabled$="[[disabled]]" bind-value="{{value}}" invalid="{{invalid}}" prevent-invalid-input="[[preventInvalidInput]]" allowed-pattern="[[allowedPattern]]" validator="[[validator]]" type$="[[type]]" pattern$="[[pattern]]" required$="[[required]]" autocomplete$="[[autocomplete]]" autofocus$="[[autofocus]]" inputmode$="[[inputmode]]" minlength$="[[minlength]]" maxlength$="[[maxlength]]" min$="[[min]]" max$="[[max]]" step$="[[step]]" name$="[[name]]" placeholder$="[[placeholder]]" readonly$="[[readonly]]" list$="[[list]]" size$="[[size]]" autocapitalize$="[[autocapitalize]]" autocorrect$="[[autocorrect]]" on-change="_onChange" tabindex$="[[tabindex]]" autosave$="[[autosave]]" results$="[[results]]" accept$="[[accept]]" multiple$="[[multiple]]">
<content select="[suffix]"></content>
@ -18761,8 +18806,7 @@ is separate from validation, and `allowed-pattern` does not affect how the input
behaviors: [
Polymer.IronFormElementBehavior,
Polymer.PaperInputBehavior,
Polymer.IronControlState
Polymer.PaperInputBehavior
]
});
</script>
@ -19892,7 +19936,11 @@ is separate from validation, and `allowed-pattern` does not affect how the input
_ariaDescribedByChanged: function(ariaDescribedBy) {
this.$.input.textarea.setAttribute('aria-describedby', ariaDescribedBy);
}
},
get _focusableElement() {
return this.$.input.textarea;
},
});
</script>
<dom-module id="paper-checkbox" assetpath="bower_components/paper-checkbox/">
@ -20668,6 +20716,8 @@ is separate from validation, and `allowed-pattern` does not affect how the input
<g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"></path></g>
<g id="notifications-active"><path d="M6.58 3.58L5.15 2.15C2.76 3.97 1.18 6.8 1.03 10h2c.15-2.65 1.51-4.97 3.55-6.42zM19.97 10h2c-.15-3.2-1.73-6.03-4.13-7.85l-1.43 1.43c2.05 1.45 3.41 3.77 3.56 6.42zm-1.97.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-2.87.68-5 3.25-5 6.32V16l-2 2v1h17v-1l-2-2v-5.5zM11.5 22c.14 0 .27-.01.4-.04.65-.13 1.19-.58 1.44-1.18.1-.24.16-.5.16-.78h-4c0 1.1.9 2 2 2z"></path></g>
<g id="notifications-off"><path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z"></path></g>
<g id="expand-less"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"></path></g>
<g id="expand-more"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path></g>
</defs>
</svg>
</iron-iconset-svg>