diff --git a/dashboard-ui/apiclient/connectionmanager.js b/dashboard-ui/apiclient/connectionmanager.js index f5afddf753..fcae17cacc 100644 --- a/dashboard-ui/apiclient/connectionmanager.js +++ b/dashboard-ui/apiclient/connectionmanager.js @@ -880,7 +880,7 @@ var tests = []; if (server.LastConnectionMode != null) { - tests.push(server.LastConnectionMode); + //tests.push(server.LastConnectionMode); } if (tests.indexOf(MediaBrowser.ConnectionMode.Manual) == -1) { tests.push(MediaBrowser.ConnectionMode.Manual); } if (tests.indexOf(MediaBrowser.ConnectionMode.Local) == -1) { tests.push(MediaBrowser.ConnectionMode.Local); } diff --git a/dashboard-ui/bower_components/iron-behaviors/.bower.json b/dashboard-ui/bower_components/iron-behaviors/.bower.json index 5d7b926afb..f499351883 100644 --- a/dashboard-ui/bower_components/iron-behaviors/.bower.json +++ b/dashboard-ui/bower_components/iron-behaviors/.bower.json @@ -27,14 +27,14 @@ "web-component-tester": "*", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, - "homepage": "https://github.com/polymerelements/iron-behaviors", + "homepage": "https://github.com/PolymerElements/iron-behaviors", "_release": "1.0.8", "_resolution": { "type": "version", "tag": "v1.0.8", "commit": "663ad706b43989f4961d945b8116cf4db346532f" }, - "_source": "git://github.com/polymerelements/iron-behaviors.git", + "_source": "git://github.com/PolymerElements/iron-behaviors.git", "_target": "^1.0.0", - "_originalSource": "polymerelements/iron-behaviors" + "_originalSource": "PolymerElements/iron-behaviors" } \ No newline at end of file diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json index 42470f3ae4..73b5a092b4 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/.bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.0.8", + "version": "1.0.9", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, @@ -35,11 +35,11 @@ "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, "homepage": "https://github.com/polymerelements/iron-overlay-behavior", - "_release": "1.0.8", + "_release": "1.0.9", "_resolution": { "type": "version", - "tag": "v1.0.8", - "commit": "cf25fe1ff2f585fa84190537bf62b94eb1579aad" + "tag": "v1.0.9", + "commit": "87f7763d323fffa07357a08777ad831b7c2c2fb8" }, "_source": "git://github.com/polymerelements/iron-overlay-behavior.git", "_target": "^1.0.0", diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json index 4deb8d2b36..30a3f04d91 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/bower.json +++ b/dashboard-ui/bower_components/iron-overlay-behavior/bower.json @@ -1,6 +1,6 @@ { "name": "iron-overlay-behavior", - "version": "1.0.8", + "version": "1.0.9", "license": "http://polymer.github.io/LICENSE.txt", "description": "Provides a behavior for making an element an overlay", "private": true, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html index df52db1a5d..837759f7e7 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/iron-overlay-behavior.html @@ -28,7 +28,8 @@ intent. Closing generally implies that the user acknowledged the content on the it will cancel whenever the user taps outside it or presses the escape key. This behavior is configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. `close()` should be called explicitly by the implementer when the user interacts with a control -in the overlay element. +in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' +event. Call `preventDefault` on this event to prevent the overlay from closing. ### Positioning @@ -199,6 +200,11 @@ context. You should place this element as a child of `` whenever possible. * Cancels the overlay. */ cancel: function() { + var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true}); + if (cancelEvent.defaultPrevented) { + return; + } + this.opened = false; this._setCanceled(true); }, diff --git a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html index 8fd17eadf4..a352a4b189 100644 --- a/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html +++ b/dashboard-ui/bower_components/iron-overlay-behavior/test/iron-overlay-behavior.html @@ -180,6 +180,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }); test('cancel an overlay by clicking outside', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + done(); + }); + Polymer.Base.fire.call(document, 'click'); + }); + }); + + test('close an overlay by clicking outside', function(done) { runAfterOpen(overlay, function() { overlay.addEventListener('iron-overlay-closed', function(event) { assert.isTrue(event.detail.canceled, 'overlay is canceled'); @@ -189,7 +198,35 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }); }); + test('cancel event can be prevented', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + event.preventDefault(); + }); + var closedListener = function(event) { + throw new Error('iron-overlay-closed should not fire'); + }; + overlay.addEventListener('iron-overlay-closed', closedListener); + Polymer.Base.fire.call(document, 'click'); + setTimeout(function() { + overlay.removeEventListener('iron-overlay-closed', closedListener); + done(); + }, 10); + }); + }); + test('cancel an overlay with esc key', function(done) { + runAfterOpen(overlay, function() { + overlay.addEventListener('iron-overlay-canceled', function(event) { + done(); + }); + fireEvent('keydown', { + keyCode: 27 + }, document); + }); + }); + + test('close an overlay with esc key', function(done) { runAfterOpen(overlay, function() { overlay.addEventListener('iron-overlay-closed', function(event) { assert.isTrue(event.detail.canceled, 'overlay is canceled'); diff --git a/dashboard-ui/components/imagedownloader/imagedownloader.js b/dashboard-ui/components/imagedownloader/imagedownloader.js index 71f34e0569..5edbff50cd 100644 --- a/dashboard-ui/components/imagedownloader/imagedownloader.js +++ b/dashboard-ui/components/imagedownloader/imagedownloader.js @@ -269,22 +269,7 @@ currentItemId = itemId; currentItemType = itemType; - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - - // without this safari will scroll the background instead of the dialog contents - // but not needed here since this is already on top of an existing dialog - // dlg.setAttribute('modal', 'modal'); - - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/imageeditor/imageeditor.js b/dashboard-ui/components/imageeditor/imageeditor.js index 4e3ff0f02f..d372d81de7 100644 --- a/dashboard-ui/components/imageeditor/imageeditor.js +++ b/dashboard-ui/components/imageeditor/imageeditor.js @@ -230,19 +230,7 @@ ApiClient.getItem(Dashboard.getCurrentUserId(), itemId).done(function (item) { - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - // without this safari will scroll the background instead of the dialog contents - dlg.setAttribute('modal', 'modal'); - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/imageuploader/imageuploader.js b/dashboard-ui/components/imageuploader/imageuploader.js index 29f45b347e..981e41b8f7 100644 --- a/dashboard-ui/components/imageuploader/imageuploader.js +++ b/dashboard-ui/components/imageuploader/imageuploader.js @@ -132,22 +132,7 @@ currentItemId = itemId; - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - - // without this safari will scroll the background instead of the dialog contents - // but not needed here since this is already on top of an existing dialog - // dlg.setAttribute('modal', 'modal'); - - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/components/paperdialoghelper.js b/dashboard-ui/components/paperdialoghelper.js index 166bb1ae9a..6b198d1d53 100644 --- a/dashboard-ui/components/paperdialoghelper.js +++ b/dashboard-ui/components/paperdialoghelper.js @@ -66,15 +66,41 @@ function close(dlg) { if (enableHashChange()) { - history.back(); + + if (dlg.opened) { + history.back(); + } + } else { dlg.close(); } } + function createDialog() { + var dlg = document.createElement('paper-dialog'); + + dlg.setAttribute('with-backdrop', 'with-backdrop'); + dlg.setAttribute('role', 'alertdialog'); + + // without this safari will scroll the background instead of the dialog contents + // but not needed here since this is already on top of an existing dialog + dlg.setAttribute('modal', 'modal'); + + // seeing max call stack size exceeded in the debugger with this + dlg.setAttribute('noAutoFocus', 'noAutoFocus'); + dlg.entryAnimation = 'scale-up-animation'; + dlg.exitAnimation = 'fade-out-animation'; + dlg.classList.add('fullscreen-editor-paper-dialog'); + dlg.classList.add('ui-body-b'); + dlg.classList.add('smoothScrollY'); + + return dlg; + } + globalScope.PaperDialogHelper = { openWithHash: openWithHash, - close: close + close: close, + createDialog: createDialog }; })(this); \ No newline at end of file diff --git a/dashboard-ui/components/prompt.js b/dashboard-ui/components/prompt.js new file mode 100644 index 0000000000..19ba0763c9 --- /dev/null +++ b/dashboard-ui/components/prompt.js @@ -0,0 +1,10 @@ +define([], function () { + return function (options) { + + var result = prompt(options.text, options.defaultText || ''); + + if (options.callback) { + options.callback(result); + } + }; +}); \ No newline at end of file diff --git a/dashboard-ui/components/subtitleeditor/subtitleeditor.js b/dashboard-ui/components/subtitleeditor/subtitleeditor.js index da7774d72e..45882a72d0 100644 --- a/dashboard-ui/components/subtitleeditor/subtitleeditor.js +++ b/dashboard-ui/components/subtitleeditor/subtitleeditor.js @@ -335,19 +335,7 @@ ApiClient.getItem(Dashboard.getCurrentUserId(), itemId).done(function (item) { - var dlg = document.createElement('paper-dialog'); - - dlg.setAttribute('with-backdrop', 'with-backdrop'); - dlg.setAttribute('role', 'alertdialog'); - // without this safari will scroll the background instead of the dialog contents - dlg.setAttribute('modal', 'modal'); - // seeing max call stack size exceeded in the debugger with this - dlg.setAttribute('noAutoFocus', 'noAutoFocus'); - dlg.entryAnimation = 'scale-up-animation'; - dlg.exitAnimation = 'fade-out-animation'; - dlg.classList.add('fullscreen-editor-paper-dialog'); - dlg.classList.add('ui-body-b'); - dlg.classList.add('smoothScrollY'); + var dlg = PaperDialogHelper.createDialog(); var html = ''; html += '

'; diff --git a/dashboard-ui/cordova/android/iap.js b/dashboard-ui/cordova/android/iap.js index 82038233f9..7f3b1bb2ba 100644 --- a/dashboard-ui/cordova/android/iap.js +++ b/dashboard-ui/cordova/android/iap.js @@ -1,8 +1,16 @@ (function () { - var unlockId = "com.mb.android.unlock"; var updatedProducts = []; + function getStoreFeatureId(feature) { + + if (feature == 'embypremieremonthly') { + return "emby.supporter.monthly"; + } + + return "com.mb.android.unlock"; + } + function updateProductInfo(id, owned, price) { updatedProducts = updatedProducts.filter(function (r) { @@ -20,7 +28,10 @@ Events.trigger(IapManager, 'productupdated', [product]); } - function getProduct(id) { + function getProduct(feature) { + + var id = getStoreFeatureId(feature); + var products = updatedProducts.filter(function (r) { return r.id == id; }); @@ -28,13 +39,14 @@ return products.length ? products[0] : null; } - function isPurchaseAvailable(id) { + function isPurchaseAvailable(feature) { return NativeIapManager.isStoreAvailable(); } - function beginPurchase(id) { - return MainActivity.beginPurchase(id); + function beginPurchase(feature, email) { + var id = getStoreFeatureId(feature); + return MainActivity.beginPurchase(id, email); } function onPurchaseComplete(result) { @@ -45,7 +57,31 @@ } function refreshPurchases() { - NativeIapManager.isPurchased(unlockId, "window.IapManager.updateProduct"); + NativeIapManager.isPurchased(getStoreFeatureId("") + "|" + getStoreFeatureId("embypremieremonthly"), "window.IapManager.updateProduct"); + //NativeIapManager.isPurchased(getStoreFeatureId("embypremieremonthly"), "window.IapManager.updateProduct"); + } + + function getSubscriptionOptions() { + var deferred = DeferredBuilder.Deferred(); + + var options = []; + + options.push({ + feature: 'embypremieremonthly', + buttonText: 'EmbyPremiereMonthlyWithPrice' + }); + + options = options.filter(function (o) { + return getProduct(o.feature) != null; + + }).map(function (o) { + + o.buttonText = Globalize.translate(o.buttonText, o.price); + return o; + }); + + deferred.resolveWith(null, [options]); + return deferred.promise(); } window.IapManager = { @@ -53,7 +89,9 @@ getProductInfo: getProduct, updateProduct: updateProductInfo, beginPurchase: beginPurchase, - onPurchaseComplete: onPurchaseComplete + onPurchaseComplete: onPurchaseComplete, + getStoreFeatureId: getStoreFeatureId, + getSubscriptionOptions: getSubscriptionOptions }; refreshPurchases(); diff --git a/dashboard-ui/cordova/iap.js b/dashboard-ui/cordova/iap.js index 616d4022a2..e7b07398c4 100644 --- a/dashboard-ui/cordova/iap.js +++ b/dashboard-ui/cordova/iap.js @@ -1,10 +1,16 @@ (function () { - var unlockAlias = "premium features"; - var unlockAppProductId = 'appunlock'; - var updatedProducts = []; + function getStoreFeatureId(feature) { + + if (feature == 'embypremieremonthly') { + return 'emby.subscription.monthly'; + } + + return 'appunlock'; + } + function updateProductInfo(product) { updatedProducts = updatedProducts.filter(function (r) { @@ -16,17 +22,9 @@ Events.trigger(IapManager, 'productupdated', [product]); } - function normalizeId(id) { + function getProduct(feature) { - // This is what i named it in itunes - id = id.replace('premiumunlock', 'appunlock'); - - return id; - } - - function getProduct(id) { - - id = normalizeId(id); + var id = getStoreFeatureId(feature); var products = updatedProducts.filter(function (r) { return r.id == id; @@ -35,19 +33,19 @@ return products.length ? products[0] : null; } - function isPurchaseAvailable(id) { - var product = getProduct(id); + function isPurchaseAvailable(feature) { + + var product = getProduct(feature); return product != null && product.valid /*&& product.canPurchase*/; } - function beginPurchase(id) { - id = normalizeId(id); + function beginPurchase(feature, email) { + var id = getStoreFeatureId(feature); store.order(id); } function restorePurchase(id) { - id = normalizeId(id); store.refresh(); } @@ -74,6 +72,36 @@ //callback(false, "Impossible to proceed with validation"); } + function initProduct(id, alias, type) { + + store.register({ + id: id, + alias: alias, + type: type + }); + + // When purchase of the full version is approved, + // show some logs and finish the transaction. + store.when(id).approved(function (order) { + order.finish(); + }); + + store.when(id).verified(function (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'); + product.finish(); + } + updateProductInfo(product); + }); + } + function initializeStore() { // Let's set a pretty high verbosity level, so that we see a lot of stuff @@ -82,35 +110,8 @@ store.validator = validateProduct; - // iOS - store.register({ - id: unlockAppProductId, - alias: unlockAlias, - type: store.NON_CONSUMABLE - }); - - // When purchase of the full version is approved, - // show some logs and finish the transaction. - store.when(unlockAppProductId).approved(function (order) { - log('You just unlocked the FULL VERSION!'); - order.finish(); - }); - - store.when(unlockAppProductId).verified(function (p) { - log("verified"); - p.finish(); - }); - - // The play button can only be accessed when the user - // owns the full version. - store.when(unlockAppProductId).updated(function (product) { - - if (product.loaded && product.valid && product.state == store.APPROVED) { - Logger.log('finishing previously created transaction'); - product.finish(); - } - updateProductInfo(product); - }); + initProduct(getStoreFeatureId(""), "premium features", store.NON_CONSUMABLE); + initProduct(getStoreFeatureId("embypremieremonthly"), "emby premiere monthly", store.PAID_SUBSCRIPTION); // When every goes as expected, it's time to celebrate! // The "ready" event should be welcomed with music and fireworks, @@ -125,11 +126,36 @@ store.refresh(); } + function getSubscriptionOptions() { + var deferred = DeferredBuilder.Deferred(); + + var options = []; + + options.push({ + feature: 'embypremieremonthly', + buttonText: 'EmbyPremiereMonthlyWithPrice' + }); + + options = options.filter(function (o) { + return getProduct(o.feature) != null; + + }).map(function (o) { + + o.buttonText = Globalize.translate(o.buttonText, o.price); + return o; + }); + + deferred.resolveWith(null, [options]); + return deferred.promise(); + } + window.IapManager = { isPurchaseAvailable: isPurchaseAvailable, getProductInfo: getProduct, beginPurchase: beginPurchase, - restorePurchase: restorePurchase + restorePurchase: restorePurchase, + getStoreFeatureId: getStoreFeatureId, + getSubscriptionOptions: getSubscriptionOptions }; initializeStore(); diff --git a/dashboard-ui/cordova/ios/backgroundfetch.js b/dashboard-ui/cordova/ios/backgroundfetch.js index 102eae1cf3..d7f4c41fdc 100644 --- a/dashboard-ui/cordova/ios/backgroundfetch.js +++ b/dashboard-ui/cordova/ios/backgroundfetch.js @@ -4,11 +4,11 @@ function onDeviceReady() { - var fetcher = window.BackgroundFetch; + //var fetcher = window.BackgroundFetch; - fetcher.configure(onBackgroundFetch, onBackgroundFetchFailed, { - stopOnTerminate: false // <-- false is default - }); + //fetcher.configure(onBackgroundFetch, onBackgroundFetchFailed, { + // stopOnTerminate: false // <-- false is default + //}); } function onSyncFinish() { @@ -86,7 +86,7 @@ }); }); - pageClassOn('pageshow', "page", function () { + pageClassOn('pageshow', "libraryPage", function () { if (!Dashboard.getCurrentUserId()) { return; diff --git a/dashboard-ui/cordova/localassetmanager.js b/dashboard-ui/cordova/localassetmanager.js index d1dd0be3a5..32f3f8bc21 100644 --- a/dashboard-ui/cordova/localassetmanager.js +++ b/dashboard-ui/cordova/localassetmanager.js @@ -523,7 +523,7 @@ function downloadFile(url, localPath, enableBackground, enableNewDownloads) { if (!enableBackground) { - return downloadWithFileTransfer(url, localPath); + return downloadWithFileTransfer(url, localPath, enableBackground); } var deferred = DeferredBuilder.Deferred(); diff --git a/dashboard-ui/cordova/prompt.js b/dashboard-ui/cordova/prompt.js new file mode 100644 index 0000000000..f924d62308 --- /dev/null +++ b/dashboard-ui/cordova/prompt.js @@ -0,0 +1,17 @@ +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 || ''); + }; +}); \ No newline at end of file diff --git a/dashboard-ui/cordova/registrationservices.js b/dashboard-ui/cordova/registrationservices.js index ba3e970533..cc72ba8a6e 100644 --- a/dashboard-ui/cordova/registrationservices.js +++ b/dashboard-ui/cordova/registrationservices.js @@ -1,86 +1,56 @@ (function () { - function isAndroid() { - - return $.browser.android; - } - - function getPremiumUnlockFeatureId() { - - if (isAndroid()) { - return "com.mb.android.unlock"; - } - - return 'appunlock'; - } - - function validatePlayback(deferred) { - - // Don't require validation on android - if (isAndroid()) { - deferred.resolve(); - return; - } - - validateFeature(getPremiumUnlockFeatureId(), deferred); - } - - function validateLiveTV(deferred) { - - validateFeature(getPremiumUnlockFeatureId(), deferred); - } - function validateServerManagement(deferred) { deferred.resolve(); } - function getRegistrationInfo(feature, enableSupporterUnlock) { + function getRegistrationInfo(feature) { - if (!enableSupporterUnlock) { - var deferred = $.Deferred(); - deferred.resolveWith(null, [{}]); - return deferred.promise(); - } return ConnectionManager.getRegistrationInfo(feature, ApiClient); } var validatedFeatures = []; - function validateFeature(id, deferred) { + function validateFeature(feature, deferred) { - if (validatedFeatures.indexOf(id) != -1) { + var id = IapManager.getStoreFeatureId(feature); + + if (validatedFeatures.indexOf(feature) != -1) { deferred.resolve(); return; } - var info = IapManager.getProductInfo(id) || {}; + var info = IapManager.getProductInfo(feature) || {}; if (info.owned) { notifyServer(id); - validatedFeatures.push(id); + validatedFeatures.push(feature); deferred.resolve(); return; } var productInfo = { - enableSupporterUnlock: true, - enableAppUnlock: IapManager.isPurchaseAvailable(id), + enableAppUnlock: IapManager.isPurchaseAvailable(feature), id: id, - price: info.price + price: info.price, + feature: feature }; - var prefix = isAndroid() ? 'android' : 'ios'; + var prefix = $.browser.android ? 'android' : 'ios'; // Get supporter status - getRegistrationInfo(prefix + 'appunlock', productInfo.enableSupporterUnlock).done(function (registrationInfo) { + getRegistrationInfo(prefix + 'appunlock').done(function (registrationInfo) { if (registrationInfo.IsRegistered) { - validatedFeatures.push(id); + validatedFeatures.push(feature); deferred.resolve(); return; } - showInAppPurchaseInfo(productInfo, registrationInfo, deferred); + IapManager.getSubscriptionOptions().done(function (subscriptionOptions) { + + showInAppPurchaseInfo(productInfo, subscriptionOptions, registrationInfo, deferred); + }); }).fail(function () { deferred.reject(); @@ -114,42 +84,32 @@ }); } - function getInAppPurchaseElement(info) { + function getInAppPurchaseElement(info, subscriptionOptions) { - cancelInAppPurchase(); + var dlg = PaperDialogHelper.createDialog(); var html = ''; - html += '
'; - html += '
'; + html += '

'; + html += ''; + html += '
' + Globalize.translate('HeaderUnlockApp') + '
'; + html += '

'; + html += '
'; - html += '
'; - - html += '

' + Globalize.translate('HeaderUnlockApp') + '

'; - + html += '
'; html += '

'; - var showSupporterInfo = info.enableSupporterUnlock && !$.browser.safari; - - if (showSupporterInfo && info.enableAppUnlock) { + if (info.enableAppUnlock) { html += Globalize.translate('MessageUnlockAppWithPurchaseOrSupporter'); } - else if (showSupporterInfo) { + else { html += Globalize.translate('MessageUnlockAppWithSupporter'); - } else if (info.enableAppUnlock) { - html += Globalize.translate('MessageUnlockAppWithPurchase'); - } else { - html += ''; - html += Globalize.translate('MessagePaymentServicesUnavailable'); - html += ''; } html += '

'; - if (showSupporterInfo) { - html += '

'; - html += Globalize.translate('MessageToValidateSupporter'); - html += '

'; - } + html += '

'; + html += Globalize.translate('MessageToValidateSupporter'); + html += '

'; if (info.enableAppUnlock) { @@ -158,76 +118,118 @@ unlockText = Globalize.translate('ButtonUnlockPrice', info.price); } html += '

'; - html += '' + unlockText + ''; + html += '' + unlockText + ''; html += '

'; - - if (IapManager.restorePurchase) { - html += '

'; - html += '' + Globalize.translate('ButtonRestorePreviousPurchase') + ''; - html += '

'; - } } - html += '

'; - html += '' + Globalize.translate('ButtonCancel') + ''; - html += '

'; + for (var i = 0, length = subscriptionOptions.length; i < length; i++) { + html += '

'; + html += ''; + html += subscriptionOptions[i].buttonText; + html += ''; + html += '

'; + } + + if (IapManager.restorePurchase) { + html += '

'; + html += '' + Globalize.translate('ButtonRestorePreviousPurchase') + ''; + html += '

'; + } + + html += '
'; html += '
'; - html += '
'; - html += '
'; + dlg.innerHTML = html; + document.body.appendChild(dlg); - $(document.body).append(html); + // init dlg content here - return $('.inAppPurchaseOverlay'); + PaperDialogHelper.openWithHash(dlg, 'iap'); + + $('.btnCloseDialog', dlg).on('click', function () { + + PaperDialogHelper.close(dlg); + }); + + dlg.classList.add('inAppPurchaseOverlay'); + + return dlg; } function cancelInAppPurchase() { - $('.inAppPurchaseOverlay').remove(); + var elem = document.querySelector('.inAppPurchaseOverlay'); + if (elem) { + PaperDialogHelper.close(elem); + } } - var currentDisplayingProductInfo = null; + var currentDisplayingProductInfos = []; var currentDisplayingDeferred = null; + var isCancelled = true; function clearCurrentDisplayingInfo() { - currentDisplayingProductInfo = null; + currentDisplayingProductInfos = []; currentDisplayingDeferred = null; } - function showInAppPurchaseInfo(info, serverRegistrationInfo, deferred) { + function showInAppPurchaseInfo(info, subscriptionOptions, serverRegistrationInfo, deferred) { - var elem = getInAppPurchaseElement(info); + require(['components/paperdialoghelper'], function () { - currentDisplayingProductInfo = info; - currentDisplayingDeferred = deferred; - - $('.btnAppUnlock', elem).on('click', function () { - - IapManager.beginPurchase(info.id); - }); - - $('.btnRestorePurchase', elem).on('click', function () { - - IapManager.restorePurchase(info.id); - }); - - $('.btnCancel', elem).on('click', function () { - - clearCurrentDisplayingInfo(); cancelInAppPurchase(); + isCancelled = true; - deferred.reject(); - }); - $('.btnSignInSupporter', elem).on('click', function () { + var elem = getInAppPurchaseElement(info, subscriptionOptions); - clearCurrentDisplayingInfo(); + // clone + currentDisplayingProductInfos = subscriptionOptions.slice(0); + currentDisplayingProductInfos.push(info); - Dashboard.alert({ - message: Globalize.translate('MessagePleaseSignInLocalNetwork'), - callback: function () { + currentDisplayingDeferred = deferred; + + $('.btnPurchase', elem).on('click', function () { + + isCancelled = false; + + if (this.getAttribute('data-email') == 'true') { + promptForEmail(this.getAttribute('data-feature')); + } else { + IapManager.beginPurchase(this.getAttribute('data-feature')); + } + }); + + $('.btnRestorePurchase', elem).on('click', function () { + + isCancelled = false; + IapManager.restorePurchase(info.feature); + }); + + $(elem).on('iron-overlay-closed', function () { + + if (isCancelled) { + clearCurrentDisplayingInfo(); cancelInAppPurchase(); - Dashboard.logout(); + + deferred.reject(); + } + }); + }); + } + + function promptForEmail(feature) { + + require(['prompt'], function (prompt) { + + prompt({ + text: Globalize.translate('TextPleaseEnterYourEmailAddressForSubscription'), + title: Globalize.translate('HeaderEmailAddress'), + callback: function(email) { + + if (email) { + IapManager.beginPurchase(this.getAttribute('data-feature'), email); + } } }); }); @@ -235,12 +237,15 @@ function onProductUpdated(e, product) { - var currentInfo = currentDisplayingProductInfo; var deferred = currentDisplayingDeferred; - if (currentInfo && deferred) { - if (product.owned && product.id == currentInfo.id) { + if (deferred && product.owned) { + if (currentDisplayingProductInfos.filter(function (p) { + + return product.id == p.id; + + }).length) { clearCurrentDisplayingInfo(); cancelInAppPurchase(); deferred.resolve(); @@ -305,11 +310,9 @@ var deferred = DeferredBuilder.Deferred(); if (name == 'playback') { - validatePlayback(deferred); + validateFeature(name, deferred); } else if (name == 'livetv') { - validateLiveTV(deferred); - } else if (name == 'manageserver') { - validateServerManagement(deferred); + validateFeature(name, deferred); } else if (name == 'sync') { validateSync(deferred); } else { @@ -324,7 +327,7 @@ Events.on(IapManager, 'productupdated', onProductUpdated); } - if (isAndroid()) { + if ($.browser.android) { requirejs(['cordova/android/iap'], onIapManagerLoaded); } else { requirejs(['cordova/iap'], onIapManagerLoaded); diff --git a/dashboard-ui/scripts/globalize.js b/dashboard-ui/scripts/globalize.js index 36956b6575..e469e5d394 100644 --- a/dashboard-ui/scripts/globalize.js +++ b/dashboard-ui/scripts/globalize.js @@ -26,8 +26,9 @@ } else { var url = getUrl(name, culture); + var requestUrl = url + "?v=" + window.dashboardVersion; - $.getJSON(url).done(function (dictionary) { + $.getJSON(requestUrl).done(function (dictionary) { dictionaries[url] = dictionary; deferred.resolve(); diff --git a/dashboard-ui/scripts/itemdetailpage.js b/dashboard-ui/scripts/itemdetailpage.js index 0e8721254e..55f899492d 100644 --- a/dashboard-ui/scripts/itemdetailpage.js +++ b/dashboard-ui/scripts/itemdetailpage.js @@ -45,6 +45,7 @@ getPromise().done(function (item) { reloadFromItem(page, item); + window.scrollTo(0, 0); }); } diff --git a/dashboard-ui/scripts/librarymenu.js b/dashboard-ui/scripts/librarymenu.js index b7d9e90e08..dd820bbad6 100644 --- a/dashboard-ui/scripts/librarymenu.js +++ b/dashboard-ui/scripts/librarymenu.js @@ -548,13 +548,7 @@ closeMainDrawer(); - requirejs(["scripts/registrationservices"], function () { - - RegistrationServices.validateFeature('manageserver').done(function () { - Dashboard.navigate('dashboard.html'); - - }); - }); + Dashboard.navigate('dashboard.html'); } function getTopParentId() { diff --git a/dashboard-ui/scripts/mysync.js b/dashboard-ui/scripts/mysync.js index b84997a842..930e4efcfa 100644 --- a/dashboard-ui/scripts/mysync.js +++ b/dashboard-ui/scripts/mysync.js @@ -6,8 +6,6 @@ if (LocalSync.isSupported()) { - page.querySelector('.localSyncStatus').classList.remove('hide'); - var status = LocalSync.getSyncStatus(); page.querySelector('.labelSyncStatus').innerHTML = Globalize.translate('LabelLocalSyncStatusValue', status); @@ -20,9 +18,6 @@ page.querySelector('.btnSyncNow').classList.remove('hide'); } - } else { - page.querySelector('.localSyncStatus').classList.add('hide'); - page.querySelector('.syncSpinner').active = false; } }); } @@ -47,7 +42,19 @@ syncNow(page); }); - }).on('pageshow', "#mySyncActivityPage", function () { + require(['localsync'], function () { + + if (LocalSync.isSupported()) { + + page.querySelector('.localSyncStatus').classList.remove('hide'); + + } else { + page.querySelector('.localSyncStatus').classList.add('hide'); + page.querySelector('.syncSpinner').active = false; + } + }); + + }).on('pagebeforeshow', "#mySyncActivityPage", function () { var page = this; diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index ece9ce084b..a310d5e258 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -100,6 +100,12 @@ var Dashboard = { return; } + // Don't bounce if the failure is in a sync service + if (url.indexOf('/sync') != -1) { + Dashboard.hideLoadingMsg(); + return; + } + // Bounce to the login screen, but not if a password entry fails, obviously if (url.indexOf('/password') == -1 && url.indexOf('/authenticate') == -1 && @@ -238,6 +244,8 @@ var Dashboard = { importCss: function (url) { + url += "?v=" + window.dashboardVersion; + if (!Dashboard.importedCss) { Dashboard.importedCss = []; } @@ -2019,12 +2027,20 @@ var AppInfo = {}; urlArgs += new Date().getTime(); } + var paths = { + velocity: "bower_components/velocity/velocity.min" + }; + + if (Dashboard.isRunningInCordova()) { + paths.prompt = "cordova/prompt"; + } else { + paths.prompt = "components/prompt"; + } + requirejs.config({ urlArgs: urlArgs, - paths: { - "velocity": "bower_components/velocity/velocity.min" - } + paths: paths }); // Required since jQuery is loaded before requireJs diff --git a/dashboard-ui/strings/javascript/javascript.json b/dashboard-ui/strings/javascript/javascript.json index 66c80d34f0..20d690e0b4 100644 --- a/dashboard-ui/strings/javascript/javascript.json +++ b/dashboard-ui/strings/javascript/javascript.json @@ -788,11 +788,10 @@ "TabScenes": "Scenes", "HeaderUnlockApp": "Unlock App", "MessageUnlockAppWithPurchase": "Unlock the full features of the app with a small one-time purchase.", - "MessageUnlockAppWithPurchaseOrSupporter": "Unlock the full features of the app with a small one-time purchase, or by signing in with an active Emby Supporter Membership.", - "MessageUnlockAppWithSupporter": "Unlock the full features of the app by signing in with an active Emby Supporter Membership.", - "MessageToValidateSupporter": "If you have an active Emby Supporter Membership, simply sign into the app using your Wifi connection within your home network.", + "MessageUnlockAppWithPurchaseOrSupporter": "Unlock the full features of the app with a small one-time purchase, or with an active Emby Premiere subscription.", + "MessageUnlockAppWithSupporter": "Unlock the full features of the app by signing in with an active Emby Premiere subscription.", + "MessageToValidateSupporter": "If you have an active Emby Premiere subscription, simply sign into the app using your Wifi connection within your home network.", "MessagePaymentServicesUnavailable": "Payment services are currently unavailable. Please try again later.", - "ButtonUnlockWithSupporter": "Sign in with Emby Supporter Membership", "MessagePleaseSignInLocalNetwork": "Before proceeding, please ensure that you're connected to your local network using a Wifi or LAN connection.", "ButtonUnlockWithPurchase": "Unlock with Purchase", "ButtonUnlockPrice": "Unlock {0}", @@ -902,6 +901,9 @@ "HeaderEmbyForAndroidHasMoved": "Emby for Android has moved!", "MessageEmbyForAndroidHasMoved": "Emby for Android has moved to a new home in the app store. Please consider checking out the new app. You may continue to use this app for as long as you wish.", "HeaderNextUp": "Next Up", - "HeaderLatestMovies": "Latest Movies", - "HeaderLatestEpisodes": "Latest Episodes" + "HeaderLatestMovies": "Latest Movies", + "HeaderLatestEpisodes": "Latest Episodes", + "EmbyPremiereMonthlyWithPrice": "Emby Premiere Monthly {0}", + "HeaderEmailAddress": "E-Mail Address", + "TextPleaseEnterYourEmailAddressForSubscription": "Please enter your e-mail address." } diff --git a/dashboard-ui/themes/ios.css b/dashboard-ui/themes/ios.css index b2c0ee89d6..fe7a7c7ce3 100644 --- a/dashboard-ui/themes/ios.css +++ b/dashboard-ui/themes/ios.css @@ -233,3 +233,6 @@ paper-tab { .nowPlayingPage { padding-top: 50px !important; } +.localSyncStatus .labelSyncStatus { + display: none !important; +} \ No newline at end of file diff --git a/dashboard-ui/vulcanize-out.html b/dashboard-ui/vulcanize-out.html index c55ad223c4..dd41df9f4b 100644 --- a/dashboard-ui/vulcanize-out.html +++ b/dashboard-ui/vulcanize-out.html @@ -9835,7 +9835,8 @@ intent. Closing generally implies that the user acknowledged the content on the it will cancel whenever the user taps outside it or presses the escape key. This behavior is configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties. `close()` should be called explicitly by the implementer when the user interacts with a control -in the overlay element. +in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled' +event. Call `preventDefault` on this event to prevent the overlay from closing. ### Positioning @@ -10006,6 +10007,11 @@ context. You should place this element as a child of `` whenever possible. * Cancels the overlay. */ cancel: function() { + var cancelEvent = this.fire('iron-overlay-canceled', undefined, {cancelable: true}); + if (cancelEvent.defaultPrevented) { + return; + } + this.opened = false; this._setCanceled(true); },