diff --git a/dashboard-ui/bower_components/paper-ripple/.bower.json b/dashboard-ui/bower_components/paper-ripple/.bower.json index 93d8b8c843..0cbf50c9ff 100644 --- a/dashboard-ui/bower_components/paper-ripple/.bower.json +++ b/dashboard-ui/bower_components/paper-ripple/.bower.json @@ -26,14 +26,14 @@ "web-component-tester": "*", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" }, - "homepage": "https://github.com/PolymerElements/paper-ripple", + "homepage": "https://github.com/polymerelements/paper-ripple", "_release": "1.0.1", "_resolution": { "type": "version", "tag": "v1.0.1", "commit": "af19d904802437c305390bb03415c11661de3d0a" }, - "_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" } \ No newline at end of file diff --git a/dashboard-ui/css/site.css b/dashboard-ui/css/site.css index 1108f5e6bd..d49530aaeb 100644 --- a/dashboard-ui/css/site.css +++ b/dashboard-ui/css/site.css @@ -429,6 +429,42 @@ h1 a:hover { margin: -10px 0 0 -10px; } +input:not(.paper-input):not(.likePaperText):not([type='checkbox']):not([type='radio']) { + -webkit-appearance: none; + -webkit-font-smoothing: antialiased; + -webkit-rtl-ordering: logical; + -webkit-user-select: text; + box-sizing: border-box; + font-family: inherit; + font-size: inherit; + height: 28.5938px; + letter-spacing: normal; + line-height: 18.2px; + list-style-image: none; + list-style-position: outside; + list-style-type: none; + min-height: 28.6px; + padding-bottom: 5.2px; + padding-left: 5.2px; + padding-right: 5.2px; + padding-top: 5.2px; + text-align: left; + text-indent: 0px; + text-rendering: auto; + text-shadow: none; + text-transform: none; + white-space: nowrap; + word-spacing: 0px; + writing-mode: lr-tb; + -webkit-writing-mode: horizontal-tb; + background: white; + width: 100%; + border: 1px solid #ccc; + color: #000; + margin: 0 0 3px 0; + border-radius: 4px; +} + .smoothScrollX { overflow-x: scroll; -webkit-overflow-scrolling: touch; diff --git a/dashboard-ui/musicgenres.html b/dashboard-ui/musicgenres.html deleted file mode 100644 index 3aba2fbd72..0000000000 --- a/dashboard-ui/musicgenres.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - Emby - - -
-
- ${TabSuggestions} - ${TabAlbums} - ${TabAlbumArtists} - ${TabArtists} - ${TabSongs} - ${TabGenres} -
-
-
-
-
- -
-
-
- -
-
- - - -
- -
- - -
- -
-
-
- - ${HeaderFilters} - - - - - - - - - -
- -
-
-
-
- - diff --git a/dashboard-ui/musicrecommended.html b/dashboard-ui/musicrecommended.html index ed55bbb729..0e67b740d9 100644 --- a/dashboard-ui/musicrecommended.html +++ b/dashboard-ui/musicrecommended.html @@ -4,47 +4,157 @@ Emby -
-
- ${TabSuggestions} - ${TabAlbums} - ${TabAlbumArtists} - ${TabArtists} - ${TabSongs} - ${TabGenres} -
-
-
- -
-

${HeaderLatestMusic}

- -
-
-
- - - - - - +
+
+ + ${TabSuggestions} + ${TabMovies} + ${TabTrailers} + ${TabCollections} + ${TabGenres} + ${TabPeople} + +
+ +
+ +
+ + + +
+
+

${HeaderLatestMusic}

+ +
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+ +
+
+ +
+ ${HeaderFilters} + + + + + + + + + + + + + + + +
+
+ +
+

${HeaderGenres}

+ +
+
+
+ +
+

${HeaderParentalRatings}

+ +
+
+
+ +
+

${HeaderTags}

+ +
+
+
+ +
+

${HeaderYears}

+ +
+
+
+ +
+
+ +
+
diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index 723bf46625..5e18f2f87d 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -50,6 +50,8 @@ // Some 1080- videos are reported as 1912? if (maxAllowedWidth >= 1900) { + options.push({ name: '1080p - 40Mbps', maxHeight: 1080, bitrate: 40000000 }); + options.push({ name: '1080p - 35Mbps', maxHeight: 1080, bitrate: 35000000 }); options.push({ name: '1080p - 30Mbps', maxHeight: 1080, bitrate: 30000000 }); options.push({ name: '1080p - 25Mbps', maxHeight: 1080, bitrate: 25000000 }); options.push({ name: '1080p - 20Mbps', maxHeight: 1080, bitrate: 20000000 }); diff --git a/dashboard-ui/scripts/musicgenres.js b/dashboard-ui/scripts/musicgenres.js index 04746437a5..7bef49e390 100644 --- a/dashboard-ui/scripts/musicgenres.js +++ b/dashboard-ui/scripts/musicgenres.js @@ -1,27 +1,46 @@ (function ($, document) { - var view = LibraryBrowser.getDefaultItemsView('Thumb', 'Thumb'); + var data = {}; + function getPageData() { + var key = getSavedQueryKey(); + var pageData = data[key]; - // The base query options - var query = { + if (!pageData) { + pageData = data[key] = { + query: { + SortBy: "SortName", + SortOrder: "Ascending", + IncludeItemTypes: "Audio,MusicVideo", + Recursive: true, + Fields: "DateCreated,SyncInfo,ItemCounts", + StartIndex: 0, + Limit: LibraryBrowser.getDefaultPageSize() + }, + view: LibraryBrowser.getSavedView(key) || LibraryBrowser.getDefaultItemsView('Thumb', 'Thumb') + }; - SortBy: "SortName", - SortOrder: "Ascending", - IncludeItemTypes: "Audio,MusicVideo", - Recursive: true, - Fields: "DateCreated,SyncInfo,ItemCounts", - StartIndex: 0 - }; + pageData.query.ParentId = LibraryMenu.getTopParentId(); + LibraryBrowser.loadSavedQueryValues(key, pageData.query); + } + return pageData; + } + + function getQuery() { + + return getPageData().query; + } function getSavedQueryKey() { - return 'musicgenres' + (query.ParentId || ''); + return getWindowUrl(); } function reloadItems(page) { Dashboard.showLoadingMsg(); + var query = getQuery(); + ApiClient.getMusicGenres(Dashboard.getCurrentUserId(), query).done(function (result) { // Scroll back up so they can see the results from the beginning @@ -29,17 +48,19 @@ var html = ''; + var view = getPageData().view; + $('.listTopPaging', page).html(LibraryBrowser.getQueryPagingHtml({ startIndex: query.StartIndex, limit: query.Limit, totalRecordCount: result.TotalRecordCount, - viewButton: true, showLimit: false, - addSelectionButton: true + updatePageSizeSetting: false, + addLayoutButton: true, + currentLayout: view + })).trigger('create'); - updateFilterControls(page); - if (view == "Thumb") { html = LibraryBrowser.getPosterViewHtml({ items: result.Items, @@ -80,61 +101,23 @@ reloadItems(page); }); + $('.btnChangeLayout', page).on('layoutchange', function (e, layout) { + getPageData().view = layout; + LibraryBrowser.saveViewSetting(getSavedQueryKey(), layout); + reloadItems(page); + }); + LibraryBrowser.saveQueryValues(getSavedQueryKey(), query); - + Dashboard.hideLoadingMsg(); }); } - function updateFilterControls(page) { + window.MusicPage.renderGenresTab = function (page, tabContent) { - $('#selectPageSize', page).val(query.Limit).selectmenu('refresh'); - } - - $(document).on('pageinit', "#musicGenresPage", function () { - - var page = this; - - $('.chkStandardFilter', this).on('change', function () { - - var filterName = this.getAttribute('data-filter'); - var filters = query.Filters || ""; - - filters = (',' + filters).replace(',' + filterName, '').substring(1); - - if (this.checked) { - filters = filters ? (filters + ',' + filterName) : filterName; - } - - query.StartIndex = 0; - query.Filters = filters; - - reloadItems(page); - }); - - $('#selectPageSize', page).on('change', function () { - query.Limit = parseInt(this.value); - query.StartIndex = 0; - reloadItems(page); - }); - - }).on('pagebeforeshow', "#musicGenresPage", function () { - - query.ParentId = LibraryMenu.getTopParentId(); - - var limit = LibraryBrowser.getDefaultPageSize(); - - // If the default page size has changed, the start index will have to be reset - if (limit != query.Limit) { - query.Limit = limit; - query.StartIndex = 0; + if (LibraryBrowser.needsRefresh(tabContent)) { + reloadItems(tabContent); } - - LibraryBrowser.loadSavedQueryValues(getSavedQueryKey(), query); - - reloadItems(this); - - updateFilterControls(this); - }); + }; })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/scripts/musicrecommended.js b/dashboard-ui/scripts/musicrecommended.js index 161bb3cd3f..46de45cdc2 100644 --- a/dashboard-ui/scripts/musicrecommended.js +++ b/dashboard-ui/scripts/musicrecommended.js @@ -186,25 +186,129 @@ }); } - $(document).on('pagebeforeshow', "#musicRecommendedPage", function () { + function loadSuggestionsTab(page, tabContent) { var parentId = LibraryMenu.getTopParentId(); - var page = this; - - var containers = page.querySelectorAll('.itemsContainer'); + var containers = tabContent.querySelectorAll('.itemsContainer'); if (enableScrollX()) { $(containers).addClass('hiddenScrollX'); } else { $(containers).removeClass('hiddenScrollX'); } - if (LibraryBrowser.needsRefresh(page)) { - loadLatest(page, parentId); - loadPlaylists(page, parentId); - loadRecentlyPlayed(page, parentId); - loadFrequentlyPlayed(page, parentId); + if (LibraryBrowser.needsRefresh(tabContent)) { + console.log('loadSuggestionsTab'); + loadLatest(tabContent, parentId); + loadPlaylists(tabContent, parentId); + loadRecentlyPlayed(tabContent, parentId); + loadFrequentlyPlayed(tabContent, parentId); } + } + + function loadTab(page, index) { + + var tabContent = page.querySelector('.pageTabContent[data-index=\'' + index + '\']'); + var depends = []; + var scope = 'MusicPage'; + var renderMethod = ''; + var initMethod = ''; + + switch (index) { + + case 0: + renderMethod = 'renderSuggestedTab'; + break; + case 1: + depends.push('scripts/movies'); + depends.push('scripts/queryfilters'); + renderMethod = 'renderMoviesTab'; + initMethod = 'initMoviesTab'; + break; + case 2: + depends.push('scripts/movietrailers'); + renderMethod = 'renderTrailerTab'; + initMethod = 'initTrailerTab'; + break; + case 3: + depends.push('scripts/moviecollections'); + renderMethod = 'renderCollectionsTab'; + initMethod = 'initCollectionsTab'; + break; + case 4: + depends.push('scripts/songs'); + renderMethod = 'renderSongsTab'; + initMethod = 'initSongsTab'; + depends.push('scripts/queryfilters'); + break; + case 5: + depends.push('scripts/musicgenres'); + renderMethod = 'renderGenresTab'; + break; + default: + break; + } + + require(depends, function () { + + if (initMethod && !tabContent.initComplete) { + + window[scope][initMethod](page, tabContent); + tabContent.initComplete = true; + } + + window[scope][renderMethod](page, tabContent); + + }); + } + + window.MusicPage = window.MusicPage || {}; + window.MusicPage.renderSuggestedTab = loadSuggestionsTab; + + $(document).on('pageinit', "#musicRecommendedPage", function () { + + var page = this; + + $('.recommendations', page).createCardMenus(); + + var tabs = page.querySelector('paper-tabs'); + var pages = page.querySelector('neon-animated-pages'); + + var baseUrl = 'musicrecommended.html'; + var topParentId = LibraryMenu.getTopParentId(); + if (topParentId) { + baseUrl += '?topParentId=' + topParentId; + } + + LibraryBrowser.configurePaperLibraryTabs(page, tabs, pages, baseUrl); + + $(pages).on('tabchange', function () { + loadTab(page, parseInt(this.selected)); + }); + + }).on('pageshowready', "#musicRecommendedPage", function () { + + var page = this; + + if (!page.getAttribute('data-title')) { + + var parentId = LibraryMenu.getTopParentId(); + + if (parentId) { + + ApiClient.getItem(Dashboard.getCurrentUserId(), parentId).done(function (item) { + + page.setAttribute('data-title', item.Name); + LibraryMenu.setTitle(item.Name); + }); + + + } else { + page.setAttribute('data-title', Globalize.translate('TabMusic')); + LibraryMenu.setTitle(Globalize.translate('TabMusic')); + } + } + }); diff --git a/dashboard-ui/scripts/songs.js b/dashboard-ui/scripts/songs.js index 6094401f93..a9079c3a9c 100644 --- a/dashboard-ui/scripts/songs.js +++ b/dashboard-ui/scripts/songs.js @@ -2,46 +2,47 @@ var defaultSortBy = "Album,SortName"; - // The base query options - var query = { + var data = {}; + function getPageData() { + var key = getSavedQueryKey(); + var pageData = data[key]; - SortBy: defaultSortBy, - SortOrder: "Ascending", - IncludeItemTypes: "Audio", - Recursive: true, - Fields: "AudioInfo,ParentId,SyncInfo", - Limit: 200, - StartIndex: 0, - ImageTypeLimit: 1, - EnableImageTypes: "Primary,Backdrop,Banner,Thumb" - }; + if (!pageData) { + pageData = data[key] = { + query: { + SortBy: defaultSortBy, + SortOrder: "Ascending", + IncludeItemTypes: "Audio", + Recursive: true, + Fields: "AudioInfo,ParentId,SyncInfo", + Limit: 100, + StartIndex: 0, + ImageTypeLimit: 1, + EnableImageTypes: "Primary,Backdrop,Banner,Thumb" + } + }; + + pageData.query.ParentId = LibraryMenu.getTopParentId(); + LibraryBrowser.loadSavedQueryValues(key, pageData.query); + } + return pageData; + } + + function getQuery() { + + return getPageData().query; + } function getSavedQueryKey() { - return 'songs' + (query.ParentId || ''); + return getWindowUrl(); } - function updateFilterControls(page) { - - // Reset form values using the last used query - $('.radioSortBy', page).each(function () { - - this.checked = (query.SortBy || '').toLowerCase() == this.getAttribute('data-sortby').toLowerCase(); - - }).checkboxradio('refresh'); - - $('.radioSortOrder', this).each(function () { - - this.checked = (query.SortOrder || '').toLowerCase() == this.getAttribute('data-sortorder').toLowerCase(); - - }).checkboxradio('refresh'); - $('#selectPageSize', page).val(query.Limit).selectmenu('refresh'); - } - - function reloadItems(page) { + function reloadItems(page, viewPanel) { Dashboard.showLoadingMsg(); + var query = getQuery(); ApiClient.getItems(Dashboard.getCurrentUserId(), query).done(function (result) { // Scroll back up so they can see the results from the beginning @@ -54,13 +55,13 @@ totalRecordCount: result.TotalRecordCount, viewButton: true, showLimit: false, + sortButton: true, + viewPanelClass: 'songsViewPanel', updatePageSizeSetting: false }); page.querySelector('.listTopPaging').innerHTML = pagingHtml; - updateFilterControls(page); - html += LibraryBrowser.getListViewHtml({ items: result.Items, showIndex: true, @@ -76,40 +77,74 @@ $('.btnNextPage', page).on('click', function () { query.StartIndex += query.Limit; - reloadItems(page); + reloadItems(page, viewPanel); }); $('.btnPreviousPage', page).on('click', function () { query.StartIndex -= query.Limit; - reloadItems(page); + reloadItems(page, viewPanel); }); - $('.lnkColumnSort', page).on('click', function () { - - var order = this.getAttribute('data-sortfield'); - - if (query.SortBy == order) { - - if (query.SortOrder == "Descending") { - - query.SortOrder = "Ascending"; - query.SortBy = defaultSortBy; - - } else { - - query.SortOrder = "Descending"; - query.SortBy = order; - } - - } else { - - query.SortOrder = "Ascending"; - query.SortBy = order; - } - - query.StartIndex = 0; - - reloadItems(page); + // On callback make sure to set StartIndex = 0 + $('.btnSort', page).on('click', function () { + LibraryBrowser.showSortMenu({ + items: [{ + name: Globalize.translate('OptionNameSort'), + id: 'SortName' + }, + { + name: Globalize.translate('OptionBudget'), + id: 'Budget,SortName' + }, + { + name: Globalize.translate('OptionImdbRating'), + id: 'CommunityRating,SortName' + }, + { + name: Globalize.translate('OptionCriticRating'), + id: 'CriticRating,SortName' + }, + { + name: Globalize.translate('OptionDateAdded'), + id: 'DateCreated,SortName' + }, + { + name: Globalize.translate('OptionDatePlayed'), + id: 'DatePlayed,SortName' + }, + { + name: Globalize.translate('OptionMetascore'), + id: 'Metascore,SortName' + }, + { + name: Globalize.translate('OptionParentalRating'), + id: 'OfficialRating,SortName' + }, + { + name: Globalize.translate('OptionPlayCount'), + id: 'PlayCount,SortName' + }, + { + name: Globalize.translate('OptionReleaseDate'), + id: 'PremiereDate,SortName' + }, + { + name: Globalize.translate('OptionRevenue'), + id: 'Revenue,SortName' + }, + { + name: Globalize.translate('OptionRuntime'), + id: 'Runtime,SortName' + }, + { + name: Globalize.translate('OptionVideoBitrate'), + id: 'VideoBitRate,SortName' + }], + callback: function () { + reloadItems(page, viewPanel); + }, + query: query + }); }); LibraryBrowser.saveQueryValues(getSavedQueryKey(), query); @@ -119,42 +154,30 @@ } var filtersLoaded; - function reloadFiltersIfNeeded(page) { + function reloadFiltersIfNeeded(page, viewPanel) { if (!filtersLoaded) { filtersLoaded = true; - QueryFilters.loadFilters(page, Dashboard.getCurrentUserId(), query, function () { + var query = getQuery(); + QueryFilters.loadFilters(viewPanel, Dashboard.getCurrentUserId(), query, function () { - reloadItems(page); + reloadItems(page, viewPanel); }); } } - $(document).on('pageinit', "#songsPage", function () { + function initPage(tabContent, viewPanel) { - var page = this; + $(viewPanel).on('panelopen', function () { - $('.viewPanel', page).on('panelopen', function () { - - reloadFiltersIfNeeded(page); + reloadFiltersIfNeeded(tabContent, viewPanel); }); - $('.radioSortBy', this).on('click', function () { - query.SortBy = this.getAttribute('data-sortby'); - query.StartIndex = 0; - reloadItems(page); - }); - - $('.radioSortOrder', this).on('click', function () { - query.SortOrder = this.getAttribute('data-sortorder'); - query.StartIndex = 0; - reloadItems(page); - }); - - $('.chkStandardFilter', this).on('change', function () { + $('.chkStandardFilter', viewPanel).on('change', function () { + var query = getQuery(); var filterName = this.getAttribute('data-filter'); var filters = query.Filters || ""; @@ -167,28 +190,22 @@ query.StartIndex = 0; query.Filters = filters; - reloadItems(page); + reloadItems(tabContent, viewPanel); }); + } - $('#selectPageSize', page).on('change', function () { - query.Limit = parseInt(this.value); - query.StartIndex = 0; - reloadItems(page); - }); + window.MusicPage.initSongsTab = function (page, tabContent) { - }).on('pagebeforeshow', "#songsPage", function () { + var viewPanel = page.querySelector('.songsViewPanel'); + initPage(tabContent, viewPanel); + }; - var page = this; + window.MusicPage.renderSongsTab = function (page, tabContent) { - query.ParentId = LibraryMenu.getTopParentId(); - - LibraryBrowser.loadSavedQueryValues(getSavedQueryKey(), query); - QueryFilters.onPageShow(page, query); - - reloadItems(page); - - updateFilterControls(this); - - }); + if (LibraryBrowser.needsRefresh(tabContent)) { + var viewPanel = page.querySelector('.songsViewPanel'); + reloadItems(tabContent, viewPanel); + } + }; })(jQuery, document); \ No newline at end of file diff --git a/dashboard-ui/songs.html b/dashboard-ui/songs.html deleted file mode 100644 index aa0be07c70..0000000000 --- a/dashboard-ui/songs.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - Emby - - -
- -
-
-
-
-
-
-
- -
-
- - - -
- -
- - -
- -
-
-
- ${HeaderFilters} - - - - - - - - - - - - - - - -
-
- -
-

${HeaderGenres}

- -
-
-
- -
-

${HeaderParentalRatings}

- -
-
-
- -
-

${HeaderTags}

- -
-
-
- -
-

${HeaderYears}

- -
-
-
- -
-
-
- - ${HeaderSortBy} - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - ${HeaderSortOrder} - - - - - - - -
-
-
-
-
- - diff --git a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.popup.js b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.popup.js index e3db5cecc1..0de3b9bd19 100644 --- a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.popup.js +++ b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.popup.js @@ -67,7 +67,7 @@ currentOptions.history = currentOptions.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled; this._on(this.document, { - "vmousedown": "_handleDocumentVmousedown" + "mousedown": "_handleDocumentVmousedown" }); // Define instance variables @@ -104,7 +104,7 @@ ._ui.focusElement = this._ui.container; // Event handlers - this._on(this._ui.screen, { "vclick": "_eatEventAndClose" }); + this._on(this._ui.screen, { "click": "_eatEventAndClose" }); this._on(this.window, { orientationchange: $.proxy(this, "_handleWindowOrientationchange"), resize: $.proxy(this, "_handleWindowResize"), diff --git a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.slider.js b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.slider.js index 47c91a8bf0..95397efe5e 100644 --- a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.slider.js +++ b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jqm.slider.js @@ -135,7 +135,7 @@ }); slider.bind("vmousedown", $.proxy(this._sliderVMouseDown, this)) - .bind("vclick", false); + .bind("click", false); // We have to instantiate a new function object for the unbind to work properly // since the method itself is defined in the prototype (causing it to unbind everything) @@ -158,7 +158,7 @@ "keyup": "_handleKeyup" }); - this.handle.bind("vclick", false); + this.handle.bind("click", false); this._handleFormReset(); diff --git a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js index 83d0a820e5..edc05d59f0 100644 --- a/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js +++ b/dashboard-ui/thirdparty/jquerymobile-1.4.5/jquery.mobile.custom.js @@ -737,7 +737,20 @@ $.ui.plugin = { (function( $, window, undefined ) { - $.extend( $.mobile, { + function parentWithClass(elem, className) { + + while (!elem.classList || !elem.classList.contains(className)) { + elem = elem.parentNode; + + if (!elem) { + return null; + } + } + + return elem; + } + + $.extend($.mobile, { // define the window and the document objects window: $( window ), document: $( document ), @@ -759,7 +772,7 @@ $.ui.plugin = { getClosestBaseUrl: function( ele ) { // Find the closest page and extract out its url. - var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ), + var url = parentWithClass(ele, 'ui-page').getAttribute("data-url"), base = $.mobile.path.documentBase.hrefNoHash; if ( !$.mobile.dynamicBaseEnabled || !url || !$.mobile.path.isPath( url ) ) { @@ -769,13 +782,6 @@ $.ui.plugin = { return $.mobile.path.makeUrlAbsolute( url, base ); }, removeActiveLinkClass: function( forceRemoval ) { - if ( !!$.mobile.activeClickedLink && - ( !$.mobile.activeClickedLink.closest( "." + $.mobile.activePageClass ).length || - forceRemoval ) ) { - - $.mobile.activeClickedLink.removeClass( $.mobile.activeBtnClass ); - } - $.mobile.activeClickedLink = null; }, // DEPRECATED in 1.4 @@ -1529,447 +1535,6 @@ if ( !$.support.boxShadow ) { }( jQuery, this )); - -// This plugin is an experiment for abstracting away the touch and mouse -// events so that developers don't have to worry about which method of input -// the device their document is loaded on supports. -// -// The idea here is to allow the developer to register listeners for the -// basic mouse events, such as mousedown, mousemove, mouseup, and click, -// and the plugin will take care of registering the correct listeners -// behind the scenes to invoke the listener at the fastest possible time -// for that device, while still retaining the order of event firing in -// the traditional mouse environment, should multiple handlers be registered -// on the same element for different events. -// -// The current version exposes the following virtual events to jQuery bind methods: -// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel" - -(function( $, window, document, undefined ) { - -var dataPropertyName = "virtualMouseBindings", - touchTargetPropertyName = "virtualTouchID", - virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ), - touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ), - mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [], - mouseEventProps = $.event.props.concat( mouseHookProps ), - activeDocHandlers = {}, - resetTimerID = 0, - startX = 0, - startY = 0, - didScroll = false, - clickBlockList = [], - blockMouseTriggers = false, - blockTouchTriggers = false, - eventCaptureSupported = false, - $document = $( document ), - nextTouchID = 1, - lastTouchID = 0, threshold, - i; - -$.vmouse = { - moveDistanceThreshold: 10, - clickDistanceThreshold: 10, - resetTimerDuration: 1500 -}; - -function getNativeEvent( event ) { - - while ( event && typeof event.originalEvent !== "undefined" ) { - event = event.originalEvent; - } - return event; -} - -function createVirtualEvent( event, eventType ) { - - var t = event.type, - oe, props, ne, prop, ct, touch, i, j, len; - - event = $.Event( event ); - event.type = eventType; - - oe = event.originalEvent; - props = $.event.props; - - // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280 - // https://github.com/jquery/jquery-mobile/issues/3280 - if ( t.search( /^(mouse|click)/ ) > -1 ) { - props = mouseEventProps; - } - - // copy original event properties over to the new event - // this would happen if we could call $.event.fix instead of $.Event - // but we don't have a way to force an event to be fixed multiple times - if ( oe ) { - for ( i = props.length, prop; i; ) { - prop = props[ --i ]; - event[ prop ] = oe[ prop ]; - } - } - - // make sure that if the mouse and click virtual events are generated - // without a .which one is defined - if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) { - event.which = 1; - } - - if ( t.search(/^touch/) !== -1 ) { - ne = getNativeEvent( oe ); - t = ne.touches; - ct = ne.changedTouches; - touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined ); - - if ( touch ) { - for ( j = 0, len = touchEventProps.length; j < len; j++) { - prop = touchEventProps[ j ]; - event[ prop ] = touch[ prop ]; - } - } - } - - return event; -} - -function getVirtualBindingFlags( element ) { - - var flags = {}, - b, k; - - while ( element ) { - - b = $.data( element, dataPropertyName ); - - for ( k in b ) { - if ( b[ k ] ) { - flags[ k ] = flags.hasVirtualBinding = true; - } - } - element = element.parentNode; - } - return flags; -} - -function getClosestElementWithVirtualBinding( element, eventType ) { - var b; - while ( element ) { - - b = $.data( element, dataPropertyName ); - - if ( b && ( !eventType || b[ eventType ] ) ) { - return element; - } - element = element.parentNode; - } - return null; -} - -function enableTouchBindings() { - blockTouchTriggers = false; -} - -function disableTouchBindings() { - blockTouchTriggers = true; -} - -function enableMouseBindings() { - lastTouchID = 0; - clickBlockList.length = 0; - blockMouseTriggers = false; - - // When mouse bindings are enabled, our - // touch bindings are disabled. - disableTouchBindings(); -} - -function disableMouseBindings() { - // When mouse bindings are disabled, our - // touch bindings are enabled. - enableTouchBindings(); -} - -function startResetTimer() { - clearResetTimer(); - resetTimerID = setTimeout( function() { - resetTimerID = 0; - enableMouseBindings(); - }, $.vmouse.resetTimerDuration ); -} - -function clearResetTimer() { - if ( resetTimerID ) { - clearTimeout( resetTimerID ); - resetTimerID = 0; - } -} - -function triggerVirtualEvent( eventType, event, flags ) { - var ve; - - if ( ( flags && flags[ eventType ] ) || - ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { - - ve = createVirtualEvent( event, eventType ); - - $( event.target).trigger( ve ); - } - - return ve; -} - -function mouseEventCallback( event ) { - var touchID = $.data( event.target, touchTargetPropertyName ), - ve; - - if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) { - ve = triggerVirtualEvent( "v" + event.type, event ); - if ( ve ) { - if ( ve.isDefaultPrevented() ) { - event.preventDefault(); - } - if ( ve.isPropagationStopped() ) { - event.stopPropagation(); - } - if ( ve.isImmediatePropagationStopped() ) { - event.stopImmediatePropagation(); - } - } - } -} - -function handleTouchStart( event ) { - - var touches = getNativeEvent( event ).touches, - target, flags, t; - - if ( touches && touches.length === 1 ) { - - target = event.target; - flags = getVirtualBindingFlags( target ); - - if ( flags.hasVirtualBinding ) { - - lastTouchID = nextTouchID++; - $.data( target, touchTargetPropertyName, lastTouchID ); - - clearResetTimer(); - - disableMouseBindings(); - didScroll = false; - - t = getNativeEvent( event ).touches[ 0 ]; - startX = t.pageX; - startY = t.pageY; - - triggerVirtualEvent( "vmouseover", event, flags ); - triggerVirtualEvent( "vmousedown", event, flags ); - } - } -} - -function handleScroll( event ) { - if ( blockTouchTriggers ) { - return; - } - - if ( !didScroll ) { - triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) ); - } - - didScroll = true; - startResetTimer(); -} - -function handleTouchMove( event ) { - if ( blockTouchTriggers ) { - return; - } - - var t = getNativeEvent( event ).touches[ 0 ], - didCancel = didScroll, - moveThreshold = $.vmouse.moveDistanceThreshold, - flags = getVirtualBindingFlags( event.target ); - - didScroll = didScroll || - ( Math.abs( t.pageX - startX ) > moveThreshold || - Math.abs( t.pageY - startY ) > moveThreshold ); - - if ( didScroll && !didCancel ) { - triggerVirtualEvent( "vmousecancel", event, flags ); - } - - triggerVirtualEvent( "vmousemove", event, flags ); - startResetTimer(); -} - -function handleTouchEnd( event ) { - if ( blockTouchTriggers ) { - return; - } - - disableTouchBindings(); - - var flags = getVirtualBindingFlags( event.target ), - ve, t; - triggerVirtualEvent( "vmouseup", event, flags ); - - if ( !didScroll ) { - ve = triggerVirtualEvent( "vclick", event, flags ); - if ( ve && ve.isDefaultPrevented() ) { - // The target of the mouse events that follow the touchend - // event don't necessarily match the target used during the - // touch. This means we need to rely on coordinates for blocking - // any click that is generated. - t = getNativeEvent( event ).changedTouches[ 0 ]; - clickBlockList.push({ - touchID: lastTouchID, - x: t.clientX, - y: t.clientY - }); - - // Prevent any mouse events that follow from triggering - // virtual event notifications. - blockMouseTriggers = true; - } - } - triggerVirtualEvent( "vmouseout", event, flags); - didScroll = false; - - startResetTimer(); -} - -function hasVirtualBindings( ele ) { - var bindings = $.data( ele, dataPropertyName ), - k; - - if ( bindings ) { - for ( k in bindings ) { - if ( bindings[ k ] ) { - return true; - } - } - } - return false; -} - -function dummyMouseHandler() {} - -function getSpecialEventObject( eventType ) { - var realType = eventType.substr( 1 ); - - return { - setup: function(/* data, namespace */) { - // If this is the first virtual mouse binding for this element, - // add a bindings object to its data. - - if ( !hasVirtualBindings( this ) ) { - $.data( this, dataPropertyName, {} ); - } - - // If setup is called, we know it is the first binding for this - // eventType, so initialize the count for the eventType to zero. - var bindings = $.data( this, dataPropertyName ); - bindings[ eventType ] = true; - - // If this is the first virtual mouse event for this type, - // register a global handler on the document. - - activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1; - - if ( activeDocHandlers[ eventType ] === 1 ) { - $document.bind( realType, mouseEventCallback ); - } - - // Some browsers, like Opera Mini, won't dispatch mouse/click events - // for elements unless they actually have handlers registered on them. - // To get around this, we register dummy handlers on the elements. - - $( this ).bind( realType, dummyMouseHandler ); - - // For now, if event capture is not supported, we rely on mouse handlers. - if ( eventCaptureSupported ) { - // If this is the first virtual mouse binding for the document, - // register our touchstart handler on the document. - - activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1; - - if ( activeDocHandlers[ "touchstart" ] === 1 ) { - $document.bind( "touchstart", handleTouchStart ) - .bind( "touchend", handleTouchEnd ) - - // On touch platforms, touching the screen and then dragging your finger - // causes the window content to scroll after some distance threshold is - // exceeded. On these platforms, a scroll prevents a click event from being - // dispatched, and on some platforms, even the touchend is suppressed. To - // mimic the suppression of the click event, we need to watch for a scroll - // event. Unfortunately, some platforms like iOS don't dispatch scroll - // events until *AFTER* the user lifts their finger (touchend). This means - // we need to watch both scroll and touchmove events to figure out whether - // or not a scroll happenens before the touchend event is fired. - - .bind( "touchmove", handleTouchMove ) - .bind( "scroll", handleScroll ); - } - } - }, - - teardown: function(/* data, namespace */) { - // If this is the last virtual binding for this eventType, - // remove its global handler from the document. - - --activeDocHandlers[ eventType ]; - - if ( !activeDocHandlers[ eventType ] ) { - $document.unbind( realType, mouseEventCallback ); - } - - if ( eventCaptureSupported ) { - // If this is the last virtual mouse binding in existence, - // remove our document touchstart listener. - - --activeDocHandlers[ "touchstart" ]; - - if ( !activeDocHandlers[ "touchstart" ] ) { - $document.unbind( "touchstart", handleTouchStart ) - .unbind( "touchmove", handleTouchMove ) - .unbind( "touchend", handleTouchEnd ) - .unbind( "scroll", handleScroll ); - } - } - - var $this = $( this ), - bindings = $.data( this, dataPropertyName ); - - // teardown may be called when an element was - // removed from the DOM. If this is the case, - // jQuery core may have already stripped the element - // of any data bindings so we need to check it before - // using it. - if ( bindings ) { - bindings[ eventType ] = false; - } - - // Unregister the dummy event handler. - - $this.unbind( realType, dummyMouseHandler ); - - // If this is the last virtual mouse binding on the - // element, remove the binding data from the element. - - if ( !hasVirtualBindings( this ) ) { - $this.removeData( dataPropertyName ); - } - } - }; -} - -// Expose our custom events to the jQuery bind/unbind mechanism. - -for ( i = 0; i < virtualEventNames.length; i++ ) { - $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] ); -} -})( jQuery, window, document ); - (function( $, undefined ) { var props = { "animation": {}, @@ -2981,30 +2546,6 @@ $.widget( "mobile.page", { } }, - bindRemove: function( callback ) { - var page = this.element; - - // when dom caching is not enabled or the page is embedded bind to remove the page on hide - if ( !page.data( "mobile-page" ).options.domCache ) { - - // TODO use _on - that is, sort out why it doesn't work in this case - page.bind( "pagehide.remove", callback || function( e, data ) { - - //check if this is a same page transition and if so don't remove the page - if( !data.samePage ){ - var $this = $( this ), - prEvent = new $.Event( "pageremove" ); - - $this.trigger( prEvent ); - - if ( !prEvent.isDefaultPrevented() ) { - $this.removeWithDependents(); - } - } - }); - } - }, - _setOptions: function (o) { var elem = this.element[0]; @@ -3069,55 +2610,6 @@ $.fn.fieldcontain = function(/* options */) { })( jQuery ); -(function( $, undefined ) { - -$.fn.grid = function( options ) { - return this.each(function() { - - var $this = $( this ), - o = $.extend({ - grid: null - }, options ), - $kids = $this.children(), - gridCols = { solo:1, a:2, b:3, c:4, d:5 }, - grid = o.grid, - iterator, - letter; - - if ( !grid ) { - if ( $kids.length <= 5 ) { - for ( letter in gridCols ) { - if ( gridCols[ letter ] === $kids.length ) { - grid = letter; - } - } - } else { - grid = "a"; - $this.addClass( "ui-grid-duo" ); - } - } - iterator = gridCols[grid]; - - $this.addClass( "ui-grid-" + grid ); - - $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" ); - - if ( iterator > 1 ) { - $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" ); - } - if ( iterator > 2 ) { - $kids.filter( ":nth-child(" + iterator + "n+3)" ).addClass( "ui-block-c" ); - } - if ( iterator > 3 ) { - $kids.filter( ":nth-child(" + iterator + "n+4)" ).addClass( "ui-block-d" ); - } - if ( iterator > 4 ) { - $kids.filter( ":nth-child(" + iterator + "n+5)" ).addClass( "ui-block-e" ); - } - }); -}; -})( jQuery ); - (function( $, undefined ) { var path, $base, dialogHashKey = "&ui-state=dialog"; @@ -4592,9 +4084,6 @@ $.fn.grid = function( options ) { // use the page widget to enhance this._enhance(jPage, settings.role); - - // remove page on hide - jPage.page("bindRemove"); }, _find: function( absUrl ) { @@ -5354,9 +4843,10 @@ $.fn.grid = function( options ) { }, // determine the current base url - _findBaseWithDefault: function() { - var closestBase = ( this.activePage && - $.mobile.getClosestBaseUrl( this.activePage ) ); + _findBaseWithDefault: function () { + + var closestBase = (this.activePage && + $.mobile.getClosestBaseUrl( this.activePage[0] ) ); return closestBase || $.mobile.path.documentBase.hrefNoHash; } }); @@ -5377,41 +4867,20 @@ $.fn.grid = function( options ) { (function( $, undefined ) { // resolved on domready - var domreadyDeferred = $.Deferred(), + var domreadyDeferred = $.Deferred(), - // resolved and nulled on window.load() - loadDeferred = $.Deferred(), + // resolved and nulled on window.load() + loadDeferred = $.Deferred(), - // function that resolves the above deferred - pageIsFullyLoaded = function() { + // function that resolves the above deferred + pageIsFullyLoaded = function() { - // Resolve and null the deferred - loadDeferred.resolve(); - loadDeferred = null; - }, + // Resolve and null the deferred + loadDeferred.resolve(); + loadDeferred = null; + }, - documentUrl = $.mobile.path.documentUrl, - - // used to track last vclicked element to make sure its value is added to form data - $lastVClicked = null; - - /* Event Bindings - hashchange, submit, and click */ - function findClosestLink( ele ) { - while ( ele ) { - // Look for the closest element with a nodeName of "a". - // Note that we are checking if we have a valid nodeName - // before attempting to access it. This is because the - // node we get called with could have originated from within - // an embedded SVG document where some symbol instance elements - // don't have nodeName defined on them, or strings are of type - // SVGAnimatedString. - if ( ( typeof ele.nodeName === "string" ) && ele.nodeName.toLowerCase() === "a" ) { - break; - } - ele = ele.parentNode; - } - return ele; - } + documentUrl = $.mobile.path.documentUrl; $.mobile.loadPage = function( url, opts ) { var container; @@ -5484,172 +4953,20 @@ $.fn.grid = function( options ) { allowSamePageTransition: false }; - $.mobile._registerInternalEvents = function() { - var getAjaxFormData = function( $form, calculateOnly ) { - var url, ret = true, formData, vclickedName, method; - if ( !$.mobile.ajaxEnabled || - // test that the form is, itself, ajax false - $form.is( ":jqmData(ajax='false')" ) || - // test that $.mobile.ignoreContentEnabled is set and - // the form or one of it's parents is ajax=false - !$form.jqmHijackable().length || - $form.attr( "target" ) ) { - return false; - } + function parentWithTag(elem, tagName) { - url = ( $lastVClicked && $lastVClicked.attr( "formaction" ) ) || - $form.attr( "action" ); - method = ( $form.attr( "method" ) || "get" ).toLowerCase(); + while (elem.tagName != tagName) { + elem = elem.parentNode; - // If no action is specified, browsers default to using the - // URL of the document containing the form. Since we dynamically - // pull in pages from external documents, the form should submit - // to the URL for the source document of the page containing - // the form. - if ( !url ) { - // Get the @data-url for the page containing the form. - url = $.mobile.getClosestBaseUrl( $form ); + if (!elem) { + return null; + } + } - // NOTE: If the method is "get", we need to strip off the query string - // because it will get replaced with the new form data. See issue #5710. - if ( method === "get" ) { - url = $.mobile.path.parseUrl( url ).hrefNoSearch; - } + return elem; + } - if ( url === $.mobile.path.documentBase.hrefNoHash ) { - // The url we got back matches the document base, - // which means the page must be an internal/embedded page, - // so default to using the actual document url as a browser - // would. - url = documentUrl.hrefNoSearch; - } - } - - url = $.mobile.path.makeUrlAbsolute( url, $.mobile.getClosestBaseUrl( $form ) ); - - if ( ( $.mobile.path.isExternal( url ) && !$.mobile.path.isPermittedCrossDomainRequest( documentUrl, url ) ) ) { - return false; - } - - if ( !calculateOnly ) { - formData = $form.serializeArray(); - - if ( $lastVClicked && $lastVClicked[ 0 ].form === $form[ 0 ] ) { - vclickedName = $lastVClicked.attr( "name" ); - if ( vclickedName ) { - // Make sure the last clicked element is included in the form - $.each( formData, function( key, value ) { - if ( value.name === vclickedName ) { - // Unset vclickedName - we've found it in the serialized data already - vclickedName = ""; - return false; - } - }); - if ( vclickedName ) { - formData.push( { name: vclickedName, value: $lastVClicked.attr( "value" ) } ); - } - } - } - - ret = { - url: url, - options: { - type: method, - data: $.param( formData ), - transition: $form.jqmData( "transition" ), - reverse: $form.jqmData( "direction" ) === "reverse", - reloadPage: true - } - }; - } - - return ret; - }; - - //bind to form submit events, handle with Ajax - $.mobile.document.delegate( "form", "submit", function( event ) { - var formData; - - if ( !event.isDefaultPrevented() ) { - formData = getAjaxFormData( $( this ) ); - if ( formData ) { - $.mobile.changePage( formData.url, formData.options ); - event.preventDefault(); - } - } - }); - - //add active state on vclick - $.mobile.document.bind( "vclick", function( event ) { - var $btn, btnEls, target = event.target, needClosest = false; - // if this isn't a left click we don't care. Its important to note - // that when the virtual event is generated it will create the which attr - if ( event.which > 1 || !$.mobile.linkBindingEnabled ) { - return; - } - - // Record that this element was clicked, in case we need it for correct - // form submission during the "submit" handler above - $lastVClicked = $( target ); - - // Try to find a target element to which the active class will be applied - if ( $.data( target, "mobile-button" ) ) { - // If the form will not be submitted via AJAX, do not add active class - if ( !getAjaxFormData( $( target ).closest( "form" ), true ) ) { - return; - } - // We will apply the active state to this button widget - the parent - // of the input that was clicked will have the associated data - if ( target.parentNode ) { - target = target.parentNode; - } - } else { - target = findClosestLink( target ); - if ( !( target && $.mobile.path.parseUrl( target.getAttribute( "href" ) || "#" ).hash !== "#" ) ) { - return; - } - - // TODO teach $.mobile.hijackable to operate on raw dom elements so the - // link wrapping can be avoided - if ( !$( target ).jqmHijackable().length ) { - return; - } - } - - // Avoid calling .closest by using the data set during .buttonMarkup() - // List items have the button data in the parent of the element clicked - if ( !!~target.className.indexOf( "ui-link-inherit" ) ) { - if ( target.parentNode ) { - btnEls = $.data( target.parentNode, "buttonElements" ); - } - // Otherwise, look for the data on the target itself - } else { - btnEls = $.data( target, "buttonElements" ); - } - // If found, grab the button's outer element - if ( btnEls ) { - target = btnEls.outer; - } else { - needClosest = true; - } - - $btn = $( target ); - // If the outer element wasn't found by the our heuristics, use .closest() - if ( needClosest ) { - $btn = $btn.closest( ".ui-btn" ); - } - - if ( $btn.length > 0 && - !( $btn.hasClass( "ui-state-disabled" || - - // DEPRECATED as of 1.4.0 - remove after 1.4.0 release - // only ui-state-disabled should be present thereafter - $btn.hasClass( "ui-disabled" ) ) ) ) { - $.mobile.removeActiveLinkClass( true ); - $.mobile.activeClickedLink = $btn; - $.mobile.activeClickedLink.addClass( $.mobile.activeBtnClass ); - } - }); + $.mobile._registerInternalEvents = function () { // click routing - direct to HTTP or Ajax, accordingly $.mobile.document.bind( "click", function( event ) { @@ -5657,48 +4974,32 @@ $.fn.grid = function( options ) { return; } - var link = findClosestLink( event.target ), - $link = $( link ), + var link = parentWithTag(event.target, 'A'); + + var $link = $( link ), - //remove active link class if external (then it won't be there if you come back) - httpCleanup = function() { - window.setTimeout(function() { $.mobile.removeActiveLinkClass( true ); }, 200 ); - }, baseUrl, href, useDefaultUrlHandling, isExternal, transition, reverse, role; - // If a button was clicked, clean up the active class added by vclick above - if ( $.mobile.activeClickedLink && - $.mobile.activeClickedLink[ 0 ] === event.target.parentNode ) { - httpCleanup(); - } - // If there is no link associated with the click or its not a left // click we want to ignore the click // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping // can be avoided - if ( !link || event.which > 1 || !$link.jqmHijackable().length ) { + if ( !link || event.which > 1 ) { return; } //if there's a data-rel=back attr, go back in history - if ( $link.is( "[data-rel='back']" ) ) { + if ( link.getAttribute('data-rel') == 'back' ) { $.mobile.back(); return false; } - baseUrl = $.mobile.getClosestBaseUrl( $link ); + baseUrl = $.mobile.getClosestBaseUrl(link); //get href, if defined, otherwise default to empty hash - href = $.mobile.path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl ); - - //if ajax is disabled, exit early - if ( !$.mobile.ajaxEnabled && !$.mobile.path.isEmbeddedPage( href ) ) { - httpCleanup(); - //use default click handling - return; - } + href = $.mobile.path.makeUrlAbsolute( link.getAttribute( "href" ) || "#", baseUrl ); // XXX_jblas: Ideally links to application pages should be specified as // an url to the application document with a hash that is either @@ -5727,7 +5028,7 @@ $.fn.grid = function( options ) { } // Should we handle this link, or let the browser deal with it? - useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( "[data-ajax='false']" ) || $link.is( "[target]" ); + useDefaultUrlHandling = link.getAttribute("rel") == "external" || link.getAttribute("data-ajax") == "false" || link.getAttribute('target'); // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR // requests if the document doing the request was loaded via the file:// protocol. @@ -5741,8 +5042,7 @@ $.fn.grid = function( options ) { // moved into more comprehensive isExternalLink isExternal = useDefaultUrlHandling || ( $.mobile.path.isExternal( href ) && !$.mobile.path.isPermittedCrossDomainRequest( documentUrl, href ) ); - if ( isExternal ) { - httpCleanup(); + if (isExternal) { //use default click handling return; } @@ -5754,33 +5054,16 @@ $.fn.grid = function( options ) { $link.jqmData( "back" ); //this may need to be more specific as we use data-rel more - role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined; + role = link.getAttribute("data-" + $.mobile.ns + "rel") || undefined; $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role, link: $link } ); event.preventDefault(); }); - //prefetch pages when anchors with data-prefetch are encountered - $.mobile.document.delegate( ".ui-page", "pageshow.prefetch", function() { - var urls = []; - var prefetchLinks = this.querySelectorAll("a[data-prefetch]"); - for (var i = 0, length = prefetchLinks.length; i < length; i++) { - var prefetchLink = prefetchLinks[i]; - var url = prefetchLink.getAttribute("href"); - - if (url && $.inArray(url, urls) === -1) { - urls.push(url); - - $.mobile.loadPage(url, { role: prefetchLink.getAttribute("data-" + $.mobile.ns + "rel"), prefetch: true }); - } - } - }); - // TODO ensure that the navigate binding in the content widget happens at the right time $.mobile.pageContainer.pagecontainer(); - //set page min-heights to be device specific - $.mobile.document.bind( "pageshow", function() { + $.mobile.document.bind( "pageshow", function(e) { // We need to wait for window.load to make sure that styles have already been rendered, // otherwise heights of external toolbars will have the wrong value @@ -5790,6 +5073,39 @@ $.fn.grid = function( options ) { $.mobile.resetActivePageHeight(); } }); + + function removePage(page) { + + $(page).removeWithDependents(); + } + + function cleanPages(currentPage) { + + var pages = document.querySelectorAll("div[data-role='page']"); + if (pages.length < 5) { + return; + } + + for (var i = 0, length = pages.length; i < length; i++) { + var page = pages[i]; + if (page != currentPage) { + removePage(page); + break; + } + } + } + + //set page min-heights to be device specific + $.mobile.document.bind("pagehide", function (e,data) { + + var page = e.target; + + //check if this is a same page transition and if so don't remove the page + if (!data.samePage) { + cleanPages(page); + } + }); + $.mobile.window.bind( "throttledresize", $.mobile.resetActivePageHeight ); };//navreadyDeferred done callback @@ -5961,7 +5277,7 @@ $.fn.grid = function( options ) { // DEPRECATED as of 1.4.0 - remove ui-disabled after 1.4.0 release // only ui-state-disabled should be present thereafter - $.mobile.document.delegate( ".ui-state-disabled,.ui-disabled", "vclick", + $.mobile.document.delegate( ".ui-state-disabled,.ui-disabled", "click", function( e ) { e.preventDefault(); e.stopImmediatePropagation(); @@ -6609,356 +5925,6 @@ $.widget( "mobile.controlgroup", $.extend( { (function( $, undefined ) { -$.widget( "mobile.textinput", { - initSelector: "input[type='text']," + - "input[type='search']," + - "*[data-type='search']," + - "input[type='number']," + - "*[data-type='number']," + - "input[type='password']," + - "input[type='email']," + - "input[type='url']," + - "input[type='tel']," + - "textarea," + - "input[type='time']," + - "input[type='date']," + - "input[type='month']," + - "input[type='week']," + - "input[type='datetime']," + - "input[type='datetime-local']," + - "input[type='color']," + - "input:not([type])," + - "input[type='file']", - - options: { - theme: null, - corners: true, - mini: false, - // This option defaults to true on iOS devices. - preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, - wrapperClass: "", - enhanced: false - }, - - _create: function() { - - var options = this.options, - isSearch = this.element.is( "[type='search'], :jqmData(type='search')" ), - isTextarea = this.element[ 0 ].tagName === "TEXTAREA", - isRange = this.element.is( "[data-" + ( $.mobile.ns || "" ) + "type='range']" ), - inputNeedsWrap = ( (this.element.is( "input" ) || - this.element.is( "[data-" + ( $.mobile.ns || "" ) + "type='search']" ) ) && - !isRange ); - - if ( this.element.prop( "disabled" ) ) { - options.disabled = true; - } - - $.extend( this, { - classes: this._classesFromOptions(), - isSearch: isSearch, - isTextarea: isTextarea, - isRange: isRange, - inputNeedsWrap: inputNeedsWrap - }); - - this._autoCorrect(); - - if ( !options.enhanced ) { - this._enhance(); - } - - this._on( { - "focus": "_handleFocus", - "blur": "_handleBlur" - }); - - }, - - refresh: function() { - this.setOptions({ - "disabled" : this.element.is( ":disabled" ) - }); - }, - - _enhance: function() { - var elementClasses = []; - - if ( this.isTextarea ) { - elementClasses.push( "ui-input-text" ); - } - - if ( this.isTextarea || this.isRange ) { - elementClasses.push( "ui-shadow-inset" ); - } - - //"search" and "text" input widgets - if ( this.inputNeedsWrap ) { - this.element.wrap( this._wrap() ); - } else { - elementClasses = elementClasses.concat( this.classes ); - } - - this.element.addClass( elementClasses.join( " " ) ); - }, - - widget: function() { - return ( this.inputNeedsWrap ) ? this.element.parent() : this.element; - }, - - _classesFromOptions: function() { - var options = this.options, - classes = []; - - classes.push( "ui-body-" + ( ( options.theme === null ) ? "inherit" : options.theme ) ); - if ( options.corners ) { - classes.push( "ui-corner-all" ); - } - if ( options.mini ) { - classes.push( "ui-mini" ); - } - if ( options.disabled ) { - classes.push( "ui-state-disabled" ); - } - if ( options.wrapperClass ) { - classes.push( options.wrapperClass ); - } - - return classes; - }, - - _wrap: function() { - return $( "" ); - }, - - _autoCorrect: function() { - // XXX: Temporary workaround for issue 785 (Apple bug 8910589). - // Turn off autocorrect and autocomplete on non-iOS 5 devices - // since the popup they use can't be dismissed by the user. Note - // that we test for the presence of the feature by looking for - // the autocorrect property on the input element. We currently - // have no test for iOS 5 or newer so we're temporarily using - // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - // - jblas - if ( typeof this.element[0].autocorrect !== "undefined" && - !$.support.touchOverflow ) { - - // Set the attribute instead of the property just in case there - // is code that attempts to make modifications via HTML. - this.element[0].setAttribute( "autocorrect", "off" ); - this.element[0].setAttribute( "autocomplete", "off" ); - } - }, - - _handleBlur: function() { - this.widget().removeClass( $.mobile.focusClass ); - if ( this.options.preventFocusZoom ) { - $.mobile.zoom.enable( true ); - } - }, - - _handleFocus: function() { - // In many situations, iOS will zoom into the input upon tap, this - // prevents that from happening - if ( this.options.preventFocusZoom ) { - $.mobile.zoom.disable( true ); - } - this.widget().addClass( $.mobile.focusClass ); - }, - - _setOptions: function ( options ) { - var outer = this.widget(); - - this._super( options ); - - if ( !( options.disabled === undefined && - options.mini === undefined && - options.corners === undefined && - options.theme === undefined && - options.wrapperClass === undefined ) ) { - - outer.removeClass( this.classes.join( " " ) ); - this.classes = this._classesFromOptions(); - outer.addClass( this.classes.join( " " ) ); - } - - if ( options.disabled !== undefined ) { - this.element.prop( "disabled", !!options.disabled ); - } - }, - - _destroy: function() { - if ( this.options.enhanced ) { - return; - } - if ( this.inputNeedsWrap ) { - this.element.unwrap(); - } - this.element.removeClass( "ui-input-text " + this.classes.join( " " ) ); - } -}); - -})( jQuery ); - -(function( $, undefined ) { - - $.widget( "mobile.textinput", $.mobile.textinput, { - options: { - autogrow:true, - keyupTimeoutBuffer: 100 - }, - - _create: function() { - this._super(); - - if ( this.options.autogrow && this.isTextarea ) { - this._autogrow(); - } - }, - - _autogrow: function() { - this.element.addClass( "ui-textinput-autogrow" ); - - this._on({ - "keyup": "_timeout", - "change": "_timeout", - "input": "_timeout", - "paste": "_timeout" - }); - - // Attach to the various you-have-become-visible notifications that the - // various framework elements emit. - // TODO: Remove all but the updatelayout handler once #6426 is fixed. - this._on( true, this.document, { - - // TODO: Move to non-deprecated event - "pageshow": "_handleShow", - "popupbeforeposition": "_handleShow", - "updatelayout": "_handleShow", - "panelopen": "_handleShow" - }); - }, - - // Synchronously fix the widget height if this widget's parents are such - // that they show/hide content at runtime. We still need to check whether - // the widget is actually visible in case it is contained inside multiple - // such containers. For example: panel contains collapsible contains - // autogrow textinput. The panel may emit "panelopen" indicating that its - // content has become visible, but the collapsible is still collapsed, so - // the autogrow textarea is still not visible. - _handleShow: function( event ) { - if ( $.contains( event.target, this.element[ 0 ] ) && - this.element.is( ":visible" ) ) { - - if ( event.type !== "popupbeforeposition" ) { - this.element - .addClass( "ui-textinput-autogrow-resize" ) - .animationComplete( - $.proxy( function() { - this.element.removeClass( "ui-textinput-autogrow-resize" ); - }, this ), - "transition" ); - } - this._prepareHeightUpdate(); - } - }, - - _unbindAutogrow: function() { - this.element.removeClass( "ui-textinput-autogrow" ); - this._off( this.element, "keyup change input paste" ); - this._off( this.document, - "pageshow popupbeforeposition updatelayout panelopen" ); - }, - - keyupTimeout: null, - - _prepareHeightUpdate: function( delay ) { - if ( this.keyupTimeout ) { - clearTimeout( this.keyupTimeout ); - } - if ( delay === undefined ) { - this._updateHeight(); - } else { - this.keyupTimeout = this._delay( "_updateHeight", delay ); - } - }, - - _timeout: function() { - this._prepareHeightUpdate( this.options.keyupTimeoutBuffer ); - }, - - _updateHeight: function() { - var paddingTop, paddingBottom, paddingHeight, scrollHeight, clientHeight, - borderTop, borderBottom, borderHeight, height, - scrollTop = this.window.scrollTop(); - this.keyupTimeout = 0; - - // IE8 textareas have the onpage property - others do not - if ( !( "onpage" in this.element[ 0 ] ) ) { - this.element.css({ - "height": 0, - "min-height": 0, - "max-height": 0 - }); - } - - scrollHeight = this.element[ 0 ].scrollHeight; - clientHeight = this.element[ 0 ].clientHeight; - borderTop = parseFloat( this.element.css( "border-top-width" ) ); - borderBottom = parseFloat( this.element.css( "border-bottom-width" ) ); - borderHeight = borderTop + borderBottom; - height = scrollHeight + borderHeight + 15; - - // Issue 6179: Padding is not included in scrollHeight and - // clientHeight by Firefox if no scrollbar is visible. Because - // textareas use the border-box box-sizing model, padding should be - // included in the new (assigned) height. Because the height is set - // to 0, clientHeight == 0 in Firefox. Therefore, we can use this to - // check if padding must be added. - if ( clientHeight === 0 ) { - paddingTop = parseFloat( this.element.css( "padding-top" ) ); - paddingBottom = parseFloat( this.element.css( "padding-bottom" ) ); - paddingHeight = paddingTop + paddingBottom; - - height += paddingHeight; - } - - this.element.css({ - "height": height, - "min-height": "", - "max-height": "" - }); - - this.window.scrollTop( scrollTop ); - }, - - refresh: function() { - if ( this.options.autogrow && this.isTextarea ) { - this._updateHeight(); - } - }, - - _setOptions: function( options ) { - - this._super( options ); - - if ( options.autogrow !== undefined && this.isTextarea ) { - if ( options.autogrow ) { - this._autogrow(); - } else { - this._unbindAutogrow(); - } - } - } - - }); -})( jQuery ); - -(function( $, undefined ) { - $.widget( "mobile.button", { initSelector: "input[type='button'], input[type='submit'], input[type='reset']", @@ -7174,13 +6140,13 @@ $.widget( "mobile.checkboxradio", $.extend( { } this._on( label.element, { - vmouseover: "_handleLabelVMouseOver", - vclick: "_handleLabelVClick" + mouseover: "_handleLabelVMouseOver", + click: "_handleLabelVClick" }); this._on( input, { - vmousedown: "_cacheVals", - vclick: "_handleInputVClick", + mousedown: "_cacheVals", + click: "_handleInputVClick", focus: "_handleInputFocus", blur: "_handleInputBlur" }); @@ -7616,7 +6582,7 @@ $.widget( "mobile.selectmenu", $.extend( { this.select .appendTo( self.button ) - .bind( "vmousedown", function() { + .bind( "mousedown", function() { // Add active class to button self.button.addClass( $.mobile.activeBtnClass ); }) @@ -7626,20 +6592,20 @@ $.widget( "mobile.selectmenu", $.extend( { .bind( "blur", function() { self.button.removeClass( $.mobile.focusClass ); }) - .bind( "focus vmouseover", function() { - self.button.trigger( "vmouseover" ); + .bind( "focus mouseover", function() { + self.button.trigger( "mouseover" ); }) - .bind( "vmousemove", function() { + .bind( "mousemove", function() { // Remove active class on scroll/touchmove self.button.removeClass( $.mobile.activeBtnClass ); }) - .bind( "change blur vmouseout", function() { - self.button.trigger( "vmouseout" ) + .bind( "change blur mouseout", function() { + self.button.trigger( "mouseout" ) .removeClass( $.mobile.activeBtnClass ); }); // In many situations, iOS will zoom into the select upon tap, this prevents that from happening - self.button.bind( "vmousedown", function() { + self.button.bind( "mousedown", function() { if ( self.options.preventFocusZoom ) { $.mobile.zoom.disable( true ); }